Skip to content

Commit ef07ec1

Browse files
committed
Fix wildcard pats
1 parent fcccbcb commit ef07ec1

File tree

8 files changed

+151
-22
lines changed

8 files changed

+151
-22
lines changed

crates/sml-core/src/check.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ impl<'a> Check<'a> {
230230
let mut names = HashSet::new();
231231
for f in fbs {
232232
let n = f[0].name;
233-
let a = f[0].pats.len();
233+
let a = f.iter().map(|fb| fb.pats.len()).max().unwrap_or(1);
234234
for fb in f.iter() {
235235
if n != fb.name {
236236
self.diags.push(Diagnostic::error(
@@ -243,13 +243,17 @@ impl<'a> Check<'a> {
243243
));
244244
}
245245
if a != fb.pats.len() {
246-
self.diags.push(Diagnostic::error(
247-
fb.span,
248-
format!(
249-
"function clause with a different number of args; expected: {}, found {}",
250-
a, fb.pats.len()
251-
)
252-
));
246+
if fb.pats.len() == 1 && fb.pats[0].data == PatKind::Wild {
247+
// okay, need to expand it
248+
} else {
249+
self.diags.push(Diagnostic::error(
250+
fb.span,
251+
format!(
252+
"function clause with a different number of args; expected: {}, found {}",
253+
a, fb.pats.len()
254+
)
255+
));
256+
}
253257
}
254258
}
255259
if !names.insert(n) {

crates/sml-core/src/elaborate.rs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,13 +1187,26 @@ impl<'a> Context<'a> {
11871187
}
11881188
_ => {
11891189
// Rule 34
1190-
// let tvar = self.arena.types.fresh_type_var(self.tyvar_rank);
1190+
let mut name = *sym;
1191+
if bindings
1192+
.iter()
1193+
.map(|(s, _)| s)
1194+
.find(|s| s == &sym)
1195+
.is_some()
1196+
{
1197+
name = self.fresh_var();
1198+
self.elab_errors.push(ElabError::new(
1199+
pat.span,
1200+
"duplicate variable in pattern, emitting bogus value",
1201+
));
1202+
}
11911203
let ty = self.fresh_tyvar();
11921204
if bind {
1193-
self.define_value(*sym, Scheme::Mono(ty), IdStatus::Var);
1205+
self.define_value(name, Scheme::Mono(ty), IdStatus::Var);
11941206
}
1195-
bindings.push((*sym, ty));
1196-
Pat::new(self.arena.pats.alloc(PatKind::Var(*sym)), ty, pat.span)
1207+
1208+
bindings.push((name, ty));
1209+
Pat::new(self.arena.pats.alloc(PatKind::Var(name)), ty, pat.span)
11971210
}
11981211
},
11991212
Wild => Pat::new(self.arena.pats.wild(), self.fresh_tyvar(), pat.span),
@@ -1399,6 +1412,7 @@ impl<'a> Context<'a> {
13991412

14001413
let mut pats = Vec::new();
14011414
let mut bindings = Vec::new();
1415+
14021416
for (idx, pat) in clause.pats.iter().enumerate() {
14031417
let pat = self.elaborate_pat_inner(pat, false, &mut bindings);
14041418
self.unify(arg_tys[idx], pat.ty, &|c| {
@@ -1407,6 +1421,16 @@ impl<'a> Context<'a> {
14071421
});
14081422
pats.push(pat);
14091423
}
1424+
if pats.len() < arity {
1425+
for idx in pats.len()..arity {
1426+
let wild = Pat::new(self.arena.pats.wild(), self.fresh_tyvar(), clause.span);
1427+
self.unify(arg_tys[idx], wild.ty, &|c| {
1428+
c.span(wild.span)
1429+
.message("function clause with argument of different type")
1430+
});
1431+
pats.push(wild);
1432+
}
1433+
}
14101434
clauses.push(PartialFnBinding {
14111435
expr: &clause.expr,
14121436
pats,
@@ -1536,9 +1560,9 @@ impl<'a> Context<'a> {
15361560
// Check to make sure all of the function clauses are consistent within each
15371561
// binding group
15381562
for f in fbs {
1539-
let n = f[0].name;
1540-
let a = f[0].pats.len();
1541-
let fns = ctx.elab_decl_fnbind_ty(n, a, f);
1563+
let name = f[0].name;
1564+
let arity = f.iter().map(|fb| fb.pats.len()).max().unwrap_or(1);
1565+
let fns = ctx.elab_decl_fnbind_ty(name, arity, f);
15421566

15431567
ctx.define_value(fns.name, Scheme::Mono(fns.ty), IdStatus::Var);
15441568
info.push(fns);

crates/sml-core/src/match_compile.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -368,15 +368,10 @@ impl<'a, 'ctx> Matrix<'a, 'ctx> {
368368
}
369369
let mut set = set.into_iter().collect::<Vec<_>>();
370370
set.sort_by(|a, b| a.0.name.cmp(&b.0.name));
371-
// We only use `true` and `false` or `cons` and `nil`, so we know
372-
// there are only 2 constructors in each datatype. Otherwise we
373-
// would need to query a context to determine this
371+
374372
let exhaustive = set.len() == type_arity as usize;
375373
let mut rules = Vec::new();
376374
for (con, arg_ty) in set {
377-
// In a real system, we would have some context to pull the number
378-
// of data constructors for a datatype, and the arity of each
379-
// data constructor. We just mock it instead
380375
let fresh = self.ctx.fresh_var();
381376
let mut f = facts.clone();
382377
let expr = self.specialize(&mut f, con, arg_ty.map(|ty| (fresh, ty)));
@@ -442,6 +437,8 @@ impl<'a, 'ctx> Matrix<'a, 'ctx> {
442437
_ => continue,
443438
}
444439
}
440+
let mut set = set.into_iter().collect::<Vec<_>>();
441+
set.sort_by(|a, b| a.cmp(&b));
445442

446443
let mut rules = Vec::new();
447444
for con in set {

crates/sml-frontend/src/ast.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use sml_util::interner::Symbol;
22
use sml_util::span::{Span, Spanned};
33

4-
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash)]
4+
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)]
55
pub enum Const {
66
Unit,
77
Int(usize),

tests/elaboration/duplicate.sml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
(* rebinding of builtin data constructors is prohibited
2+
3+
-- expected stdout:
4+
-- 0 warnings, 1 errors
5+
6+
-- expected stderr:
7+
-- Error
8+
-- 12,11 duplicate variable in pattern, emitting bogus value
9+
10+
*)
11+
12+
fun dup x x = x

tests/elaboration/rebind.sml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
(* rebinding of builtin data constructors is prohibited
2+
3+
-- expected stdout:
4+
-- 2 warnings, 2 errors
5+
6+
-- expected stderr:
7+
-- Warn
8+
-- 21,20 rebound data constructor: ::
9+
--
10+
-- Warn
11+
-- 21,41 rebound data constructor: nil
12+
--
13+
-- Error
14+
-- 21,20 builtin data constructor '::' cannot be rebound
15+
--
16+
-- Error
17+
-- 21,41 builtin data constructor 'nil' cannot be rebound
18+
--
19+
*)
20+
21+
datatype 'a list = :: of 'a * 'a list | nil;

tests/match_compile/wildcard.sml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
(* wildcard pats in multi-arity functions
2+
3+
-- args: --vv
4+
-- expected stdout:
5+
-- val m: int -> int -> int = fn a => fn b =>
6+
-- let
7+
-- val e: int -> int = fn d =>
8+
-- let
9+
-- val x: int = d
10+
-- in
11+
-- x
12+
-- end
13+
-- val g: int -> int = fn f =>
14+
-- let
15+
-- val y: int = f
16+
-- in
17+
-- y
18+
-- end
19+
-- val i: unit -> int = fn h => 0
20+
-- in
21+
--
22+
-- case a
23+
-- of 0 =>
24+
-- case b
25+
-- of 0 => e a
26+
-- | _ => g b
27+
-- | _ =>
28+
-- case b
29+
-- of 0 => e a
30+
-- | _ => i ()
31+
-- end
32+
--
33+
34+
*)
35+
36+
fun m x 0 = x
37+
| m 0 y = y
38+
| m _ = 0

tests/typecheck/value_res.sml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
(* value restriction. example from MLton
2+
3+
-- expected stdout:
4+
-- 0 warnings, 1 errors
5+
6+
-- expected stderr:
7+
-- Error
8+
-- 33,9 Type unification: can't unify function with argument types. Type constructors differ: int, string
9+
*)
10+
11+
datatype 'a option = Some of 'a | None
12+
val 'a := = primitive "Assign" : 'a ref * 'a -> unit
13+
val 'a ! = primitive "Deref" : 'a ref -> 'a
14+
15+
infix 3 :=
16+
17+
val f: 'a -> 'a =
18+
let
19+
val r: 'a option ref = ref None
20+
in
21+
fn x =>
22+
let
23+
val y = !r
24+
val () = r := (Some x)
25+
in
26+
case y of
27+
None => x
28+
| Some y => y
29+
end
30+
end
31+
end
32+
val _ = f 13
33+
val _ = f "foo"

0 commit comments

Comments
 (0)