Skip to content

Commit 1b65ccb

Browse files
committed
Case optimizaton for value bindings
1 parent 29d7836 commit 1b65ccb

File tree

5 files changed

+104
-24
lines changed

5 files changed

+104
-24
lines changed

crates/sml-core/src/elaborate.rs

+27-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
};
1010
use sml_frontend::ast;
1111
use sml_frontend::parser::precedence::{self, Fixity, Precedence, Query};
12-
use sml_util::diagnostics::Diagnostic;
12+
use sml_util::diagnostics::{Diagnostic, Level};
1313
use sml_util::interner::{Interner, Symbol};
1414
use sml_util::pretty_print::PrettyPrinter;
1515
use sml_util::span::Span;
@@ -201,7 +201,7 @@ impl<'a> Context<'a> {
201201
}
202202

203203
#[inline]
204-
fn scope_depth(&self) -> usize {
204+
pub fn scope_depth(&self) -> usize {
205205
self.namespaces[self.current].depth
206206
}
207207

@@ -370,6 +370,7 @@ pub struct ElabError {
370370
sp: Span,
371371
message: String,
372372
kind: ErrorKind,
373+
level: Level,
373374
}
374375

375376
pub enum ErrorKind {
@@ -389,6 +390,7 @@ impl ElabError {
389390
sp,
390391
message: msg.into(),
391392
kind: ErrorKind::Message,
393+
level: Level::Error,
392394
}
393395
}
394396

@@ -397,6 +399,11 @@ impl ElabError {
397399
self
398400
}
399401

402+
pub fn level(mut self, level: Level) -> Self {
403+
self.level = level;
404+
self
405+
}
406+
400407
fn convert_err(self, pp: &mut PrettyPrinter<'_>) -> Option<Diagnostic> {
401408
let mut buffer = String::new();
402409
match self.kind {
@@ -428,7 +435,11 @@ impl ElabError {
428435
buffer = self.message;
429436
}
430437
}
431-
Some(Diagnostic::error(self.sp, buffer))
438+
match self.level {
439+
Level::Warn => Some(Diagnostic::warn(self.sp, buffer)),
440+
Level::Error => Some(Diagnostic::error(self.sp, buffer)),
441+
Level::Bug => Some(Diagnostic::bug(self.sp, buffer)),
442+
}
432443
}
433444
}
434445

@@ -1705,18 +1716,28 @@ impl<'a> Context<'a> {
17051716
});
17061717

17071718
let dontgeneralize = !expr.non_expansive() || pat.flexible();
1708-
for (var, tv) in bindings {
1719+
for (var, tv) in &bindings {
17091720
let sch = match dontgeneralize {
17101721
false => ctx.generalize(tv),
17111722
true => {
17121723
// ctx.elab_errors.push(ElabError::new(pat.span, "type variables not generalized!").kind(ErrorKind::Generalize));
17131724
Scheme::Mono(tv)
17141725
}
17151726
};
1716-
ctx.define_value(var, pat.span, sch, IdStatus::Var);
1727+
ctx.define_value(*var, pat.span, sch, IdStatus::Var);
17171728
}
17181729

1719-
elab.push(Decl::Val(Rule { pat, expr }));
1730+
match pat.kind {
1731+
PatKind::Var(_) | PatKind::Wild => {
1732+
elab.push(Decl::Val(Rule { pat, expr }));
1733+
}
1734+
_ => {
1735+
// If we have some kind of compound binding, go ahead and
1736+
// do a source->source rewrite
1737+
let rule = crate::match_compile::val(ctx, expr, pat, &bindings);
1738+
elab.push(Decl::Val(rule));
1739+
}
1740+
}
17201741
})
17211742
}
17221743

crates/sml-core/src/match_compile.rs

+32-17
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::builtin::constructors::{C_BIND, C_MATCH};
1717
use crate::elaborate::{Context, ElabError, ErrorKind};
1818
use crate::types::{Constructor, Type};
1919
use crate::{Decl, Expr, ExprKind, Lambda, Pat, PatKind, Row, Rule, SortedRecord};
20+
use sml_util::diagnostics::Level;
2021
use sml_util::interner::Symbol;
2122
use sml_util::span::Span;
2223
use sml_util::Const;
@@ -117,12 +118,7 @@ pub fn val<'a>(
117118
let test = ctx.fresh_var();
118119

119120
let (ret_ty, rpat, rexpr) = match bindings.len() {
120-
0 => {
121-
return Rule {
122-
pat,
123-
expr: scrutinee,
124-
}
125-
}
121+
0 => (pat.ty, pat, scrutinee),
126122
1 => {
127123
let (sym, ty) = bindings[0];
128124
let p = Pat::new(ctx.arena.pats.alloc(PatKind::Var(sym)), ty, Span::dummy());
@@ -134,6 +130,8 @@ pub fn val<'a>(
134130
(ty, p, e)
135131
}
136132
_ => {
133+
// This is incredibly ugly, maybe clean it up at cost of performance?
134+
137135
let mut vt = Vec::new();
138136
let mut vp = Vec::new();
139137
let mut ve = Vec::new();
@@ -176,8 +174,6 @@ pub fn val<'a>(
176174
}
177175
};
178176

179-
// let expr = crate::match_compile::case(ctx, expr, rw_ty, vec![Rule { pat, expr: rw_expr}], pat.span + expr.span);
180-
181177
let pats = vec![vec![pat]];
182178
let mut diags = MatchDiags::with_capacity(span, 1, C_BIND);
183179
let (mut decls, rules) = preflight(ctx, vec![Rule { pat, expr: rexpr }], &mut diags);
@@ -367,10 +363,25 @@ impl MatchDiags {
367363
}
368364
}
369365
if self.inexhaustive {
370-
ctx.elab_errors.push(
371-
ElabError::new(self.span, "inexhaustive `case` expression")
372-
.kind(ErrorKind::Redundant),
373-
);
366+
match self.constr {
367+
C_BIND => {
368+
let level = match ctx.scope_depth() {
369+
0 => Level::Warn,
370+
_ => Level::Error,
371+
};
372+
ctx.elab_errors.push(
373+
ElabError::new(self.span, "inexhaustive `val` binding")
374+
.kind(ErrorKind::Inexhaustive)
375+
.level(level),
376+
);
377+
}
378+
_ => {
379+
ctx.elab_errors.push(
380+
ElabError::new(self.span, "inexhaustive `case` expression")
381+
.kind(ErrorKind::Inexhaustive),
382+
);
383+
}
384+
}
374385
}
375386
}
376387
}
@@ -609,7 +620,7 @@ impl<'a, 'ctx> Matrix<'a, 'ctx> {
609620
set.sort_by(|a, b| a.cmp(&b));
610621

611622
let mut rules = Vec::new();
612-
for con in set {
623+
for &con in &set {
613624
// Clone facts so we don't polute other branches with identical bound
614625
let mut f = facts.clone();
615626
let expr = self.specialize_const(&mut f, diags, con);
@@ -622,9 +633,13 @@ impl<'a, 'ctx> Matrix<'a, 'ctx> {
622633
rules.push(Rule { pat, expr });
623634
}
624635

625-
let pat = self.mk_wild(self.pats[0][0].ty);
626-
let expr = self.default_matrix(facts, diags);
627-
rules.push(Rule { pat, expr });
636+
if set.len() == 1 && set[0] == &Const::Unit {
637+
// Unit is exhaustive
638+
} else {
639+
let pat = self.mk_wild(self.pats[0][0].ty);
640+
let expr = self.default_matrix(facts, diags);
641+
rules.push(Rule { pat, expr });
642+
}
628643

629644
Expr::new(
630645
self.ctx.arena.exprs.alloc(ExprKind::Case(
@@ -658,7 +673,7 @@ impl<'a, 'ctx> Matrix<'a, 'ctx> {
658673
self.ctx
659674
.arena
660675
.exprs
661-
.alloc(ExprKind::Con(crate::builtin::constructors::C_MATCH, vec![])),
676+
.alloc(ExprKind::Con(diags.constr, vec![])),
662677
self.ret_ty,
663678
Span::zero(),
664679
);

tests/match_compile/val_bind.sml

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
(* give warning on top-level non-exhaustive value binding
2+
3+
-- args: --vv
4+
-- expected stdout:
5+
-- val (x, y): int * int list =
6+
-- let
7+
-- val c: int * int list -> int * int list = fn b =>
8+
-- let
9+
-- val (x, y): int * int list = b
10+
-- in
11+
-- (x, y)
12+
-- end
13+
-- val a: int list = [1, 2, 3]
14+
-- in
15+
--
16+
-- case a
17+
-- of :: d =>
18+
-- let
19+
-- val (e, f): int * int list = d
20+
-- in
21+
-- c (e, f)
22+
-- end
23+
-- | _ => raise Bind
24+
-- end
25+
-- 1 warnings, 0 errors
26+
27+
-- expected stderr:
28+
-- Warn
29+
-- 34 |
30+
-- 35 | val x::y = [1,2,3]
31+
-- ^~~~~~~~~~~~~^ inexhaustive `val` binding
32+
33+
*)
34+
35+
val x::y = [1,2,3]

tests/match_compile/val_bind2.sml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
(* check that elaboration of variable patterns does not become complicated
2+
3+
-- args: --vv
4+
-- expected stdout:
5+
-- val x: int = 1
6+
7+
*)
8+
9+
val x = 1

tests/typecheck/flex_pass.sml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
-- args: --v
44
-- expected stdout:
55
-- val record: {x: int, y: bool, c: string, d: int ref}
6-
-- val {x= x, y= y}: {x: int, y: bool, c: string, d: int ref}
6+
-- val (x, y): int * bool
77
88
*)
99

0 commit comments

Comments
 (0)