mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Re-constrain closures
This commit is contained in:
parent
593811d27f
commit
c7dd1978fb
7 changed files with 340 additions and 700 deletions
|
@ -12,13 +12,12 @@ pub enum Expr {
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Str(Box<str>),
|
Str(Box<str>),
|
||||||
List(Variable, Vec<Located<Expr>>),
|
List(Variable, Vec<Located<Expr>>),
|
||||||
EmptyList,
|
|
||||||
|
|
||||||
// Lookups
|
// Lookups
|
||||||
Var(Variable, Symbol),
|
Var(Variable, Symbol),
|
||||||
/// Works the same as Var, but has an important marking purpose.
|
/// Works the same as Var, but has an important marking purpose.
|
||||||
/// See 13623e3f5f65ea2d703cf155f16650c1e8246502 for the bug this fixed.
|
/// See 13623e3f5f65ea2d703cf155f16650c1e8246502 for the bug this fixed.
|
||||||
FunctionPointer(Variable, Symbol),
|
FunctionPointer(Symbol),
|
||||||
|
|
||||||
// Pattern Matching
|
// Pattern Matching
|
||||||
Case(
|
Case(
|
||||||
|
@ -26,7 +25,7 @@ pub enum Expr {
|
||||||
Box<Located<Expr>>,
|
Box<Located<Expr>>,
|
||||||
Vec<(Located<Pattern>, Located<Expr>)>,
|
Vec<(Located<Pattern>, Located<Expr>)>,
|
||||||
),
|
),
|
||||||
Define(
|
Defs(
|
||||||
Variable,
|
Variable,
|
||||||
Vec<(Located<Pattern>, Located<Expr>)>,
|
Vec<(Located<Pattern>, Located<Expr>)>,
|
||||||
Box<Located<Expr>>,
|
Box<Located<Expr>>,
|
||||||
|
|
526
src/can/mod.rs
526
src/can/mod.rs
|
@ -129,13 +129,14 @@ fn canonicalize_expr(
|
||||||
}
|
}
|
||||||
ast::Expr::List(loc_elems) => {
|
ast::Expr::List(loc_elems) => {
|
||||||
if loc_elems.is_empty() {
|
if loc_elems.is_empty() {
|
||||||
|
let list_var = subs.mk_flex_var();
|
||||||
let constraint = Eq(
|
let constraint = Eq(
|
||||||
constrain::empty_list_type(subs.mk_flex_var()),
|
constrain::empty_list_type(list_var),
|
||||||
expected,
|
expected,
|
||||||
region.clone(),
|
region.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
(EmptyList, Output::new(constraint))
|
(List(list_var, Vec::new()), Output::new(constraint))
|
||||||
} else {
|
} else {
|
||||||
let mut can_elems = Vec::with_capacity(loc_elems.len());
|
let mut can_elems = Vec::with_capacity(loc_elems.len());
|
||||||
let list_var = subs.mk_flex_var(); // `v` in the type (List v)
|
let list_var = subs.mk_flex_var(); // `v` in the type (List v)
|
||||||
|
@ -182,7 +183,7 @@ fn canonicalize_expr(
|
||||||
// A list literal is never a tail call!
|
// A list literal is never a tail call!
|
||||||
output.tail_call = None;
|
output.tail_call = None;
|
||||||
|
|
||||||
(List(subs.mk_flex_var(), can_elems), output)
|
(List(list_var, can_elems), output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,52 +215,101 @@ fn canonicalize_expr(
|
||||||
// (expr, output)
|
// (expr, output)
|
||||||
//}
|
//}
|
||||||
ast::Expr::Apply((loc_fn, loc_args)) => {
|
ast::Expr::Apply((loc_fn, loc_args)) => {
|
||||||
panic!("TODO canonicalize Apply");
|
// The expression that evaluates to the function being called, e.g. `foo` in
|
||||||
// let expected_type = panic!("TODO expected type for Apply");
|
// (foo) bar baz
|
||||||
// // Canonicalize the function expression and its arguments
|
let fn_var = subs.mk_flex_var();
|
||||||
// let (fn_expr, mut output) = canonicalize_expr(
|
let fn_type = Variable(fn_var);
|
||||||
// env,
|
let fn_region = loc_fn.region.clone();
|
||||||
// subs,
|
let fn_expected = NoExpectation(fn_type.clone());
|
||||||
// scope,
|
// TODO look up the name and use NamedFnArg if possible.
|
||||||
// loc_fn.region.clone(),
|
let fn_reason = Reason::AnonymousFnCall(loc_args.len() as u8);
|
||||||
// &loc_fn.value,
|
|
||||||
// expected_type,
|
|
||||||
// );
|
|
||||||
// let mut args = Vec::new();
|
|
||||||
// let mut outputs = Vec::new();
|
|
||||||
|
|
||||||
// for loc_arg in loc_args.iter() {
|
// Canonicalize the function expression and its arguments
|
||||||
// let expected_type = panic!("TODO expected type for Apply arg");
|
let (fn_expr, mut output) = canonicalize_expr(
|
||||||
// let (arg_expr, arg_out) = canonicalize_expr(
|
env,
|
||||||
// env,
|
subs,
|
||||||
// subs,
|
scope,
|
||||||
// scope,
|
loc_fn.region.clone(),
|
||||||
// loc_arg.region.clone(),
|
&loc_fn.value,
|
||||||
// &loc_arg.value,
|
fn_expected,
|
||||||
// expected_type,
|
);
|
||||||
// );
|
let fn_con = output.constraint;
|
||||||
|
|
||||||
// args.push(arg_expr);
|
// The function's return type
|
||||||
// outputs.push(arg_out);
|
let ret_var = subs.mk_flex_var();
|
||||||
// }
|
let ret_type = Variable(ret_var);
|
||||||
|
|
||||||
// match &fn_expr.value {
|
// This will be used in the occurs check
|
||||||
// &Var(_, ref sym) => {
|
let mut vars = Vec::with_capacity(2 + loc_args.len());
|
||||||
// output.references.calls.insert(sym.clone());
|
|
||||||
// }
|
|
||||||
// _ => (),
|
|
||||||
// };
|
|
||||||
|
|
||||||
// let expr = Call(subs.mk_flex_var(), Box::new(fn_expr), args);
|
vars.push(fn_var);
|
||||||
|
vars.push(ret_var);
|
||||||
|
|
||||||
// for arg_out in outputs {
|
let mut arg_types = Vec::with_capacity(loc_args.len());
|
||||||
// output.references = output.references.union(arg_out.references);
|
let mut arg_cons = Vec::with_capacity(loc_args.len());
|
||||||
// }
|
|
||||||
|
|
||||||
// // We're not tail-calling a symbol (by name), we're tail-calling a function value.
|
let mut args = Vec::new();
|
||||||
// output.tail_call = None;
|
let mut outputs = Vec::new();
|
||||||
|
|
||||||
// (expr, output)
|
for (index, loc_arg) in loc_args.iter().enumerate() {
|
||||||
|
let region = loc_arg.region.clone();
|
||||||
|
let arg_var = subs.mk_flex_var();
|
||||||
|
let arg_type = Variable(arg_var);
|
||||||
|
// TODO look up the name and use NamedFnArg if possible.
|
||||||
|
let reason = Reason::AnonymousFnArg(index as u8);
|
||||||
|
let expected_arg = ForReason(reason, arg_type.clone(), region.clone());
|
||||||
|
let (arg_expr, arg_out) = canonicalize_expr(
|
||||||
|
env,
|
||||||
|
subs,
|
||||||
|
scope,
|
||||||
|
loc_arg.region.clone(),
|
||||||
|
&loc_arg.value,
|
||||||
|
expected_arg,
|
||||||
|
);
|
||||||
|
|
||||||
|
let arg_con = arg_out.constraint.clone();
|
||||||
|
|
||||||
|
vars.push(arg_var);
|
||||||
|
arg_types.push(arg_type);
|
||||||
|
arg_cons.push(arg_con);
|
||||||
|
|
||||||
|
args.push(arg_expr);
|
||||||
|
outputs.push(arg_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
match &fn_expr.value {
|
||||||
|
&Var(_, ref sym) => {
|
||||||
|
output.references.calls.insert(sym.clone());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
let expr = Call(subs.mk_flex_var(), Box::new(fn_expr), args);
|
||||||
|
|
||||||
|
for arg_out in outputs {
|
||||||
|
output.references = output.references.union(arg_out.references);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're not tail-calling a symbol (by name), we're tail-calling a function value.
|
||||||
|
output.tail_call = None;
|
||||||
|
|
||||||
|
// TODO occurs check!
|
||||||
|
// return $ exists vars $ CAnd ...
|
||||||
|
|
||||||
|
let expected_fn_type = ForReason(
|
||||||
|
fn_reason,
|
||||||
|
Function(arg_types, Box::new(ret_type.clone())),
|
||||||
|
region.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
output.constraint = And(vec![
|
||||||
|
fn_con,
|
||||||
|
Eq(fn_type, expected_fn_type, fn_region),
|
||||||
|
And(arg_cons),
|
||||||
|
Eq(ret_type, expected, region.clone()),
|
||||||
|
]);
|
||||||
|
|
||||||
|
(expr, output)
|
||||||
}
|
}
|
||||||
ast::Expr::Operator((loc_left, loc_op, loc_right)) => {
|
ast::Expr::Operator((loc_left, loc_op, loc_right)) => {
|
||||||
let op = loc_op.value;
|
let op = loc_op.value;
|
||||||
|
@ -468,170 +518,198 @@ fn canonicalize_expr(
|
||||||
can_defs(env, subs, scope.clone(), defs, expected, loc_ret)
|
can_defs(env, subs, scope.clone(), defs, expected, loc_ret)
|
||||||
}
|
}
|
||||||
ast::Expr::Closure((loc_arg_patterns, loc_body_expr)) => {
|
ast::Expr::Closure((loc_arg_patterns, loc_body_expr)) => {
|
||||||
panic!("TODO constrain Closure");
|
// The globally unique symbol that will refer to this closure once it gets converted
|
||||||
// // The globally unique symbol that will refer to this closure once it gets converted
|
// into a top-level procedure for code gen.
|
||||||
// // into a top-level procedure for code gen.
|
//
|
||||||
// //
|
// The symbol includes the module name, the top-level declaration name, and the
|
||||||
// // The symbol includes the module name, the top-level declaration name, and the
|
// index (0-based) of the closure within that declaration.
|
||||||
// // index (0-based) of the closure within that declaration.
|
//
|
||||||
// //
|
// Example: "MyModule$main$3" if this is the 4th closure in MyModule.main.
|
||||||
// // Example: "MyModule$main$3" if this is the 4th closure in MyModule.main.
|
let symbol = scope.gen_unique_symbol();
|
||||||
// let symbol = scope.gen_unique_symbol();
|
|
||||||
|
|
||||||
// // The body expression gets a new scope for canonicalization.
|
// The body expression gets a new scope for canonicalization.
|
||||||
// // Shadow `scope` to make sure we don't accidentally use the original one for the
|
// Shadow `scope` to make sure we don't accidentally use the original one for the
|
||||||
// // rest of this block.
|
// rest of this block.
|
||||||
// let mut scope = scope.clone();
|
let mut scope = scope.clone();
|
||||||
|
|
||||||
// let arg_idents: Vec<(Ident, (Symbol, Region))> =
|
let arg_idents: Vec<(Ident, (Symbol, Region))> =
|
||||||
// idents_from_patterns(loc_arg_patterns.iter(), &scope);
|
idents_from_patterns(loc_arg_patterns.iter(), &scope);
|
||||||
|
|
||||||
// // Add the arguments' idents to scope.idents. If there's a collision,
|
// Add the arguments' idents to scope.idents. If there's a collision,
|
||||||
// // it means there was shadowing, which will be handled later.
|
// it means there was shadowing, which will be handled later.
|
||||||
// scope.idents = union_pairs(scope.idents, arg_idents.iter());
|
scope.idents = union_pairs(scope.idents, arg_idents.iter());
|
||||||
|
|
||||||
// let mut can_args: Vec<Located<Pattern>> = Vec::with_capacity(loc_arg_patterns.len());
|
let mut state = PatternState {
|
||||||
|
assignment_types: ImMap::default(),
|
||||||
|
vars: Vec::with_capacity(loc_arg_patterns.len()),
|
||||||
|
reversed_constraints: Vec::with_capacity(1),
|
||||||
|
};
|
||||||
|
let args = constrain_args(loc_arg_patterns.iter(), &scope, subs, &mut state);
|
||||||
|
let mut can_args: Vec<Located<Pattern>> = Vec::with_capacity(loc_arg_patterns.len());
|
||||||
|
|
||||||
// for loc_pattern in loc_arg_patterns.into_iter() {
|
for loc_pattern in loc_arg_patterns.into_iter() {
|
||||||
// // Exclude the current ident from shadowable_idents; you can't shadow yourself!
|
// Exclude the current ident from shadowable_idents; you can't shadow yourself!
|
||||||
// // (However, still include it in scope, because you *can* recursively refer to yourself.)
|
// (However, still include it in scope, because you *can* recursively refer to yourself.)
|
||||||
// let mut shadowable_idents = scope.idents.clone();
|
let mut shadowable_idents = scope.idents.clone();
|
||||||
// remove_idents(&loc_pattern.value, &mut shadowable_idents);
|
remove_idents(&loc_pattern.value, &mut shadowable_idents);
|
||||||
|
|
||||||
// can_args.push(canonicalize_pattern(
|
can_args.push(canonicalize_pattern(
|
||||||
// env,
|
env,
|
||||||
// subs,
|
subs,
|
||||||
// constraints,
|
&mut scope,
|
||||||
// &mut scope,
|
&FunctionArg,
|
||||||
// &FunctionArg,
|
&loc_pattern,
|
||||||
// &loc_pattern,
|
&mut shadowable_idents,
|
||||||
// &mut shadowable_idents,
|
))
|
||||||
// ))
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// let (loc_body_expr, mut output) = canonicalize_expr(
|
let body_type = NoExpectation(args.ret_type);
|
||||||
// env,
|
let (loc_body_expr, mut output) = canonicalize_expr(
|
||||||
// constraints,
|
env,
|
||||||
// subs,
|
subs,
|
||||||
// &mut scope,
|
&mut scope,
|
||||||
// loc_body_expr.region.clone(),
|
loc_body_expr.region.clone(),
|
||||||
// &loc_body_expr.value,
|
&loc_body_expr.value,
|
||||||
// panic!("TODO expected type"),
|
body_type,
|
||||||
// );
|
);
|
||||||
|
|
||||||
// // Now that we've collected all the references, check to see if any of the args we defined
|
state.reversed_constraints.reverse();
|
||||||
// // went unreferenced. If any did, report them as unused arguments.
|
|
||||||
// for (ident, (arg_symbol, region)) in arg_idents {
|
|
||||||
// if !output.references.has_local(&arg_symbol) {
|
|
||||||
// // The body never referenced this argument we declared. It's an unused argument!
|
|
||||||
// env.problem(Problem::UnusedArgument(Located {
|
|
||||||
// region,
|
|
||||||
// value: ident,
|
|
||||||
// }));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // We shouldn't ultimately count arguments as referenced locals. Otherwise,
|
let assignments_constraint = And(state.reversed_constraints);
|
||||||
// // we end up with weird conclusions like the expression (\x -> x + 1)
|
let ret_constraint = output.constraint;
|
||||||
// // references the (nonexistant) local variable x!
|
|
||||||
// output.references.locals.remove(&arg_symbol);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let var = subs.mk_flex_var(); // TODO constrain this!
|
// panic!("TODO occurs check");
|
||||||
|
|
||||||
// // We've finished analyzing the closure. Its references.locals are now the values it closes over,
|
// We need a var to record in the procedure for later.
|
||||||
// // since we removed the only locals it shouldn't close over (its arguments).
|
// We'll set it equal to the overall `expected` we've been passed.
|
||||||
// // Register it as a top-level procedure in the Env!
|
let var = subs.mk_flex_var();
|
||||||
// env.register_closure(
|
let typ = Variable(var);
|
||||||
// symbol.clone(),
|
|
||||||
// can_args,
|
|
||||||
// loc_body_expr,
|
|
||||||
// region.clone(),
|
|
||||||
// output.references.clone(),
|
|
||||||
// var,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// // Always return a function pointer, in case that's how the closure is being used (e.g. with Apply).
|
output.constraint = And(vec![
|
||||||
// (FunctionPointer(subs.mk_flex_var(), symbol), output)
|
Let(Box::new(LetConstraint {
|
||||||
//}
|
rigid_vars: Vec::new(),
|
||||||
|
flex_vars: state.vars,
|
||||||
|
assignment_types: state.assignment_types,
|
||||||
|
assignments_constraint,
|
||||||
|
ret_constraint,
|
||||||
|
})),
|
||||||
|
// "the closure's type is equal to the var we've stored for later use in the proc"
|
||||||
|
Eq(args.typ, NoExpectation(typ.clone()), region.clone()),
|
||||||
|
// "the var we've stored for later is equal to the overall expected type"
|
||||||
|
Eq(typ, expected, region.clone()),
|
||||||
|
]);
|
||||||
|
|
||||||
//ast::Expr::Case(loc_cond, branches) => {
|
// Now that we've collected all the references, check to see if any of the args we defined
|
||||||
// // Canonicalize the conditional
|
// went unreferenced. If any did, report them as unused arguments.
|
||||||
// let (can_cond, mut output) = canonicalize(env, scope, *loc_cond);
|
for (ident, (arg_symbol, region)) in arg_idents {
|
||||||
// let mut can_branches = Vec::with_capacity(branches.len());
|
if !output.references.has_local(&arg_symbol) {
|
||||||
// let mut recorded_tail_call = false;
|
// The body never referenced this argument we declared. It's an unused argument!
|
||||||
|
env.problem(Problem::UnusedArgument(Located {
|
||||||
|
region,
|
||||||
|
value: ident,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
// for (loc_pattern, loc_expr) in branches {
|
// We shouldn't ultimately count arguments as referenced locals. Otherwise,
|
||||||
// // Each case branch gets a new scope for canonicalization.
|
// we end up with weird conclusions like the expression (\x -> x + 1)
|
||||||
// // Shadow `scope` to make sure we don't accidentally use the original one for the
|
// references the (nonexistant) local variable x!
|
||||||
// // rest of this block.
|
output.references.locals.remove(&arg_symbol);
|
||||||
// let mut scope = scope.clone();
|
}
|
||||||
|
|
||||||
// // Exclude the current ident from shadowable_idents; you can't shadow yourself!
|
// We've finished analyzing the closure. Its references.locals are now the values it closes over,
|
||||||
// // (However, still include it in scope, because you *can* recursively refer to yourself.)
|
// since we removed the only locals it shouldn't close over (its arguments).
|
||||||
// let mut shadowable_idents = scope.idents.clone();
|
// Register it as a top-level procedure in the Env!
|
||||||
// remove_idents(loc_pattern.value.clone(), &mut shadowable_idents);
|
env.register_closure(
|
||||||
|
symbol.clone(),
|
||||||
|
can_args,
|
||||||
|
loc_body_expr,
|
||||||
|
region.clone(),
|
||||||
|
output.references.clone(),
|
||||||
|
var,
|
||||||
|
);
|
||||||
|
|
||||||
// let loc_can_pattern = canonicalize_pattern(
|
// Always return a function pointer, in case that's how the closure is being used (e.g. with Apply).
|
||||||
// env,
|
(FunctionPointer(symbol), output)
|
||||||
// &mut scope,
|
|
||||||
// &CaseBranch,
|
|
||||||
// &loc_pattern,
|
|
||||||
// &mut shadowable_idents,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// // Patterns introduce new idents to the scope!
|
|
||||||
// // Add the assigned identifiers to scope. If there's a collision, it means there
|
|
||||||
// // was shadowing, which will be handled later.
|
|
||||||
// let assigned_idents: Vec<(Ident, (Symbol, Region))> =
|
|
||||||
// idents_from_patterns(std::iter::once(&loc_pattern), &scope);
|
|
||||||
|
|
||||||
// scope.idents = union_pairs(scope.idents, assigned_idents.iter());
|
|
||||||
|
|
||||||
// let (can_expr, branch_output) = canonicalize(env, &mut scope, loc_expr);
|
|
||||||
|
|
||||||
// output.references = output.references.union(branch_output.references);
|
|
||||||
|
|
||||||
// // If all branches are tail calling the same symbol, then so is the conditional as a whole.
|
|
||||||
// if !recorded_tail_call {
|
|
||||||
// // If we haven't recorded output.tail_call yet, record it.
|
|
||||||
// output.tail_call = branch_output.tail_call;
|
|
||||||
// recorded_tail_call = true;
|
|
||||||
// } else if branch_output.tail_call != output.tail_call {
|
|
||||||
// // If we recorded output.tail_call, but what we recorded differs from what we just saw,
|
|
||||||
// // then game over. This can't possibly be a self tail call!
|
|
||||||
// output.tail_call = None;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Now that we've collected all the references for this branch, check to see if
|
|
||||||
// // any of the new idents it defined were unused. If any were, report it.
|
|
||||||
// for (ident, (symbol, region)) in assigned_idents {
|
|
||||||
// if !output.references.has_local(&symbol) {
|
|
||||||
// let loc_ident = Located {
|
|
||||||
// region: region.clone(),
|
|
||||||
// value: ident.clone(),
|
|
||||||
// };
|
|
||||||
|
|
||||||
// env.problem(Problem::UnusedAssignment(loc_ident));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// can_branches.push((loc_can_pattern, can_expr));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // One of the branches should have flipped this, so this should only happen
|
|
||||||
// // in the situation where the case had no branches. That can come up, though!
|
|
||||||
// // A case with no branches is a runtime error, but it will mess things up
|
|
||||||
// // if code gen mistakenly thinks this is a tail call just because its condition
|
|
||||||
// // happend to be one. (The condition gave us our initial output value.)
|
|
||||||
// if !recorded_tail_call {
|
|
||||||
// output.tail_call = None;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Incorporate all three expressions into a combined Output value.
|
|
||||||
// let expr = Case(Box::new(can_cond), can_branches);
|
|
||||||
|
|
||||||
// (expr, output)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ast::Expr::Case(loc_cond, branches) => {
|
||||||
|
// // Canonicalize the conditional
|
||||||
|
// let (can_cond, mut output) = canonicalize(env, scope, *loc_cond);
|
||||||
|
// let mut can_branches = Vec::with_capacity(branches.len());
|
||||||
|
// let mut recorded_tail_call = false;
|
||||||
|
|
||||||
|
// for (loc_pattern, loc_expr) in branches {
|
||||||
|
// // Each case branch gets a new scope for canonicalization.
|
||||||
|
// // Shadow `scope` to make sure we don't accidentally use the original one for the
|
||||||
|
// // rest of this block.
|
||||||
|
// let mut scope = scope.clone();
|
||||||
|
|
||||||
|
// // Exclude the current ident from shadowable_idents; you can't shadow yourself!
|
||||||
|
// // (However, still include it in scope, because you *can* recursively refer to yourself.)
|
||||||
|
// let mut shadowable_idents = scope.idents.clone();
|
||||||
|
// remove_idents(loc_pattern.value.clone(), &mut shadowable_idents);
|
||||||
|
|
||||||
|
// let loc_can_pattern = canonicalize_pattern(
|
||||||
|
// env,
|
||||||
|
// &mut scope,
|
||||||
|
// &CaseBranch,
|
||||||
|
// &loc_pattern,
|
||||||
|
// &mut shadowable_idents,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Patterns introduce new idents to the scope!
|
||||||
|
// // Add the assigned identifiers to scope. If there's a collision, it means there
|
||||||
|
// // was shadowing, which will be handled later.
|
||||||
|
// let assigned_idents: Vec<(Ident, (Symbol, Region))> =
|
||||||
|
// idents_from_patterns(std::iter::once(&loc_pattern), &scope);
|
||||||
|
|
||||||
|
// scope.idents = union_pairs(scope.idents, assigned_idents.iter());
|
||||||
|
|
||||||
|
// let (can_expr, branch_output) = canonicalize(env, &mut scope, loc_expr);
|
||||||
|
|
||||||
|
// output.references = output.references.union(branch_output.references);
|
||||||
|
|
||||||
|
// // If all branches are tail calling the same symbol, then so is the conditional as a whole.
|
||||||
|
// if !recorded_tail_call {
|
||||||
|
// // If we haven't recorded output.tail_call yet, record it.
|
||||||
|
// output.tail_call = branch_output.tail_call;
|
||||||
|
// recorded_tail_call = true;
|
||||||
|
// } else if branch_output.tail_call != output.tail_call {
|
||||||
|
// // If we recorded output.tail_call, but what we recorded differs from what we just saw,
|
||||||
|
// // then game over. This can't possibly be a self tail call!
|
||||||
|
// output.tail_call = None;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Now that we've collected all the references for this branch, check to see if
|
||||||
|
// // any of the new idents it defined were unused. If any were, report it.
|
||||||
|
// for (ident, (symbol, region)) in assigned_idents {
|
||||||
|
// if !output.references.has_local(&symbol) {
|
||||||
|
// let loc_ident = Located {
|
||||||
|
// region: region.clone(),
|
||||||
|
// value: ident.clone(),
|
||||||
|
// };
|
||||||
|
|
||||||
|
// env.problem(Problem::UnusedAssignment(loc_ident));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// can_branches.push((loc_can_pattern, can_expr));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // One of the branches should have flipped this, so this should only happen
|
||||||
|
// // in the situation where the case had no branches. That can come up, though!
|
||||||
|
// // A case with no branches is a runtime error, but it will mess things up
|
||||||
|
// // if code gen mistakenly thinks this is a tail call just because its condition
|
||||||
|
// // happend to be one. (The condition gave us our initial output value.)
|
||||||
|
// if !recorded_tail_call {
|
||||||
|
// output.tail_call = None;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Incorporate all three expressions into a combined Output value.
|
||||||
|
// let expr = Case(Box::new(can_cond), can_branches);
|
||||||
|
|
||||||
|
// (expr, output)
|
||||||
|
// }
|
||||||
ast::Expr::SpaceBefore(sub_expr, _spaces) => {
|
ast::Expr::SpaceBefore(sub_expr, _spaces) => {
|
||||||
// Continue on; spaces don't do anything.
|
// Continue on; spaces don't do anything.
|
||||||
return canonicalize_expr(env, subs, scope, region, sub_expr, expected);
|
return canonicalize_expr(env, subs, scope, region, sub_expr, expected);
|
||||||
|
@ -1185,7 +1263,16 @@ impl Info {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type BoundTypeVars = ImMap<Box<str>, Type>;
|
/// This lets us share bound type variables between nested annotations, e.g.
|
||||||
|
///
|
||||||
|
/// blah : Map k v -> Int
|
||||||
|
/// blah mapping =
|
||||||
|
/// nested : Map k v # <-- the same k and v from the top-level annotation
|
||||||
|
/// nested = mapping
|
||||||
|
/// 42
|
||||||
|
///
|
||||||
|
/// In elm/compiler this is called RTV - the "Rigid Type Variables" dictionary.
|
||||||
|
// type BoundTypeVars = ImMap<Box<str>, Type>;
|
||||||
|
|
||||||
struct PatternState {
|
struct PatternState {
|
||||||
assignment_types: ImMap<Symbol, Located<Type>>,
|
assignment_types: ImMap<Symbol, Located<Type>>,
|
||||||
|
@ -1387,7 +1474,7 @@ fn can_defs<'a>(
|
||||||
(
|
(
|
||||||
&ast::Pattern::Identifier(ref name),
|
&ast::Pattern::Identifier(ref name),
|
||||||
&Pattern::Identifier(_, ref assigned_symbol),
|
&Pattern::Identifier(_, ref assigned_symbol),
|
||||||
&FunctionPointer(_, ref symbol),
|
&FunctionPointer(ref symbol),
|
||||||
) => {
|
) => {
|
||||||
// Since everywhere in the code it'll be referred to by its assigned name,
|
// Since everywhere in the code it'll be referred to by its assigned name,
|
||||||
// remove its generated name from the procedure map. (We'll re-insert it later.)
|
// remove its generated name from the procedure map. (We'll re-insert it later.)
|
||||||
|
@ -1589,7 +1676,7 @@ fn can_defs<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
Define(subs.mk_flex_var(), can_assignments, Box::new(ret_expr)),
|
Defs(subs.mk_flex_var(), can_assignments, Box::new(ret_expr)),
|
||||||
output,
|
output,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1631,3 +1718,58 @@ fn can_defs<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Args {
|
||||||
|
pub vars: Vec<Variable>,
|
||||||
|
pub typ: Type,
|
||||||
|
pub ret_type: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn constrain_args<'a, I>(args: I, scope: &Scope, subs: &mut Subs, state: &mut PatternState) -> Args
|
||||||
|
where
|
||||||
|
I: Iterator<Item = &'a Located<ast::Pattern<'a>>>,
|
||||||
|
{
|
||||||
|
let (mut vars, arg_types) = patterns_to_variables(args.into_iter(), scope, subs, state);
|
||||||
|
|
||||||
|
let ret_var = subs.mk_flex_var();
|
||||||
|
let ret_type = Type::Variable(ret_var);
|
||||||
|
|
||||||
|
vars.push(ret_var);
|
||||||
|
|
||||||
|
let typ = Type::Function(arg_types, Box::new(ret_type.clone()));
|
||||||
|
|
||||||
|
Args {
|
||||||
|
vars,
|
||||||
|
typ,
|
||||||
|
ret_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn patterns_to_variables<'a, I>(
|
||||||
|
patterns: I,
|
||||||
|
scope: &Scope,
|
||||||
|
subs: &mut Subs,
|
||||||
|
state: &mut PatternState,
|
||||||
|
) -> (Vec<Variable>, Vec<Type>)
|
||||||
|
where
|
||||||
|
I: Iterator<Item = &'a Located<ast::Pattern<'a>>>,
|
||||||
|
{
|
||||||
|
let mut vars = Vec::with_capacity(state.vars.capacity());
|
||||||
|
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
||||||
|
|
||||||
|
for loc_pattern in patterns {
|
||||||
|
let pattern_var = subs.mk_flex_var();
|
||||||
|
let pattern_type = Type::Variable(pattern_var);
|
||||||
|
|
||||||
|
state.add_pattern(
|
||||||
|
scope,
|
||||||
|
loc_pattern.clone(),
|
||||||
|
NoExpectation(pattern_type.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
vars.push(pattern_var);
|
||||||
|
pattern_types.push(pattern_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
(vars, pattern_types)
|
||||||
|
}
|
||||||
|
|
|
@ -3,13 +3,11 @@ use can::problem::Problem;
|
||||||
use can::scope::Scope;
|
use can::scope::Scope;
|
||||||
use can::symbol::Symbol;
|
use can::symbol::Symbol;
|
||||||
use collections::ImMap;
|
use collections::ImMap;
|
||||||
use constrain::{self, Constraints};
|
|
||||||
use ident::{Ident, VariantName};
|
use ident::{Ident, VariantName};
|
||||||
use parse::ast;
|
use parse::ast;
|
||||||
use region::{Located, Region};
|
use region::{Located, Region};
|
||||||
use subs::Subs;
|
use subs::Subs;
|
||||||
use subs::Variable;
|
use subs::Variable;
|
||||||
use types::Constraint;
|
|
||||||
|
|
||||||
/// A pattern, including possible problems (e.g. shadowing) so that
|
/// A pattern, including possible problems (e.g. shadowing) so that
|
||||||
/// codegen can generate a runtime error if this pattern is reached.
|
/// codegen can generate a runtime error if this pattern is reached.
|
||||||
|
|
498
src/constrain.rs
498
src/constrain.rs
|
@ -1,48 +1,10 @@
|
||||||
use can::expr::Expr;
|
use region::Region;
|
||||||
// use can::pattern::Pattern;
|
|
||||||
use can::procedure::Procedure;
|
|
||||||
// use can::symbol::Symbol;
|
|
||||||
use collections::ImMap;
|
|
||||||
use operator::{ArgSide, Operator};
|
|
||||||
use region::{Located, Region};
|
|
||||||
use subs::{Subs, Variable};
|
use subs::{Subs, Variable};
|
||||||
use types::Constraint::{self, *};
|
use types::Constraint::{self, *};
|
||||||
use types::Expected::{self, *};
|
use types::Expected::{self, *};
|
||||||
use types::Type::{self, *};
|
use types::Type::{self, *};
|
||||||
use types::{self, Reason};
|
use types::{self, Reason};
|
||||||
|
|
||||||
/// This lets us share bound type variables between nested annotations, e.g.
|
|
||||||
///
|
|
||||||
/// blah : Map k v -> Int
|
|
||||||
/// blah mapping =
|
|
||||||
/// nested : Map k v # <-- the same k and v from the top-level annotation
|
|
||||||
/// nested = mapping
|
|
||||||
/// 42
|
|
||||||
///
|
|
||||||
/// In elm/compiler this is called RTV - the "Rigid Type Variables" dictionary.
|
|
||||||
type BoundTypeVars<'a> = ImMap<&'a str, Type>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Constraints(Vec<Constraint>);
|
|
||||||
|
|
||||||
impl Constraints {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Constraints(Vec::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(&mut self, constraint: Constraint) {
|
|
||||||
self.0.push(constraint)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> std::slice::Iter<'_, Constraint> {
|
|
||||||
self.0.iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_iter(self) -> std::vec::IntoIter<Constraint> {
|
|
||||||
self.0.into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn int_literal(subs: &mut Subs, expected: Expected<Type>, region: Region) -> Constraint {
|
pub fn int_literal(subs: &mut Subs, expected: Expected<Type>, region: Region) -> Constraint {
|
||||||
let typ = number_literal_type("Int", "Integer");
|
let typ = number_literal_type("Int", "Integer");
|
||||||
let reason = Reason::IntLiteral;
|
let reason = Reason::IntLiteral;
|
||||||
|
@ -94,277 +56,6 @@ fn builtin_type(module_name: &str, type_name: &str, args: Vec<Type>) -> Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constrain<'a>(
|
|
||||||
bound_vars: &'a BoundTypeVars<'a>,
|
|
||||||
subs: &'a mut Subs,
|
|
||||||
loc_expr: Located<Expr>,
|
|
||||||
expected: Expected<Type>,
|
|
||||||
) -> Constraint {
|
|
||||||
panic!("TODO inline constrain");
|
|
||||||
// let region = loc_expr.region;
|
|
||||||
|
|
||||||
// match loc_expr.value {
|
|
||||||
// List(elems) => list(elems, bound_vars, subs, expected, region),
|
|
||||||
// Var(sym) | FunctionPointer(sym) => Lookup(sym, expected, region),
|
|
||||||
// Assign(assignments, ret_expr) => {
|
|
||||||
// let ret_con = constrain(bound_vars, subs, *ret_expr, expected);
|
|
||||||
|
|
||||||
// if assignments.len() == 1 {
|
|
||||||
// // Don't bother allocating a Vec of them if there's only one!
|
|
||||||
// let (loc_pattern, loc_expr) = assignments.into_iter().next().unwrap();
|
|
||||||
|
|
||||||
// constrain_def(loc_pattern, loc_expr, bound_vars, subs, ret_con)
|
|
||||||
// } else {
|
|
||||||
// constrain_defs(assignments, bound_vars, subs, ret_con)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// Call(box_loc_fn_expr, args) => {
|
|
||||||
// constrain_call(bound_vars, subs, *box_loc_fn_expr, args, expected, region)
|
|
||||||
// }
|
|
||||||
// Expr::Operator(l_box_loc_expr, loc_op, r_box_loc_expr) => constrain_op(
|
|
||||||
// bound_vars,
|
|
||||||
// subs,
|
|
||||||
// *l_box_loc_expr,
|
|
||||||
// loc_op,
|
|
||||||
// *r_box_loc_expr,
|
|
||||||
// expected,
|
|
||||||
// region,
|
|
||||||
// ),
|
|
||||||
// _ => panic!("TODO constraints for {:?}", loc_expr.value),
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn constrain_op(
|
|
||||||
// bound_vars: &BoundTypeVars,
|
|
||||||
// subs: &mut Subs,
|
|
||||||
// l_loc_expr: Located<Expr>,
|
|
||||||
// loc_op: Located<Operator>,
|
|
||||||
// r_loc_expr: Located<Expr>,
|
|
||||||
// expected: Expected<Type>,
|
|
||||||
// region: Region,
|
|
||||||
// ) -> Constraint {
|
|
||||||
// let op = loc_op.value;
|
|
||||||
// let op_types = Type::for_operator(op);
|
|
||||||
// // TODO use fn_var
|
|
||||||
// let _fn_var = subs.mk_flex_var();
|
|
||||||
// let ret_var = subs.mk_flex_var();
|
|
||||||
// let ret_type = Variable(ret_var);
|
|
||||||
// let ret_reason = Reason::OperatorRet(op);
|
|
||||||
// let expected_ret_type = ForReason(ret_reason, op_types.ret, region.clone());
|
|
||||||
|
|
||||||
// let (_l_var, l_con) = constrain_op_arg(
|
|
||||||
// ArgSide::Left,
|
|
||||||
// bound_vars,
|
|
||||||
// subs,
|
|
||||||
// op,
|
|
||||||
// op_types.left,
|
|
||||||
// l_loc_expr,
|
|
||||||
// );
|
|
||||||
// let (_r_var, r_con) = constrain_op_arg(
|
|
||||||
// ArgSide::Right,
|
|
||||||
// bound_vars,
|
|
||||||
// subs,
|
|
||||||
// op,
|
|
||||||
// op_types.right,
|
|
||||||
// r_loc_expr,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// // TODO occurs check!
|
|
||||||
// // let vars = vec![fn_var, ret_var, l_var, r_var];
|
|
||||||
// // return $ exists (funcVar:resultVar:argVars) $ CAnd ...
|
|
||||||
|
|
||||||
// And(vec![
|
|
||||||
// // the constraint from constrain on l_expr, expecting its hardcoded type
|
|
||||||
// l_con,
|
|
||||||
// // the constraint from constrain on r_expr, expecting its hardcoded type
|
|
||||||
// r_con,
|
|
||||||
// // The operator's args and return type should be its hardcoded types
|
|
||||||
// Eq(ret_type.clone(), expected_ret_type, region.clone()),
|
|
||||||
// // Finally, link the operator's return type to the given expected type
|
|
||||||
// Eq(ret_type, expected, region),
|
|
||||||
// ])
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[inline(always)]
|
|
||||||
// pub fn constrain_op_arg(
|
|
||||||
// arg_side: ArgSide,
|
|
||||||
// bound_vars: &BoundTypeVars,
|
|
||||||
// subs: &mut Subs,
|
|
||||||
// op: Operator,
|
|
||||||
// typ: Type,
|
|
||||||
// loc_arg: Located<Expr>,
|
|
||||||
// ) -> (Variable, Constraint) {
|
|
||||||
// let region = loc_arg.region.clone();
|
|
||||||
// let arg_var = subs.mk_flex_var();
|
|
||||||
// let arg_type = Variable(arg_var);
|
|
||||||
// let reason = Reason::OperatorArg(op, arg_side);
|
|
||||||
// let expected_arg = ForReason(reason, typ, region.clone());
|
|
||||||
// let arg_con = And(vec![
|
|
||||||
// // Recursively constrain the variable
|
|
||||||
// constrain(bound_vars, subs, loc_arg, NoExpectation(arg_type.clone())),
|
|
||||||
// // The variable should ultimately equal the hardcoded expected type
|
|
||||||
// Eq(arg_type, expected_arg, region),
|
|
||||||
// ]);
|
|
||||||
|
|
||||||
// (arg_var, arg_con)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn constrain_call(
|
|
||||||
// bound_vars: &BoundTypeVars,
|
|
||||||
// subs: &mut Subs,
|
|
||||||
// loc_expr: Located<Expr>,
|
|
||||||
// args: Vec<Located<Expr>>,
|
|
||||||
// expected: Expected<Type>,
|
|
||||||
// region: Region,
|
|
||||||
// ) -> Constraint {
|
|
||||||
// // The expression that evaluates to the function being called, e.g. `foo` in
|
|
||||||
// // (foo) bar baz
|
|
||||||
// let fn_var = subs.mk_flex_var();
|
|
||||||
// let fn_type = Variable(fn_var);
|
|
||||||
// let fn_region = loc_expr.region.clone();
|
|
||||||
// let fn_expected = NoExpectation(fn_type.clone());
|
|
||||||
// let fn_con = constrain(bound_vars, subs, loc_expr, fn_expected);
|
|
||||||
// let fn_reason =
|
|
||||||
// // TODO look up the name and use NamedFnArg if possible.
|
|
||||||
// Reason::AnonymousFnCall(args.len() as u8);
|
|
||||||
|
|
||||||
// // The function's return type
|
|
||||||
// let ret_var = subs.mk_flex_var();
|
|
||||||
// let ret_type = Variable(ret_var);
|
|
||||||
|
|
||||||
// // This will be used in the occurs check
|
|
||||||
// let mut vars = Vec::with_capacity(2 + args.len());
|
|
||||||
|
|
||||||
// vars.push(fn_var);
|
|
||||||
// vars.push(ret_var);
|
|
||||||
|
|
||||||
// let mut arg_types = Vec::with_capacity(args.len());
|
|
||||||
// let mut arg_cons = Vec::with_capacity(args.len());
|
|
||||||
|
|
||||||
// for (index, loc_arg) in args.into_iter().enumerate() {
|
|
||||||
// let region = loc_arg.region.clone();
|
|
||||||
// let arg_var = subs.mk_flex_var();
|
|
||||||
// let arg_type = Variable(arg_var);
|
|
||||||
// let reason =
|
|
||||||
// // TODO look up the name and use NamedFnArg if possible.
|
|
||||||
// Reason::AnonymousFnArg(index as u8);
|
|
||||||
// let expected_arg = ForReason(reason, arg_type.clone(), region.clone());
|
|
||||||
// let arg_con = constrain(bound_vars, subs, loc_arg, expected_arg);
|
|
||||||
|
|
||||||
// vars.push(arg_var);
|
|
||||||
// arg_types.push(arg_type);
|
|
||||||
// arg_cons.push(arg_con);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TODO occurs check!
|
|
||||||
// // return $ exists vars $ CAnd ...
|
|
||||||
|
|
||||||
// let expected_fn_type = ForReason(
|
|
||||||
// fn_reason,
|
|
||||||
// Function(arg_types, Box::new(ret_type.clone())),
|
|
||||||
// region.clone(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// And(vec![
|
|
||||||
// fn_con,
|
|
||||||
// Eq(fn_type, expected_fn_type, fn_region),
|
|
||||||
// And(arg_cons),
|
|
||||||
// Eq(ret_type, expected, region),
|
|
||||||
// ])
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn constrain_defs(
|
|
||||||
// assignments: Vec<(Located<Pattern>, Located<Expr>)>,
|
|
||||||
// bound_vars: &BoundTypeVars,
|
|
||||||
// subs: &mut Subs,
|
|
||||||
// ret_con: Constraint,
|
|
||||||
// ) -> Constraint {
|
|
||||||
// let rigid_info = Info::with_capacity(assignments.len());
|
|
||||||
// let mut flex_info = Info::with_capacity(assignments.len());
|
|
||||||
|
|
||||||
// for (loc_pattern, loc_expr) in assignments {
|
|
||||||
// let mut state = PatternState {
|
|
||||||
// assignment_types: ImMap::default(),
|
|
||||||
// vars: Vec::with_capacity(1),
|
|
||||||
// reversed_constraints: Vec::with_capacity(1),
|
|
||||||
// };
|
|
||||||
// let pattern_var = subs.mk_flex_var();
|
|
||||||
// let pattern_type = Type::Variable(pattern_var);
|
|
||||||
|
|
||||||
// flex_info.vars.push(pattern_var);
|
|
||||||
|
|
||||||
// state.add_pattern(loc_pattern.clone(), NoExpectation(pattern_type.clone()));
|
|
||||||
// state.reversed_constraints.reverse();
|
|
||||||
|
|
||||||
// let assignments_constraint = And(state.reversed_constraints);
|
|
||||||
|
|
||||||
// // Set up types for the expr we're assigned to.
|
|
||||||
// let expr_var = subs.mk_flex_var();
|
|
||||||
// let expr_type = Type::Variable(expr_var);
|
|
||||||
|
|
||||||
// // Any time there's a lookup on this symbol in the outer Let,
|
|
||||||
// // it should result in this expression's type. After all, this
|
|
||||||
// // is the type to which this symbol is assigned!
|
|
||||||
// add_pattern_to_lookup_types(
|
|
||||||
// loc_pattern,
|
|
||||||
// &mut flex_info.assignment_types,
|
|
||||||
// expr_type.clone(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// let expr_con = constrain(bound_vars, subs, loc_expr, NoExpectation(expr_type));
|
|
||||||
// let def_con = Let(Box::new(LetConstraint {
|
|
||||||
// rigid_vars: Vec::new(),
|
|
||||||
// flex_vars: state.vars,
|
|
||||||
// assignment_types: state.assignment_types,
|
|
||||||
// assignments_constraint,
|
|
||||||
// ret_constraint: expr_con,
|
|
||||||
// }));
|
|
||||||
|
|
||||||
// flex_info.constraints.push(def_con);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Rigid constraint
|
|
||||||
// Let(Box::new(LetConstraint {
|
|
||||||
// rigid_vars: rigid_info.vars,
|
|
||||||
// flex_vars: Vec::new(),
|
|
||||||
// assignment_types: rigid_info.assignment_types,
|
|
||||||
// assignments_constraint:
|
|
||||||
// // Flex constraint
|
|
||||||
// Let(Box::new(LetConstraint {
|
|
||||||
// rigid_vars: Vec::new(),
|
|
||||||
// flex_vars: flex_info.vars,
|
|
||||||
// assignment_types: flex_info.assignment_types.clone(),
|
|
||||||
// assignments_constraint:
|
|
||||||
// // Final flex constraints
|
|
||||||
// Let(Box::new(LetConstraint {
|
|
||||||
// rigid_vars: Vec::new(),
|
|
||||||
// flex_vars: Vec::new(),
|
|
||||||
// assignment_types: flex_info.assignment_types,
|
|
||||||
// assignments_constraint: True,
|
|
||||||
// ret_constraint: And(flex_info.constraints)
|
|
||||||
// })),
|
|
||||||
// ret_constraint: And(vec![And(rigid_info.constraints), ret_con])
|
|
||||||
// })),
|
|
||||||
// ret_constraint: True,
|
|
||||||
// }))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// struct Info {
|
|
||||||
// pub vars: Vec<Variable>,
|
|
||||||
// pub constraints: Vec<Constraint>,
|
|
||||||
// pub assignment_types: ImMap<Symbol, Located<Type>>,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl Info {
|
|
||||||
// pub fn with_capacity(capacity: usize) -> Self {
|
|
||||||
// Info {
|
|
||||||
// vars: Vec::with_capacity(capacity),
|
|
||||||
// constraints: Vec::with_capacity(capacity),
|
|
||||||
// assignment_types: ImMap::default(),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn empty_list_type(var: Variable) -> Type {
|
pub fn empty_list_type(var: Variable) -> Type {
|
||||||
list_type(Type::Variable(var))
|
list_type(Type::Variable(var))
|
||||||
}
|
}
|
||||||
|
@ -376,190 +67,3 @@ pub fn list_type(typ: Type) -> Type {
|
||||||
pub fn str_type() -> Type {
|
pub fn str_type() -> Type {
|
||||||
builtin_type("Str", "Str", Vec::new())
|
builtin_type("Str", "Str", Vec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn constrain_def(
|
|
||||||
// loc_pattern: Located<Pattern>,
|
|
||||||
// loc_expr: Located<Expr>,
|
|
||||||
// bound_vars: &BoundTypeVars,
|
|
||||||
// subs: &mut Subs,
|
|
||||||
// ret_constraint: Constraint,
|
|
||||||
// ) -> Constraint {
|
|
||||||
// let mut state = PatternState {
|
|
||||||
// assignment_types: ImMap::default(),
|
|
||||||
// vars: Vec::with_capacity(1),
|
|
||||||
// reversed_constraints: Vec::with_capacity(1),
|
|
||||||
// };
|
|
||||||
// let mut vars = Vec::with_capacity(state.vars.capacity());
|
|
||||||
// let pattern_var = subs.mk_flex_var();
|
|
||||||
// let pattern_type = Type::Variable(pattern_var);
|
|
||||||
|
|
||||||
// state.add_pattern(loc_pattern.clone(), NoExpectation(pattern_type.clone()));
|
|
||||||
|
|
||||||
// vars.push(pattern_var);
|
|
||||||
|
|
||||||
// // Set up types for the expr we're assigned to.
|
|
||||||
// let expr_var = subs.mk_flex_var();
|
|
||||||
// let expr_type = Type::Variable(expr_var);
|
|
||||||
|
|
||||||
// // These types are *only* for the current pattern. In contrast, the ones
|
|
||||||
// // in PatternState represent
|
|
||||||
// let mut lookup_types: ImMap<Symbol, Located<Type>> = ImMap::default();
|
|
||||||
|
|
||||||
// // Any time there's a lookup on this symbol in the outer Let,
|
|
||||||
// // it should result in this expression's type. After all, this
|
|
||||||
// // is the type to which this symbol is assigned!
|
|
||||||
// add_pattern_to_lookup_types(loc_pattern, &mut lookup_types, expr_type.clone());
|
|
||||||
|
|
||||||
// state.reversed_constraints.reverse();
|
|
||||||
|
|
||||||
// Let(Box::new(LetConstraint {
|
|
||||||
// rigid_vars: Vec::new(),
|
|
||||||
// flex_vars: vars,
|
|
||||||
// assignments_constraint:
|
|
||||||
// // This nested constraint represents the actually constrained expr.
|
|
||||||
// Let(Box::new(LetConstraint {
|
|
||||||
// rigid_vars: Vec::new(),
|
|
||||||
// flex_vars: state.vars,
|
|
||||||
// assignment_types: state.assignment_types,
|
|
||||||
// assignments_constraint: And(state.reversed_constraints),
|
|
||||||
// ret_constraint: constrain(bound_vars, subs, loc_expr, NoExpectation(expr_type))
|
|
||||||
// })),
|
|
||||||
// assignment_types: lookup_types,
|
|
||||||
// ret_constraint,
|
|
||||||
// }))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn add_pattern_to_lookup_types(
|
|
||||||
// loc_pattern: Located<Pattern>,
|
|
||||||
// lookup_types: &mut ImMap<Symbol, Located<Type>>,
|
|
||||||
// expr_type: Type,
|
|
||||||
// ) {
|
|
||||||
// let region = loc_pattern.region;
|
|
||||||
|
|
||||||
// match loc_pattern.value {
|
|
||||||
// Pattern::Identifier(symbol) => {
|
|
||||||
// let loc_type = Located {
|
|
||||||
// region,
|
|
||||||
// value: expr_type,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// lookup_types.insert(symbol, loc_type);
|
|
||||||
// }
|
|
||||||
// _ => panic!("TODO constrain patterns other than Identifier"),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn constrain_procedure<'a>(
|
|
||||||
bound_vars: &'a BoundTypeVars<'a>,
|
|
||||||
constraints: &'a mut Constraints,
|
|
||||||
subs: &'a mut Subs,
|
|
||||||
proc: Procedure,
|
|
||||||
expected: Expected<Type>,
|
|
||||||
) -> Constraint {
|
|
||||||
panic!("TODO inline constrain_procedure");
|
|
||||||
// let mut state = PatternState {
|
|
||||||
// assignment_types: ImMap::default(),
|
|
||||||
// vars: Vec::with_capacity(proc.args.len()),
|
|
||||||
// reversed_constraints: Vec::with_capacity(1),
|
|
||||||
// };
|
|
||||||
|
|
||||||
// let args = constrain_args(proc.args.into_iter(), subs, &mut state);
|
|
||||||
// let body_type = NoExpectation(args.ret_type);
|
|
||||||
// let ret_constraint = constrain(bound_vars, subs, proc.body, body_type);
|
|
||||||
|
|
||||||
// state.reversed_constraints.reverse();
|
|
||||||
|
|
||||||
// let assignments_constraint = And(state.reversed_constraints);
|
|
||||||
|
|
||||||
// // panic!("TODO occurs check");
|
|
||||||
|
|
||||||
// And(vec![
|
|
||||||
// Let(Box::new(LetConstraint {
|
|
||||||
// rigid_vars: Vec::new(),
|
|
||||||
// flex_vars: state.vars,
|
|
||||||
// assignment_types: state.assignment_types,
|
|
||||||
// assignments_constraint,
|
|
||||||
// ret_constraint,
|
|
||||||
// })),
|
|
||||||
// Eq(args.typ, expected, proc.definition),
|
|
||||||
// ])
|
|
||||||
}
|
|
||||||
|
|
||||||
// struct Args {
|
|
||||||
// pub vars: Vec<Variable>,
|
|
||||||
// pub typ: Type,
|
|
||||||
// pub ret_type: Type,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn constrain_args<I>(args: I, subs: &mut Subs, state: &mut PatternState) -> Args
|
|
||||||
// where
|
|
||||||
// I: Iterator<Item = Located<Pattern>>,
|
|
||||||
// {
|
|
||||||
// let (mut vars, arg_types) = patterns_to_variables(args.into_iter(), subs, state);
|
|
||||||
|
|
||||||
// let ret_var = subs.mk_flex_var();
|
|
||||||
// let ret_type = Type::Variable(ret_var);
|
|
||||||
|
|
||||||
// vars.push(ret_var);
|
|
||||||
|
|
||||||
// let typ = Type::Function(arg_types, Box::new(ret_type.clone()));
|
|
||||||
|
|
||||||
// Args {
|
|
||||||
// vars,
|
|
||||||
// typ,
|
|
||||||
// ret_type,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn patterns_to_variables<I>(
|
|
||||||
// patterns: I,
|
|
||||||
// subs: &mut Subs,
|
|
||||||
// state: &mut PatternState,
|
|
||||||
// ) -> (Vec<Variable>, Vec<Type>)
|
|
||||||
// where
|
|
||||||
// I: Iterator<Item = Located<Pattern>>,
|
|
||||||
// {
|
|
||||||
// let mut vars = Vec::with_capacity(state.vars.capacity());
|
|
||||||
// let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
|
||||||
|
|
||||||
// for loc_pattern in patterns {
|
|
||||||
// let pattern_var = subs.mk_flex_var();
|
|
||||||
// let pattern_type = Type::Variable(pattern_var);
|
|
||||||
|
|
||||||
// state.add_pattern(loc_pattern, NoExpectation(pattern_type.clone()));
|
|
||||||
|
|
||||||
// vars.push(pattern_var);
|
|
||||||
// pattern_types.push(pattern_type);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// (vars, pattern_types)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl PatternState {
|
|
||||||
// pub fn add_pattern(&mut self, loc_pattern: Located<Pattern>, expected: Expected<Type>) {
|
|
||||||
// use can::pattern::Pattern::*;
|
|
||||||
|
|
||||||
// let region = loc_pattern.region;
|
|
||||||
|
|
||||||
// match loc_pattern.value {
|
|
||||||
// Identifier(symbol) => self.add_to_assignment_types(region, symbol, expected),
|
|
||||||
// Underscore => (),
|
|
||||||
// _ => panic!("TODO other patterns"),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn add_to_assignment_types(
|
|
||||||
// &mut self,
|
|
||||||
// region: Region,
|
|
||||||
// symbol: Symbol,
|
|
||||||
// expected: Expected<Type>,
|
|
||||||
// ) {
|
|
||||||
// self.assignment_types.insert(
|
|
||||||
// symbol,
|
|
||||||
// Located {
|
|
||||||
// region,
|
|
||||||
// value: expected.get_type(),
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
@ -5,8 +5,6 @@ use region::Located;
|
||||||
use region::Region;
|
use region::Region;
|
||||||
use subs::Variable;
|
use subs::Variable;
|
||||||
|
|
||||||
type ModuleName = String;
|
|
||||||
|
|
||||||
// The standard modules
|
// The standard modules
|
||||||
pub const MOD_FLOAT: &'static str = "Float";
|
pub const MOD_FLOAT: &'static str = "Float";
|
||||||
pub const MOD_INT: &'static str = "Int";
|
pub const MOD_INT: &'static str = "Int";
|
||||||
|
|
|
@ -9,7 +9,6 @@ use roc::can::procedure::Procedure;
|
||||||
use roc::can::symbol::Symbol;
|
use roc::can::symbol::Symbol;
|
||||||
use roc::can::Output;
|
use roc::can::Output;
|
||||||
use roc::collections::{ImMap, MutMap};
|
use roc::collections::{ImMap, MutMap};
|
||||||
use roc::constrain::Constraints;
|
|
||||||
use roc::ident::Ident;
|
use roc::ident::Ident;
|
||||||
use roc::parse;
|
use roc::parse;
|
||||||
use roc::parse::ast::{self, Attempting};
|
use roc::parse::ast::{self, Attempting};
|
||||||
|
@ -19,10 +18,12 @@ use roc::region::{Located, Region};
|
||||||
use roc::subs::{Subs, Variable};
|
use roc::subs::{Subs, Variable};
|
||||||
use roc::types::{Expected, Type};
|
use roc::types::{Expected, Type};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
||||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
||||||
let state = State::new(&input, Attempting::Module);
|
let state = State::new(&input, Attempting::Module);
|
||||||
let parser = space0_before(loc(parse::expr(0)), 0);
|
let parser = space0_before(loc(parse::expr(0)), 0);
|
||||||
|
|
|
@ -13,13 +13,11 @@ mod test_infer {
|
||||||
use helpers::can_expr;
|
use helpers::can_expr;
|
||||||
use roc::infer::infer_expr;
|
use roc::infer::infer_expr;
|
||||||
use roc::pretty_print_types::content_to_string;
|
use roc::pretty_print_types::content_to_string;
|
||||||
use roc::region::Located;
|
|
||||||
use roc::subs::Subs;
|
|
||||||
|
|
||||||
// HELPERS
|
// HELPERS
|
||||||
|
|
||||||
fn infer_eq(src: &str, expected: &str) {
|
fn infer_eq(src: &str, expected: &str) {
|
||||||
let (expr, output, _, procedures, mut subs, variable) = can_expr(src);
|
let (_, output, _, procedures, mut subs, variable) = can_expr(src);
|
||||||
|
|
||||||
let content = infer_expr(&mut subs, procedures, &output.constraint, variable);
|
let content = infer_expr(&mut subs, procedures, &output.constraint, variable);
|
||||||
let actual_str = content_to_string(content, &mut subs);
|
let actual_str = content_to_string(content, &mut subs);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue