mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Constrain canonical exprs and modules
This commit is contained in:
parent
be2d20162c
commit
ffd3cc4211
17 changed files with 1130 additions and 431 deletions
|
@ -5,6 +5,7 @@ use crate::can::expr::{
|
||||||
canonicalize_expr, local_successors, references_from_call, references_from_local, union_pairs,
|
canonicalize_expr, local_successors, references_from_call, references_from_local, union_pairs,
|
||||||
Output, Recursive,
|
Output, Recursive,
|
||||||
};
|
};
|
||||||
|
use crate::can::ident::Lowercase;
|
||||||
use crate::can::pattern::remove_idents;
|
use crate::can::pattern::remove_idents;
|
||||||
use crate::can::pattern::PatternType::*;
|
use crate::can::pattern::PatternType::*;
|
||||||
use crate::can::pattern::{canonicalize_pattern, idents_from_patterns, Pattern};
|
use crate::can::pattern::{canonicalize_pattern, idents_from_patterns, Pattern};
|
||||||
|
@ -26,9 +27,11 @@ use std::fmt::Debug;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Def {
|
pub struct Def {
|
||||||
pub pattern: Located<Pattern>,
|
pub loc_pattern: Located<Pattern>,
|
||||||
pub expr: Located<Expr>,
|
pub loc_expr: Located<Expr>,
|
||||||
pub vars_by_symbol: SendMap<Symbol, Variable>,
|
pub body_var: Variable,
|
||||||
|
pub pattern_vars: SendMap<Symbol, Variable>,
|
||||||
|
pub annotation: Option<(Type, SendMap<Variable, Lowercase>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -41,6 +44,7 @@ pub struct CanDefs {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn canonicalize_defs<'a>(
|
pub fn canonicalize_defs<'a>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
|
found_rigids: &mut SendMap<Variable, Lowercase>,
|
||||||
var_store: &VarStore,
|
var_store: &VarStore,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
loc_defs: &'a bumpalo::collections::Vec<'a, &'a Located<ast::Def<'a>>>,
|
loc_defs: &'a bumpalo::collections::Vec<'a, &'a Located<ast::Def<'a>>>,
|
||||||
|
@ -77,6 +81,7 @@ pub fn canonicalize_defs<'a>(
|
||||||
|
|
||||||
canonicalize_def(
|
canonicalize_def(
|
||||||
env,
|
env,
|
||||||
|
found_rigids,
|
||||||
Located {
|
Located {
|
||||||
region: loc_def.region,
|
region: loc_def.region,
|
||||||
value: &typed,
|
value: &typed,
|
||||||
|
@ -90,6 +95,7 @@ pub fn canonicalize_defs<'a>(
|
||||||
_ => {
|
_ => {
|
||||||
canonicalize_def(
|
canonicalize_def(
|
||||||
env,
|
env,
|
||||||
|
found_rigids,
|
||||||
Located {
|
Located {
|
||||||
region: loc_def.region,
|
region: loc_def.region,
|
||||||
value: &loc_def.value,
|
value: &loc_def.value,
|
||||||
|
@ -106,6 +112,7 @@ pub fn canonicalize_defs<'a>(
|
||||||
_ => {
|
_ => {
|
||||||
canonicalize_def(
|
canonicalize_def(
|
||||||
env,
|
env,
|
||||||
|
found_rigids,
|
||||||
Located {
|
Located {
|
||||||
region: loc_def.region,
|
region: loc_def.region,
|
||||||
value: &loc_def.value,
|
value: &loc_def.value,
|
||||||
|
@ -229,10 +236,12 @@ pub fn sort_can_defs(
|
||||||
let mut new_def = can_def.clone();
|
let mut new_def = can_def.clone();
|
||||||
|
|
||||||
// Determine recursivity of closures that are not tail-recursive
|
// Determine recursivity of closures that are not tail-recursive
|
||||||
if let Closure(name, Recursive::NotRecursive, args, body) = new_def.expr.value {
|
if let Closure(name, Recursive::NotRecursive, args, body) =
|
||||||
|
new_def.loc_expr.value
|
||||||
|
{
|
||||||
let recursion = closure_recursivity(symbol.clone(), &env.closures);
|
let recursion = closure_recursivity(symbol.clone(), &env.closures);
|
||||||
|
|
||||||
new_def.expr.value = Closure(name, recursion, args, body);
|
new_def.loc_expr.value = Closure(name, recursion, args, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
can_defs.push(new_def);
|
can_defs.push(new_def);
|
||||||
|
@ -272,7 +281,7 @@ pub fn sort_can_defs(
|
||||||
let mut regions = Vec::with_capacity(can_defs_by_symbol.len());
|
let mut regions = Vec::with_capacity(can_defs_by_symbol.len());
|
||||||
|
|
||||||
for def in can_defs_by_symbol.values() {
|
for def in can_defs_by_symbol.values() {
|
||||||
regions.push((def.pattern.region, def.expr.region));
|
regions.push((def.loc_pattern.region, def.loc_expr.region));
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
|
@ -288,7 +297,6 @@ fn canonicalize_def_pattern(
|
||||||
loc_pattern: &Located<ast::Pattern>,
|
loc_pattern: &Located<ast::Pattern>,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
var_store: &VarStore,
|
var_store: &VarStore,
|
||||||
expr_type: Type,
|
|
||||||
) -> Located<Pattern> {
|
) -> Located<Pattern> {
|
||||||
// Exclude the current ident from shadowable_idents; you can't shadow yourself!
|
// Exclude the current ident from shadowable_idents; you can't shadow yourself!
|
||||||
// (However, still include it in scope, because you *can* recursively refer to yourself.)
|
// (However, still include it in scope, because you *can* recursively refer to yourself.)
|
||||||
|
@ -308,6 +316,7 @@ fn canonicalize_def_pattern(
|
||||||
|
|
||||||
fn canonicalize_def<'a>(
|
fn canonicalize_def<'a>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
|
found_rigids: &mut SendMap<Variable, Lowercase>,
|
||||||
loc_def: Located<&'a ast::Def<'a>>,
|
loc_def: Located<&'a ast::Def<'a>>,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
can_defs_by_symbol: &mut MutMap<Symbol, Def>,
|
can_defs_by_symbol: &mut MutMap<Symbol, Def>,
|
||||||
|
@ -318,21 +327,22 @@ fn canonicalize_def<'a>(
|
||||||
|
|
||||||
// Make types for the body expr, even if we won't end up having a body.
|
// Make types for the body expr, even if we won't end up having a body.
|
||||||
let expr_var = var_store.fresh();
|
let expr_var = var_store.fresh();
|
||||||
let expr_type = Type::Variable(expr_var);
|
|
||||||
let mut vars_by_symbol = SendMap::default();
|
let mut vars_by_symbol = SendMap::default();
|
||||||
|
|
||||||
// Each def gets to have all the idents in scope that are defined in this
|
// 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!
|
// block. Order of defs doesn't matter, thanks to referential transparency!
|
||||||
match loc_def.value {
|
match loc_def.value {
|
||||||
Annotation(loc_pattern, loc_annotation) => {
|
Annotation(loc_pattern, loc_annotation) => {
|
||||||
let loc_can_pattern =
|
let loc_can_pattern = canonicalize_def_pattern(env, loc_pattern, scope, var_store);
|
||||||
canonicalize_def_pattern(env, loc_pattern, scope, var_store, expr_type.clone());
|
|
||||||
|
|
||||||
// annotation sans body cannot introduce new rigids that are visible in other annotations
|
// 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
|
// but the rigids can show up in type error messages, so still register them
|
||||||
let (_, can_annotation) = canonicalize_annotation(&loc_annotation.value, var_store);
|
let (seen_rigids, can_annotation) =
|
||||||
if true {
|
canonicalize_annotation(&loc_annotation.value, var_store);
|
||||||
panic!("TODO replace this call to canonicalize_annotation with something that *only* gets the arity, since that's all we use");
|
|
||||||
|
// union seen rigids with already found ones
|
||||||
|
for (k, v) in seen_rigids {
|
||||||
|
found_rigids.insert(k, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
let arity = can_annotation.arity();
|
let arity = can_annotation.arity();
|
||||||
|
@ -364,7 +374,7 @@ fn canonicalize_def<'a>(
|
||||||
region: loc_annotation.region,
|
region: loc_annotation.region,
|
||||||
};
|
};
|
||||||
|
|
||||||
let body = Box::new((var_store.fresh(), body_expr));
|
let body = Box::new((body_expr, var_store.fresh()));
|
||||||
|
|
||||||
Located {
|
Located {
|
||||||
value: Closure(symbol, Recursive::NotRecursive, underscores, body),
|
value: Closure(symbol, Recursive::NotRecursive, underscores, body),
|
||||||
|
@ -376,22 +386,31 @@ fn canonicalize_def<'a>(
|
||||||
can_defs_by_symbol.insert(
|
can_defs_by_symbol.insert(
|
||||||
symbol,
|
symbol,
|
||||||
Def {
|
Def {
|
||||||
|
body_var: var_store.fresh(),
|
||||||
// TODO try to remove this .clone()!
|
// TODO try to remove this .clone()!
|
||||||
pattern: loc_can_pattern.clone(),
|
loc_pattern: loc_can_pattern.clone(),
|
||||||
expr: Located {
|
loc_expr: Located {
|
||||||
region: loc_can_expr.region,
|
region: loc_can_expr.region,
|
||||||
// TODO try to remove this .clone()!
|
// TODO try to remove this .clone()!
|
||||||
value: loc_can_expr.value.clone(),
|
value: loc_can_expr.value.clone(),
|
||||||
},
|
},
|
||||||
vars_by_symbol: im::HashMap::clone(&vars_by_symbol),
|
pattern_vars: im::HashMap::clone(&vars_by_symbol),
|
||||||
|
annotation: Some((can_annotation.clone(), found_rigids.clone())),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedDef(loc_pattern, loc_annotation, loc_expr) => {
|
TypedDef(loc_pattern, loc_annotation, loc_expr) => {
|
||||||
let loc_can_pattern =
|
let (seen_rigids, can_annotation) =
|
||||||
canonicalize_def_pattern(env, loc_pattern, scope, var_store, expr_type.clone());
|
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 loc_can_pattern = canonicalize_def_pattern(env, loc_pattern, scope, var_store);
|
||||||
|
|
||||||
// bookkeeping for tail-call detection. If we're assigning to an
|
// bookkeeping for tail-call detection. If we're assigning to an
|
||||||
// identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called.
|
// identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called.
|
||||||
|
@ -468,8 +487,6 @@ fn canonicalize_def<'a>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut defined_symbols = Vec::new();
|
|
||||||
|
|
||||||
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
||||||
// which defined names reference each other.
|
// which defined names reference each other.
|
||||||
for (ident, (symbol, region)) in
|
for (ident, (symbol, region)) in
|
||||||
|
@ -496,21 +513,19 @@ fn canonicalize_def<'a>(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
defined_symbols.push(symbol.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
for symbol in defined_symbols {
|
|
||||||
can_defs_by_symbol.insert(
|
can_defs_by_symbol.insert(
|
||||||
symbol,
|
symbol,
|
||||||
Def {
|
Def {
|
||||||
|
body_var: var_store.fresh(),
|
||||||
// TODO try to remove this .clone()!
|
// TODO try to remove this .clone()!
|
||||||
pattern: loc_can_pattern.clone(),
|
loc_pattern: loc_can_pattern.clone(),
|
||||||
expr: Located {
|
loc_expr: Located {
|
||||||
region: loc_can_expr.region,
|
region: loc_can_expr.region,
|
||||||
// TODO try to remove this .clone()!
|
// TODO try to remove this .clone()!
|
||||||
value: loc_can_expr.value.clone(),
|
value: loc_can_expr.value.clone(),
|
||||||
},
|
},
|
||||||
vars_by_symbol: im::HashMap::clone(&vars_by_symbol),
|
pattern_vars: im::HashMap::clone(&vars_by_symbol),
|
||||||
|
annotation: Some((can_annotation.clone(), found_rigids.clone())),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -518,8 +533,7 @@ fn canonicalize_def<'a>(
|
||||||
// If we have a pattern, then the def has a body (that is, it's not 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.
|
// standalone annotation), so we need to canonicalize the pattern and expr.
|
||||||
Body(loc_pattern, loc_expr) => {
|
Body(loc_pattern, loc_expr) => {
|
||||||
let loc_can_pattern =
|
let loc_can_pattern = canonicalize_def_pattern(env, loc_pattern, scope, var_store);
|
||||||
canonicalize_def_pattern(env, loc_pattern, scope, var_store, expr_type.clone());
|
|
||||||
|
|
||||||
// bookkeeping for tail-call detection. If we're assigning to an
|
// bookkeeping for tail-call detection. If we're assigning to an
|
||||||
// identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called.
|
// identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called.
|
||||||
|
@ -600,8 +614,6 @@ fn canonicalize_def<'a>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut defined_symbols = Vec::new();
|
|
||||||
|
|
||||||
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
||||||
// which defined names reference each other.
|
// which defined names reference each other.
|
||||||
for (ident, (symbol, region)) in
|
for (ident, (symbol, region)) in
|
||||||
|
@ -628,21 +640,19 @@ fn canonicalize_def<'a>(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
defined_symbols.push(symbol.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
for symbol in defined_symbols {
|
|
||||||
can_defs_by_symbol.insert(
|
can_defs_by_symbol.insert(
|
||||||
symbol,
|
symbol,
|
||||||
Def {
|
Def {
|
||||||
|
body_var: var_store.fresh(),
|
||||||
// TODO try to remove this .clone()!
|
// TODO try to remove this .clone()!
|
||||||
pattern: loc_can_pattern.clone(),
|
loc_pattern: loc_can_pattern.clone(),
|
||||||
expr: Located {
|
loc_expr: Located {
|
||||||
region: loc_can_expr.region,
|
region: loc_can_expr.region,
|
||||||
// TODO try to remove this .clone()!
|
// TODO try to remove this .clone()!
|
||||||
value: loc_can_expr.value.clone(),
|
value: loc_can_expr.value.clone(),
|
||||||
},
|
},
|
||||||
vars_by_symbol: im::HashMap::clone(&vars_by_symbol),
|
pattern_vars: im::HashMap::clone(&vars_by_symbol),
|
||||||
|
annotation: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -651,6 +661,7 @@ fn canonicalize_def<'a>(
|
||||||
Nested(value) => {
|
Nested(value) => {
|
||||||
canonicalize_def(
|
canonicalize_def(
|
||||||
env,
|
env,
|
||||||
|
found_rigids,
|
||||||
Located {
|
Located {
|
||||||
value,
|
value,
|
||||||
region: loc_def.region,
|
region: loc_def.region,
|
||||||
|
@ -723,7 +734,8 @@ pub fn can_defs_with_return<'a>(
|
||||||
loc_defs: &'a bumpalo::collections::Vec<'a, &'a Located<ast::Def<'a>>>,
|
loc_defs: &'a bumpalo::collections::Vec<'a, &'a Located<ast::Def<'a>>>,
|
||||||
loc_ret: &'a Located<ast::Expr<'a>>,
|
loc_ret: &'a Located<ast::Expr<'a>>,
|
||||||
) -> (Expr, Output) {
|
) -> (Expr, Output) {
|
||||||
let unsorted = canonicalize_defs(env, var_store, &mut scope, loc_defs);
|
let mut found_rigids = SendMap::default();
|
||||||
|
let unsorted = canonicalize_defs(env, &mut found_rigids, var_store, &mut scope, loc_defs);
|
||||||
|
|
||||||
// The def as a whole is a tail call iff its return expression is a tail call.
|
// 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!
|
// Use its output as a starting point because its tail_call already has the right answer!
|
||||||
|
@ -732,6 +744,8 @@ pub fn can_defs_with_return<'a>(
|
||||||
|
|
||||||
let (can_defs, mut output) = sort_can_defs(env, unsorted, output);
|
let (can_defs, mut output) = sort_can_defs(env, unsorted, output);
|
||||||
|
|
||||||
|
output.rigids = output.rigids.union(found_rigids);
|
||||||
|
|
||||||
match can_defs {
|
match can_defs {
|
||||||
Ok(defs) => (Defs(defs, Box::new(ret_expr)), output),
|
Ok(defs) => (Defs(defs, Box::new(ret_expr)), output),
|
||||||
Err(err) => (RuntimeError(err), output),
|
Err(err) => (RuntimeError(err), output),
|
||||||
|
|
|
@ -5,9 +5,9 @@ use crate::can::num::{
|
||||||
finish_parsing_base, finish_parsing_float, finish_parsing_int, float_expr_from_result,
|
finish_parsing_base, finish_parsing_float, finish_parsing_int, float_expr_from_result,
|
||||||
int_expr_from_result,
|
int_expr_from_result,
|
||||||
};
|
};
|
||||||
|
use crate::can::pattern::idents_from_patterns;
|
||||||
use crate::can::pattern::PatternType::*;
|
use crate::can::pattern::PatternType::*;
|
||||||
use crate::can::pattern::{canonicalize_pattern, remove_idents, Pattern};
|
use crate::can::pattern::{canonicalize_pattern, remove_idents, Pattern};
|
||||||
use crate::can::pattern::{idents_from_patterns, PatternState};
|
|
||||||
use crate::can::problem::Problem;
|
use crate::can::problem::Problem;
|
||||||
use crate::can::problem::RuntimeError;
|
use crate::can::problem::RuntimeError;
|
||||||
use crate::can::problem::RuntimeError::*;
|
use crate::can::problem::RuntimeError::*;
|
||||||
|
@ -20,23 +20,16 @@ use crate::operator::CalledVia;
|
||||||
use crate::parse::ast;
|
use crate::parse::ast;
|
||||||
use crate::region::{Located, Region};
|
use crate::region::{Located, Region};
|
||||||
use crate::subs::{VarStore, Variable};
|
use crate::subs::{VarStore, Variable};
|
||||||
use crate::types::AnnotationSource::*;
|
|
||||||
use crate::types::Expected::{self, *};
|
|
||||||
use crate::types::Type::{self, *};
|
|
||||||
use im_rc::Vector;
|
use im_rc::Vector;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::i64;
|
use std::i64;
|
||||||
use std::ops::Neg;
|
use std::ops::Neg;
|
||||||
|
|
||||||
/// Whenever we encounter a user-defined type variable (a "rigid" var for short),
|
|
||||||
/// for example `a` in the annotation `identity : a -> a`, we add it to this
|
|
||||||
/// map so that expressions within that annotation can share these vars.
|
|
||||||
pub type Rigids = ImMap<Box<str>, Type>;
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, PartialEq)]
|
#[derive(Clone, Default, Debug, PartialEq)]
|
||||||
pub struct Output {
|
pub struct Output {
|
||||||
pub references: References,
|
pub references: References,
|
||||||
pub tail_call: Option<Symbol>,
|
pub tail_call: Option<Symbol>,
|
||||||
|
pub rigids: SendMap<Variable, Lowercase>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -49,31 +42,35 @@ pub enum Expr {
|
||||||
List(Variable, Vec<(Variable, Located<Expr>)>),
|
List(Variable, Vec<(Variable, Located<Expr>)>),
|
||||||
|
|
||||||
// Lookups
|
// Lookups
|
||||||
Var(Variable, Symbol),
|
Var {
|
||||||
/// Works the same as Var, but has an important marking purpose.
|
symbol_for_lookup: Symbol,
|
||||||
/// See 13623e3f5f65ea2d703cf155f16650c1e8246502 for the bug this fixed.
|
resolved_symbol: Symbol,
|
||||||
FunctionPointer(Variable, Symbol),
|
},
|
||||||
|
|
||||||
// Pattern Matching
|
// Pattern Matching
|
||||||
/// When is guaranteed to be exhaustive at this point. (If it wasn't, then
|
/// When is guaranteed to be exhaustive at this point. (If it wasn't, then
|
||||||
/// a _ branch was added at the end that will throw a runtime error.)
|
/// a _ branch was added at the end that will throw a runtime error.)
|
||||||
/// Also, `If` is desugared into `When` matching on `False` and `_` at this point.
|
/// Also, `If` is desugared into `When` matching on `False` and `_` at this point.
|
||||||
When(
|
When {
|
||||||
Variable,
|
cond_var: Variable,
|
||||||
Box<Located<Expr>>,
|
expr_var: Variable,
|
||||||
Vec<((Variable, Located<Pattern>), (Variable, Located<Expr>))>,
|
loc_cond: Box<Located<Expr>>,
|
||||||
),
|
branches: Vec<(Located<Pattern>, Located<Expr>)>,
|
||||||
|
},
|
||||||
Defs(Vec<Def>, Box<Located<Expr>>),
|
Defs(Vec<Def>, Box<Located<Expr>>),
|
||||||
|
|
||||||
/// This is *only* for calling functions, not for tag application.
|
/// This is *only* for calling functions, not for tag application.
|
||||||
/// The Tag variant contains any applied values inside it.
|
/// The Tag variant contains any applied values inside it.
|
||||||
Call(Box<Expr>, Vec<(Variable, Located<Expr>)>, CalledVia),
|
Call(
|
||||||
|
Box<(Variable, Located<Expr>, Variable)>,
|
||||||
|
Vec<(Variable, Located<Expr>)>,
|
||||||
|
CalledVia,
|
||||||
|
),
|
||||||
|
|
||||||
Closure(
|
Closure(
|
||||||
Symbol,
|
Symbol,
|
||||||
Recursive,
|
Recursive,
|
||||||
Vec<(Variable, Located<Pattern>)>,
|
Vec<(Variable, Located<Pattern>)>,
|
||||||
Box<(Variable, Located<Expr>)>,
|
Box<(Located<Expr>, Variable)>,
|
||||||
),
|
),
|
||||||
|
|
||||||
// Product Types
|
// Product Types
|
||||||
|
@ -190,11 +187,10 @@ pub fn canonicalize_expr(
|
||||||
// The expression that evaluates to the function being called, e.g. `foo` in
|
// The expression that evaluates to the function being called, e.g. `foo` in
|
||||||
// (foo) bar baz
|
// (foo) bar baz
|
||||||
let fn_region = loc_fn.region;
|
let fn_region = loc_fn.region;
|
||||||
// TODO look up the name and use NamedFnArg if possible.
|
|
||||||
|
|
||||||
// Canonicalize the function expression and its arguments
|
// Canonicalize the function expression and its arguments
|
||||||
let (fn_expr, mut output) =
|
let (fn_expr, mut output) =
|
||||||
canonicalize_expr(env, var_store, scope, loc_fn.region, &loc_fn.value);
|
canonicalize_expr(env, var_store, scope, fn_region, &loc_fn.value);
|
||||||
|
|
||||||
// The function's return type
|
// The function's return type
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
|
@ -212,26 +208,35 @@ pub fn canonicalize_expr(
|
||||||
output.tail_call = None;
|
output.tail_call = None;
|
||||||
|
|
||||||
let expr = match fn_expr.value {
|
let expr = match fn_expr.value {
|
||||||
Var(_, ref sym) | FunctionPointer(_, ref sym) => {
|
Var {
|
||||||
// In the FunctionPointer case, we're calling an inline closure;
|
ref resolved_symbol,
|
||||||
// something like ((\a b -> a + b) 1 2).
|
..
|
||||||
output.references.calls.insert(sym.clone());
|
} => {
|
||||||
|
output.references.calls.insert(resolved_symbol.clone());
|
||||||
|
|
||||||
// we're tail-calling a symbol by name, check if it's the tail-callable symbol
|
// we're tail-calling a symbol by name, check if it's the tail-callable symbol
|
||||||
output.tail_call = match &env.tailcallable_symbol {
|
output.tail_call = match &env.tailcallable_symbol {
|
||||||
Some(tc_sym) if tc_sym == sym => Some(sym.clone()),
|
Some(tc_sym) if tc_sym == resolved_symbol => Some(resolved_symbol.clone()),
|
||||||
Some(_) | None => None,
|
Some(_) | None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
Call(Box::new(fn_expr.value), args, *application_style)
|
Call(
|
||||||
|
Box::new((var_store.fresh(), fn_expr, var_store.fresh())),
|
||||||
|
args,
|
||||||
|
*application_style,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
RuntimeError(_) => {
|
RuntimeError(_) => {
|
||||||
// We can't call a runtime error; bail out by propagating it!
|
// We can't call a runtime error; bail out by propagating it!
|
||||||
return (fn_expr, output);
|
return (fn_expr, output);
|
||||||
}
|
}
|
||||||
not_var => {
|
_ => {
|
||||||
// This could be something like ((if True then fn1 else fn2) arg1 arg2).
|
// This could be something like ((if True then fn1 else fn2) arg1 arg2).
|
||||||
Call(Box::new(not_var), args, *application_style)
|
Call(
|
||||||
|
Box::new((var_store.fresh(), fn_expr, var_store.fresh())),
|
||||||
|
args,
|
||||||
|
*application_style,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -250,7 +255,7 @@ pub fn canonicalize_expr(
|
||||||
|
|
||||||
let ident = Ident::new(module_parts, name);
|
let ident = Ident::new(module_parts, name);
|
||||||
|
|
||||||
canonicalize_lookup(env, scope, ident, symbol, region, var_store)
|
canonicalize_lookup(env, scope, ident, symbol, region)
|
||||||
} //ast::Expr::InterpolatedStr(pairs, suffix) => {
|
} //ast::Expr::InterpolatedStr(pairs, suffix) => {
|
||||||
// let mut output = Output::new();
|
// let mut output = Output::new();
|
||||||
// let can_pairs: Vec<(String, Located<Expr>)> = pairs
|
// let can_pairs: Vec<(String, Located<Expr>)> = pairs
|
||||||
|
@ -326,11 +331,6 @@ pub fn canonicalize_expr(
|
||||||
// it means there was shadowing, which will be handled later.
|
// it means there was shadowing, which will be handled later.
|
||||||
scope.idents = union_pairs(scope.idents, arg_idents.iter());
|
scope.idents = union_pairs(scope.idents, arg_idents.iter());
|
||||||
|
|
||||||
let mut state = PatternState {
|
|
||||||
headers: SendMap::default(),
|
|
||||||
vars: Vec::with_capacity(loc_arg_patterns.len()),
|
|
||||||
constraints: Vec::with_capacity(1),
|
|
||||||
};
|
|
||||||
let mut can_args = Vec::with_capacity(loc_arg_patterns.len());
|
let mut can_args = Vec::with_capacity(loc_arg_patterns.len());
|
||||||
|
|
||||||
for loc_pattern in loc_arg_patterns.into_iter() {
|
for loc_pattern in loc_arg_patterns.into_iter() {
|
||||||
|
@ -384,7 +384,7 @@ pub fn canonicalize_expr(
|
||||||
symbol,
|
symbol,
|
||||||
Recursive::NotRecursive,
|
Recursive::NotRecursive,
|
||||||
can_args,
|
can_args,
|
||||||
Box::new((var_store.fresh(), loc_body_expr)),
|
Box::new((loc_body_expr, var_store.fresh())),
|
||||||
),
|
),
|
||||||
output,
|
output,
|
||||||
)
|
)
|
||||||
|
@ -392,7 +392,6 @@ pub fn canonicalize_expr(
|
||||||
ast::Expr::When(loc_cond, branches) => {
|
ast::Expr::When(loc_cond, branches) => {
|
||||||
// Infer the condition expression's type.
|
// Infer the condition expression's type.
|
||||||
let cond_var = var_store.fresh();
|
let cond_var = var_store.fresh();
|
||||||
let cond_type = Variable(cond_var);
|
|
||||||
let (can_cond, mut output) =
|
let (can_cond, mut output) =
|
||||||
canonicalize_expr(env, var_store, scope, region, &loc_cond.value);
|
canonicalize_expr(env, var_store, scope, region, &loc_cond.value);
|
||||||
|
|
||||||
|
@ -401,7 +400,7 @@ pub fn canonicalize_expr(
|
||||||
|
|
||||||
let mut can_branches = Vec::with_capacity(branches.len());
|
let mut can_branches = Vec::with_capacity(branches.len());
|
||||||
|
|
||||||
for (index, (loc_pattern, loc_expr)) in branches.into_iter().enumerate() {
|
for (loc_pattern, loc_expr) in branches {
|
||||||
let mut shadowable_idents = scope.idents.clone();
|
let mut shadowable_idents = scope.idents.clone();
|
||||||
|
|
||||||
remove_idents(&loc_pattern.value, &mut shadowable_idents);
|
remove_idents(&loc_pattern.value, &mut shadowable_idents);
|
||||||
|
@ -418,10 +417,7 @@ pub fn canonicalize_expr(
|
||||||
|
|
||||||
output.references = output.references.union(branch_references);
|
output.references = output.references.union(branch_references);
|
||||||
|
|
||||||
can_branches.push((
|
can_branches.push((can_pattern, loc_can_expr));
|
||||||
(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
|
// A "when" with no branches is a runtime error, but it will mess things up
|
||||||
|
@ -432,7 +428,12 @@ pub fn canonicalize_expr(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Incorporate all three expressions into a combined Output value.
|
// Incorporate all three expressions into a combined Output value.
|
||||||
let expr = When(cond_var, Box::new(can_cond), can_branches);
|
let expr = When {
|
||||||
|
expr_var: var_store.fresh(),
|
||||||
|
cond_var,
|
||||||
|
loc_cond: Box::new(can_cond),
|
||||||
|
branches: can_branches,
|
||||||
|
};
|
||||||
|
|
||||||
(expr, output)
|
(expr, output)
|
||||||
}
|
}
|
||||||
|
@ -549,15 +550,17 @@ fn canonicalize_lookup(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
ident: Ident,
|
ident: Ident,
|
||||||
symbol: Symbol,
|
symbol_for_lookup: Symbol,
|
||||||
region: Region,
|
region: Region,
|
||||||
var_store: &VarStore,
|
|
||||||
) -> (Expr, Output) {
|
) -> (Expr, Output) {
|
||||||
use self::Expr::*;
|
use self::Expr::*;
|
||||||
|
|
||||||
let mut output = Output::default();
|
let mut output = Output::default();
|
||||||
let can_expr = match resolve_ident(&env, &scope, ident, &mut output.references) {
|
let can_expr = match resolve_ident(&env, &scope, ident, &mut output.references) {
|
||||||
Ok(sub_symbol) => Var(var_store.fresh(), sub_symbol),
|
Ok(resolved_symbol) => Var {
|
||||||
|
symbol_for_lookup,
|
||||||
|
resolved_symbol,
|
||||||
|
},
|
||||||
Err(ident) => {
|
Err(ident) => {
|
||||||
let loc_ident = Located {
|
let loc_ident = Located {
|
||||||
region,
|
region,
|
||||||
|
|
|
@ -6,8 +6,9 @@ use crate::can::scope::Scope;
|
||||||
use crate::can::symbol::Symbol;
|
use crate::can::symbol::Symbol;
|
||||||
use crate::collections::SendMap;
|
use crate::collections::SendMap;
|
||||||
use crate::parse::ast::{self, ExposesEntry};
|
use crate::parse::ast::{self, ExposesEntry};
|
||||||
use crate::region::Located;
|
use crate::region::{Located, Region};
|
||||||
use crate::subs::{VarStore, Variable};
|
use crate::subs::{VarStore, Variable};
|
||||||
|
use crate::types::Constraint;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -15,6 +16,7 @@ pub struct Module {
|
||||||
pub name: Option<Box<str>>,
|
pub name: Option<Box<str>>,
|
||||||
pub defs: Vec<Def>,
|
pub defs: Vec<Def>,
|
||||||
pub exposed_imports: SendMap<Symbol, Variable>,
|
pub exposed_imports: SendMap<Symbol, Variable>,
|
||||||
|
pub constraint: Constraint,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonicalize_module_defs<'a, I>(
|
pub fn canonicalize_module_defs<'a, I>(
|
||||||
|
@ -24,7 +26,11 @@ pub fn canonicalize_module_defs<'a, I>(
|
||||||
_exposes: I,
|
_exposes: I,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
var_store: &VarStore,
|
var_store: &VarStore,
|
||||||
) -> (Vec<Def>, SendMap<Symbol, Variable>)
|
) -> (
|
||||||
|
Vec<Def>,
|
||||||
|
SendMap<Symbol, Variable>,
|
||||||
|
Vec<(Symbol, Variable, Region)>,
|
||||||
|
)
|
||||||
where
|
where
|
||||||
I: Iterator<Item = Located<ExposesEntry<'a>>>,
|
I: Iterator<Item = Located<ExposesEntry<'a>>>,
|
||||||
{
|
{
|
||||||
|
@ -48,6 +54,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut env = Env::new(home);
|
let mut env = Env::new(home);
|
||||||
|
let mut lookups = Vec::with_capacity(scope.idents.len());
|
||||||
|
|
||||||
// Exposed values are treated like defs that appear before any others, e.g.
|
// Exposed values are treated like defs that appear before any others, e.g.
|
||||||
//
|
//
|
||||||
|
@ -62,17 +69,24 @@ where
|
||||||
// by canonicalizing them right before we canonicalize the actual ast::Def nodes.
|
// by canonicalizing them right before we canonicalize the actual ast::Def nodes.
|
||||||
for (ident, (symbol, region)) in scope.idents.iter() {
|
for (ident, (symbol, region)) in scope.idents.iter() {
|
||||||
if ident.first_char().is_lowercase() {
|
if ident.first_char().is_lowercase() {
|
||||||
|
let expr_var = var_store.fresh();
|
||||||
|
|
||||||
// Add an entry to exposed_imports using the current module's name
|
// 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
|
// 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
|
// 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.
|
// anything references `baz` in this Foo module, it will resolve to Bar.baz.
|
||||||
exposed_imports.insert(scope.symbol(&*ident.clone().name()), var_store.fresh());
|
exposed_imports.insert(scope.symbol(&*ident.clone().name()), expr_var);
|
||||||
|
|
||||||
|
// This will be used during constraint generation,
|
||||||
|
// to add the usual Lookup constraint as if this were a normal def.
|
||||||
|
lookups.push((symbol.clone(), expr_var, *region));
|
||||||
} else {
|
} else {
|
||||||
// TODO add type aliases to type alias dictionary, based on exposed types
|
// TODO add type aliases to type alias dictionary, based on exposed types
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let defs = canonicalize_defs(&mut env, var_store, scope, &desugared);
|
let mut output = Output::default();
|
||||||
|
let defs = canonicalize_defs(&mut env, &mut output.rigids, var_store, scope, &desugared);
|
||||||
let defs = match sort_can_defs(&mut env, defs, Output::default()) {
|
let defs = match sort_can_defs(&mut env, defs, Output::default()) {
|
||||||
(Ok(defs), _) => {
|
(Ok(defs), _) => {
|
||||||
// TODO examine the patterns, extract toplevel identifiers from them,
|
// TODO examine the patterns, extract toplevel identifiers from them,
|
||||||
|
@ -89,5 +103,5 @@ where
|
||||||
// TODO incorporate rigids into here (possibly by making this be a Let instead
|
// TODO incorporate rigids into here (possibly by making this be a Let instead
|
||||||
// of an And)
|
// of an And)
|
||||||
|
|
||||||
(defs, exposed_imports)
|
(defs, exposed_imports, lookups)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,12 @@ use crate::can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_
|
||||||
use crate::can::problem::Problem;
|
use crate::can::problem::Problem;
|
||||||
use crate::can::scope::Scope;
|
use crate::can::scope::Scope;
|
||||||
use crate::can::symbol::Symbol;
|
use crate::can::symbol::Symbol;
|
||||||
use crate::collections::{ImMap, SendMap};
|
use crate::collections::ImMap;
|
||||||
use crate::ident::Ident;
|
use crate::ident::Ident;
|
||||||
use crate::parse::ast;
|
use crate::parse::ast;
|
||||||
use crate::region::{Located, Region};
|
use crate::region::{Located, Region};
|
||||||
use crate::subs::VarStore;
|
use crate::subs::VarStore;
|
||||||
use crate::subs::Variable;
|
use crate::subs::Variable;
|
||||||
use crate::types::{Constraint, PExpected, PatternCategory, Type};
|
|
||||||
use im_rc::Vector;
|
use im_rc::Vector;
|
||||||
|
|
||||||
/// A pattern, including possible problems (e.g. shadowing) so that
|
/// A pattern, including possible problems (e.g. shadowing) so that
|
||||||
|
@ -23,8 +22,16 @@ pub enum Pattern {
|
||||||
AppliedTag(Symbol, Vec<Located<Pattern>>),
|
AppliedTag(Symbol, Vec<Located<Pattern>>),
|
||||||
IntLiteral(i64),
|
IntLiteral(i64),
|
||||||
FloatLiteral(f64),
|
FloatLiteral(f64),
|
||||||
ExactString(Box<str>),
|
StrLiteral(Box<str>),
|
||||||
RecordDestructure(Vec<(Located<Pattern>, Option<Located<Pattern>>)>),
|
RecordDestructure(
|
||||||
|
Variable,
|
||||||
|
Vec<(
|
||||||
|
Variable,
|
||||||
|
Lowercase,
|
||||||
|
Symbol,
|
||||||
|
Option<(Variable, Located<Pattern>)>,
|
||||||
|
)>,
|
||||||
|
),
|
||||||
Underscore,
|
Underscore,
|
||||||
|
|
||||||
// Runtime Exceptions
|
// Runtime Exceptions
|
||||||
|
@ -137,7 +144,7 @@ pub fn canonicalize_pattern<'a>(
|
||||||
&StrLiteral(_string) => match pattern_type {
|
&StrLiteral(_string) => match pattern_type {
|
||||||
WhenBranch => {
|
WhenBranch => {
|
||||||
panic!("TODO check whether string pattern is malformed.");
|
panic!("TODO check whether string pattern is malformed.");
|
||||||
// Pattern::ExactString((*string).into())
|
// Pattern::StrLiteral((*string).into())
|
||||||
}
|
}
|
||||||
ptype @ Assignment | ptype @ TopLevelDef | ptype @ FunctionArg => {
|
ptype @ Assignment | ptype @ TopLevelDef | ptype @ FunctionArg => {
|
||||||
unsupported_pattern(env, ptype, region)
|
unsupported_pattern(env, ptype, region)
|
||||||
|
@ -157,32 +164,49 @@ pub fn canonicalize_pattern<'a>(
|
||||||
}
|
}
|
||||||
&RecordDestructure(patterns) => {
|
&RecordDestructure(patterns) => {
|
||||||
let mut fields = Vec::with_capacity(patterns.len());
|
let mut fields = Vec::with_capacity(patterns.len());
|
||||||
|
|
||||||
for loc_pattern in patterns {
|
for loc_pattern in patterns {
|
||||||
match loc_pattern.value {
|
match loc_pattern.value {
|
||||||
Identifier(ref name) => {
|
Identifier(label) => {
|
||||||
let result = match canonicalize_pattern_identifier(
|
let symbol = match canonicalize_pattern_identifier(
|
||||||
name,
|
&label,
|
||||||
env,
|
env,
|
||||||
scope,
|
scope,
|
||||||
region,
|
region,
|
||||||
shadowable_idents,
|
shadowable_idents,
|
||||||
) {
|
) {
|
||||||
Ok(symbol) => Pattern::Identifier(symbol),
|
Ok(symbol) => symbol,
|
||||||
Err(loc_shadowed_ident) => Pattern::Shadowed(loc_shadowed_ident),
|
Err(loc_shadowed_ident) => {
|
||||||
|
// If any idents are shadowed, consider the entire
|
||||||
|
// destructure pattern shadowed!
|
||||||
|
let _loc_pattern = Located {
|
||||||
|
region,
|
||||||
|
value: Pattern::Shadowed(loc_shadowed_ident),
|
||||||
|
};
|
||||||
|
panic!("TODO gather all the shadowing errors, not just the first one, and report them in Problems.");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fields.push((Located::at(region, result), None));
|
fields.push((var_store.fresh(), Lowercase::from(label), symbol, None));
|
||||||
}
|
}
|
||||||
RecordField(ref name, loc_guard) => {
|
RecordField(label, loc_guard) => {
|
||||||
let result = match canonicalize_pattern_identifier(
|
let symbol = match canonicalize_pattern_identifier(
|
||||||
name,
|
&label,
|
||||||
env,
|
env,
|
||||||
scope,
|
scope,
|
||||||
region,
|
region,
|
||||||
shadowable_idents,
|
shadowable_idents,
|
||||||
) {
|
) {
|
||||||
Ok(symbol) => Pattern::Identifier(symbol),
|
Ok(symbol) => symbol,
|
||||||
Err(loc_shadowed_ident) => Pattern::Shadowed(loc_shadowed_ident),
|
Err(loc_shadowed_ident) => {
|
||||||
|
// If any idents are shadowed, consider the entire
|
||||||
|
// destructure pattern shadowed!
|
||||||
|
let _loc_pattern = Located {
|
||||||
|
region,
|
||||||
|
value: Pattern::Shadowed(loc_shadowed_ident),
|
||||||
|
};
|
||||||
|
panic!("TODO gather all the shadowing errors, not just the first one, and report them in Problems.");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let can_guard = canonicalize_pattern(
|
let can_guard = canonicalize_pattern(
|
||||||
|
@ -195,12 +219,18 @@ pub fn canonicalize_pattern<'a>(
|
||||||
shadowable_idents,
|
shadowable_idents,
|
||||||
);
|
);
|
||||||
|
|
||||||
fields.push((Located::at(region, result), Some(can_guard)));
|
fields.push((
|
||||||
|
var_store.fresh(),
|
||||||
|
Lowercase::from(label),
|
||||||
|
symbol,
|
||||||
|
Some((var_store.fresh(), can_guard)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
_ => panic!("invalid pattern in record"),
|
_ => panic!("invalid pattern in record"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Pattern::RecordDestructure(fields)
|
|
||||||
|
Pattern::RecordDestructure(var_store.fresh(), fields)
|
||||||
}
|
}
|
||||||
&RecordField(_name, _loc_pattern) => {
|
&RecordField(_name, _loc_pattern) => {
|
||||||
unreachable!("should be handled in RecordDestructure");
|
unreachable!("should be handled in RecordDestructure");
|
||||||
|
@ -279,10 +309,8 @@ pub fn canonicalize_pattern_identifier<'a>(
|
||||||
// tag application patterns, which can bring multiple
|
// tag application patterns, which can bring multiple
|
||||||
// new idents into scope. For example, it's important that
|
// new idents into scope. For example, it's important that
|
||||||
// we catch (Blah foo foo) -> … as being an example of shadowing.
|
// we catch (Blah foo foo) -> … as being an example of shadowing.
|
||||||
scope
|
shadowable_idents.insert(new_ident.clone(), symbol_and_region.clone());
|
||||||
.idents
|
scope.idents.insert(new_ident, symbol_and_region);
|
||||||
.insert(new_ident.clone(), symbol_and_region.clone());
|
|
||||||
shadowable_idents.insert(new_ident, symbol_and_region);
|
|
||||||
|
|
||||||
Ok(symbol)
|
Ok(symbol)
|
||||||
}
|
}
|
||||||
|
@ -299,131 +327,6 @@ fn unsupported_pattern(env: &mut Env, pattern_type: PatternType, region: Region)
|
||||||
Pattern::UnsupportedPattern(region)
|
Pattern::UnsupportedPattern(region)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CONSTRAIN
|
|
||||||
|
|
||||||
pub struct PatternState {
|
|
||||||
pub headers: SendMap<Symbol, Located<Type>>,
|
|
||||||
pub vars: Vec<Variable>,
|
|
||||||
pub constraints: Vec<Constraint>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_constraints<'a>(
|
|
||||||
pattern: &'a ast::Pattern<'a>,
|
|
||||||
scope: &'a Scope,
|
|
||||||
region: Region,
|
|
||||||
expected: PExpected<Type>,
|
|
||||||
state: &'a mut PatternState,
|
|
||||||
var_store: &VarStore,
|
|
||||||
) {
|
|
||||||
use crate::parse::ast::Pattern::*;
|
|
||||||
|
|
||||||
match pattern {
|
|
||||||
Underscore | Malformed(_) | QualifiedIdentifier(_) => {
|
|
||||||
// Neither the _ pattern nor malformed ones add any constraints.
|
|
||||||
}
|
|
||||||
Identifier(name) => {
|
|
||||||
state.headers.insert(
|
|
||||||
scope.symbol(name),
|
|
||||||
Located {
|
|
||||||
region,
|
|
||||||
value: expected.get_type(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
IntLiteral(_) | NonBase10Literal { .. } => {
|
|
||||||
state.constraints.push(Constraint::Pattern(
|
|
||||||
region,
|
|
||||||
PatternCategory::Int,
|
|
||||||
Type::int(),
|
|
||||||
expected,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
FloatLiteral(_) => {
|
|
||||||
state.constraints.push(Constraint::Pattern(
|
|
||||||
region,
|
|
||||||
PatternCategory::Float,
|
|
||||||
Type::float(),
|
|
||||||
expected,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
StrLiteral(_) => {
|
|
||||||
state.constraints.push(Constraint::Pattern(
|
|
||||||
region,
|
|
||||||
PatternCategory::Str,
|
|
||||||
Type::string(),
|
|
||||||
expected,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockStrLiteral(_) => {
|
|
||||||
state.constraints.push(Constraint::Pattern(
|
|
||||||
region,
|
|
||||||
PatternCategory::Str,
|
|
||||||
Type::string(),
|
|
||||||
expected,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
SpaceBefore(pattern, _) | SpaceAfter(pattern, _) | Nested(pattern) => {
|
|
||||||
add_constraints(pattern, scope, region, expected, state, var_store)
|
|
||||||
}
|
|
||||||
|
|
||||||
RecordDestructure(patterns) => {
|
|
||||||
let ext_var = var_store.fresh();
|
|
||||||
let ext_type = Type::Variable(ext_var);
|
|
||||||
|
|
||||||
let mut field_types: SendMap<Lowercase, Type> = SendMap::default();
|
|
||||||
for loc_pattern in patterns {
|
|
||||||
let pat_var = var_store.fresh();
|
|
||||||
let pat_type = Type::Variable(pat_var);
|
|
||||||
let expected = PExpected::NoExpectation(pat_type.clone());
|
|
||||||
|
|
||||||
match loc_pattern.value {
|
|
||||||
Identifier(name) | RecordField(name, _) => {
|
|
||||||
let symbol = scope.symbol(name);
|
|
||||||
if !state.headers.contains_key(&symbol) {
|
|
||||||
state
|
|
||||||
.headers
|
|
||||||
.insert(symbol, Located::at(region, pat_type.clone()));
|
|
||||||
}
|
|
||||||
field_types.insert(name.into(), pat_type.clone());
|
|
||||||
}
|
|
||||||
_ => panic!("invalid record pattern"),
|
|
||||||
}
|
|
||||||
|
|
||||||
if let RecordField(_, guard) = loc_pattern.value {
|
|
||||||
add_constraints(
|
|
||||||
&guard.value,
|
|
||||||
scope,
|
|
||||||
guard.region,
|
|
||||||
expected,
|
|
||||||
state,
|
|
||||||
var_store,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.vars.push(pat_var);
|
|
||||||
}
|
|
||||||
|
|
||||||
let record_type = Type::Record(field_types, Box::new(ext_type));
|
|
||||||
let record_con =
|
|
||||||
Constraint::Pattern(region, PatternCategory::Record, record_type, expected);
|
|
||||||
|
|
||||||
state.constraints.push(record_con);
|
|
||||||
}
|
|
||||||
|
|
||||||
RecordField(_, _) => {
|
|
||||||
// unreachable, this pattern is handled by already by RecordDestructure
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalTag(_) | PrivateTag(_) | Apply(_, _) => {
|
|
||||||
panic!("TODO add_constraints for {:?}", pattern);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_idents(pattern: &ast::Pattern, idents: &mut ImMap<Ident, (Symbol, Region)>) {
|
pub fn remove_idents(pattern: &ast::Pattern, idents: &mut ImMap<Ident, (Symbol, Region)>) {
|
||||||
use crate::parse::ast::Pattern::*;
|
use crate::parse::ast::Pattern::*;
|
||||||
|
|
||||||
|
|
72
src/constrain/builtins.rs
Normal file
72
src/constrain/builtins.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
use crate::region::Region;
|
||||||
|
use crate::subs::Variable;
|
||||||
|
use crate::types::Constraint::{self, *};
|
||||||
|
use crate::types::Expected::{self, *};
|
||||||
|
use crate::types::Type::{self, *};
|
||||||
|
use crate::types::{self, Reason};
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn int_literal(var: Variable, expected: Expected<Type>, region: Region) -> Constraint {
|
||||||
|
let typ = number_literal_type("Int", "Integer");
|
||||||
|
let reason = Reason::IntLiteral;
|
||||||
|
|
||||||
|
num_literal(var, typ, reason, expected, region)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn float_literal(var: Variable, expected: Expected<Type>, region: Region) -> Constraint {
|
||||||
|
let typ = number_literal_type("Float", "FloatingPoint");
|
||||||
|
let reason = Reason::FloatLiteral;
|
||||||
|
|
||||||
|
num_literal(var, typ, reason, expected, region)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn num_literal(
|
||||||
|
num_var: Variable,
|
||||||
|
literal_type: Type,
|
||||||
|
reason: Reason,
|
||||||
|
expected: Expected<Type>,
|
||||||
|
region: Region,
|
||||||
|
) -> Constraint {
|
||||||
|
let num_type = Variable(num_var);
|
||||||
|
let expected_literal = ForReason(reason, literal_type, region);
|
||||||
|
|
||||||
|
And(vec![
|
||||||
|
Eq(num_type.clone(), expected_literal, region),
|
||||||
|
Eq(num_type, expected, region),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn number_literal_type(module_name: &str, type_name: &str) -> Type {
|
||||||
|
builtin_type(
|
||||||
|
types::MOD_NUM,
|
||||||
|
types::TYPE_NUM,
|
||||||
|
vec![builtin_type(module_name, type_name, Vec::new())],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn builtin_type(module_name: &str, type_name: &str, args: Vec<Type>) -> Type {
|
||||||
|
Type::Apply {
|
||||||
|
module_name: module_name.into(),
|
||||||
|
name: type_name.into(),
|
||||||
|
args,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn empty_list_type(var: Variable) -> Type {
|
||||||
|
list_type(Type::Variable(var))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn list_type(typ: Type) -> Type {
|
||||||
|
builtin_type("List", "List", vec![typ])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn str_type() -> Type {
|
||||||
|
builtin_type("Str", "Str", Vec::new())
|
||||||
|
}
|
603
src/constrain/expr.rs
Normal file
603
src/constrain/expr.rs
Normal file
|
@ -0,0 +1,603 @@
|
||||||
|
use crate::can::def::Def;
|
||||||
|
use crate::can::expr::Expr::{self, *};
|
||||||
|
use crate::can::ident::Lowercase;
|
||||||
|
use crate::can::pattern::Pattern;
|
||||||
|
use crate::can::symbol::Symbol;
|
||||||
|
use crate::collections::{ImMap, SendMap};
|
||||||
|
use crate::constrain::builtins::{
|
||||||
|
empty_list_type, float_literal, int_literal, list_type, str_type,
|
||||||
|
};
|
||||||
|
use crate::constrain::pattern::{constrain_pattern, PatternState};
|
||||||
|
use crate::region::{Located, Region};
|
||||||
|
use crate::subs::Variable;
|
||||||
|
use crate::types::AnnotationSource::*;
|
||||||
|
use crate::types::Constraint::{self, *};
|
||||||
|
use crate::types::Expected::{self, *};
|
||||||
|
use crate::types::PReason;
|
||||||
|
use crate::types::Type::{self, *};
|
||||||
|
use crate::types::{LetConstraint, PExpected, Reason};
|
||||||
|
|
||||||
|
/// Whenever we encounter a user-defined type variable (a "rigid" var for short),
|
||||||
|
/// for example `a` in the annotation `identity : a -> a`, we add it to this
|
||||||
|
/// map so that expressions within that annotation can share these vars.
|
||||||
|
pub type Rigids = ImMap<Lowercase, Type>;
|
||||||
|
|
||||||
|
/// This is for constraining Defs
|
||||||
|
#[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 exists(flex_vars: Vec<Variable>, constraint: Constraint) -> Constraint {
|
||||||
|
Let(Box::new(LetConstraint {
|
||||||
|
rigid_vars: Vec::new(),
|
||||||
|
flex_vars,
|
||||||
|
def_types: SendMap::default(),
|
||||||
|
defs_constraint: constraint,
|
||||||
|
ret_constraint: Constraint::True,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn constrain_expr(
|
||||||
|
rigids: &Rigids,
|
||||||
|
region: Region,
|
||||||
|
expr: &Expr,
|
||||||
|
expected: Expected<Type>,
|
||||||
|
) -> Constraint {
|
||||||
|
match expr {
|
||||||
|
Int(var, _) => int_literal(*var, expected, region),
|
||||||
|
Float(var, _) => float_literal(*var, expected, region),
|
||||||
|
EmptyRecord => constrain_empty_record(region, expected),
|
||||||
|
Expr::Record(stored_var, fields) => {
|
||||||
|
if fields.is_empty() {
|
||||||
|
constrain_empty_record(region, expected)
|
||||||
|
} 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());
|
||||||
|
|
||||||
|
for (label, (field_var, loc_field_expr)) in fields {
|
||||||
|
let (field_type, field_con) =
|
||||||
|
constrain_field(rigids, *field_var, loc_field_expr);
|
||||||
|
|
||||||
|
field_vars.push(*field_var);
|
||||||
|
field_exprs.insert(label.clone(), loc_field_expr);
|
||||||
|
field_types.insert(label.clone(), field_type);
|
||||||
|
|
||||||
|
constraints.push(field_con);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_con = Eq(Type::Variable(*stored_var), expected, region);
|
||||||
|
|
||||||
|
field_vars.push(*stored_var);
|
||||||
|
constraints.push(stored_con);
|
||||||
|
|
||||||
|
exists(field_vars, And(constraints))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Str(_) | BlockStr(_) => Eq(str_type(), expected, region),
|
||||||
|
List(list_var, loc_elems) => {
|
||||||
|
if loc_elems.is_empty() {
|
||||||
|
Eq(empty_list_type(*list_var), expected, region)
|
||||||
|
} else {
|
||||||
|
let list_elem_type = Type::Variable(*list_var);
|
||||||
|
let mut constraints = Vec::with_capacity(1 + (loc_elems.len() * 2));
|
||||||
|
|
||||||
|
for (elem_var, loc_elem) in loc_elems {
|
||||||
|
let elem_type = Variable(*elem_var);
|
||||||
|
let elem_expected = NoExpectation(elem_type.clone());
|
||||||
|
let list_elem_constraint = Eq(
|
||||||
|
list_elem_type.clone(),
|
||||||
|
ForReason(Reason::ElemInList, elem_type, region),
|
||||||
|
region,
|
||||||
|
);
|
||||||
|
let constraint =
|
||||||
|
constrain_expr(rigids, loc_elem.region, &loc_elem.value, elem_expected);
|
||||||
|
|
||||||
|
constraints.push(list_elem_constraint);
|
||||||
|
constraints.push(constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
constraints.push(Eq(list_type(list_elem_type), expected, region));
|
||||||
|
|
||||||
|
And(constraints)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Call(boxed, loc_args, _application_style) => {
|
||||||
|
let (fn_var, loc_fn, ret_var) = &**boxed;
|
||||||
|
// The expression that evaluates to the function being called, e.g. `foo` in
|
||||||
|
// (foo) bar baz
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
let fn_con = constrain_expr(rigids, loc_fn.region, &loc_fn.value, fn_expected);
|
||||||
|
|
||||||
|
// The function's return type
|
||||||
|
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());
|
||||||
|
|
||||||
|
for (index, (arg_var, loc_arg)) in loc_args.iter().enumerate() {
|
||||||
|
let region = loc_arg.region;
|
||||||
|
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_con = constrain_expr(rigids, loc_arg.region, &loc_arg.value, expected_arg);
|
||||||
|
|
||||||
|
vars.push(*arg_var);
|
||||||
|
arg_types.push(arg_type);
|
||||||
|
arg_cons.push(arg_con);
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected_fn_type = ForReason(
|
||||||
|
fn_reason,
|
||||||
|
Function(arg_types, Box::new(ret_type.clone())),
|
||||||
|
region,
|
||||||
|
);
|
||||||
|
|
||||||
|
exists(
|
||||||
|
vars,
|
||||||
|
And(vec![
|
||||||
|
fn_con,
|
||||||
|
Eq(fn_type, expected_fn_type, fn_region),
|
||||||
|
And(arg_cons),
|
||||||
|
Eq(ret_type, expected, region),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Var {
|
||||||
|
symbol_for_lookup, ..
|
||||||
|
} => Lookup(symbol_for_lookup.clone(), expected, region),
|
||||||
|
Closure(_symbol, _recursive, args, boxed) => {
|
||||||
|
let (loc_body_expr, ret_var) = &**boxed;
|
||||||
|
let mut state = PatternState {
|
||||||
|
headers: SendMap::default(),
|
||||||
|
vars: Vec::with_capacity(args.len()),
|
||||||
|
constraints: Vec::with_capacity(1),
|
||||||
|
};
|
||||||
|
let mut vars = Vec::with_capacity(state.vars.capacity() + 1);
|
||||||
|
let mut pattern_types = Vec::with_capacity(state.vars.capacity());
|
||||||
|
let ret_var = *ret_var;
|
||||||
|
let ret_type = Type::Variable(ret_var);
|
||||||
|
|
||||||
|
vars.push(ret_var);
|
||||||
|
|
||||||
|
for (pattern_var, loc_pattern) in args {
|
||||||
|
let pattern_type = Type::Variable(*pattern_var);
|
||||||
|
let pattern_expected = PExpected::NoExpectation(pattern_type.clone());
|
||||||
|
|
||||||
|
pattern_types.push(pattern_type);
|
||||||
|
|
||||||
|
constrain_pattern(
|
||||||
|
&loc_pattern.value,
|
||||||
|
loc_pattern.region,
|
||||||
|
pattern_expected,
|
||||||
|
&mut state,
|
||||||
|
);
|
||||||
|
|
||||||
|
vars.push(*pattern_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
let fn_typ = Type::Function(pattern_types, Box::new(ret_type.clone()));
|
||||||
|
let body_type = NoExpectation(ret_type);
|
||||||
|
let ret_constraint = constrain_expr(
|
||||||
|
rigids,
|
||||||
|
loc_body_expr.region,
|
||||||
|
&loc_body_expr.value,
|
||||||
|
body_type,
|
||||||
|
);
|
||||||
|
|
||||||
|
let defs_constraint = And(state.constraints);
|
||||||
|
|
||||||
|
exists(
|
||||||
|
vars,
|
||||||
|
And(vec![
|
||||||
|
Let(Box::new(LetConstraint {
|
||||||
|
rigid_vars: Vec::new(),
|
||||||
|
flex_vars: state.vars,
|
||||||
|
def_types: state.headers,
|
||||||
|
defs_constraint,
|
||||||
|
ret_constraint,
|
||||||
|
})),
|
||||||
|
// "the closure's type is equal to expected type"
|
||||||
|
Eq(fn_typ, expected, region),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
When {
|
||||||
|
cond_var,
|
||||||
|
expr_var,
|
||||||
|
loc_cond,
|
||||||
|
branches,
|
||||||
|
} => {
|
||||||
|
// Infer the condition expression's type.
|
||||||
|
let cond_var = *cond_var;
|
||||||
|
let cond_type = Variable(cond_var);
|
||||||
|
let expr_con = constrain_expr(
|
||||||
|
rigids,
|
||||||
|
region,
|
||||||
|
&loc_cond.value,
|
||||||
|
NoExpectation(cond_type.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
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 branch_con = constrain_when_branch(
|
||||||
|
rigids,
|
||||||
|
region,
|
||||||
|
loc_pattern,
|
||||||
|
loc_expr,
|
||||||
|
PExpected::ForReason(
|
||||||
|
PReason::WhenMatch { index },
|
||||||
|
cond_type.clone(),
|
||||||
|
region,
|
||||||
|
),
|
||||||
|
FromAnnotation(
|
||||||
|
name.clone(),
|
||||||
|
arity,
|
||||||
|
TypedWhenBranch(index),
|
||||||
|
typ.clone(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO investigate: why doesn't this use expr_var?
|
||||||
|
// Shouldn't it?
|
||||||
|
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]),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
let branch_type = Variable(*expr_var);
|
||||||
|
let mut branch_cons = Vec::with_capacity(branches.len());
|
||||||
|
|
||||||
|
for (index, (loc_pattern, loc_expr)) in branches.into_iter().enumerate() {
|
||||||
|
let branch_con = constrain_when_branch(
|
||||||
|
rigids,
|
||||||
|
region,
|
||||||
|
loc_pattern,
|
||||||
|
loc_expr,
|
||||||
|
PExpected::ForReason(
|
||||||
|
PReason::WhenMatch { index },
|
||||||
|
cond_type.clone(),
|
||||||
|
region,
|
||||||
|
),
|
||||||
|
ForReason(Reason::WhenBranch { index }, branch_type.clone(), region),
|
||||||
|
);
|
||||||
|
|
||||||
|
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),
|
||||||
|
]),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
And(constraints)
|
||||||
|
}
|
||||||
|
Access {
|
||||||
|
ext_var,
|
||||||
|
field_var,
|
||||||
|
loc_expr,
|
||||||
|
field,
|
||||||
|
} => {
|
||||||
|
let ext_var = *ext_var;
|
||||||
|
let ext_type = Type::Variable(ext_var);
|
||||||
|
let field_var = *field_var;
|
||||||
|
let field_type = Type::Variable(field_var);
|
||||||
|
|
||||||
|
let mut rec_field_types = SendMap::default();
|
||||||
|
|
||||||
|
rec_field_types.insert(field.clone(), field_type.clone());
|
||||||
|
|
||||||
|
let record_type = Type::Record(rec_field_types, Box::new(ext_type));
|
||||||
|
let record_expected = Expected::NoExpectation(record_type);
|
||||||
|
|
||||||
|
let constraint =
|
||||||
|
constrain_expr(&ImMap::default(), region, &loc_expr.value, record_expected);
|
||||||
|
|
||||||
|
exists(
|
||||||
|
vec![field_var, ext_var],
|
||||||
|
And(vec![constraint, Eq(field_type, expected, region)]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Accessor {
|
||||||
|
field,
|
||||||
|
ext_var,
|
||||||
|
field_var,
|
||||||
|
} => {
|
||||||
|
let ext_var = *ext_var;
|
||||||
|
let ext_type = Variable(ext_var);
|
||||||
|
let field_var = *field_var;
|
||||||
|
let field_type = Variable(field_var);
|
||||||
|
|
||||||
|
let mut field_types = SendMap::default();
|
||||||
|
field_types.insert(field.clone(), field_type.clone());
|
||||||
|
let record_type = Type::Record(field_types, Box::new(ext_type));
|
||||||
|
|
||||||
|
exists(
|
||||||
|
vec![field_var, ext_var],
|
||||||
|
Eq(
|
||||||
|
Type::Function(vec![record_type], Box::new(field_type)),
|
||||||
|
expected,
|
||||||
|
region,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Defs(defs, loc_ret) => constrain_defs_with_return(
|
||||||
|
rigids,
|
||||||
|
defs,
|
||||||
|
expected,
|
||||||
|
Info::with_capacity(defs.len()),
|
||||||
|
Info::with_capacity(defs.len()),
|
||||||
|
loc_ret,
|
||||||
|
),
|
||||||
|
Tag(_, _) => {
|
||||||
|
panic!("TODO constrain Tag");
|
||||||
|
}
|
||||||
|
RuntimeError(_) => True,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn constrain_when_branch<'a>(
|
||||||
|
rigids: &Rigids,
|
||||||
|
region: Region,
|
||||||
|
loc_pattern: &Located<Pattern>,
|
||||||
|
loc_expr: &Located<Expr>,
|
||||||
|
pattern_expected: PExpected<Type>,
|
||||||
|
expr_expected: Expected<Type>,
|
||||||
|
) -> Constraint {
|
||||||
|
let ret_constraint = constrain_expr(rigids, region, &loc_expr.value, expr_expected);
|
||||||
|
|
||||||
|
let mut state = PatternState {
|
||||||
|
headers: SendMap::default(),
|
||||||
|
vars: Vec::with_capacity(1),
|
||||||
|
constraints: Vec::with_capacity(1),
|
||||||
|
};
|
||||||
|
|
||||||
|
constrain_pattern(
|
||||||
|
&loc_pattern.value,
|
||||||
|
loc_pattern.region,
|
||||||
|
pattern_expected,
|
||||||
|
&mut state,
|
||||||
|
);
|
||||||
|
|
||||||
|
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,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn constrain_field(
|
||||||
|
rigids: &Rigids,
|
||||||
|
field_var: Variable,
|
||||||
|
loc_expr: &Located<Expr>,
|
||||||
|
) -> (Type, Constraint) {
|
||||||
|
let field_type = Variable(field_var);
|
||||||
|
let field_expected = NoExpectation(field_type.clone());
|
||||||
|
let constraint = constrain_expr(rigids, loc_expr.region, &loc_expr.value, field_expected);
|
||||||
|
|
||||||
|
(field_type, constraint)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn constrain_empty_record(region: Region, expected: Expected<Type>) -> Constraint {
|
||||||
|
Eq(EmptyRec, expected, region)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn constrain_defs(
|
||||||
|
rigids: &Rigids,
|
||||||
|
found_rigids: &mut SendMap<Variable, Lowercase>,
|
||||||
|
defs: &[Def],
|
||||||
|
flex_info: &mut Info,
|
||||||
|
) {
|
||||||
|
for def in defs {
|
||||||
|
constrain_def(rigids, found_rigids, def, flex_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn constrain_def_pattern(loc_pattern: &Located<Pattern>, expr_type: Type) -> PatternState {
|
||||||
|
// 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 pattern_expected = PExpected::NoExpectation(expr_type);
|
||||||
|
|
||||||
|
let mut state = PatternState {
|
||||||
|
headers: SendMap::default(),
|
||||||
|
vars: Vec::with_capacity(1),
|
||||||
|
constraints: Vec::with_capacity(1),
|
||||||
|
};
|
||||||
|
|
||||||
|
constrain_pattern(
|
||||||
|
&loc_pattern.value,
|
||||||
|
loc_pattern.region,
|
||||||
|
pattern_expected,
|
||||||
|
&mut state,
|
||||||
|
);
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn constrain_def(
|
||||||
|
rigids: &Rigids,
|
||||||
|
found_rigids: &mut SendMap<Variable, Lowercase>,
|
||||||
|
def: &Def,
|
||||||
|
flex_info: &mut Info,
|
||||||
|
) {
|
||||||
|
use crate::types::AnnotationSource;
|
||||||
|
|
||||||
|
let expr_var = def.body_var;
|
||||||
|
let expr_type = Type::Variable(expr_var);
|
||||||
|
|
||||||
|
flex_info.vars.push(expr_var);
|
||||||
|
|
||||||
|
let pattern_state = constrain_def_pattern(&def.loc_pattern, expr_type.clone());
|
||||||
|
|
||||||
|
for (k, v) in &pattern_state.headers {
|
||||||
|
flex_info.def_types.insert(k.clone(), v.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret_constraint = match &def.annotation {
|
||||||
|
Some((annotation, seen_rigids)) => {
|
||||||
|
let mut ftv: Rigids = rigids.clone();
|
||||||
|
|
||||||
|
for (var, name) in seen_rigids {
|
||||||
|
// 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.clone(), Type::Variable(*var));
|
||||||
|
|
||||||
|
// mark this variable as a rigid
|
||||||
|
found_rigids.insert(*var, name.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let annotation_expected = FromAnnotation(
|
||||||
|
def.loc_pattern.clone(),
|
||||||
|
annotation.arity(),
|
||||||
|
AnnotationSource::TypedBody,
|
||||||
|
annotation.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// ensure expected type unifies with annotated type
|
||||||
|
flex_info.constraints.push(Eq(
|
||||||
|
expr_type,
|
||||||
|
annotation_expected.clone(),
|
||||||
|
def.loc_expr.region,
|
||||||
|
));
|
||||||
|
|
||||||
|
constrain_expr(
|
||||||
|
&ftv,
|
||||||
|
def.loc_expr.region,
|
||||||
|
&def.loc_expr.value,
|
||||||
|
annotation_expected,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
None => constrain_expr(
|
||||||
|
rigids,
|
||||||
|
def.loc_expr.region,
|
||||||
|
&def.loc_expr.value,
|
||||||
|
NoExpectation(expr_type),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn constrain_defs_with_return<'a>(
|
||||||
|
rigids: &Rigids,
|
||||||
|
defs: &[Def],
|
||||||
|
expected: Expected<Type>,
|
||||||
|
mut flex_info: Info,
|
||||||
|
rigid_info: Info,
|
||||||
|
loc_ret: &'a Located<Expr>,
|
||||||
|
) -> Constraint {
|
||||||
|
let mut found_rigids = SendMap::default();
|
||||||
|
|
||||||
|
constrain_defs(rigids, &mut found_rigids, defs, &mut flex_info);
|
||||||
|
|
||||||
|
// 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_con = constrain_expr(rigids, loc_ret.region, &loc_ret.value, expected);
|
||||||
|
|
||||||
|
// 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(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]),
|
||||||
|
})),
|
||||||
|
}))
|
||||||
|
}
|
|
@ -1,85 +1,4 @@
|
||||||
use crate::collections::SendMap;
|
pub mod builtins;
|
||||||
use crate::region::Region;
|
pub mod expr;
|
||||||
use crate::subs::{VarStore, Variable};
|
pub mod module;
|
||||||
use crate::types::Constraint::{self, *};
|
pub mod pattern;
|
||||||
use crate::types::Expected::{self, *};
|
|
||||||
use crate::types::Type::{self, *};
|
|
||||||
use crate::types::{self, LetConstraint, Reason};
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn exists(flex_vars: Vec<Variable>, constraint: Constraint) -> Constraint {
|
|
||||||
Constraint::Let(Box::new(LetConstraint {
|
|
||||||
rigid_vars: Vec::new(),
|
|
||||||
flex_vars,
|
|
||||||
def_types: SendMap::default(),
|
|
||||||
defs_constraint: constraint,
|
|
||||||
ret_constraint: Constraint::True,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn int_literal(var_store: &VarStore, expected: Expected<Type>, region: Region) -> Constraint {
|
|
||||||
let typ = number_literal_type("Int", "Integer");
|
|
||||||
let reason = Reason::IntLiteral;
|
|
||||||
|
|
||||||
num_literal(var_store, typ, reason, expected, region)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn float_literal(var_store: &VarStore, expected: Expected<Type>, region: Region) -> Constraint {
|
|
||||||
let typ = number_literal_type("Float", "FloatingPoint");
|
|
||||||
let reason = Reason::FloatLiteral;
|
|
||||||
|
|
||||||
num_literal(var_store, typ, reason, expected, region)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn num_literal(
|
|
||||||
var_store: &VarStore,
|
|
||||||
literal_type: Type,
|
|
||||||
reason: Reason,
|
|
||||||
expected: Expected<Type>,
|
|
||||||
region: Region,
|
|
||||||
) -> Constraint {
|
|
||||||
let num_var = var_store.fresh();
|
|
||||||
let num_type = Variable(num_var);
|
|
||||||
let expected_literal = ForReason(reason, literal_type, region);
|
|
||||||
|
|
||||||
And(vec![
|
|
||||||
Eq(num_type.clone(), expected_literal, region),
|
|
||||||
Eq(num_type, expected, region),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn number_literal_type(module_name: &str, type_name: &str) -> Type {
|
|
||||||
builtin_type(
|
|
||||||
types::MOD_NUM,
|
|
||||||
types::TYPE_NUM,
|
|
||||||
vec![builtin_type(module_name, type_name, Vec::new())],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn builtin_type(module_name: &str, type_name: &str, args: Vec<Type>) -> Type {
|
|
||||||
Type::Apply {
|
|
||||||
module_name: module_name.into(),
|
|
||||||
name: type_name.into(),
|
|
||||||
args,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn empty_list_type(var: Variable) -> Type {
|
|
||||||
list_type(Type::Variable(var))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn list_type(typ: Type) -> Type {
|
|
||||||
builtin_type("List", "List", vec![typ])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn str_type() -> Type {
|
|
||||||
builtin_type("Str", "Str", Vec::new())
|
|
||||||
}
|
|
||||||
|
|
31
src/constrain/module.rs
Normal file
31
src/constrain/module.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
use crate::can::def::Def;
|
||||||
|
use crate::can::symbol::Symbol;
|
||||||
|
use crate::collections::{ImMap, SendMap};
|
||||||
|
use crate::constrain::expr::{constrain_defs, Info};
|
||||||
|
use crate::region::Region;
|
||||||
|
use crate::subs::Variable;
|
||||||
|
use crate::types::Constraint::{self, *};
|
||||||
|
use crate::types::Expected::*;
|
||||||
|
use crate::types::Type;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn constrain_module(defs: &[Def], lookups: Vec<(Symbol, Variable, Region)>) -> Constraint {
|
||||||
|
let mut flex_info = Info::default();
|
||||||
|
|
||||||
|
for (symbol, expr_var, region) in lookups {
|
||||||
|
// 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, expected, region));
|
||||||
|
}
|
||||||
|
|
||||||
|
constrain_defs(
|
||||||
|
&ImMap::default(),
|
||||||
|
&mut SendMap::default(),
|
||||||
|
&defs,
|
||||||
|
&mut flex_info,
|
||||||
|
);
|
||||||
|
|
||||||
|
Constraint::And(flex_info.constraints)
|
||||||
|
}
|
106
src/constrain/pattern.rs
Normal file
106
src/constrain/pattern.rs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
use crate::can::ident::Lowercase;
|
||||||
|
use crate::can::pattern::Pattern::{self, *};
|
||||||
|
use crate::can::symbol::Symbol;
|
||||||
|
use crate::collections::SendMap;
|
||||||
|
use crate::region::{Located, Region};
|
||||||
|
use crate::subs::Variable;
|
||||||
|
use crate::types::{Constraint, PExpected, PatternCategory, Type};
|
||||||
|
|
||||||
|
pub struct PatternState {
|
||||||
|
pub headers: SendMap<Symbol, Located<Type>>,
|
||||||
|
pub vars: Vec<Variable>,
|
||||||
|
pub constraints: Vec<Constraint>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This accepts PatternState (rather than returning it) so that the caller can
|
||||||
|
/// intiialize the Vecs in PatternState using with_capacity
|
||||||
|
/// based on its knowledge of their lengths.
|
||||||
|
pub fn constrain_pattern(
|
||||||
|
pattern: &Pattern,
|
||||||
|
region: Region,
|
||||||
|
expected: PExpected<Type>,
|
||||||
|
state: &mut PatternState,
|
||||||
|
) {
|
||||||
|
match pattern {
|
||||||
|
Underscore | UnsupportedPattern(_) => {
|
||||||
|
// Neither the _ pattern nor erroneous ones add any constraints.
|
||||||
|
}
|
||||||
|
Identifier(symbol) => {
|
||||||
|
state.headers.insert(
|
||||||
|
symbol.clone(),
|
||||||
|
Located {
|
||||||
|
region,
|
||||||
|
value: expected.get_type(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
IntLiteral(_) => {
|
||||||
|
state.constraints.push(Constraint::Pattern(
|
||||||
|
region,
|
||||||
|
PatternCategory::Int,
|
||||||
|
Type::int(),
|
||||||
|
expected,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatLiteral(_) => {
|
||||||
|
state.constraints.push(Constraint::Pattern(
|
||||||
|
region,
|
||||||
|
PatternCategory::Float,
|
||||||
|
Type::float(),
|
||||||
|
expected,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
StrLiteral(_) => {
|
||||||
|
state.constraints.push(Constraint::Pattern(
|
||||||
|
region,
|
||||||
|
PatternCategory::Str,
|
||||||
|
Type::string(),
|
||||||
|
expected,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordDestructure(ext_var, patterns) => {
|
||||||
|
let ext_type = Type::Variable(*ext_var);
|
||||||
|
|
||||||
|
let mut field_types: SendMap<Lowercase, Type> = SendMap::default();
|
||||||
|
|
||||||
|
for (pat_var, label, symbol, opt_guard) in patterns {
|
||||||
|
let pat_type = Type::Variable(*pat_var);
|
||||||
|
let expected = PExpected::NoExpectation(pat_type.clone());
|
||||||
|
|
||||||
|
if !state.headers.contains_key(&symbol) {
|
||||||
|
state
|
||||||
|
.headers
|
||||||
|
.insert(symbol.clone(), Located::at(region, pat_type.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
field_types.insert(label.clone(), pat_type.clone());
|
||||||
|
|
||||||
|
// TODO investigate: shouldn't guard_var be constrained somewhere?
|
||||||
|
if let Some((_guard_var, loc_guard)) = opt_guard {
|
||||||
|
constrain_pattern(&loc_guard.value, loc_guard.region, expected, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.vars.push(*pat_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
let record_type = Type::Record(field_types, Box::new(ext_type));
|
||||||
|
let record_con =
|
||||||
|
Constraint::Pattern(region, PatternCategory::Record, record_type, expected);
|
||||||
|
|
||||||
|
state.constraints.push(record_con);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag(_) => {
|
||||||
|
panic!("TODO constrain Tag pattern");
|
||||||
|
}
|
||||||
|
AppliedTag(_, _) => {
|
||||||
|
panic!("TODO constrain AppliedTag pattern");
|
||||||
|
}
|
||||||
|
Shadowed(_) => {
|
||||||
|
panic!("TODO constrain Shadowed pattern");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -118,20 +118,24 @@ fn compile_expr<'ctx, 'env>(
|
||||||
match *expr {
|
match *expr {
|
||||||
Int(_, num) => IntConst(env.context.i64_type().const_int(num as u64, false)),
|
Int(_, num) => IntConst(env.context.i64_type().const_int(num as u64, false)),
|
||||||
Float(_, num) => FloatConst(env.context.f64_type().const_float(num)),
|
Float(_, num) => FloatConst(env.context.f64_type().const_float(num)),
|
||||||
When(_, ref loc_cond_expr, ref branches) => {
|
When {
|
||||||
|
ref loc_cond,
|
||||||
|
ref branches,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
if branches.len() < 2 {
|
if branches.len() < 2 {
|
||||||
panic!("TODO support when-expressions of fewer than 2 branches.");
|
panic!("TODO support when-expressions of fewer than 2 branches.");
|
||||||
}
|
}
|
||||||
if branches.len() == 2 {
|
if branches.len() == 2 {
|
||||||
let mut iter = branches.iter();
|
let mut iter = branches.iter();
|
||||||
|
|
||||||
let ((_pattern_var, pattern), (_expr_var, branch_expr)) = iter.next().unwrap();
|
let (pattern, branch_expr) = iter.next().unwrap();
|
||||||
let (_, (_, else_expr)) = iter.next().unwrap();
|
let (_, else_expr) = iter.next().unwrap();
|
||||||
|
|
||||||
compile_when_branch(
|
compile_when_branch(
|
||||||
env,
|
env,
|
||||||
parent,
|
parent,
|
||||||
&loc_cond_expr.value,
|
&loc_cond.value,
|
||||||
pattern.value.clone(),
|
pattern.value.clone(),
|
||||||
&branch_expr.value,
|
&branch_expr.value,
|
||||||
&else_expr.value,
|
&else_expr.value,
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::can::module::{canonicalize_module_defs, Module};
|
||||||
use crate::can::scope::Scope;
|
use crate::can::scope::Scope;
|
||||||
use crate::can::symbol::Symbol;
|
use crate::can::symbol::Symbol;
|
||||||
use crate::collections::{ImMap, SendMap, SendSet};
|
use crate::collections::{ImMap, SendMap, SendSet};
|
||||||
|
use crate::constrain::module::constrain_module;
|
||||||
use crate::ident::Ident;
|
use crate::ident::Ident;
|
||||||
use crate::module::ModuleName;
|
use crate::module::ModuleName;
|
||||||
use crate::parse::ast::{self, Attempting, ExposesEntry, ImportsEntry};
|
use crate::parse::ast::{self, Attempting, ExposesEntry, ImportsEntry};
|
||||||
|
@ -12,6 +13,7 @@ use crate::region::{Located, Region};
|
||||||
use crate::solve;
|
use crate::solve;
|
||||||
use crate::subs::VarStore;
|
use crate::subs::VarStore;
|
||||||
use crate::subs::{Subs, Variable};
|
use crate::subs::{Subs, Variable};
|
||||||
|
use crate::types::Constraint;
|
||||||
use crate::unify::Problems;
|
use crate::unify::Problems;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
|
@ -217,8 +219,7 @@ fn load_filename(
|
||||||
|
|
||||||
let mut scope =
|
let mut scope =
|
||||||
Scope::new(format!("{}.", declared_name).into(), scope_from_imports);
|
Scope::new(format!("{}.", declared_name).into(), scope_from_imports);
|
||||||
|
let (defs, exposed_imports, constraint) = process_defs(
|
||||||
let (defs, exposed_imports) = parse_and_canonicalize_defs(
|
|
||||||
&arena,
|
&arena,
|
||||||
state,
|
state,
|
||||||
declared_name.clone(),
|
declared_name.clone(),
|
||||||
|
@ -230,6 +231,7 @@ fn load_filename(
|
||||||
name: Some(declared_name),
|
name: Some(declared_name),
|
||||||
defs,
|
defs,
|
||||||
exposed_imports,
|
exposed_imports,
|
||||||
|
constraint,
|
||||||
};
|
};
|
||||||
|
|
||||||
LoadedModule::Valid(module)
|
LoadedModule::Valid(module)
|
||||||
|
@ -258,7 +260,7 @@ fn load_filename(
|
||||||
let mut scope = Scope::new(".".into(), scope_from_imports);
|
let mut scope = Scope::new(".".into(), scope_from_imports);
|
||||||
|
|
||||||
// The app module has no declared name. Pass it as "".
|
// The app module has no declared name. Pass it as "".
|
||||||
let (defs, exposed_imports) = parse_and_canonicalize_defs(
|
let (defs, exposed_imports, constraint) = process_defs(
|
||||||
&arena,
|
&arena,
|
||||||
state,
|
state,
|
||||||
"".into(),
|
"".into(),
|
||||||
|
@ -270,6 +272,7 @@ fn load_filename(
|
||||||
name: None,
|
name: None,
|
||||||
defs,
|
defs,
|
||||||
exposed_imports,
|
exposed_imports,
|
||||||
|
constraint,
|
||||||
};
|
};
|
||||||
|
|
||||||
LoadedModule::Valid(module)
|
LoadedModule::Valid(module)
|
||||||
|
@ -286,14 +289,14 @@ fn load_filename(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_and_canonicalize_defs<'a, I>(
|
fn process_defs<'a, I>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
home: Box<str>,
|
home: Box<str>,
|
||||||
exposes: I,
|
exposes: I,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
var_store: &VarStore,
|
var_store: &VarStore,
|
||||||
) -> (Vec<Def>, SendMap<Symbol, Variable>)
|
) -> (Vec<Def>, SendMap<Symbol, Variable>, Constraint)
|
||||||
where
|
where
|
||||||
I: Iterator<Item = Located<ExposesEntry<'a>>>,
|
I: Iterator<Item = Located<ExposesEntry<'a>>>,
|
||||||
{
|
{
|
||||||
|
@ -301,7 +304,12 @@ where
|
||||||
.parse(arena, state)
|
.parse(arena, state)
|
||||||
.expect("TODO gracefully handle parse error on module defs");
|
.expect("TODO gracefully handle parse error on module defs");
|
||||||
|
|
||||||
canonicalize_module_defs(arena, parsed_defs, home, exposes, scope, var_store)
|
let (defs, exposed_imports, lookups) =
|
||||||
|
canonicalize_module_defs(arena, parsed_defs, home, exposes, scope, var_store);
|
||||||
|
|
||||||
|
let constraint = constrain_module(&defs, lookups);
|
||||||
|
|
||||||
|
(defs, exposed_imports, constraint)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_import(
|
fn load_import(
|
||||||
|
@ -360,21 +368,16 @@ pub fn solve_loaded(
|
||||||
use LoadedModule::*;
|
use LoadedModule::*;
|
||||||
|
|
||||||
let mut vars_by_symbol: ImMap<Symbol, Variable> = ImMap::default();
|
let mut vars_by_symbol: ImMap<Symbol, Variable> = ImMap::default();
|
||||||
let mut constraints = Vec::with_capacity(loaded_deps.len() + 1);
|
let mut dep_constraints = Vec::with_capacity(loaded_deps.len());
|
||||||
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
|
// All the exposed imports should be available in the solver's vars_by_symbol
|
||||||
for (symbol, var) in module.exposed_imports.iter() {
|
for (symbol, expr_var) in module.exposed_imports.iter() {
|
||||||
vars_by_symbol.insert(symbol.clone(), var.clone());
|
vars_by_symbol.insert(symbol.clone(), expr_var.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// All the top-level defs should also be available in vars_by_symbol
|
// All the top-level defs should also be available in vars_by_symbol
|
||||||
for def in module.defs.iter() {
|
for def in module.defs.iter() {
|
||||||
for (symbol, var) in def.vars_by_symbol.iter() {
|
for (symbol, var) in def.pattern_vars.iter() {
|
||||||
vars_by_symbol.insert(symbol.clone(), var.clone());
|
vars_by_symbol.insert(symbol.clone(), var.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -391,16 +394,18 @@ pub fn solve_loaded(
|
||||||
// in the solver's vars_by_symbol. (The map's keys are
|
// in the solver's vars_by_symbol. (The map's keys are
|
||||||
// fully qualified, so there won't be any collisions
|
// fully qualified, so there won't be any collisions
|
||||||
// with the primary module's exposed imports!)
|
// with the primary module's exposed imports!)
|
||||||
for (symbol, var) in valid_dep.exposed_imports {
|
for (symbol, expr_var) in valid_dep.exposed_imports {
|
||||||
vars_by_symbol.insert(symbol, var);
|
vars_by_symbol.insert(symbol, expr_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
// All its top-level defs should also be available in vars_by_symbol
|
// All its top-level defs should also be available in vars_by_symbol
|
||||||
for def in valid_dep.defs {
|
for def in valid_dep.defs {
|
||||||
for (symbol, var) in def.vars_by_symbol {
|
for (symbol, var) in def.pattern_vars {
|
||||||
vars_by_symbol.insert(symbol, var);
|
vars_by_symbol.insert(symbol, var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dep_constraints.push(valid_dep.constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
broken @ FileProblem { .. } => {
|
broken @ FileProblem { .. } => {
|
||||||
|
@ -413,9 +418,9 @@ pub fn solve_loaded(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for constraint in constraints {
|
for dep_constraint in dep_constraints {
|
||||||
solve::run(&vars_by_symbol, problems, subs, &constraint);
|
solve::run(&vars_by_symbol, problems, subs, &dep_constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
solve::run(&vars_by_symbol, problems, subs, &module_constraint);
|
solve::run(&vars_by_symbol, problems, subs, &module.constraint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ pub struct Env {
|
||||||
pub procedures: ImMap<Symbol, Procedure>,
|
pub procedures: ImMap<Symbol, Procedure>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn canonicalize_declaration(
|
pub fn canonicalize_declaration(
|
||||||
var_store: &VarStore,
|
var_store: &VarStore,
|
||||||
region: Region,
|
region: Region,
|
||||||
|
@ -97,7 +96,7 @@ fn canonicalize_pattern(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
ExactString(_) => {
|
StrLiteral(_) => {
|
||||||
state.constraints.push(Constraint::Pattern(
|
state.constraints.push(Constraint::Pattern(
|
||||||
pattern.region,
|
pattern.region,
|
||||||
PatternCategory::Str,
|
PatternCategory::Str,
|
||||||
|
@ -106,33 +105,33 @@ fn canonicalize_pattern(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordDestructure(patterns) => {
|
RecordDestructure(ext_var, patterns) => {
|
||||||
let ext_var = var_store.fresh();
|
let ext_type = Type::Variable(*ext_var);
|
||||||
let ext_type = Type::Variable(ext_var);
|
|
||||||
|
|
||||||
let mut field_types: SendMap<Lowercase, Type> = SendMap::default();
|
let mut field_types: SendMap<Lowercase, Type> = SendMap::default();
|
||||||
for (pattern, maybe_guard) in patterns {
|
for (pat_var, label, symbol, maybe_guard) in patterns {
|
||||||
let pat_var = var_store.fresh();
|
let pat_type = Type::Variable(*pat_var);
|
||||||
let pat_type = Type::Variable(pat_var);
|
|
||||||
let pattern_expected = PExpected::NoExpectation(pat_type.clone());
|
let pattern_expected = PExpected::NoExpectation(pat_type.clone());
|
||||||
|
|
||||||
if let Some(loc_guard) = maybe_guard {
|
match maybe_guard {
|
||||||
canonicalize_pattern(var_store, state, pattern, pattern_expected.clone());
|
Some((_guard_var, loc_guard)) => {
|
||||||
canonicalize_pattern(var_store, state, loc_guard, pattern_expected);
|
state.headers.insert(
|
||||||
} else {
|
symbol.clone(),
|
||||||
canonicalize_pattern(var_store, state, pattern, pattern_expected);
|
Located {
|
||||||
|
region: pattern.region,
|
||||||
|
value: pat_type.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
canonicalize_pattern(var_store, state, loc_guard, pattern_expected);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
canonicalize_pattern(var_store, state, pattern, pattern_expected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = if let Identifier(n) = &pattern.value {
|
state.vars.push(*pat_var);
|
||||||
let a: Box<str> = n.clone().into();
|
field_types.insert(label.clone(), pat_type);
|
||||||
let b: Lowercase = a.into();
|
|
||||||
b
|
|
||||||
} else {
|
|
||||||
unreachable!("the lhs must be an identifier at this point");
|
|
||||||
};
|
|
||||||
|
|
||||||
state.vars.push(pat_var);
|
|
||||||
field_types.insert(name, pat_type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let record_type =
|
let record_type =
|
||||||
|
@ -292,22 +291,25 @@ pub fn canonicalize_expr(
|
||||||
(output, And(constraints))
|
(output, And(constraints))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Var(variable, symbol) => {
|
Var {
|
||||||
var_usage.register(symbol);
|
symbol_for_lookup, ..
|
||||||
match var_usage.get_usage(symbol) {
|
} => {
|
||||||
|
var_usage.register(symbol_for_lookup);
|
||||||
|
match var_usage.get_usage(symbol_for_lookup) {
|
||||||
Some(sharing::ReferenceCount::Shared) => {
|
Some(sharing::ReferenceCount::Shared) => {
|
||||||
// the variable is used/consumed more than once, so it must be Shared
|
// the variable is used/consumed more than once, so it must be Shared
|
||||||
let val_var = *variable;
|
let val_var = var_store.fresh();
|
||||||
let uniq_var = var_store.fresh();
|
let uniq_var = var_store.fresh();
|
||||||
|
|
||||||
let val_type = Variable(val_var);
|
let val_type = Variable(val_var);
|
||||||
let uniq_type = Variable(uniq_var);
|
let uniq_type = Variable(uniq_var);
|
||||||
|
|
||||||
let attr_type = constrain::attr_type(uniq_type.clone(), val_type);
|
let attr_type = constrain::attr_type(uniq_type.clone(), val_type);
|
||||||
|
|
||||||
(
|
(
|
||||||
Output::default(),
|
Output::default(),
|
||||||
And(vec![
|
And(vec![
|
||||||
Lookup(symbol.clone(), expected.clone(), region),
|
Lookup(symbol_for_lookup.clone(), expected.clone(), region),
|
||||||
Eq(attr_type, expected, region),
|
Eq(attr_type, expected, region),
|
||||||
Eq(
|
Eq(
|
||||||
uniq_type,
|
uniq_type,
|
||||||
|
@ -321,24 +323,14 @@ pub fn canonicalize_expr(
|
||||||
// no additional constraints, keep uniqueness unbound
|
// no additional constraints, keep uniqueness unbound
|
||||||
(
|
(
|
||||||
Output::default(),
|
Output::default(),
|
||||||
Lookup(symbol.clone(), expected.clone(), region),
|
Lookup(symbol_for_lookup.clone(), expected.clone(), region),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
None => panic!("symbol not analyzed"),
|
None => panic!("symbol not analyzed"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
FunctionPointer(_variable, symbol) => match env.bound_names.get(symbol) {
|
|
||||||
// constraint expected ~ the type of this symbol in the environment
|
|
||||||
None => panic!("FunctionPointer: no variable for {:?}", symbol),
|
|
||||||
Some(var) => Output::new(Eq(Variable(*var), expected, Region::zero())),
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
FunctionPointer(_, _) => {
|
|
||||||
panic!("TODO implement function pointer?");
|
|
||||||
}
|
|
||||||
Closure(_symbol, _recursion, args, boxed_body) => {
|
Closure(_symbol, _recursion, args, boxed_body) => {
|
||||||
let (ret_var, body) = &**boxed_body;
|
let (body, ret_var) = &**boxed_body;
|
||||||
|
|
||||||
// first, generate constraints for the arguments
|
// first, generate constraints for the arguments
|
||||||
let mut arg_types = Vec::new();
|
let mut arg_types = Vec::new();
|
||||||
|
@ -409,13 +401,12 @@ pub fn canonicalize_expr(
|
||||||
(output, constraint)
|
(output, constraint)
|
||||||
}
|
}
|
||||||
|
|
||||||
Call(fn_expr, loc_args, _) => {
|
Call(boxed, loc_args, _) => {
|
||||||
let fn_var = var_store.fresh();
|
let (fn_var, fn_expr, ret_var) = &**boxed;
|
||||||
let fn_type = Variable(fn_var);
|
let fn_type = Variable(*fn_var);
|
||||||
let ret_var = var_store.fresh();
|
let ret_type = Variable(*ret_var);
|
||||||
let ret_type = Variable(ret_var);
|
|
||||||
let fn_expected = Expected::NoExpectation(fn_type.clone());
|
let fn_expected = Expected::NoExpectation(fn_type.clone());
|
||||||
let fn_region = Region::zero();
|
let fn_region = fn_expr.region;
|
||||||
|
|
||||||
let mut vars = Vec::with_capacity(2 + loc_args.len());
|
let mut vars = Vec::with_capacity(2 + loc_args.len());
|
||||||
|
|
||||||
|
@ -425,7 +416,7 @@ pub fn canonicalize_expr(
|
||||||
var_store,
|
var_store,
|
||||||
var_usage,
|
var_usage,
|
||||||
fn_region,
|
fn_region,
|
||||||
&fn_expr,
|
&fn_expr.value,
|
||||||
fn_expected,
|
fn_expected,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -484,8 +475,13 @@ pub fn canonicalize_expr(
|
||||||
Output::default(),
|
Output::default(),
|
||||||
can_defs(rigids, var_store, var_usage, defs, expected, loc_ret),
|
can_defs(rigids, var_store, var_usage, defs, expected, loc_ret),
|
||||||
),
|
),
|
||||||
When(variable, loc_cond, branches) => {
|
When {
|
||||||
let cond_var = *variable;
|
cond_var,
|
||||||
|
loc_cond,
|
||||||
|
branches,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let cond_var = *cond_var;
|
||||||
let cond_type = Variable(cond_var);
|
let cond_type = Variable(cond_var);
|
||||||
let (mut output, expr_con) = canonicalize_expr(
|
let (mut output, expr_con) = canonicalize_expr(
|
||||||
rigids,
|
rigids,
|
||||||
|
@ -502,9 +498,7 @@ pub fn canonicalize_expr(
|
||||||
|
|
||||||
match expected {
|
match expected {
|
||||||
Expected::FromAnnotation(name, arity, _, typ) => {
|
Expected::FromAnnotation(name, arity, _, typ) => {
|
||||||
for (index, ((_patter_var, loc_pattern), (_expr_var, loc_expr))) in
|
for (index, (loc_pattern, loc_expr)) in branches.iter().enumerate() {
|
||||||
branches.iter().enumerate()
|
|
||||||
{
|
|
||||||
let mut branch_var_usage = old_var_usage.clone();
|
let mut branch_var_usage = old_var_usage.clone();
|
||||||
let branch_con = canonicalize_when_branch(
|
let branch_con = canonicalize_when_branch(
|
||||||
var_store,
|
var_store,
|
||||||
|
@ -554,9 +548,7 @@ pub fn canonicalize_expr(
|
||||||
let branch_type = Variable(branch_var);
|
let branch_type = Variable(branch_var);
|
||||||
let mut branch_cons = Vec::with_capacity(branches.len());
|
let mut branch_cons = Vec::with_capacity(branches.len());
|
||||||
|
|
||||||
for (index, ((_pattern_var, loc_pattern), (_expr_var, loc_expr))) in
|
for (index, (loc_pattern, loc_expr)) in branches.iter().enumerate() {
|
||||||
branches.iter().enumerate()
|
|
||||||
{
|
|
||||||
let mut branch_var_usage = old_var_usage.clone();
|
let mut branch_var_usage = old_var_usage.clone();
|
||||||
let branch_con = canonicalize_when_branch(
|
let branch_con = canonicalize_when_branch(
|
||||||
var_store,
|
var_store,
|
||||||
|
@ -778,7 +770,7 @@ fn can_defs(
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(1),
|
||||||
};
|
};
|
||||||
|
|
||||||
canonicalize_pattern(var_store, &mut state, &def.pattern, pattern_expected);
|
canonicalize_pattern(var_store, &mut state, &def.loc_pattern, pattern_expected);
|
||||||
|
|
||||||
flex_info.vars.push(pattern_var);
|
flex_info.vars.push(pattern_var);
|
||||||
|
|
||||||
|
@ -788,19 +780,19 @@ fn can_defs(
|
||||||
rigids,
|
rigids,
|
||||||
var_store,
|
var_store,
|
||||||
var_usage,
|
var_usage,
|
||||||
def.expr.region,
|
def.loc_expr.region,
|
||||||
&def.expr.value,
|
&def.loc_expr.value,
|
||||||
Expected::NoExpectation(expr_type.clone()),
|
Expected::NoExpectation(expr_type.clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
add_pattern_to_lookup_types(
|
add_pattern_to_lookup_types(
|
||||||
// TODO can we we avoid this clone?
|
// TODO can we we avoid this clone?
|
||||||
def.pattern.clone(),
|
def.loc_pattern.clone(),
|
||||||
&mut flex_info.def_types,
|
&mut flex_info.def_types,
|
||||||
expr_type.clone(),
|
expr_type.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
bound_symbols.extend(pattern::symbols_from_pattern(&def.pattern.value));
|
bound_symbols.extend(pattern::symbols_from_pattern(&def.loc_pattern.value));
|
||||||
|
|
||||||
flex_info.constraints.push(Let(Box::new(LetConstraint {
|
flex_info.constraints.push(Let(Box::new(LetConstraint {
|
||||||
rigid_vars: Vec::new(),
|
rigid_vars: Vec::new(),
|
||||||
|
|
|
@ -9,6 +9,7 @@ use roc::can::problem::Problem;
|
||||||
use roc::can::scope::Scope;
|
use roc::can::scope::Scope;
|
||||||
use roc::can::symbol::Symbol;
|
use roc::can::symbol::Symbol;
|
||||||
use roc::collections::{ImMap, MutMap, SendSet};
|
use roc::collections::{ImMap, MutMap, SendSet};
|
||||||
|
use roc::constrain::expr::constrain_expr;
|
||||||
use roc::ident::Ident;
|
use roc::ident::Ident;
|
||||||
use roc::parse;
|
use roc::parse;
|
||||||
use roc::parse::ast::{self, Attempting};
|
use roc::parse::ast::{self, Attempting};
|
||||||
|
@ -191,13 +192,18 @@ pub fn can_expr_with(
|
||||||
let scope_prefix = format!("{}.{}$", home, name).into();
|
let scope_prefix = format!("{}.{}$", home, name).into();
|
||||||
let mut scope = Scope::new(scope_prefix, declared_idents.clone());
|
let mut scope = Scope::new(scope_prefix, declared_idents.clone());
|
||||||
let mut env = Env::new(home.into());
|
let mut env = Env::new(home.into());
|
||||||
let (loc_expr, output, constraint) = canonicalize_expr(
|
let (loc_expr, output) = canonicalize_expr(
|
||||||
&ImMap::default(),
|
|
||||||
&mut env,
|
&mut env,
|
||||||
&var_store,
|
&var_store,
|
||||||
&mut scope,
|
&mut scope,
|
||||||
Region::zero(),
|
Region::zero(),
|
||||||
&loc_expr.value,
|
&loc_expr.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
let constraint = constrain_expr(
|
||||||
|
&ImMap::default(),
|
||||||
|
loc_expr.region,
|
||||||
|
&loc_expr.value,
|
||||||
expected,
|
expected,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,33 @@ mod test_canonicalize {
|
||||||
assert_eq!(actual.value, expected);
|
assert_eq!(actual.value, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assert_can_float(input: &str, expected: f64) {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let (loc_actual, _, _, _, _, _) = can_expr_with(&arena, "Blah", input, &ImMap::default());
|
||||||
|
|
||||||
|
match loc_actual.value {
|
||||||
|
Expr::Float(_, actual) => {
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
|
actual => {
|
||||||
|
panic!("Expected a Float, but got: {:?}", actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn assert_can_int(input: &str, expected: i64) {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let (loc_actual, _, _, _, _, _) = can_expr_with(&arena, "Blah", input, &ImMap::default());
|
||||||
|
|
||||||
|
match loc_actual.value {
|
||||||
|
Expr::Int(_, actual) => {
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
|
actual => {
|
||||||
|
panic!("Expected an Int, but got: {:?}", actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NUMBER LITERALS
|
// NUMBER LITERALS
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -106,67 +133,67 @@ mod test_canonicalize {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn zero() {
|
fn zero() {
|
||||||
assert_can("0", Int(0));
|
assert_can_int("0", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn minus_zero() {
|
fn minus_zero() {
|
||||||
assert_can("-0", Int(0));
|
assert_can_int("-0", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn zero_point_zero() {
|
fn zero_point_zero() {
|
||||||
assert_can("0.0", Float(0.0));
|
assert_can_float("0.0", 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn minus_zero_point_zero() {
|
fn minus_zero_point_zero() {
|
||||||
assert_can("-0.0", Float(-0.0));
|
assert_can_float("-0.0", -0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hex_zero() {
|
fn hex_zero() {
|
||||||
assert_can("0x0", Int(0x0));
|
assert_can_int("0x0", 0x0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hex_one_b() {
|
fn hex_one_b() {
|
||||||
assert_can("0x1b", Int(0x1b));
|
assert_can_int("0x1b", 0x1b);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn minus_hex_one_b() {
|
fn minus_hex_one_b() {
|
||||||
assert_can("-0x1b", Int(-0x1b));
|
assert_can_int("-0x1b", -0x1b);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn octal_zero() {
|
fn octal_zero() {
|
||||||
assert_can("0o0", Int(0o0));
|
assert_can_int("0o0", 0o0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn octal_one_two() {
|
fn octal_one_two() {
|
||||||
assert_can("0o12", Int(0o12));
|
assert_can_int("0o12", 0o12);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn minus_octal_one_two() {
|
fn minus_octal_one_two() {
|
||||||
assert_can("-0o12", Int(-0o12));
|
assert_can_int("-0o12", -0o12);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn binary_zero() {
|
fn binary_zero() {
|
||||||
assert_can("0b0", Int(0b0));
|
assert_can_int("0b0", 0b0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn binary_one_one() {
|
fn binary_one_one() {
|
||||||
assert_can("0b11", Int(0b11));
|
assert_can_int("0b11", 0b11);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn minus_binary_one_one() {
|
fn minus_binary_one_one() {
|
||||||
assert_can("-0b11", Int(-0b11));
|
assert_can_int("-0b11", -0b11);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LOCALS
|
// LOCALS
|
||||||
|
@ -232,7 +259,7 @@ mod test_canonicalize {
|
||||||
|
|
||||||
fn get_closure(expr: &Expr, i: usize) -> roc::can::expr::Recursive {
|
fn get_closure(expr: &Expr, i: usize) -> roc::can::expr::Recursive {
|
||||||
match expr {
|
match expr {
|
||||||
Defs(assignments, _) => match &assignments.get(i).map(|def| &def.expr.value) {
|
Defs(assignments, _) => match &assignments.get(i).map(|def| &def.loc_expr.value) {
|
||||||
Some(Closure(_, recursion, _, _)) => recursion.clone(),
|
Some(Closure(_, recursion, _, _)) => recursion.clone(),
|
||||||
Some(other @ _) => {
|
Some(other @ _) => {
|
||||||
panic!("assignment at {} is not a closure, but a {:?}", i, other)
|
panic!("assignment at {} is not a closure, but a {:?}", i, other)
|
||||||
|
|
|
@ -908,7 +908,7 @@ mod test_infer {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
foo : Int -> Bool
|
foo: Int -> Bool
|
||||||
|
|
||||||
foo 2
|
foo 2
|
||||||
"#
|
"#
|
||||||
|
|
|
@ -171,7 +171,7 @@ mod test_load {
|
||||||
assert_eq!(expected_types.len(), module.defs.len());
|
assert_eq!(expected_types.len(), module.defs.len());
|
||||||
|
|
||||||
for def in module.defs {
|
for def in module.defs {
|
||||||
for (symbol, expr_var) in def.vars_by_symbol {
|
for (symbol, expr_var) in def.pattern_vars {
|
||||||
let content = subs.get(expr_var).content;
|
let content = subs.get(expr_var).content;
|
||||||
|
|
||||||
name_all_type_vars(expr_var, &mut subs);
|
name_all_type_vars(expr_var, &mut subs);
|
||||||
|
|
|
@ -888,9 +888,9 @@ mod test_infer_uniq {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when foo is
|
when foo is
|
||||||
{ x: 4 }-> x
|
{ x: 4 }-> x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Int",
|
"Int",
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue