Skip to content

Commit 195aa12

Browse files
committed
Fix/implement or-pattern type checking
Fixes #204.
1 parent 55bf6ba commit 195aa12

File tree

3 files changed

+79
-2
lines changed

3 files changed

+79
-2
lines changed

src/type_checker/pat.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,55 @@ pub(super) fn check_pat(tc_state: &mut TcFunState, pat: &mut ast::L<ast::Pat>, l
220220
ast::Pat::Char(_) => Ty::char(),
221221

222222
ast::Pat::Or(pat1, pat2) => {
223+
// Collect binders for `pat1` and `pat2` in separate envs.
224+
tc_state.env.enter();
223225
let pat1_ty = check_pat(tc_state, pat1, level);
226+
let pat1_binders = tc_state.env.exit();
227+
228+
tc_state.env.enter();
224229
let pat2_ty = check_pat(tc_state, pat2, level);
225-
// TODO: Check that the patterns bind the same variables of same types.
226-
// TODO: Any other checks here?
230+
let pat2_binders = tc_state.env.exit();
231+
232+
// Check that patterns bind the same variables.
233+
let pat1_binder_keys: Set<&Id> = pat1_binders.keys().collect();
234+
let pat2_binder_keys: Set<&Id> = pat2_binders.keys().collect();
235+
236+
if pat1_binder_keys != pat2_binder_keys {
237+
let mut left_vars: Vec<Id> =
238+
pat1_binder_keys.iter().map(|id| (*id).clone()).collect();
239+
left_vars.sort();
240+
let mut right_vars: Vec<Id> =
241+
pat2_binder_keys.iter().map(|id| (*id).clone()).collect();
242+
right_vars.sort();
243+
panic!(
244+
"{}: Or pattern alternatives bind different set of variables:
245+
Left = {}
246+
Right = {}",
247+
loc_display(&pat.loc),
248+
left_vars.join(", "),
249+
right_vars.join(", "),
250+
)
251+
}
252+
253+
// Unify pattern binders to make sure they bind the values with same types.
254+
for binder in pat1_binder_keys {
255+
let ty1 = pat1_binders.get(binder).unwrap();
256+
let ty2 = pat2_binders.get(binder).unwrap();
257+
unify(
258+
ty1,
259+
ty2,
260+
tc_state.tys.tys.cons(),
261+
tc_state.var_gen,
262+
level,
263+
&pat.loc,
264+
);
265+
}
266+
267+
// Add bound variables back to the env.
268+
for (k, v) in pat1_binders {
269+
tc_state.env.insert(k, v);
270+
}
271+
227272
unify(
228273
&pat1_ty,
229274
&pat2_ty,
@@ -232,6 +277,7 @@ pub(super) fn check_pat(tc_state: &mut TcFunState, pat: &mut ast::L<ast::Pat>, l
232277
level,
233278
&pat.loc,
234279
);
280+
235281
pat1_ty
236282
}
237283
}

tests/OrPatFail1.fir

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
type Test:
2+
T1(Str)
3+
T2(U32)
4+
5+
main():
6+
match f():
7+
Test.T1(a) | Test.T2(a): print(a)
8+
9+
f() Test:
10+
Test.T1("hi")
11+
12+
# args: --no-backtrace
13+
# expected exit status: 101
14+
# expected stderr: tests/OrPatFail1.fir:7:9: Unable to unify types Str and U32

tests/OrPatFail2.fir

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
type Foo:
2+
A(U32, U32)
3+
B(U32)
4+
5+
main():
6+
match f():
7+
Foo.A(x, y) | Foo.B(x): print(x)
8+
9+
f() Foo:
10+
Foo.B(123)
11+
12+
# args: --no-backtrace
13+
# expected exit status: 101
14+
# expected stderr:
15+
# tests/OrPatFail2.fir:7:9: Or pattern alternatives bind different set of variables:
16+
# Left = x, y
17+
# Right = x

0 commit comments

Comments
 (0)