mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Canonicalize without constraining.
This commit is contained in:
parent
7a07c19275
commit
be2d20162c
8 changed files with 221 additions and 817 deletions
246
src/can/def.rs
246
src/can/def.rs
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
584
src/can/expr.rs
584
src/can/expr.rs
|
@ -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) => {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue