Skip to content

Commit 9ec5a1d

Browse files
committed
small improvements to typechecking, especially of C tails
1 parent 3b4e471 commit 9ec5a1d

File tree

7 files changed

+63
-41
lines changed

7 files changed

+63
-41
lines changed

interp_Lint.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,16 @@ def interp_stmt(s):
2424
print(interp_exp(arg))
2525
case Expr(value):
2626
interp_exp(value)
27+
case _:
28+
raise Exception('error in interp_stmt, unexpected ' + repr(s))
2729

2830
def interp(p):
2931
match p:
3032
case Module(body):
3133
for s in body:
3234
interp_stmt(s)
35+
case _:
36+
raise Exception('error in interp, unexpected ' + repr(p))
3337

3438
# This version is for InterpLvar to inherit from
3539
class InterpLint:
@@ -77,6 +81,8 @@ def interp(self, p):
7781
match p:
7882
case Module(body):
7983
self.interp_stmts(body, {})
84+
case _:
85+
raise Exception('error in interp, unexpected ' + repr(p))
8086

8187
if __name__ == "__main__":
8288
eight = Constant(8)

type_check_Cfun.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ def type_check_exp(self, e, env):
3131
return super().type_check_exp(e, env)
3232
case Call(Name('len'), [tup]):
3333
return super().type_check_exp(e, env)
34-
case Call(Name('array_len'), [tup]):
34+
case Call(Name('array_len'), _):
35+
return super().type_check_exp(e, env)
36+
case Call(Name('array_store'), _):
37+
return super().type_check_exp(e, env)
38+
case Call(Name('array_load'), _):
3539
return super().type_check_exp(e, env)
3640
case Call(func, args):
3741
func_t = self.type_check_exp(func, env)
@@ -49,6 +53,15 @@ def type_check_exp(self, e, env):
4953
case _:
5054
return super().type_check_exp(e, env)
5155

56+
def type_check_tail(self, s, env):
57+
match s:
58+
case Return(value):
59+
return self.type_check_exp(value, env)
60+
case TailCall(func, args):
61+
return self.type_check_exp(Call(func, args), env)
62+
case _:
63+
super().type_check_tail(s, env)
64+
5265
def type_check_def(self, d, env):
5366
match d:
5467
case FunctionDef(name, params, blocks, dl, returns, comment):
@@ -58,26 +71,24 @@ def type_check_def(self, d, env):
5871
while True:
5972
old_env = copy.deepcopy(new_env)
6073
for (l,ss) in blocks.items():
61-
self.type_check_stmts(ss, new_env)
74+
self.type_check_stmts(ss[:-1], new_env)
75+
if len(ss) > 0: # blocks should only be empty if they are unreachable, but we don't check this
76+
t = self.type_check_tail(ss[-1], new_env)
77+
if t != None: # block ended in Return with this type
78+
self.check_type_equal(returns,t,ss[-1])
6279
# trace('type_check_Cfun iterating ' + repr(new_env))
6380
if new_env == old_env:
6481
break
65-
# todo check return type
82+
# following is too strong, because in some variants unused variables may never get assigned a type
83+
# undefs = [x for x,t in new_env.items() if t == Bottom()]
84+
# if undefs:
85+
# raise Exception('type_check_def: undefined type for ' + str(undefs))
6686
d.var_types = new_env
6787
# trace('type_check_Cfun var_types for ' + name)
6888
# trace(d.var_types)
6989
case _:
7090
raise Exception('type_check_def: unexpected ' + repr(d))
7191

72-
def type_check_stmt(self, s, env):
73-
match s:
74-
case Return(value):
75-
self.type_check_exp(value, env)
76-
case TailCall(func, args):
77-
self.type_check_exp(Call(func, args), env)
78-
case _:
79-
super().type_check_stmt(s, env)
80-
8192
def type_check(self, p):
8293
match p:
8394
case CProgramDefs(defs):

type_check_Cif.py

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ def combine_types(self, t1, t2):
2323
def type_check_atm(self, e, env):
2424
match e:
2525
case Name(id):
26-
return env.get(id, Bottom())
26+
t = env.get(id, Bottom())
27+
env[id] = t # make sure this gets into the environment for later definedness checking
28+
return t
2729
case Constant(value) if isinstance(value, bool):
2830
return BoolType()
2931
case Constant(value) if isinstance(value, int):
@@ -37,13 +39,6 @@ def type_check_exp(self, e, env):
3739
return self.type_check_atm(e, env)
3840
case Constant(value):
3941
return self.type_check_atm(e, env)
40-
case IfExp(test, body, orelse):
41-
test_t = self.type_check_exp(test, env)
42-
self.check_type_equal(BoolType(), test_t, test)
43-
body_t = self.type_check_exp(body, env)
44-
orelse_t = self.type_check_exp(orelse, env)
45-
self.check_type_equal(body_t, orelse_t, e)
46-
return body_t
4742
case BinOp(left, op, right) if isinstance(op, Add) or isinstance(op, Sub):
4843
l = self.type_check_atm(left, env)
4944
self.check_type_equal(l, IntType(), e)
@@ -72,11 +67,6 @@ def type_check_exp(self, e, env):
7267
return BoolType()
7368
case Call(Name('input_int'), []):
7469
return IntType()
75-
# case Let(Name(x), rhs, body):
76-
# t = self.type_check_exp(rhs, env)
77-
# new_env = dict(env)
78-
# new_env[x] = t
79-
# return self.type_check_exp(body, new_env)
8070
case Begin(ss, e):
8171
self.type_check_stmts(ss, env)
8272
return self.type_check_exp(e, env)
@@ -91,40 +81,44 @@ def type_check_stmt(self, s, env):
9181
match s:
9282
case Assign([lhs], value):
9383
t = self.type_check_exp(value, env)
94-
if lhs.id in env:
95-
lhs_ty = env.get(lhs.id, Bottom())
96-
self.check_type_equal(lhs_ty, t, s)
97-
env[lhs.id] = self.combine_types(t, lhs_ty)
98-
else:
99-
env[lhs.id] = t
84+
lhs_ty = env.get(lhs.id, Bottom())
85+
self.check_type_equal(lhs_ty, t, s)
86+
env[lhs.id] = self.combine_types(t, lhs_ty)
10087
case Expr(Call(Name('print'), [arg])):
10188
t = self.type_check_exp(arg, env)
10289
self.check_type_equal(t, IntType(), s)
10390
case Expr(value):
10491
self.type_check_exp(value, env)
105-
case If(Compare(left, [cmp], [right]), body, orelse):
92+
case _:
93+
raise Exception('error in type_check_stmt, unexpected ' + repr(s))
94+
95+
def type_check_tail(self, s, env):
96+
match s:
97+
case If(Compare(left, [cmp], [right]), [Goto(_)], [Goto(_)]):
10698
left_t = self.type_check_atm(left, env)
10799
right_t = self.type_check_atm(right, env)
108100
self.check_type_equal(left_t, right_t, s) # not quite strict enough
109-
self.type_check_stmts(body, env)
110-
self.type_check_stmts(orelse, env)
111101
case Goto(label):
112102
pass
113103
case Return(value):
114104
value_t = self.type_check_exp(value, env)
115105
case _:
116-
raise Exception('error in type_check_stmt, unexpected ' + repr(s))
117-
106+
raise Exception('error in type_check_tail, unexpected' + repr(s))
107+
118108
def type_check(self, p):
119109
match p:
120110
case CProgram(body):
121111
env = {}
122112
while True:
123113
old_env = copy.deepcopy(env)
124114
for (l, ss) in body.items():
125-
self.type_check_stmts(ss, env)
115+
self.type_check_stmts(ss[:-1], env)
116+
self.type_check_tail(ss[-1], env)
126117
if env == old_env:
127118
break
119+
undefs = [x for x,t in env.items() if t == Bottom()]
120+
if undefs:
121+
raise Exception('error: undefined type for ' + str(undefs))
128122
p.var_types = env
129123
case _:
130124
raise Exception('error in type_check, unexpected ' + repr(p))

type_check_Ctup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def type_check_stmt(self, s, env):
5757
case Collect(size):
5858
pass
5959
case Assign([Subscript(tup, Constant(index), Store())], value):
60-
tup_t = self.type_check_atm(tup, env)
60+
tup_t = self.type_check_exp(tup, env)
6161
value_t = self.type_check_atm(value, env)
6262
match tup_t:
6363
case TupleType(ts):

type_check_Lfun.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,10 @@ def type_check_stmts(self, ss, env):
9999
else:
100100
new_params = params
101101
new_returns = returns
102-
for (x,t) in new_params:
102+
unique_names = {x for x,_ in new_params}
103+
if len(unique_names) != len(new_params):
104+
raise Exception('type_check: duplicate parameter name in function ' + repr(name))
105+
for x,t in new_params:
103106
new_env[x] = t
104107
rt = self.type_check_stmts(body, new_env)
105108
self.check_type_equal(new_returns, rt, ss[0])
@@ -121,6 +124,8 @@ def type_check(self, p):
121124
for p in params.args]
122125
else:
123126
params_t = [t for (x,t) in params]
127+
if name in env:
128+
raise Exception('type_check: duplicate function name ' + name)
124129
env[name] = FunctionType(params_t, self.parse_type_annot(returns))
125130
self.type_check_stmts(body, env)
126131
case _:

type_check_Llambda.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def type_check_exp(self, e, env):
1919
case Closure(arity, es):
2020
ts = [self.type_check_exp(e, env) for e in es]
2121
e.has_type = TupleType(ts)
22-
return e.has_type
22+
return e.has_type # this is just wrong: closure values with different shapes must be given compatible types
2323
case Lambda(params, body):
2424
raise Exception('cannot synthesize a type for lambda: ' + str(e))
2525
case AllocateClosure(length, typ, arity):

type_check_Ltup.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def check_type_equal(self, t1, t2, e):
99
match t1:
1010
case TupleType(ts1):
1111
match t2:
12-
case TupleType(ts2):
12+
case TupleType(ts2): # if len(ts1) == len(ts2): -- including this breaks Llambda type checking because of bogus treatment of closure types there
1313
for (ty1, ty2) in zip(ts1,ts2):
1414
self.check_type_equal(ty1, ty2, e)
1515
case _:
@@ -51,6 +51,12 @@ def type_check_exp(self, e, env):
5151
case GlobalValue(name):
5252
return IntType()
5353
case Allocate(length, typ):
54+
match typ:
55+
case TupleType(_):
56+
e.has_type = typ
57+
return typ
58+
case _:
59+
raise Exception('type_check_exp: Allocate expected a tuple, not ' + repr(typ))
5460
return typ
5561
case _:
5662
return super().type_check_exp(e, env)

0 commit comments

Comments
 (0)