Fix bug with inferring functions in Let

This commit is contained in:
Richard Feldman 2019-08-30 00:26:40 -04:00
parent 10631d0dd0
commit c2f3f6c789
3 changed files with 35 additions and 22 deletions

View file

@ -38,16 +38,17 @@ pub fn constrain(
List(elems) => { list(elems, bound_vars, subs, expected, region) }, List(elems) => { list(elems, bound_vars, subs, expected, region) },
Var(symbol) => Lookup(symbol, expected, region), Var(symbol) => Lookup(symbol, expected, region),
Assign(assignments, ret_expr) => { Assign(assignments, ret_expr) => {
let ret_con = constrain(bound_vars, subs, *ret_expr, expected);
if assignments.len() > 1 { if assignments.len() > 1 {
panic!("TODO can't handle multiple assignments yet"); panic!("TODO can't handle multiple assignments yet");
} else {
let ret_con = constrain(bound_vars, subs, *ret_expr, expected);
let (loc_pattern, loc_expr) = assignments.first().unwrap_or_else(|| {
panic!("assignments.first() failed")
});
constrain_def(loc_pattern.clone(), loc_expr.clone(), bound_vars, subs, ret_con)
} }
for (loc_pattern, loc_expr) in assignments {
return constrain_def(loc_pattern, loc_expr, bound_vars, subs, ret_con);
}
unreachable!();
} }
_ => { panic!("TODO constraints for {:?}", loc_expr.value) } _ => { panic!("TODO constraints for {:?}", loc_expr.value) }
} }
@ -136,21 +137,26 @@ pub fn constrain_def(
vars: Vec::with_capacity(1), vars: Vec::with_capacity(1),
reversed_constraints: Vec::with_capacity(1) reversed_constraints: Vec::with_capacity(1)
}; };
let (mut vars, arg_types) = let (vars, _) =
patterns_to_variables(std::iter::once(loc_pattern.clone()), subs, &mut state); patterns_to_variables(std::iter::once(loc_pattern.clone()), subs, &mut state);
let region = loc_pattern.region;
let ret_var = subs.mk_flex_var(); // Set up types for the expr we're assigned to.
let ret_type = Type::Variable(ret_var); let expr_var = subs.mk_flex_var();
let expr_type = Type::Variable(expr_var);
vars.push(ret_var); // These types are *only* for the current pattern. In contrast, the ones
// in PatternState represent
let mut assignment_types: ImMap<Symbol, Located<Type>> = ImMap::default(); 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!
match loc_pattern.value { match loc_pattern.value {
Pattern::Identifier(symbol) => { Pattern::Identifier(symbol) => {
let loc_type = Located {region: loc_pattern.region, value: ret_type.clone()}; let value = expr_type.clone();
assignment_types.insert(symbol, loc_type); lookup_types.insert(symbol, Located {region, value});
}, },
_ => panic!("TODO constrain patterns other than Identifier") _ => panic!("TODO constrain patterns other than Identifier")
} }
@ -161,14 +167,15 @@ pub fn constrain_def(
rigid_vars: Vec::new(), rigid_vars: Vec::new(),
flex_vars: vars, flex_vars: vars,
assignments_constraint: assignments_constraint:
// This nested constraint represents the actually constrained expr.
Let(Box::new(LetConstraint { Let(Box::new(LetConstraint {
rigid_vars: Vec::new(), rigid_vars: Vec::new(),
flex_vars: state.vars, flex_vars: state.vars,
assignment_types: state.assignment_types, assignment_types: state.assignment_types,
assignments_constraint: And(state.reversed_constraints), assignments_constraint: And(state.reversed_constraints),
ret_constraint: constrain(bound_vars, subs, loc_expr, NoExpectation(ret_type)) ret_constraint: constrain(bound_vars, subs, loc_expr, NoExpectation(expr_type))
})), })),
assignment_types, assignment_types: lookup_types,
ret_constraint, ret_constraint,
})) }))
} }

View file

@ -27,8 +27,9 @@ pub fn infer_expr(subs: &mut Subs, loc_expr: Located<Expr>, procedures: MutMap<S
// Next, constrain the expression. // Next, constrain the expression.
let variable = subs.mk_flex_var(); let variable = subs.mk_flex_var();
let expected = NoExpectation(Variable(variable)); let expected = NoExpectation(Variable(variable));
let constraint = constrain(&bound_vars, subs, loc_expr, expected);
constraints.push(constrain(&bound_vars, subs, loc_expr, expected)); constraints.push(constraint);
solve(&env, subs, Constraint::And(constraints)); solve(&env, subs, Constraint::And(constraints));

View file

@ -60,10 +60,16 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: Constraint) {
// Add a variable for each assignment to the env. // Add a variable for each assignment to the env.
let mut new_env = env.clone(); let mut new_env = env.clone();
for (name, loc_type) in let_con.assignment_types { for (symbol, loc_type) in let_con.assignment_types {
// We must not overwrite existing symbols! If we do,
// we will overwrite procedure entries, which were
// inserted earlier in solving. (If we allowed
// shadowing, we'd need to do something fancier here.)
if !new_env.contains_key(&symbol) {
let var = type_to_variable(subs, loc_type.value); let var = type_to_variable(subs, loc_type.value);
new_env.insert(name, var); new_env.insert(symbol, var);
}
} }
// Now solve the body, using the new env which includes // Now solve the body, using the new env which includes
@ -96,7 +102,6 @@ fn type_to_variable(subs: &mut Subs, typ: Type) -> Variable {
subs.fresh(Descriptor::from(content)) subs.fresh(Descriptor::from(content))
}, },
Function(arg_types, ret_type) => { Function(arg_types, ret_type) => {
let arg_vars = arg_types.into_iter().map(|arg_type| { let arg_vars = arg_types.into_iter().map(|arg_type| {
type_to_variable(subs, arg_type) type_to_variable(subs, arg_type)