Canonicalize without constraining.

This commit is contained in:
Richard Feldman 2019-12-29 19:47:52 -08:00
parent 7a07c19275
commit be2d20162c
8 changed files with 221 additions and 817 deletions

View file

@ -3,12 +3,11 @@ use crate::can::env::Env;
use crate::can::expr::Expr::{self, *};
use crate::can::expr::{
canonicalize_expr, local_successors, references_from_call, references_from_local, union_pairs,
Output, Recursive, Rigids,
Output, Recursive,
};
use crate::can::ident::Lowercase;
use crate::can::pattern::remove_idents;
use crate::can::pattern::PatternType::*;
use crate::can::pattern::{canonicalize_pattern, idents_from_patterns, Pattern};
use crate::can::pattern::{remove_idents, PatternState};
use crate::can::problem::Problem;
use crate::can::problem::RuntimeError;
use crate::can::problem::RuntimeError::*;
@ -21,10 +20,7 @@ use crate::ident::Ident;
use crate::parse::ast;
use crate::region::{Located, Region};
use crate::subs::{VarStore, Variable};
use crate::types::Constraint::{self, *};
use crate::types::Expected::{self, *};
use crate::types::Type;
use crate::types::{LetConstraint, PExpected};
use im_rc::Vector;
use std::fmt::Debug;
@ -42,32 +38,12 @@ pub struct CanDefs {
pub defined_idents: Vector<(Ident, (Symbol, Region))>,
}
#[derive(Default)]
pub struct Info {
pub vars: Vec<Variable>,
pub constraints: Vec<Constraint>,
pub def_types: SendMap<Symbol, Located<Type>>,
}
impl Info {
pub fn with_capacity(capacity: usize) -> Self {
Info {
vars: Vec::with_capacity(capacity),
constraints: Vec::with_capacity(capacity),
def_types: SendMap::default(),
}
}
}
#[inline(always)]
pub fn canonicalize_defs<'a>(
rigids: &Rigids,
found_rigids: &mut SendMap<Variable, Lowercase>,
env: &mut Env,
var_store: &VarStore,
scope: &mut Scope,
loc_defs: &'a bumpalo::collections::Vec<'a, &'a Located<ast::Def<'a>>>,
flex_info: &mut Info,
) -> CanDefs {
use crate::parse::ast::Def::*;
@ -100,8 +76,6 @@ pub fn canonicalize_defs<'a>(
let typed = TypedDef(body_pattern, annotation.clone(), body_expr);
canonicalize_def(
rigids,
found_rigids,
env,
Located {
region: loc_def.region,
@ -109,15 +83,12 @@ pub fn canonicalize_defs<'a>(
},
scope,
&mut can_defs_by_symbol,
flex_info,
var_store,
&mut refs_by_symbol,
);
}
_ => {
canonicalize_def(
rigids,
found_rigids,
env,
Located {
region: loc_def.region,
@ -125,7 +96,6 @@ pub fn canonicalize_defs<'a>(
},
scope,
&mut can_defs_by_symbol,
flex_info,
var_store,
&mut refs_by_symbol,
);
@ -135,8 +105,6 @@ pub fn canonicalize_defs<'a>(
_ => {
canonicalize_def(
rigids,
found_rigids,
env,
Located {
region: loc_def.region,
@ -144,7 +112,6 @@ pub fn canonicalize_defs<'a>(
},
scope,
&mut can_defs_by_symbol,
flex_info,
var_store,
&mut refs_by_symbol,
);
@ -322,98 +289,54 @@ fn canonicalize_def_pattern(
scope: &mut Scope,
var_store: &VarStore,
expr_type: Type,
) -> (PatternState, Located<Pattern>) {
) -> Located<Pattern> {
// 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);
let pattern_expected = PExpected::NoExpectation(expr_type);
let mut state = PatternState {
headers: SendMap::default(),
vars: Vec::with_capacity(1),
constraints: Vec::with_capacity(1),
};
let loc_can_pattern = canonicalize_pattern(
canonicalize_pattern(
env,
&mut state,
var_store,
scope,
Assignment,
&loc_pattern.value,
loc_pattern.region,
&mut shadowable_idents,
pattern_expected,
);
(state, loc_can_pattern)
)
}
#[allow(clippy::too_many_arguments)]
fn canonicalize_def<'a>(
rigids: &Rigids,
found_rigids: &mut SendMap<Variable, Lowercase>,
env: &mut Env,
loc_def: Located<&'a ast::Def<'a>>,
scope: &mut Scope,
can_defs_by_symbol: &mut MutMap<Symbol, Def>,
flex_info: &mut Info,
var_store: &VarStore,
refs_by_symbol: &mut MutMap<Symbol, (Located<Ident>, References)>,
) {
use crate::parse::ast::Def::*;
use crate::types::AnnotationSource;
// Make types for the body expr, even if we won't end up having a body.
let expr_var = var_store.fresh();
let expr_type = Type::Variable(expr_var);
let mut vars_by_symbol = SendMap::default();
flex_info.vars.push(expr_var);
// Each def gets to have all the idents in scope that are defined in this
// block. Order of defs doesn't matter, thanks to referential transparency!
match loc_def.value {
Annotation(loc_pattern, loc_annotation) => {
let (pattern_state, loc_can_pattern) =
let loc_can_pattern =
canonicalize_def_pattern(env, loc_pattern, scope, var_store, expr_type.clone());
for (k, v) in pattern_state.headers.clone() {
flex_info.def_types.insert(k, v);
}
// annotation sans body cannot introduce new rigids that are visible in other annotations
// but the rigids can show up in type error messages, so still register them
let (seen_rigids, can_annotation) =
canonicalize_annotation(&loc_annotation.value, var_store);
// union seen rigids with already found ones
for (k, v) in seen_rigids {
found_rigids.insert(k, v);
let (_, can_annotation) = canonicalize_annotation(&loc_annotation.value, var_store);
if true {
panic!("TODO replace this call to canonicalize_annotation with something that *only* gets the arity, since that's all we use");
}
let arity = can_annotation.arity();
let annotation_expected = FromAnnotation(
loc_can_pattern.clone(),
arity,
AnnotationSource::TypedBody,
can_annotation,
);
flex_info.constraints.push(Let(Box::new(LetConstraint {
rigid_vars: Vec::new(),
flex_vars: pattern_state.vars,
def_types: pattern_state.headers,
defs_constraint: And(vec![
And(pattern_state.constraints),
Eq(expr_type, annotation_expected, loc_annotation.region),
]),
ret_constraint: True,
})));
// Fabricate a body for this annotation, that will error at runtime
let value = Expr::RuntimeError(NoImplementation);
let loc_can_expr = if arity > 0 {
@ -433,13 +356,15 @@ fn canonicalize_def<'a>(
region: Region::zero(),
};
underscores.push(underscore);
underscores.push((var_store.fresh(), underscore));
}
let body = Box::new(Located {
let body_expr = Located {
value,
region: loc_annotation.region,
});
};
let body = Box::new((var_store.fresh(), body_expr));
Located {
value: Closure(symbol, Recursive::NotRecursive, underscores, body),
@ -465,13 +390,9 @@ fn canonicalize_def<'a>(
}
TypedDef(loc_pattern, loc_annotation, loc_expr) => {
let (pattern_state, loc_can_pattern) =
let loc_can_pattern =
canonicalize_def_pattern(env, loc_pattern, scope, var_store, expr_type.clone());
for (k, v) in &pattern_state.headers {
flex_info.def_types.insert(k.clone(), v.clone());
}
// bookkeeping for tail-call detection. If we're assigning to an
// identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called.
let outer_identifier = env.tailcallable_symbol.clone();
@ -483,58 +404,12 @@ fn canonicalize_def<'a>(
vars_by_symbol.insert(defined_symbol.clone(), expr_var);
};
let (seen_rigids, can_annotation) =
canonicalize_annotation(&loc_annotation.value, var_store);
let mut ftv: Rigids = rigids.clone();
for (var, name_lowercase) in seen_rigids {
let name: Box<str> = name_lowercase.clone().into();
// if the rigid is known already, nothing needs to happen
// otherwise register it.
if !rigids.contains_key(&name) {
// possible use this rigid in nested def's
ftv.insert(name, Type::Variable(var));
// mark this variable as a rigid
found_rigids.insert(var, name_lowercase);
}
}
let annotation_expected = FromAnnotation(
loc_can_pattern.clone(),
can_annotation.arity(),
AnnotationSource::TypedBody,
can_annotation,
);
let (mut loc_can_expr, can_output, ret_constraint) = canonicalize_expr(
&ftv,
env,
var_store,
scope,
loc_expr.region,
&loc_expr.value,
annotation_expected.clone(),
);
// ensure expected type unifies with annotated type
flex_info
.constraints
.push(Eq(expr_type, annotation_expected, loc_def.region));
let (mut loc_can_expr, can_output) =
canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value);
// reset the tailcallable_symbol
env.tailcallable_symbol = outer_identifier;
flex_info.constraints.push(Let(Box::new(LetConstraint {
rigid_vars: Vec::new(),
flex_vars: pattern_state.vars,
def_types: pattern_state.headers,
defs_constraint: And(pattern_state.constraints),
ret_constraint,
})));
// see below: a closure needs a fresh References!
let mut is_closure = false;
@ -643,13 +518,9 @@ fn canonicalize_def<'a>(
// If we have a pattern, then the def has a body (that is, it's not a
// standalone annotation), so we need to canonicalize the pattern and expr.
Body(loc_pattern, loc_expr) => {
let (pattern_state, loc_can_pattern) =
let loc_can_pattern =
canonicalize_def_pattern(env, loc_pattern, scope, var_store, expr_type.clone());
for (k, v) in &pattern_state.headers {
flex_info.def_types.insert(k.clone(), v.clone());
}
// bookkeeping for tail-call detection. If we're assigning to an
// identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called.
let outer_identifier = env.tailcallable_symbol.clone();
@ -665,27 +536,12 @@ fn canonicalize_def<'a>(
vars_by_symbol.insert(defined_symbol.clone(), expr_var);
};
let (mut loc_can_expr, can_output, ret_constraint) = canonicalize_expr(
rigids,
env,
var_store,
scope,
loc_expr.region,
&loc_expr.value,
NoExpectation(expr_type),
);
let (mut loc_can_expr, can_output) =
canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value);
// reset the tailcallable_symbol
env.tailcallable_symbol = outer_identifier;
flex_info.constraints.push(Let(Box::new(LetConstraint {
rigid_vars: Vec::new(),
flex_vars: pattern_state.vars,
def_types: pattern_state.headers,
defs_constraint: And(pattern_state.constraints),
ret_constraint,
})));
// see below: a closure needs a fresh References!
let mut is_closure = false;
@ -794,8 +650,6 @@ fn canonicalize_def<'a>(
Nested(value) => {
canonicalize_def(
rigids,
found_rigids,
env,
Located {
value,
@ -803,7 +657,6 @@ fn canonicalize_def<'a>(
},
scope,
can_defs_by_symbol,
flex_info,
var_store,
refs_by_symbol,
);
@ -863,72 +716,25 @@ where
}
#[inline(always)]
#[allow(clippy::too_many_arguments)]
pub fn can_defs_with_return<'a>(
rigids: &Rigids,
env: &mut Env,
var_store: &VarStore,
mut scope: Scope,
loc_defs: &'a bumpalo::collections::Vec<'a, &'a Located<ast::Def<'a>>>,
expected: Expected<Type>,
mut flex_info: Info,
rigid_info: Info,
loc_ret: &'a Located<ast::Expr<'a>>,
) -> (Expr, Output, Constraint) {
let mut found_rigids = SendMap::default();
let unsorted = canonicalize_defs(
rigids,
&mut found_rigids,
env,
var_store,
&mut scope,
loc_defs,
&mut flex_info,
);
) -> (Expr, Output) {
let unsorted = canonicalize_defs(env, var_store, &mut scope, loc_defs);
// The def as a whole is a tail call iff its return expression is a tail call.
// Use its output as a starting point because its tail_call already has the right answer!
let (ret_expr, output, ret_con) = canonicalize_expr(
rigids,
env,
var_store,
&mut scope,
loc_ret.region,
&loc_ret.value,
expected,
);
let (ret_expr, output) =
canonicalize_expr(env, var_store, &mut scope, loc_ret.region, &loc_ret.value);
let (can_defs, mut output) = sort_can_defs(env, unsorted, output);
output.rigids = output.rigids.union(found_rigids);
// Rigid constraint for the def expr as a whole.
// This is a "LetRec" constraint; it supports recursion.
// (The only advantage of "Let" over "LetRec" is if you want to
// shadow things, and Roc disallows shadowing anyway.)
let constraint = Let(Box::new(LetConstraint {
rigid_vars: rigid_info.vars,
flex_vars: Vec::new(),
def_types: rigid_info.def_types,
defs_constraint: True,
ret_constraint: Let(Box::new(LetConstraint {
rigid_vars: Vec::new(),
flex_vars: flex_info.vars,
def_types: flex_info.def_types.clone(),
defs_constraint: Let(Box::new(LetConstraint {
flex_vars: Vec::new(),
rigid_vars: Vec::new(),
def_types: flex_info.def_types,
defs_constraint: True,
ret_constraint: And(flex_info.constraints),
})),
ret_constraint: And(vec![And(rigid_info.constraints), ret_con]),
})),
}));
match can_defs {
Ok(defs) => (Defs(defs, Box::new(ret_expr)), output, constraint),
Err(err) => (RuntimeError(err), output, constraint),
Ok(defs) => (Defs(defs, Box::new(ret_expr)), output),
Err(err) => (RuntimeError(err), output),
}
}

View file

@ -1,4 +1,4 @@
use crate::can::def::{can_defs_with_return, Def, Info};
use crate::can::def::{can_defs_with_return, Def};
use crate::can::env::Env;
use crate::can::ident::Lowercase;
use crate::can::num::{
@ -15,17 +15,14 @@ use crate::can::procedure::References;
use crate::can::scope::Scope;
use crate::can::symbol::Symbol;
use crate::collections::{ImMap, ImSet, MutMap, MutSet, SendMap};
use crate::constrain::{self, exists};
use crate::ident::Ident;
use crate::operator::CalledVia;
use crate::parse::ast;
use crate::region::{Located, Region};
use crate::subs::{VarStore, Variable};
use crate::types::AnnotationSource::*;
use crate::types::Constraint::{self, *};
use crate::types::Expected::{self, *};
use crate::types::Type::{self, *};
use crate::types::{LetConstraint, PExpected, PReason, Reason};
use im_rc::Vector;
use std::fmt::Debug;
use std::i64;
@ -40,17 +37,16 @@ pub type Rigids = ImMap<Box<str>, Type>;
pub struct Output {
pub references: References,
pub tail_call: Option<Symbol>,
pub rigids: SendMap<Variable, Lowercase>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Expr {
// Literals
Int(i64),
Float(f64),
Int(Variable, i64),
Float(Variable, f64),
Str(Box<str>),
BlockStr(Box<str>),
List(Variable, Vec<Located<Expr>>),
List(Variable, Vec<(Variable, Located<Expr>)>),
// Lookups
Var(Variable, Symbol),
@ -65,26 +61,40 @@ pub enum Expr {
When(
Variable,
Box<Located<Expr>>,
Vec<(Located<Pattern>, Located<Expr>)>,
Vec<((Variable, Located<Pattern>), (Variable, Located<Expr>))>,
),
Defs(Vec<Def>, Box<Located<Expr>>),
/// This is *only* for calling functions, not for tag application.
/// The Tag variant contains any applied values inside it.
Call(Box<Expr>, Vec<Located<Expr>>, CalledVia),
Call(Box<Expr>, Vec<(Variable, Located<Expr>)>, CalledVia),
Closure(Symbol, Recursive, Vec<Located<Pattern>>, Box<Located<Expr>>),
Closure(
Symbol,
Recursive,
Vec<(Variable, Located<Pattern>)>,
Box<(Variable, Located<Expr>)>,
),
// Product Types
Record(Variable, SendMap<Lowercase, Located<Expr>>),
Record(Variable, SendMap<Lowercase, (Variable, Located<Expr>)>),
/// Empty record constant
EmptyRecord,
/// Look up exactly one field on a record, e.g. (expr).foo.
Access(Box<Located<Expr>>, Box<str>),
Access {
ext_var: Variable,
field_var: Variable,
loc_expr: Box<Located<Expr>>,
field: Lowercase,
},
/// field accessor as a function, e.g. (.foo) expr
Accessor(Box<str>),
Accessor {
ext_var: Variable,
field_var: Variable,
field: Lowercase,
},
// Sum Types
Tag(Box<str>, Vec<Expr>),
@ -101,141 +111,71 @@ pub enum Recursive {
}
pub fn canonicalize_expr(
rigids: &Rigids,
env: &mut Env,
var_store: &VarStore,
scope: &mut Scope,
region: Region,
expr: &ast::Expr,
expected: Expected<Type>,
) -> (Located<Expr>, Output, Constraint) {
) -> (Located<Expr>, Output) {
use self::Expr::*;
let (expr, output, constraint) = match expr {
let (expr, output) = match expr {
ast::Expr::Int(string) => {
let (constraint, answer) =
int_expr_from_result(var_store, finish_parsing_int(string), env, expected, region);
let answer = int_expr_from_result(var_store, finish_parsing_int(string), env);
(answer, Output::default(), constraint)
(answer, Output::default())
}
ast::Expr::Float(string) => {
let (constraint, answer) = float_expr_from_result(
var_store,
finish_parsing_float(string),
env,
expected,
region,
);
let answer = float_expr_from_result(var_store, finish_parsing_float(string), env);
(answer, Output::default(), constraint)
(answer, Output::default())
}
ast::Expr::Record(fields) => {
if fields.is_empty() {
let constraint = Eq(EmptyRec, expected, region);
(EmptyRecord, Output::default(), constraint)
(EmptyRecord, Output::default())
} else {
let mut field_exprs = SendMap::default();
let mut field_types = SendMap::default();
let mut field_vars = Vec::with_capacity(fields.len());
// Constraints need capacity for each field + 1 for the record itself.
let mut constraints = Vec::with_capacity(1 + fields.len());
let mut can_fields = SendMap::default();
let mut output = Output::default();
for loc_field in fields.iter() {
let (label, field_expr, field_out, field_var, field_type, field_con) =
canonicalize_field(
rigids,
env,
var_store,
scope,
&loc_field.value,
loc_field.region,
);
field_vars.push(field_var);
field_exprs.insert(label.clone(), field_expr);
field_types.insert(label, field_type);
constraints.push(field_con);
output.references = output.references.union(field_out.references);
}
let record_type = Type::Record(
field_types,
// TODO can we avoid doing Box::new on every single one of these?
// For example, could we have a single lazy_static global Box they
// could all share?
Box::new(Type::EmptyRec),
);
let record_con = Eq(record_type, expected.clone(), region);
constraints.push(record_con);
// variable to store in the AST
let stored_var = var_store.fresh();
let stored_con = Eq(Type::Variable(stored_var), expected, region);
field_vars.push(stored_var);
constraints.push(stored_con);
let constraint = exists(field_vars, And(constraints));
(Record(stored_var, field_exprs), output, constraint)
}
}
ast::Expr::Str(string) => {
let constraint = Eq(constrain::str_type(), expected, region);
(Str((*string).into()), Output::default(), constraint)
}
ast::Expr::BlockStr(lines) => {
let constraint = Eq(constrain::str_type(), expected, region);
let joined = lines.iter().copied().collect::<Vec<&str>>().join("\n");
(BlockStr(joined.into()), Output::default(), constraint)
}
ast::Expr::List(loc_elems) => {
if loc_elems.is_empty() {
let list_var = var_store.fresh();
let constraint = Eq(constrain::empty_list_type(list_var), expected, region);
(List(list_var, Vec::new()), Output::default(), constraint)
} else {
let mut can_elems = Vec::with_capacity(loc_elems.len());
let list_var = var_store.fresh(); // `v` in the type (List v)
let list_type = Type::Variable(list_var);
let mut constraints = Vec::with_capacity(1 + (loc_elems.len() * 2));
let mut references = References::new();
for loc_elem in loc_elems.iter() {
let elem_var = var_store.fresh();
let elem_type = Variable(elem_var);
let elem_expected = NoExpectation(elem_type.clone());
let list_elem_constraint = Eq(
list_type.clone(),
ForReason(Reason::ElemInList, elem_type, region),
region,
);
let (can_expr, elem_out, constraint) = canonicalize_expr(
rigids,
let (label, field_expr, field_out, field_var) = canonicalize_field(
env,
var_store,
scope,
loc_elem.region,
&loc_elem.value,
elem_expected,
&loc_field.value,
loc_field.region,
);
constraints.push(list_elem_constraint);
constraints.push(constraint);
can_fields.insert(label, (field_var, field_expr));
output.references = output.references.union(field_out.references);
}
(Record(var_store.fresh(), can_fields), output)
}
}
ast::Expr::Str(string) => (Str((*string).into()), Output::default()),
ast::Expr::BlockStr(lines) => {
let joined = lines.iter().copied().collect::<Vec<&str>>().join("\n");
(BlockStr(joined.into()), Output::default())
}
ast::Expr::List(loc_elems) => {
if loc_elems.is_empty() {
(List(var_store.fresh(), Vec::new()), Output::default())
} else {
let mut can_elems = Vec::with_capacity(loc_elems.len());
let mut references = References::new();
for loc_elem in loc_elems.iter() {
let (can_expr, elem_out) =
canonicalize_expr(env, var_store, scope, loc_elem.region, &loc_elem.value);
references = references.union(elem_out.references);
can_elems.push(can_expr);
can_elems.push((var_store.fresh(), can_expr));
}
constraints.push(Eq(constrain::list_type(list_type), expected, region));
let mut output = Output::default();
output.references = references;
@ -243,72 +183,28 @@ pub fn canonicalize_expr(
// A list literal is never a tail call!
output.tail_call = None;
(List(list_var, can_elems), output, And(constraints))
(List(var_store.fresh(), can_elems), output)
}
}
ast::Expr::Apply(loc_fn, loc_args, application_style) => {
// The expression that evaluates to the function being called, e.g. `foo` in
// (foo) bar baz
let fn_var = var_store.fresh();
let fn_type = Variable(fn_var);
let fn_region = loc_fn.region;
let fn_expected = NoExpectation(fn_type.clone());
// TODO look up the name and use NamedFnArg if possible.
let fn_reason = Reason::AnonymousFnCall {
arity: loc_args.len() as u8,
};
// Canonicalize the function expression and its arguments
let (fn_expr, mut output, fn_con) = canonicalize_expr(
rigids,
env,
var_store,
scope,
loc_fn.region,
&loc_fn.value,
fn_expected,
);
let (fn_expr, mut output) =
canonicalize_expr(env, var_store, scope, loc_fn.region, &loc_fn.value);
// The function's return type
let ret_var = var_store.fresh();
let ret_type = Variable(ret_var);
// This will be used in the occurs check
let mut vars = Vec::with_capacity(2 + loc_args.len());
vars.push(fn_var);
vars.push(ret_var);
let mut arg_types = Vec::with_capacity(loc_args.len());
let mut arg_cons = Vec::with_capacity(loc_args.len());
let mut args = Vec::new();
let mut outputs = Vec::new();
for (index, loc_arg) in loc_args.iter().enumerate() {
let region = loc_arg.region;
let arg_var = var_store.fresh();
let arg_type = Variable(arg_var);
// TODO look up the name and use NamedFnArg if possible.
let reason = Reason::AnonymousFnArg {
arg_index: index as u8,
};
let expected_arg = ForReason(reason, arg_type.clone(), region);
let (arg_expr, arg_out, arg_con) = canonicalize_expr(
rigids,
env,
var_store,
scope,
loc_arg.region,
&loc_arg.value,
expected_arg,
);
for loc_arg in loc_args {
let (arg_expr, arg_out) =
canonicalize_expr(env, var_store, scope, loc_arg.region, &loc_arg.value);
vars.push(arg_var);
arg_types.push(arg_type);
arg_cons.push(arg_con);
args.push(arg_expr);
args.push((var_store.fresh(), arg_expr));
outputs.push(arg_out);
}
@ -331,7 +227,7 @@ pub fn canonicalize_expr(
}
RuntimeError(_) => {
// We can't call a runtime error; bail out by propagating it!
return (fn_expr, output, True);
return (fn_expr, output);
}
not_var => {
// This could be something like ((if True then fn1 else fn2) arg1 arg2).
@ -343,23 +239,7 @@ pub fn canonicalize_expr(
output.references = output.references.union(arg_out.references);
}
let expected_fn_type = ForReason(
fn_reason,
Function(arg_types, Box::new(ret_type.clone())),
region,
);
let constraint = exists(
vars,
And(vec![
fn_con,
Eq(fn_type, expected_fn_type, fn_region),
And(arg_cons),
Eq(ret_type, expected, region),
]),
);
(expr, output, constraint)
(expr, output)
}
ast::Expr::Var(module_parts, name) => {
let symbol = if module_parts.is_empty() {
@ -370,10 +250,8 @@ pub fn canonicalize_expr(
let ident = Ident::new(module_parts, name);
canonicalize_lookup(env, scope, ident, symbol, region, expected, var_store)
}
//ast::Expr::InterpolatedStr(pairs, suffix) => {
canonicalize_lookup(env, scope, ident, symbol, region, var_store)
} //ast::Expr::InterpolatedStr(pairs, suffix) => {
// let mut output = Output::new();
// let can_pairs: Vec<(String, Located<Expr>)> = pairs
// .into_iter()
@ -415,16 +293,12 @@ pub fn canonicalize_expr(
//}
ast::Expr::Defs(loc_defs, loc_ret) => {
can_defs_with_return(
rigids,
env,
var_store,
// The body expression gets a new scope for canonicalization,
// so clone it.
scope.clone(),
loc_defs,
expected,
Info::with_capacity(loc_defs.len()),
Info::with_capacity(loc_defs.len()),
loc_ret,
)
}
@ -457,13 +331,7 @@ pub fn canonicalize_expr(
vars: Vec::with_capacity(loc_arg_patterns.len()),
constraints: Vec::with_capacity(1),
};
let mut can_args: Vec<Located<Pattern>> = Vec::with_capacity(loc_arg_patterns.len());
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
let ret_var = var_store.fresh();
let ret_type = Type::Variable(ret_var);
vars.push(ret_var);
let mut can_args = 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!
@ -471,56 +339,25 @@ pub fn canonicalize_expr(
let mut shadowable_idents = scope.idents.clone();
remove_idents(&loc_pattern.value, &mut shadowable_idents);
let pattern_var = var_store.fresh();
let pattern_type = Type::Variable(pattern_var);
let pattern_expected = PExpected::NoExpectation(pattern_type.clone());
pattern_types.push(pattern_type);
let can_arg = canonicalize_pattern(
env,
&mut state,
var_store,
&mut scope,
FunctionArg,
&loc_pattern.value,
loc_pattern.region,
&mut shadowable_idents,
pattern_expected,
);
vars.push(pattern_var);
can_args.push(can_arg);
can_args.push((var_store.fresh(), can_arg));
}
let fn_typ = Type::Function(pattern_types, Box::new(ret_type.clone()));
let body_type = NoExpectation(ret_type);
let (loc_body_expr, mut output, ret_constraint) = canonicalize_expr(
rigids,
let (loc_body_expr, mut output) = canonicalize_expr(
env,
var_store,
&mut scope,
loc_body_expr.region,
&loc_body_expr.value,
body_type,
);
let defs_constraint = And(state.constraints);
let constraint = exists(
vars,
And(vec![
Let(Box::new(LetConstraint {
rigid_vars: Vec::new(),
flex_vars: state.vars,
def_types: state.headers,
defs_constraint: defs_constraint,
ret_constraint,
})),
// "the closure's type is equal to expected type"
Eq(fn_typ, expected, region),
]),
);
// Now that we've collected all the references, check to see if any of the args we defined
@ -547,127 +384,44 @@ pub fn canonicalize_expr(
symbol,
Recursive::NotRecursive,
can_args,
Box::new(loc_body_expr),
Box::new((var_store.fresh(), loc_body_expr)),
),
output,
constraint,
)
}
ast::Expr::When(loc_cond, branches) => {
// Infer the condition expression's type.
let cond_var = var_store.fresh();
let cond_type = Variable(cond_var);
let (can_cond, mut output, expr_con) = canonicalize_expr(
rigids,
env,
var_store,
scope,
region,
&loc_cond.value,
NoExpectation(cond_type.clone()),
);
let (can_cond, mut output) =
canonicalize_expr(env, var_store, scope, region, &loc_cond.value);
// the condition can never be a tail-call
output.tail_call = None;
let mut can_branches = Vec::with_capacity(branches.len());
let mut constraints = Vec::with_capacity(branches.len() + 1);
match expected {
FromAnnotation(name, arity, _, typ) => {
for (index, (loc_pattern, loc_expr)) in branches.into_iter().enumerate() {
let mut shadowable_idents = scope.idents.clone();
remove_idents(&loc_pattern.value, &mut shadowable_idents);
let (can_pattern, loc_can_expr, branch_con, branch_references) =
canonicalize_when_branch(
env,
var_store,
rigids,
scope,
region,
loc_pattern,
loc_expr,
PExpected::ForReason(
PReason::WhenMatch { index },
cond_type.clone(),
region,
),
FromAnnotation(
name.clone(),
arity,
TypedWhenBranch(index),
typ.clone(),
),
&mut output,
);
for (index, (loc_pattern, loc_expr)) in branches.into_iter().enumerate() {
let mut shadowable_idents = scope.idents.clone();
output.references = output.references.union(branch_references);
remove_idents(&loc_pattern.value, &mut shadowable_idents);
can_branches.push((can_pattern, loc_can_expr));
let (can_pattern, loc_can_expr, branch_references) = canonicalize_when_branch(
env,
var_store,
scope,
region,
loc_pattern,
loc_expr,
&mut output,
);
constraints.push(exists(
vec![cond_var],
// Each branch's pattern must have the same type
// as the condition expression did.
And(vec![expr_con.clone(), branch_con]),
));
}
}
output.references = output.references.union(branch_references);
_ => {
let branch_var = var_store.fresh();
let branch_type = Variable(branch_var);
let mut branch_cons = Vec::with_capacity(branches.len());
for (index, (loc_pattern, loc_expr)) in branches.into_iter().enumerate() {
let mut shadowable_idents = scope.idents.clone();
remove_idents(&loc_pattern.value, &mut shadowable_idents);
let (can_pattern, loc_can_expr, branch_con, branch_references) =
canonicalize_when_branch(
env,
var_store,
rigids,
scope,
region,
loc_pattern,
loc_expr,
PExpected::ForReason(
PReason::WhenMatch { index },
cond_type.clone(),
region,
),
ForReason(
Reason::WhenBranch { index },
branch_type.clone(),
region,
),
&mut output,
);
output.references = output.references.union(branch_references);
can_branches.push((can_pattern, loc_can_expr));
branch_cons.push(branch_con);
}
constraints.push(exists(
vec![cond_var],
And(vec![
// Record the original conditional expression's constraint.
expr_con,
// Each branch's pattern must have the same type
// as the condition expression did.
And(branch_cons),
// The return type of each branch must equal
// the return type of the entire when-expression.
Eq(branch_type, expected, region),
]),
));
}
can_branches.push((
(var_store.fresh(), can_pattern),
(var_store.fresh(), loc_can_expr),
));
}
// A "when" with no branches is a runtime error, but it will mess things up
@ -680,73 +434,35 @@ pub fn canonicalize_expr(
// Incorporate all three expressions into a combined Output value.
let expr = When(cond_var, Box::new(can_cond), can_branches);
// TODO check for exhaustiveness. If this `case` is non-exaustive, then:
//
// 1. Record a Problem.
// 2. Add an extra _ branch at the end which throws a runtime error.
(expr, output, And(constraints))
(expr, output)
}
ast::Expr::Access(record_expr, field) => {
let ext_var = var_store.fresh();
let field_var = var_store.fresh();
let ext_type = Type::Variable(ext_var);
let field_type = Type::Variable(field_var);
let mut rec_field_types = SendMap::default();
rec_field_types.insert(Lowercase::from(*field), field_type.clone());
let record_type = Type::Record(rec_field_types, Box::new(ext_type));
let record_expected = Expected::NoExpectation(record_type);
let (loc_expr, output, mut constraint) = canonicalize_expr(
&ImMap::default(),
env,
var_store,
scope,
region,
record_expr,
record_expected,
);
constraint = exists(
vec![field_var, ext_var],
And(vec![constraint, Eq(field_type, expected, region)]),
);
let (loc_expr, output) = canonicalize_expr(env, var_store, scope, region, record_expr);
(
Access(Box::new(loc_expr), Lowercase::from(*field).into()),
Access {
field_var: var_store.fresh(),
ext_var: var_store.fresh(),
loc_expr: Box::new(loc_expr),
field: Lowercase::from(*field),
},
output,
constraint,
)
}
ast::Expr::AccessorFunction(field) => {
let ext_var = var_store.fresh();
let ext_type = Variable(ext_var);
let field_var = var_store.fresh();
let field_type = Variable(field_var);
let mut field_types = SendMap::default();
let field_name: Lowercase = (*field).into();
field_types.insert(field_name.clone(), field_type.clone());
let record_type = Type::Record(field_types, Box::new(ext_type));
(
Accessor(field_name.into()),
Accessor {
field: field_name,
ext_var,
field_var,
},
Output::default(),
exists(
vec![field_var, ext_var],
Eq(
Type::Function(vec![record_type], Box::new(field_type)),
expected,
region,
),
),
)
}
ast::Expr::If(_)
| ast::Expr::GlobalTag(_)
| ast::Expr::PrivateTag(_)
@ -759,10 +475,9 @@ pub fn canonicalize_expr(
);
}
ast::Expr::Nested(sub_expr) => {
let (answer, output, constraint) =
canonicalize_expr(rigids, env, var_store, scope, region, sub_expr, expected);
let (answer, output) = canonicalize_expr(env, var_store, scope, region, sub_expr);
(answer.value, output, constraint)
(answer.value, output)
}
ast::Expr::NonBase10Int {
string,
@ -775,10 +490,9 @@ pub fn canonicalize_expr(
result = result.map(i64::neg);
}
let (constraint, answer) =
int_expr_from_result(var_store, result, env, expected, region);
let answer = int_expr_from_result(var_store, result, env);
(answer, Output::default(), constraint)
(answer, Output::default())
}
// Below this point, we shouln't see any of these nodes anymore because
// operator desugaring should have removed them!
@ -827,7 +541,6 @@ pub fn canonicalize_expr(
value: expr,
},
output,
constraint,
)
}
@ -838,17 +551,13 @@ fn canonicalize_lookup(
ident: Ident,
symbol: Symbol,
region: Region,
expected: Expected<Type>,
var_store: &VarStore,
) -> (Expr, Output, Constraint) {
) -> (Expr, Output) {
use self::Expr::*;
let mut output = Output::default();
let (constraint, can_expr) = match resolve_ident(&env, &scope, ident, &mut output.references) {
Ok(sub_symbol) => (
Lookup(symbol, expected, region),
Var(var_store.fresh(), sub_symbol),
),
let can_expr = match resolve_ident(&env, &scope, ident, &mut output.references) {
Ok(sub_symbol) => Var(var_store.fresh(), sub_symbol),
Err(ident) => {
let loc_ident = Located {
region,
@ -857,28 +566,23 @@ fn canonicalize_lookup(
env.problem(Problem::UnrecognizedConstant(loc_ident.clone()));
(True, RuntimeError(UnrecognizedConstant(loc_ident)))
RuntimeError(UnrecognizedConstant(loc_ident))
}
};
(can_expr, output, constraint)
(can_expr, output)
}
// TODO trim down these arguments
#[allow(clippy::too_many_arguments)]
#[inline(always)]
fn canonicalize_when_branch<'a>(
env: &mut Env,
var_store: &VarStore,
rigids: &Rigids,
scope: &Scope,
region: Region,
loc_pattern: &Located<ast::Pattern<'a>>,
loc_expr: &Located<ast::Expr<'a>>,
pattern_expected: PExpected<Type>,
expr_expected: Expected<Type>,
output: &mut Output,
) -> (Located<Pattern>, Located<Expr>, Constraint, References) {
) -> (Located<Pattern>, Located<Expr>, References) {
// 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.
@ -897,15 +601,8 @@ fn canonicalize_when_branch<'a>(
scope.idents = union_pairs(scope.idents, defined_idents.iter());
let (can_expr, branch_output, ret_constraint) = canonicalize_expr(
rigids,
env,
var_store,
&mut scope,
region,
&loc_expr.value,
expr_expected,
);
let (can_expr, branch_output) =
canonicalize_expr(env, var_store, &mut scope, region, &loc_expr.value);
// If we already recorded a tail call then keep it, else use this branch's tail call
match output.tail_call {
@ -926,38 +623,17 @@ fn canonicalize_when_branch<'a>(
}
}
let mut state = PatternState {
headers: SendMap::default(),
vars: Vec::with_capacity(1),
constraints: Vec::with_capacity(1),
};
let loc_can_pattern = canonicalize_pattern(
env,
&mut state,
var_store,
&mut scope,
WhenBranch,
&loc_pattern.value,
loc_pattern.region,
&mut shadowable_idents,
pattern_expected,
);
let constraint = Constraint::Let(Box::new(LetConstraint {
rigid_vars: Vec::new(),
flex_vars: state.vars,
def_types: state.headers,
defs_constraint: Constraint::And(state.constraints),
ret_constraint,
}));
(
loc_can_pattern,
can_expr,
constraint,
branch_output.references,
)
(loc_can_pattern, can_expr, branch_output.references)
}
pub fn union_pairs<'a, K, V, I>(mut map: ImMap<K, V>, pairs: I) -> ImMap<K, V>
@ -1155,38 +831,26 @@ fn resolve_ident<'a>(
}
fn canonicalize_field<'a>(
rigids: &Rigids,
env: &mut Env,
var_store: &VarStore,
scope: &mut Scope,
field: &'a ast::AssignedField<'a, ast::Expr<'a>>,
region: Region,
) -> (Lowercase, Located<Expr>, Output, Variable, Type, Constraint) {
) -> (Lowercase, Located<Expr>, Output, Variable) {
use crate::parse::ast::AssignedField::*;
match field {
// Both a label and a value, e.g. `{ name: "blah" }`
LabeledValue(label, _, loc_expr) => {
let field_var = var_store.fresh();
let field_type = Variable(field_var);
let field_expected = NoExpectation(field_type.clone());
let (loc_can_expr, output, constraint) = canonicalize_expr(
rigids,
env,
var_store,
scope,
loc_expr.region,
&loc_expr.value,
field_expected,
);
let (loc_can_expr, output) =
canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value);
(
Lowercase::from(label.value),
loc_can_expr,
output,
field_var,
field_type,
constraint,
)
}
@ -1196,7 +860,7 @@ fn canonicalize_field<'a>(
}
SpaceBefore(sub_field, _) | SpaceAfter(sub_field, _) => {
canonicalize_field(rigids, env, var_store, scope, sub_field, region)
canonicalize_field(env, var_store, scope, sub_field, region)
}
Malformed(_string) => {

View file

@ -1,16 +1,13 @@
use crate::can::def::{canonicalize_defs, sort_can_defs, Def, Info};
use crate::can::def::{canonicalize_defs, sort_can_defs, Def};
use crate::can::env::Env;
use crate::can::expr::Output;
use crate::can::operator::desugar_def;
use crate::can::scope::Scope;
use crate::can::symbol::Symbol;
use crate::collections::{ImMap, SendMap};
use crate::collections::SendMap;
use crate::parse::ast::{self, ExposesEntry};
use crate::region::Located;
use crate::subs::{VarStore, Variable};
use crate::types::Constraint::{self, *};
use crate::types::Expected::*;
use crate::types::Type;
use bumpalo::Bump;
#[derive(Clone, Debug, PartialEq)]
@ -18,7 +15,6 @@ pub struct Module {
pub name: Option<Box<str>>,
pub defs: Vec<Def>,
pub exposed_imports: SendMap<Symbol, Variable>,
pub constraint: Constraint,
}
pub fn canonicalize_module_defs<'a, I>(
@ -28,7 +24,7 @@ pub fn canonicalize_module_defs<'a, I>(
_exposes: I,
scope: &mut Scope,
var_store: &VarStore,
) -> (Vec<Def>, SendMap<Symbol, Variable>, Constraint)
) -> (Vec<Def>, SendMap<Symbol, Variable>)
where
I: Iterator<Item = Located<ExposesEntry<'a>>>,
{
@ -52,8 +48,6 @@ where
}
let mut env = Env::new(home);
let rigids = ImMap::default();
let mut flex_info = Info::default();
// Exposed values are treated like defs that appear before any others, e.g.
//
@ -68,38 +62,17 @@ where
// by canonicalizing them right before we canonicalize the actual ast::Def nodes.
for (ident, (symbol, region)) in scope.idents.iter() {
if ident.first_char().is_lowercase() {
let expr_var = var_store.fresh();
// Add an entry to exposed_imports using the current module's name
// as the key; e.g. if this is the Foo module and we have
// exposes [ Bar.{ baz } ] then insert Foo.baz as the key, so when
// anything references `baz` in this Foo module, it will resolve to Bar.baz.
exposed_imports.insert(scope.symbol(&*ident.clone().name()), expr_var);
// Add the usual Lookup constraint as if this were a normal def.
let expr_type = Type::Variable(expr_var);
let expected = NoExpectation(expr_type.clone());
flex_info
.constraints
.push(Lookup(symbol.clone(), expected, *region));
exposed_imports.insert(scope.symbol(&*ident.clone().name()), var_store.fresh());
} else {
// TODO add type aliases to type alias dictionary, based on exposed types
}
}
let mut found_rigids = SendMap::default();
let defs = canonicalize_defs(
&rigids,
&mut found_rigids,
&mut env,
var_store,
scope,
&desugared,
&mut flex_info,
);
let defs = canonicalize_defs(&mut env, var_store, scope, &desugared);
let defs = match sort_can_defs(&mut env, defs, Output::default()) {
(Ok(defs), _) => {
// TODO examine the patterns, extract toplevel identifiers from them,
@ -116,5 +89,5 @@ where
// TODO incorporate rigids into here (possibly by making this be a Let instead
// of an And)
(defs, exposed_imports, And(flex_info.constraints))
(defs, exposed_imports)
}

View file

@ -2,13 +2,8 @@ use crate::can::env::Env;
use crate::can::expr::Expr;
use crate::can::problem::Problem;
use crate::can::problem::RuntimeError::*;
use crate::constrain;
use crate::parse::ast::Base;
use crate::region::Region;
use crate::subs::VarStore;
use crate::types::Constraint::{self, *};
use crate::types::Expected;
use crate::types::Type;
use std::i64;
#[inline(always)]
@ -16,20 +11,15 @@ pub fn int_expr_from_result(
var_store: &VarStore,
result: Result<i64, &str>,
env: &mut Env,
expected: Expected<Type>,
region: Region,
) -> (Constraint, Expr) {
) -> Expr {
match result {
Ok(int) => (
constrain::int_literal(var_store, expected, region),
Expr::Int(int),
),
Ok(int) => Expr::Int(var_store.fresh(), int),
Err(raw) => {
let runtime_error = IntOutsideRange(raw.into());
env.problem(Problem::RuntimeError(runtime_error.clone()));
(True, Expr::RuntimeError(runtime_error))
Expr::RuntimeError(runtime_error)
}
}
}
@ -39,20 +29,15 @@ pub fn float_expr_from_result(
var_store: &VarStore,
result: Result<f64, &str>,
env: &mut Env,
expected: Expected<Type>,
region: Region,
) -> (Constraint, Expr) {
) -> Expr {
match result {
Ok(float) => (
constrain::float_literal(var_store, expected, region),
Expr::Float(float),
),
Ok(float) => Expr::Float(var_store.fresh(), float),
Err(raw) => {
let runtime_error = FloatOutsideRange(raw.into());
env.problem(Problem::RuntimeError(runtime_error.clone()));
(True, Expr::RuntimeError(runtime_error))
Expr::RuntimeError(runtime_error)
}
}
}

View file

@ -58,43 +58,14 @@ pub enum PatternType {
WhenBranch,
}
// TODO trim down these arguments
#[allow(clippy::too_many_arguments)]
pub fn canonicalize_pattern<'a>(
env: &'a mut Env,
state: &'a mut PatternState,
var_store: &VarStore,
scope: &mut Scope,
pattern_type: PatternType,
pattern: &'a ast::Pattern<'a>,
region: Region,
shadowable_idents: &'a mut ImMap<Ident, (Symbol, Region)>,
expected: PExpected<Type>,
) -> Located<Pattern> {
// add_constraints recurses by itself
add_constraints(&pattern, &scope, region, expected, state, var_store);
canonicalize_pattern_help(
env,
state,
scope,
pattern_type,
pattern,
region,
shadowable_idents,
)
}
// TODO trim down these arguments
#[allow(clippy::too_many_arguments)]
fn canonicalize_pattern_help<'a>(
env: &'a mut Env,
state: &'a mut PatternState,
scope: &mut Scope,
pattern_type: PatternType,
pattern: &'a ast::Pattern<'a>,
region: Region,
shadowable_idents: &'a mut ImMap<Ident, (Symbol, Region)>,
) -> Located<Pattern> {
use self::PatternType::*;
use crate::parse::ast::Pattern::*;
@ -174,9 +145,9 @@ fn canonicalize_pattern_help<'a>(
},
&SpaceBefore(sub_pattern, _) | SpaceAfter(sub_pattern, _) | Nested(sub_pattern) => {
return canonicalize_pattern_help(
return canonicalize_pattern(
env,
state,
var_store,
scope,
pattern_type,
sub_pattern,
@ -214,9 +185,9 @@ fn canonicalize_pattern_help<'a>(
Err(loc_shadowed_ident) => Pattern::Shadowed(loc_shadowed_ident),
};
let can_guard = canonicalize_pattern_help(
let can_guard = canonicalize_pattern(
env,
state,
var_store,
scope,
pattern_type,
&loc_guard.value,

View file

@ -116,8 +116,8 @@ fn compile_expr<'ctx, 'env>(
use crate::can::expr::Expr::*;
match *expr {
Int(num) => IntConst(env.context.i64_type().const_int(num as u64, false)),
Float(num) => FloatConst(env.context.f64_type().const_float(num)),
Int(_, num) => IntConst(env.context.i64_type().const_int(num as u64, false)),
Float(_, num) => FloatConst(env.context.f64_type().const_float(num)),
When(_, ref loc_cond_expr, ref branches) => {
if branches.len() < 2 {
panic!("TODO support when-expressions of fewer than 2 branches.");
@ -125,7 +125,8 @@ fn compile_expr<'ctx, 'env>(
if branches.len() == 2 {
let mut iter = branches.iter();
let (pattern, branch_expr) = iter.next().unwrap();
let ((_pattern_var, pattern), (_expr_var, branch_expr)) = iter.next().unwrap();
let (_, (_, else_expr)) = iter.next().unwrap();
compile_when_branch(
env,
@ -133,7 +134,7 @@ fn compile_expr<'ctx, 'env>(
&loc_cond_expr.value,
pattern.value.clone(),
&branch_expr.value,
&iter.next().unwrap().1.value,
&else_expr.value,
vars,
)
} else {

View file

@ -12,7 +12,6 @@ use crate::region::{Located, Region};
use crate::solve;
use crate::subs::VarStore;
use crate::subs::{Subs, Variable};
use crate::types::Constraint;
use crate::unify::Problems;
use bumpalo::Bump;
use futures::future::join_all;
@ -219,7 +218,7 @@ fn load_filename(
let mut scope =
Scope::new(format!("{}.", declared_name).into(), scope_from_imports);
let (defs, exposed_imports, constraint) = parse_and_canonicalize_defs(
let (defs, exposed_imports) = parse_and_canonicalize_defs(
&arena,
state,
declared_name.clone(),
@ -231,7 +230,6 @@ fn load_filename(
name: Some(declared_name),
defs,
exposed_imports,
constraint,
};
LoadedModule::Valid(module)
@ -260,7 +258,7 @@ fn load_filename(
let mut scope = Scope::new(".".into(), scope_from_imports);
// The app module has no declared name. Pass it as "".
let (defs, exposed_imports, constraint) = parse_and_canonicalize_defs(
let (defs, exposed_imports) = parse_and_canonicalize_defs(
&arena,
state,
"".into(),
@ -272,7 +270,6 @@ fn load_filename(
name: None,
defs,
exposed_imports,
constraint,
};
LoadedModule::Valid(module)
@ -296,7 +293,7 @@ fn parse_and_canonicalize_defs<'a, I>(
exposes: I,
scope: &mut Scope,
var_store: &VarStore,
) -> (Vec<Def>, SendMap<Symbol, Variable>, Constraint)
) -> (Vec<Def>, SendMap<Symbol, Variable>)
where
I: Iterator<Item = Located<ExposesEntry<'a>>>,
{
@ -364,6 +361,11 @@ pub fn solve_loaded(
let mut vars_by_symbol: ImMap<Symbol, Variable> = ImMap::default();
let mut constraints = Vec::with_capacity(loaded_deps.len() + 1);
let module_constraint = if true {
panic!("TODO populate constraints for each module");
} else {
crate::types::Constraint::True
};
// All the exposed imports should be available in the solver's vars_by_symbol
for (symbol, var) in module.exposed_imports.iter() {
@ -393,8 +395,6 @@ pub fn solve_loaded(
vars_by_symbol.insert(symbol, var);
}
constraints.push(valid_dep.constraint);
// All its top-level defs should also be available in vars_by_symbol
for def in valid_dep.defs {
for (symbol, var) in def.vars_by_symbol {
@ -417,5 +417,5 @@ pub fn solve_loaded(
solve::run(&vars_by_symbol, problems, subs, &constraint);
}
solve::run(&vars_by_symbol, problems, subs, &module.constraint);
solve::run(&vars_by_symbol, problems, subs, &module_constraint);
}

View file

@ -173,11 +173,11 @@ pub fn canonicalize_expr(
pub use crate::can::expr::Expr::*;
match expr {
Int(_) => {
Int(_, _) => {
let constraint = constrain::int_literal(var_store, expected, region);
(Output::default(), constraint)
}
Float(_) => {
Float(_, _) => {
let constraint = constrain::float_literal(var_store, expected, region);
(Output::default(), constraint)
}
@ -201,7 +201,7 @@ pub fn canonicalize_expr(
let mut constraints = Vec::with_capacity(1 + fields.len());
let mut output = Output::default();
for (label, loc_expr) in fields.iter() {
for (label, (_, loc_expr)) in fields.iter() {
let field_var = var_store.fresh();
let field_type = Variable(field_var);
let field_expected = Expected::NoExpectation(field_type.clone());
@ -257,9 +257,8 @@ pub fn canonicalize_expr(
let mut constraints = Vec::with_capacity(1 + (loc_elems.len() * 2));
let mut references = References::new();
for loc_elem in loc_elems.iter() {
let elem_var = var_store.fresh();
let elem_type = Variable(elem_var);
for (elem_var, loc_elem) in loc_elems.iter() {
let elem_type = Variable(*elem_var);
let elem_expected = Expected::NoExpectation(elem_type.clone());
let list_elem_constraint = Eq(
list_type.clone(),
@ -338,7 +337,9 @@ pub fn canonicalize_expr(
FunctionPointer(_, _) => {
panic!("TODO implement function pointer?");
}
Closure(_symbol, _recursion, args, body) => {
Closure(_symbol, _recursion, args, boxed_body) => {
let (ret_var, body) = &**boxed_body;
// first, generate constraints for the arguments
let mut arg_types = Vec::new();
let mut arg_vars = Vec::new();
@ -350,14 +351,12 @@ pub fn canonicalize_expr(
};
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
let ret_var = var_store.fresh();
let ret_type = Variable(ret_var);
let ret_type = Variable(*ret_var);
vars.push(ret_var);
vars.push(*ret_var);
for pattern in args {
let arg_var = var_store.fresh();
let arg_typ = Variable(arg_var);
for (arg_var, pattern) in args {
let arg_typ = Variable(*arg_var);
canonicalize_pattern(
var_store,
&mut state,
@ -367,11 +366,9 @@ pub fn canonicalize_expr(
arg_types.push(arg_typ);
arg_vars.push(arg_var);
vars.push(arg_var);
vars.push(*arg_var);
}
vars.push(ret_var);
let fn_typ = constrain::lift(
var_store,
Type::Function(arg_types, Box::new(ret_type.clone())),
@ -387,7 +384,7 @@ pub fn canonicalize_expr(
);
// remove identifiers bound in the arguments from VarUsage
for pattern in args {
for (_, pattern) in args {
for identifier in pattern::symbols_from_pattern(&pattern.value) {
var_usage.unregister(&identifier);
}
@ -440,10 +437,9 @@ pub fn canonicalize_expr(
let mut arg_types = Vec::with_capacity(loc_args.len());
let mut arg_cons = Vec::with_capacity(loc_args.len());
for (index, loc_arg) in loc_args.iter().enumerate() {
for (index, (arg_var, loc_arg)) in loc_args.iter().enumerate() {
let region = loc_arg.region;
let arg_var = var_store.fresh();
let arg_type = Variable(arg_var);
let arg_type = Variable(*arg_var);
let reason = Reason::AnonymousFnArg {
arg_index: index as u8,
@ -459,7 +455,7 @@ pub fn canonicalize_expr(
expected_arg,
);
vars.push(arg_var);
vars.push(*arg_var);
arg_types.push(arg_type);
arg_cons.push(arg_con);
}
@ -506,7 +502,9 @@ pub fn canonicalize_expr(
match expected {
Expected::FromAnnotation(name, arity, _, typ) => {
for (index, (loc_pattern, loc_expr)) in branches.iter().enumerate() {
for (index, ((_patter_var, loc_pattern), (_expr_var, loc_expr))) in
branches.iter().enumerate()
{
let mut branch_var_usage = old_var_usage.clone();
let branch_con = canonicalize_when_branch(
var_store,
@ -556,7 +554,9 @@ pub fn canonicalize_expr(
let branch_type = Variable(branch_var);
let mut branch_cons = Vec::with_capacity(branches.len());
for (index, (loc_pattern, loc_expr)) in branches.iter().enumerate() {
for (index, ((_pattern_var, loc_pattern), (_expr_var, loc_expr))) in
branches.iter().enumerate()
{
let mut branch_var_usage = old_var_usage.clone();
let branch_con = canonicalize_when_branch(
var_store,
@ -613,11 +613,14 @@ pub fn canonicalize_expr(
(output, And(constraints))
}
Access(loc_expr, field) => {
let ext_var = var_store.fresh();
let field_var = var_store.fresh();
let ext_type = Type::Variable(ext_var);
let field_type = Type::Variable(field_var);
Access {
ext_var,
field_var,
loc_expr,
field,
} => {
let ext_type = Type::Variable(*ext_var);
let field_type = Type::Variable(*field_var);
let mut rec_field_types = SendMap::default();
@ -637,22 +640,23 @@ pub fn canonicalize_expr(
);
constraint = exists(
vec![field_var, ext_var],
vec![*field_var, *ext_var],
And(vec![constraint, Eq(field_type, expected, region)]),
);
(output, constraint)
}
Accessor(name) => {
let ext_var = var_store.fresh();
let ext_type = Variable(ext_var);
let field_var = var_store.fresh();
let field_type = Variable(field_var);
Accessor {
field,
field_var,
ext_var,
} => {
let ext_type = Variable(*ext_var);
let field_type = Variable(*field_var);
let mut field_types = SendMap::default();
let field_name: Lowercase = name.clone().into();
let field_name = field.clone();
field_types.insert(field_name, field_type.clone());
let record_type =
constrain::lift(var_store, Type::Record(field_types, Box::new(ext_type)));
@ -660,7 +664,7 @@ pub fn canonicalize_expr(
(
Output::default(),
exists(
vec![field_var, ext_var],
vec![*field_var, *ext_var],
Eq(
Type::Function(vec![record_type], Box::new(field_type)),
expected,