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),
|
||||
Str(Box<str>),
|
||||
List(Variable, Vec<Located<Expr>>),
|
||||
EmptyList,
|
||||
|
||||
// Lookups
|
||||
Var(Variable, Symbol),
|
||||
/// Works the same as Var, but has an important marking purpose.
|
||||
/// See 13623e3f5f65ea2d703cf155f16650c1e8246502 for the bug this fixed.
|
||||
FunctionPointer(Variable, Symbol),
|
||||
FunctionPointer(Symbol),
|
||||
|
||||
// Pattern Matching
|
||||
Case(
|
||||
|
@ -26,7 +25,7 @@ pub enum Expr {
|
|||
Box<Located<Expr>>,
|
||||
Vec<(Located<Pattern>, Located<Expr>)>,
|
||||
),
|
||||
Define(
|
||||
Defs(
|
||||
Variable,
|
||||
Vec<(Located<Pattern>, Located<Expr>)>,
|
||||
Box<Located<Expr>>,
|
||||
|
|
382
src/can/mod.rs
382
src/can/mod.rs
|
@ -129,13 +129,14 @@ fn canonicalize_expr(
|
|||
}
|
||||
ast::Expr::List(loc_elems) => {
|
||||
if loc_elems.is_empty() {
|
||||
let list_var = subs.mk_flex_var();
|
||||
let constraint = Eq(
|
||||
constrain::empty_list_type(subs.mk_flex_var()),
|
||||
constrain::empty_list_type(list_var),
|
||||
expected,
|
||||
region.clone(),
|
||||
);
|
||||
|
||||
(EmptyList, Output::new(constraint))
|
||||
(List(list_var, Vec::new()), Output::new(constraint))
|
||||
} else {
|
||||
let mut can_elems = Vec::with_capacity(loc_elems.len());
|
||||
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!
|
||||
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)
|
||||
//}
|
||||
ast::Expr::Apply((loc_fn, loc_args)) => {
|
||||
panic!("TODO canonicalize Apply");
|
||||
// let expected_type = panic!("TODO expected type for Apply");
|
||||
// // Canonicalize the function expression and its arguments
|
||||
// let (fn_expr, mut output) = canonicalize_expr(
|
||||
// env,
|
||||
// subs,
|
||||
// scope,
|
||||
// loc_fn.region.clone(),
|
||||
// &loc_fn.value,
|
||||
// expected_type,
|
||||
// );
|
||||
// let mut args = Vec::new();
|
||||
// let mut outputs = Vec::new();
|
||||
// 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_fn.region.clone();
|
||||
let fn_expected = NoExpectation(fn_type.clone());
|
||||
// TODO look up the name and use NamedFnArg if possible.
|
||||
let fn_reason = Reason::AnonymousFnCall(loc_args.len() as u8);
|
||||
|
||||
// for loc_arg in loc_args.iter() {
|
||||
// let expected_type = panic!("TODO expected type for Apply arg");
|
||||
// let (arg_expr, arg_out) = canonicalize_expr(
|
||||
// env,
|
||||
// subs,
|
||||
// scope,
|
||||
// loc_arg.region.clone(),
|
||||
// &loc_arg.value,
|
||||
// expected_type,
|
||||
// );
|
||||
// Canonicalize the function expression and its arguments
|
||||
let (fn_expr, mut output) = canonicalize_expr(
|
||||
env,
|
||||
subs,
|
||||
scope,
|
||||
loc_fn.region.clone(),
|
||||
&loc_fn.value,
|
||||
fn_expected,
|
||||
);
|
||||
let fn_con = output.constraint;
|
||||
|
||||
// args.push(arg_expr);
|
||||
// outputs.push(arg_out);
|
||||
// }
|
||||
// The function's return type
|
||||
let ret_var = subs.mk_flex_var();
|
||||
let ret_type = Variable(ret_var);
|
||||
|
||||
// match &fn_expr.value {
|
||||
// &Var(_, ref sym) => {
|
||||
// output.references.calls.insert(sym.clone());
|
||||
// }
|
||||
// _ => (),
|
||||
// };
|
||||
// This will be used in the occurs check
|
||||
let mut vars = Vec::with_capacity(2 + loc_args.len());
|
||||
|
||||
// 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 {
|
||||
// output.references = output.references.union(arg_out.references);
|
||||
// }
|
||||
let mut arg_types = Vec::with_capacity(loc_args.len());
|
||||
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.
|
||||
// output.tail_call = None;
|
||||
let mut args = Vec::new();
|
||||
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)) => {
|
||||
let op = loc_op.value;
|
||||
|
@ -468,93 +518,121 @@ fn canonicalize_expr(
|
|||
can_defs(env, subs, scope.clone(), defs, expected, loc_ret)
|
||||
}
|
||||
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
|
||||
// // into a top-level procedure for code gen.
|
||||
// //
|
||||
// // The symbol includes the module name, the top-level declaration name, and the
|
||||
// // index (0-based) of the closure within that declaration.
|
||||
// //
|
||||
// // Example: "MyModule$main$3" if this is the 4th closure in MyModule.main.
|
||||
// let symbol = scope.gen_unique_symbol();
|
||||
// The globally unique symbol that will refer to this closure once it gets converted
|
||||
// into a top-level procedure for code gen.
|
||||
//
|
||||
// The symbol includes the module name, the top-level declaration name, and the
|
||||
// index (0-based) of the closure within that declaration.
|
||||
//
|
||||
// Example: "MyModule$main$3" if this is the 4th closure in MyModule.main.
|
||||
let symbol = scope.gen_unique_symbol();
|
||||
|
||||
// // The body expression 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();
|
||||
// The body expression 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();
|
||||
|
||||
// let arg_idents: Vec<(Ident, (Symbol, Region))> =
|
||||
// idents_from_patterns(loc_arg_patterns.iter(), &scope);
|
||||
let arg_idents: Vec<(Ident, (Symbol, Region))> =
|
||||
idents_from_patterns(loc_arg_patterns.iter(), &scope);
|
||||
|
||||
// // Add the arguments' idents to scope.idents. If there's a collision,
|
||||
// // it means there was shadowing, which will be handled later.
|
||||
// scope.idents = union_pairs(scope.idents, arg_idents.iter());
|
||||
// Add the arguments' idents to scope.idents. If there's a collision,
|
||||
// it means there was shadowing, which will be handled later.
|
||||
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() {
|
||||
// // 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, &mut shadowable_idents);
|
||||
for loc_pattern in loc_arg_patterns.into_iter() {
|
||||
// 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, &mut shadowable_idents);
|
||||
|
||||
// can_args.push(canonicalize_pattern(
|
||||
// env,
|
||||
// subs,
|
||||
// constraints,
|
||||
// &mut scope,
|
||||
// &FunctionArg,
|
||||
// &loc_pattern,
|
||||
// &mut shadowable_idents,
|
||||
// ))
|
||||
// }
|
||||
can_args.push(canonicalize_pattern(
|
||||
env,
|
||||
subs,
|
||||
&mut scope,
|
||||
&FunctionArg,
|
||||
&loc_pattern,
|
||||
&mut shadowable_idents,
|
||||
))
|
||||
}
|
||||
|
||||
// let (loc_body_expr, mut output) = canonicalize_expr(
|
||||
// env,
|
||||
// constraints,
|
||||
// subs,
|
||||
// &mut scope,
|
||||
// loc_body_expr.region.clone(),
|
||||
// &loc_body_expr.value,
|
||||
// panic!("TODO expected type"),
|
||||
// );
|
||||
let body_type = NoExpectation(args.ret_type);
|
||||
let (loc_body_expr, mut output) = canonicalize_expr(
|
||||
env,
|
||||
subs,
|
||||
&mut scope,
|
||||
loc_body_expr.region.clone(),
|
||||
&loc_body_expr.value,
|
||||
body_type,
|
||||
);
|
||||
|
||||
// // Now that we've collected all the references, check to see if any of the args we defined
|
||||
// // 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,
|
||||
// }));
|
||||
// }
|
||||
state.reversed_constraints.reverse();
|
||||
|
||||
// // We shouldn't ultimately count arguments as referenced locals. Otherwise,
|
||||
// // we end up with weird conclusions like the expression (\x -> x + 1)
|
||||
// // references the (nonexistant) local variable x!
|
||||
// output.references.locals.remove(&arg_symbol);
|
||||
// }
|
||||
let assignments_constraint = And(state.reversed_constraints);
|
||||
let ret_constraint = output.constraint;
|
||||
|
||||
// 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,
|
||||
// // since we removed the only locals it shouldn't close over (its arguments).
|
||||
// // Register it as a top-level procedure in the Env!
|
||||
// env.register_closure(
|
||||
// symbol.clone(),
|
||||
// can_args,
|
||||
// loc_body_expr,
|
||||
// region.clone(),
|
||||
// output.references.clone(),
|
||||
// var,
|
||||
// );
|
||||
// We need a var to record in the procedure for later.
|
||||
// We'll set it equal to the overall `expected` we've been passed.
|
||||
let var = subs.mk_flex_var();
|
||||
let typ = Variable(var);
|
||||
|
||||
// // Always return a function pointer, in case that's how the closure is being used (e.g. with Apply).
|
||||
// (FunctionPointer(subs.mk_flex_var(), symbol), output)
|
||||
//}
|
||||
output.constraint = And(vec![
|
||||
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
|
||||
// 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,
|
||||
// we end up with weird conclusions like the expression (\x -> x + 1)
|
||||
// references the (nonexistant) local variable x!
|
||||
output.references.locals.remove(&arg_symbol);
|
||||
}
|
||||
|
||||
// We've finished analyzing the closure. Its references.locals are now the values it closes over,
|
||||
// since we removed the only locals it shouldn't close over (its arguments).
|
||||
// Register it as a top-level procedure in the Env!
|
||||
env.register_closure(
|
||||
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).
|
||||
(FunctionPointer(symbol), 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());
|
||||
|
@ -631,7 +709,7 @@ fn canonicalize_expr(
|
|||
// let expr = Case(Box::new(can_cond), can_branches);
|
||||
|
||||
// (expr, output)
|
||||
}
|
||||
// }
|
||||
ast::Expr::SpaceBefore(sub_expr, _spaces) => {
|
||||
// Continue on; spaces don't do anything.
|
||||
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 {
|
||||
assignment_types: ImMap<Symbol, Located<Type>>,
|
||||
|
@ -1387,7 +1474,7 @@ fn can_defs<'a>(
|
|||
(
|
||||
&ast::Pattern::Identifier(ref name),
|
||||
&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,
|
||||
// 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,
|
||||
)
|
||||
}
|
||||
|
@ -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::symbol::Symbol;
|
||||
use collections::ImMap;
|
||||
use constrain::{self, Constraints};
|
||||
use ident::{Ident, VariantName};
|
||||
use parse::ast;
|
||||
use region::{Located, Region};
|
||||
use subs::Subs;
|
||||
use subs::Variable;
|
||||
use types::Constraint;
|
||||
|
||||
/// A pattern, including possible problems (e.g. shadowing) so that
|
||||
/// 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 can::pattern::Pattern;
|
||||
use can::procedure::Procedure;
|
||||
// use can::symbol::Symbol;
|
||||
use collections::ImMap;
|
||||
use operator::{ArgSide, Operator};
|
||||
use region::{Located, Region};
|
||||
use region::Region;
|
||||
use subs::{Subs, Variable};
|
||||
use types::Constraint::{self, *};
|
||||
use types::Expected::{self, *};
|
||||
use types::Type::{self, *};
|
||||
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 {
|
||||
let typ = number_literal_type("Int", "Integer");
|
||||
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 {
|
||||
list_type(Type::Variable(var))
|
||||
}
|
||||
|
@ -376,190 +67,3 @@ pub fn list_type(typ: Type) -> Type {
|
|||
pub fn str_type() -> Type {
|
||||
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 subs::Variable;
|
||||
|
||||
type ModuleName = String;
|
||||
|
||||
// The standard modules
|
||||
pub const MOD_FLOAT: &'static str = "Float";
|
||||
pub const MOD_INT: &'static str = "Int";
|
||||
|
|
|
@ -9,7 +9,6 @@ use roc::can::procedure::Procedure;
|
|||
use roc::can::symbol::Symbol;
|
||||
use roc::can::Output;
|
||||
use roc::collections::{ImMap, MutMap};
|
||||
use roc::constrain::Constraints;
|
||||
use roc::ident::Ident;
|
||||
use roc::parse;
|
||||
use roc::parse::ast::{self, Attempting};
|
||||
|
@ -19,10 +18,12 @@ use roc::region::{Located, Region};
|
|||
use roc::subs::{Subs, Variable};
|
||||
use roc::types::{Expected, Type};
|
||||
|
||||
#[allow(dead_code)]
|
||||
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)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
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 parser = space0_before(loc(parse::expr(0)), 0);
|
||||
|
|
|
@ -13,13 +13,11 @@ mod test_infer {
|
|||
use helpers::can_expr;
|
||||
use roc::infer::infer_expr;
|
||||
use roc::pretty_print_types::content_to_string;
|
||||
use roc::region::Located;
|
||||
use roc::subs::Subs;
|
||||
|
||||
// HELPERS
|
||||
|
||||
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 actual_str = content_to_string(content, &mut subs);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue