Merge remote-tracking branch 'origin/various-bugfixes' into trunk

This commit is contained in:
Richard Feldman 2020-03-06 18:23:40 -05:00
commit d8cf402528
14 changed files with 784 additions and 115 deletions

View file

@ -6,7 +6,7 @@ use roc_types::solved_types::{BuiltinAlias, SolvedType};
use roc_types::subs::VarId;
use std::collections::HashMap;
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
pub enum Mode {
Standard,
Uniqueness,

View file

@ -25,6 +25,22 @@ const UVAR4: VarId = VarId::from_u32(1004);
const UVAR5: VarId = VarId::from_u32(1005);
const UVAR6: VarId = VarId::from_u32(1006);
pub struct IDStore(u32);
impl IDStore {
fn new() -> Self {
IDStore(2000)
}
fn fresh(&mut self) -> VarId {
let result = VarId::from_u32(self.0);
self.0 += 1;
result
}
}
fn shared() -> SolvedType {
SolvedType::Boolean(SolvedAtom::Zero, vec![])
}
@ -207,16 +223,6 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
},
);
// List a : [ @List a ]
add_alias(
Symbol::LIST_LIST,
BuiltinAlias {
region: Region::zero(),
vars: vec![Located::at(Region::zero(), "elem".into())],
typ: single_private_tag(Symbol::LIST_AT_LIST, vec![flex(TVAR1)]),
},
);
// Result a e : [ Ok a, Err e ]
add_alias(
Symbol::RESULT_RESULT,
@ -236,6 +242,39 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
},
);
// List a : [ @List a ]
add_alias(
Symbol::LIST_LIST,
BuiltinAlias {
region: Region::zero(),
vars: vec![Located::at(Region::zero(), "elem".into())],
typ: single_private_tag(Symbol::LIST_AT_LIST, vec![flex(TVAR1)]),
},
);
// Map key value : [ @Map key value ]
add_alias(
Symbol::MAP_MAP,
BuiltinAlias {
region: Region::zero(),
vars: vec![
Located::at(Region::zero(), "key".into()),
Located::at(Region::zero(), "value".into()),
],
typ: single_private_tag(Symbol::MAP_AT_MAP, vec![flex(TVAR1), flex(TVAR2)]),
},
);
// Set key : [ @Set key ]
add_alias(
Symbol::SET_SET,
BuiltinAlias {
region: Region::zero(),
vars: vec![Located::at(Region::zero(), "key".into())],
typ: single_private_tag(Symbol::SET_AT_SET, vec![flex(TVAR1)]),
},
);
// Str : [ @Str ]
add_alias(
Symbol::STR_STR,
@ -342,6 +381,12 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
),
);
// toFloat : Num a -> Float
add_type(
Symbol::NUM_TO_FLOAT,
unique_function(vec![num_type(UVAR1, TVAR1)], float_type(UVAR2)),
);
// Int module
// highest : Int
@ -396,6 +441,18 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
// Bool module
// isEq or (==) : Attr u1 Bool, Attr u2 Bool -> Attr u3 Bool
add_type(
Symbol::BOOL_EQ,
unique_function(vec![bool_type(UVAR1), bool_type(UVAR2)], bool_type(UVAR3)),
);
// isNeq or (!=) : Attr u1 Bool, Attr u2 Bool -> Attr u3 Bool
add_type(
Symbol::BOOL_NEQ,
unique_function(vec![bool_type(UVAR1), bool_type(UVAR2)], bool_type(UVAR3)),
);
// and or (&&) : Attr u1 Bool, Attr u2 Bool -> Attr u3 Bool
add_type(
Symbol::BOOL_AND,
@ -557,6 +614,267 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
),
);
// Map module
// empty : Map k v
add_type(Symbol::MAP_EMPTY, map_type(UVAR1, TVAR1, TVAR2));
// singleton : k, v -> Map k v
add_type(
Symbol::MAP_SINGLETON,
unique_function(
vec![flex(TVAR1), flex(TVAR2)],
map_type(UVAR1, TVAR1, TVAR2),
),
);
// get : Attr (u | v | *) (Map (Attr u key) (Attr v val), (Attr * key) -> Attr * (Result (Attr v val) [ KeyNotFound ]*)
let key_not_found = SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
SolvedType::Wildcard,
SolvedType::TagUnion(
vec![(TagName::Global("KeyNotFound".into()), vec![])],
Box::new(SolvedType::Wildcard),
),
],
);
add_type(Symbol::MAP_GET, {
let mut store = IDStore::new();
let u = store.fresh();
let v = store.fresh();
let key = store.fresh();
let val = store.fresh();
let star1 = store.fresh();
let star2 = store.fresh();
let star3 = store.fresh();
unique_function(
vec![
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
disjunction(star1, vec![u, v]),
SolvedType::Apply(
Symbol::MAP_MAP,
vec![attr_type(u, key), attr_type(v, val)],
),
],
),
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![disjunction(star2, vec![u]), flex(key)],
),
],
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
flex(star3),
SolvedType::Apply(
Symbol::RESULT_RESULT,
vec![attr_type(v, val), key_not_found],
),
],
),
)
});
// insert : Attr (u | v | *) (Map (Attr u key) (Attr v val)), Attr (u | *) key, Attr (v | *) val -> Attr * (Map (Attr u key) (Attr v val))
add_type(Symbol::MAP_INSERT, {
let mut store = IDStore::new();
let u = store.fresh();
let v = store.fresh();
let key = store.fresh();
let val = store.fresh();
let star1 = store.fresh();
let star2 = store.fresh();
let star3 = store.fresh();
unique_function(
vec![
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
disjunction(star1, vec![u, v]),
SolvedType::Apply(
Symbol::MAP_MAP,
vec![attr_type(u, key), attr_type(v, val)],
),
],
),
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![disjunction(star2, vec![u]), flex(key)],
),
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![disjunction(star2, vec![v]), flex(val)],
),
],
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
flex(star3),
SolvedType::Apply(Symbol::MAP_MAP, vec![attr_type(u, key), attr_type(v, val)]),
],
),
)
});
// Set module
// empty : Set a
add_type(Symbol::SET_EMPTY, set_type(UVAR1, TVAR1));
// singleton : a -> Set a
add_type(
Symbol::SET_SINGLETON,
unique_function(vec![flex(TVAR1)], set_type(UVAR1, TVAR1)),
);
// op : Attr (u | *) (Set (Attr u a)), Attr (u | *) (Set (Attr u a)) -> Attr * Set (Attr u a)
let set_combine = {
let mut store = IDStore::new();
let u = store.fresh();
let a = store.fresh();
let star1 = store.fresh();
let star2 = store.fresh();
let star3 = store.fresh();
unique_function(
vec![
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
disjunction(star1, vec![u]),
SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]),
],
),
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
disjunction(star2, vec![u]),
SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]),
],
),
],
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
flex(star3),
SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]),
],
),
)
};
// union : Set a, Set a -> Set a
add_type(Symbol::SET_UNION, set_combine.clone());
// diff : Set a, Set a -> Set a
add_type(Symbol::SET_DIFF, set_combine);
// foldl : Attr (u | *) (Set (Attr u a)), Attr Shared (Attr u a -> b -> b), b -> b
add_type(Symbol::SET_FOLDL, {
let mut store = IDStore::new();
let u = store.fresh();
let a = store.fresh();
let b = store.fresh();
let star1 = store.fresh();
unique_function(
vec![
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
disjunction(star1, vec![u]),
SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]),
],
),
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
shared(),
SolvedType::Func(vec![attr_type(u, a), flex(b)], Box::new(flex(b))),
],
),
flex(b),
],
flex(b),
)
});
// insert : Attr (u | *) (Set (Attr u a)), Attr (u | *) a -> Attr * (Set (Attr u a))
add_type(Symbol::SET_INSERT, {
let mut store = IDStore::new();
let u = store.fresh();
let a = store.fresh();
let star1 = store.fresh();
let star2 = store.fresh();
let star3 = store.fresh();
unique_function(
vec![
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
disjunction(star1, vec![u]),
SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]),
],
),
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![disjunction(star2, vec![u]), flex(a)],
),
],
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
flex(star3),
SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]),
],
),
)
});
// we can remove a key that is shared from a set of unique keys
// remove : Attr (u | *) (Set (Attr u a)), Attr * a -> Attr * (Set (Attr u a))
add_type(Symbol::SET_REMOVE, {
let mut store = IDStore::new();
let u = store.fresh();
let a = store.fresh();
let star1 = store.fresh();
let star2 = store.fresh();
let star3 = store.fresh();
unique_function(
vec![
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
disjunction(star1, vec![u]),
SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]),
],
),
SolvedType::Apply(Symbol::ATTR_ATTR, vec![flex(star2), flex(a)]),
],
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
flex(star3),
SolvedType::Apply(Symbol::SET_SET, vec![attr_type(u, a)]),
],
),
)
});
// Str module
// isEmpty : Attr u Str -> Attr v Bool
@ -688,3 +1006,22 @@ fn list_type(u: VarId, a: VarId) -> SolvedType {
vec![flex(u), SolvedType::Apply(Symbol::LIST_LIST, vec![flex(a)])],
)
}
#[inline(always)]
fn set_type(u: VarId, a: VarId) -> SolvedType {
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![flex(u), SolvedType::Apply(Symbol::SET_SET, vec![flex(a)])],
)
}
#[inline(always)]
fn map_type(u: VarId, key: VarId, value: VarId) -> SolvedType {
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
flex(u),
SolvedType::Apply(Symbol::MAP_MAP, vec![flex(key), flex(value)]),
],
)
}

View file

@ -1,6 +1,6 @@
use crate::env::Env;
use crate::scope::Scope;
use roc_collections::all::{ImMap, MutMap, MutSet, SendMap};
use roc_collections::all::{MutSet, SendMap};
use roc_module::ident::Ident;
use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::Symbol;
@ -13,19 +13,13 @@ use roc_types::types::{Alias, Problem, Type};
#[derive(Clone, Debug, PartialEq)]
pub struct Annotation {
pub typ: Type,
pub ftv: MutMap<Variable, Lowercase>,
pub rigids: ImMap<Lowercase, Variable>,
pub introduced_variables: IntroducedVariables,
pub references: MutSet<Symbol>,
pub aliases: SendMap<Symbol, Alias>,
}
pub fn canonicalize_annotation(
env: &mut Env,
scope: &mut Scope,
annotation: &roc_parse::ast::TypeAnnotation,
region: Region,
var_store: &VarStore,
) -> Annotation {
#[derive(Clone, Debug, PartialEq, Default)]
pub struct IntroducedVariables {
// NOTE on rigids
//
// Rigids must be unique within a type annoation.
@ -36,7 +30,44 @@ pub fn canonicalize_annotation(
// But then between annotations, the same name can occur multiple times,
// but a variable can only have one name. Therefore
// `ftv : SendMap<Variable, Lowercase>`.
let mut rigids = ImMap::default();
pub wildcards: Vec<Variable>,
pub var_by_name: SendMap<Lowercase, Variable>,
pub name_by_var: SendMap<Variable, Lowercase>,
}
impl IntroducedVariables {
pub fn insert_named(&mut self, name: Lowercase, var: Variable) {
self.var_by_name.insert(name.clone(), var);
self.name_by_var.insert(var, name);
}
pub fn insert_wildcard(&mut self, var: Variable) {
self.wildcards.push(var);
}
pub fn union(&mut self, other: &Self) {
self.wildcards.extend(other.wildcards.iter().cloned());
self.var_by_name.extend(other.var_by_name.clone());
self.name_by_var.extend(other.name_by_var.clone());
}
pub fn var_by_name(&self, name: &Lowercase) -> Option<&Variable> {
self.var_by_name.get(name)
}
pub fn name_by_var(&self, var: Variable) -> Option<&Lowercase> {
self.name_by_var.get(&var)
}
}
pub fn canonicalize_annotation(
env: &mut Env,
scope: &mut Scope,
annotation: &roc_parse::ast::TypeAnnotation,
region: Region,
var_store: &VarStore,
) -> Annotation {
let mut introduced_variables = IntroducedVariables::default();
let mut aliases = SendMap::default();
let mut references = MutSet::default();
let typ = can_annotation_help(
@ -45,22 +76,15 @@ pub fn canonicalize_annotation(
region,
scope,
var_store,
&mut rigids,
&mut introduced_variables,
&mut aliases,
&mut references,
);
let mut ftv = MutMap::default();
for (k, v) in rigids.clone() {
ftv.insert(v, k);
}
Annotation {
typ,
ftv,
introduced_variables,
references,
rigids,
aliases,
}
}
@ -72,7 +96,7 @@ fn can_annotation_help(
region: Region,
scope: &mut Scope,
var_store: &VarStore,
rigids: &mut ImMap<Lowercase, Variable>,
introduced_variables: &mut IntroducedVariables,
local_aliases: &mut SendMap<Symbol, Alias>,
references: &mut MutSet<Symbol>,
) -> Type {
@ -89,7 +113,7 @@ fn can_annotation_help(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
references,
);
@ -103,7 +127,7 @@ fn can_annotation_help(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
references,
);
@ -148,7 +172,7 @@ fn can_annotation_help(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
references,
);
@ -161,12 +185,12 @@ fn can_annotation_help(
BoundVariable(v) => {
let name = Lowercase::from(*v);
match rigids.get(&name) {
match introduced_variables.var_by_name(&name) {
Some(var) => Type::Variable(*var),
None => {
let var = var_store.fresh();
rigids.insert(name, var);
introduced_variables.insert_named(name, var);
Type::Variable(var)
}
@ -200,7 +224,7 @@ fn can_annotation_help(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
references,
);
@ -214,12 +238,12 @@ fn can_annotation_help(
BoundVariable(ident) => {
let var_name = Lowercase::from(ident);
if let Some(var) = rigids.get(&var_name) {
if let Some(var) = introduced_variables.var_by_name(&var_name) {
vars.push((var_name, Type::Variable(*var)));
} else {
let var = var_store.fresh();
rigids.insert(var_name.clone(), var);
introduced_variables.insert_named(var_name.clone(), var);
vars.push((var_name.clone(), Type::Variable(var)));
lowercase_vars.push(Located::at(loc_var.region, (var_name, var)));
@ -276,7 +300,7 @@ fn can_annotation_help(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
&mut field_types,
references,
@ -290,7 +314,7 @@ fn can_annotation_help(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
references,
),
@ -309,7 +333,7 @@ fn can_annotation_help(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
&mut tag_types,
references,
@ -323,7 +347,7 @@ fn can_annotation_help(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
references,
),
@ -338,13 +362,15 @@ fn can_annotation_help(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
references,
),
Wildcard | Malformed(_) => {
let var = var_store.fresh();
introduced_variables.insert_wildcard(var);
Type::Variable(var)
}
}
@ -358,7 +384,7 @@ fn can_assigned_field<'a>(
region: Region,
scope: &mut Scope,
var_store: &VarStore,
rigids: &mut ImMap<Lowercase, Variable>,
introduced_variables: &mut IntroducedVariables,
local_aliases: &mut SendMap<Symbol, Alias>,
field_types: &mut SendMap<Lowercase, Type>,
references: &mut MutSet<Symbol>,
@ -373,7 +399,7 @@ fn can_assigned_field<'a>(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
references,
);
@ -385,11 +411,11 @@ fn can_assigned_field<'a>(
// Interpret { a, b } as { a : a, b : b }
let field_name = Lowercase::from(loc_field_name.value);
let field_type = {
if let Some(var) = rigids.get(&field_name) {
if let Some(var) = introduced_variables.var_by_name(&field_name) {
Type::Variable(*var)
} else {
let field_var = var_store.fresh();
rigids.insert(field_name.clone(), field_var);
introduced_variables.insert_named(field_name.clone(), field_var);
Type::Variable(field_var)
}
};
@ -402,7 +428,7 @@ fn can_assigned_field<'a>(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
field_types,
references,
@ -419,7 +445,7 @@ fn can_tag<'a>(
region: Region,
scope: &mut Scope,
var_store: &VarStore,
rigids: &mut ImMap<Lowercase, Variable>,
introduced_variables: &mut IntroducedVariables,
local_aliases: &mut SendMap<Symbol, Alias>,
tag_types: &mut Vec<(TagName, Vec<Type>)>,
references: &mut MutSet<Symbol>,
@ -436,7 +462,7 @@ fn can_tag<'a>(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
references,
);
@ -458,7 +484,7 @@ fn can_tag<'a>(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
references,
);
@ -474,7 +500,7 @@ fn can_tag<'a>(
region,
scope,
var_store,
rigids,
introduced_variables,
local_aliases,
tag_types,
references,

View file

@ -1,4 +1,5 @@
use crate::annotation::canonicalize_annotation;
use crate::annotation::IntroducedVariables;
use crate::env::Env;
use crate::expr::Expr::{self, *};
use crate::expr::{
@ -28,7 +29,7 @@ pub struct Def {
pub loc_expr: Located<Expr>,
pub expr_var: Variable,
pub pattern_vars: SendMap<Symbol, Variable>,
pub annotation: Option<(Type, SendMap<Lowercase, Variable>, SendMap<Symbol, Alias>)>,
pub annotation: Option<(Type, IntroducedVariables, SendMap<Symbol, Alias>)>,
}
#[derive(Debug)]
@ -192,7 +193,10 @@ pub fn canonicalize_defs<'a>(
Vec::with_capacity(vars.len());
for loc_lowercase in vars {
if let Some(var) = can_ann.rigids.get(&loc_lowercase.value) {
if let Some(var) = can_ann
.introduced_variables
.var_by_name(&loc_lowercase.value)
{
// This is a valid lowercase rigid var for the alias.
can_vars.push(Located {
value: (loc_lowercase.value.clone(), *var),
@ -713,14 +717,7 @@ fn canonicalize_pending_def<'a>(
aliases.insert(symbol, alias);
}
// union seen rigids with already found ones
for (k, v) in ann.rigids {
output.rigids.insert(k, v);
}
for (k, v) in ann.ftv {
output.ftv.insert(k, v);
}
output.introduced_variables.union(&ann.introduced_variables);
pattern_to_vars_by_symbol(&mut vars_by_symbol, &loc_can_pattern.value, expr_var);
@ -790,7 +787,11 @@ fn canonicalize_pending_def<'a>(
value: loc_can_expr.value.clone(),
},
pattern_vars: im::HashMap::clone(&vars_by_symbol),
annotation: Some((typ.clone(), output.rigids.clone(), ann.aliases.clone())),
annotation: Some((
typ.clone(),
output.introduced_variables.clone(),
ann.aliases.clone(),
)),
},
);
}
@ -810,7 +811,10 @@ fn canonicalize_pending_def<'a>(
let mut can_vars: Vec<Located<(Lowercase, Variable)>> = Vec::with_capacity(vars.len());
for loc_lowercase in vars {
if let Some(var) = can_ann.rigids.get(&loc_lowercase.value) {
if let Some(var) = can_ann
.introduced_variables
.var_by_name(&loc_lowercase.value)
{
// This is a valid lowercase rigid var for the alias.
can_vars.push(Located {
value: (loc_lowercase.value.clone(), *var),
@ -840,15 +844,9 @@ fn canonicalize_pending_def<'a>(
let alias = scope.lookup_alias(symbol).expect("alias was not added");
aliases.insert(symbol, alias.clone());
// aliases cannot introduce new rigids that are visible in other annotations
// but the rigids can show up in type error messages, so still register them
for (k, v) in can_ann.rigids {
output.rigids.insert(k, v);
}
for (k, v) in can_ann.ftv {
output.ftv.insert(k, v);
}
output
.introduced_variables
.union(&can_ann.introduced_variables);
}
TypedBody(loc_pattern, loc_can_pattern, loc_ann, loc_expr) => {
let ann =
@ -867,14 +865,7 @@ fn canonicalize_pending_def<'a>(
aliases.insert(symbol, alias);
}
// union seen rigids with already found ones
for (k, v) in ann.rigids {
output.rigids.insert(k, v);
}
for (k, v) in ann.ftv {
output.ftv.insert(k, v);
}
output.introduced_variables.union(&ann.introduced_variables);
// bookkeeping for tail-call detection. If we're assigning to an
// identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called.
@ -991,7 +982,11 @@ fn canonicalize_pending_def<'a>(
value: loc_can_expr.value.clone(),
},
pattern_vars: im::HashMap::clone(&vars_by_symbol),
annotation: Some((typ.clone(), output.rigids.clone(), ann.aliases.clone())),
annotation: Some((
typ.clone(),
output.introduced_variables.clone(),
ann.aliases.clone(),
)),
},
);
}
@ -1157,8 +1152,9 @@ pub fn can_defs_with_return<'a>(
let (ret_expr, mut output) =
canonicalize_expr(env, var_store, &mut scope, loc_ret.region, &loc_ret.value);
output.rigids = output.rigids.union(defs_output.rigids);
output.ftv = output.ftv.union(defs_output.ftv);
output
.introduced_variables
.union(&defs_output.introduced_variables);
output.references = output.references.union(defs_output.references);
// Now that we've collected all the references, check to see if any of the new idents

View file

@ -1,3 +1,4 @@
use crate::annotation::IntroducedVariables;
use crate::def::{can_defs_with_return, Def};
use crate::env::Env;
use crate::num::{
@ -25,8 +26,7 @@ use std::ops::Neg;
pub struct Output {
pub references: References,
pub tail_call: Option<Symbol>,
pub rigids: SendMap<Lowercase, Variable>,
pub ftv: SendMap<Variable, Lowercase>,
pub introduced_variables: IntroducedVariables,
pub aliases: SendMap<Symbol, Alias>,
}

View file

@ -121,7 +121,7 @@ pub fn canonicalize_module_defs<'a>(
}
}
for (var, lowercase) in output.ftv.clone() {
for (var, lowercase) in output.introduced_variables.name_by_var.clone() {
rigid_variables.insert(var, lowercase);
}

View file

@ -1,5 +1,6 @@
use crate::builtins::{empty_list_type, float_literal, int_literal, list_type, str_type};
use crate::pattern::{constrain_pattern, PatternState};
use roc_can::annotation::IntroducedVariables;
use roc_can::constraint::Constraint::{self, *};
use roc_can::constraint::LetConstraint;
use roc_can::def::{Declaration, Def};
@ -754,7 +755,7 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
let mut new_rigids = Vec::new();
let expr_con = match &def.annotation {
Some((annotation, free_vars, ann_def_aliases)) => {
Some((annotation, introduced_vars, ann_def_aliases)) => {
def_aliases = ann_def_aliases.clone();
let arity = annotation.arity();
@ -763,7 +764,7 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
let annotation = instantiate_rigids(
annotation,
&free_vars,
&introduced_vars,
&mut new_rigids,
&mut ftv,
&def.loc_pattern,
@ -821,7 +822,7 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
fn instantiate_rigids(
annotation: &Type,
free_vars: &SendMap<Lowercase, Variable>,
introduced_vars: &IntroducedVariables,
new_rigids: &mut Vec<Variable>,
ftv: &mut ImMap<Lowercase, Variable>,
loc_pattern: &Located<Pattern>,
@ -830,8 +831,8 @@ fn instantiate_rigids(
let mut annotation = annotation.clone();
let mut rigid_substitution: ImMap<Variable, Type> = ImMap::default();
for (name, var) in free_vars {
if let Some(existing_rigid) = ftv.get(name) {
for (name, var) in introduced_vars.var_by_name.iter() {
if let Some(existing_rigid) = ftv.get(&name) {
rigid_substitution.insert(*var, Type::Variable(*existing_rigid));
} else {
// It's possible to use this rigid in nested defs
@ -854,6 +855,8 @@ fn instantiate_rigids(
}
}
new_rigids.extend(introduced_vars.wildcards.iter().cloned());
annotation
}
@ -922,7 +925,7 @@ pub fn rec_defs_help(
flex_info.def_types.extend(pattern_state.headers);
}
Some((annotation, free_vars, ann_def_aliases)) => {
Some((annotation, introduced_vars, ann_def_aliases)) => {
for (symbol, alias) in ann_def_aliases.clone() {
def_aliases.insert(symbol, alias);
}
@ -932,7 +935,7 @@ pub fn rec_defs_help(
let annotation = instantiate_rigids(
annotation,
&free_vars,
&introduced_vars,
&mut new_rigids,
&mut ftv,
&def.loc_pattern,

View file

@ -72,6 +72,10 @@ pub fn constrain_imported_values(
rigid_vars.push(var);
}
for var in free_vars.wildcards {
rigid_vars.push(var);
}
// Variables can lose their name during type inference. But the unnamed
// variables are still part of a signature, and thus must be treated as rigids here!
for (_, var) in free_vars.unnamed_vars {
@ -151,9 +155,10 @@ pub fn load_builtin_aliases(
pub struct FreeVars {
pub named_vars: ImMap<Lowercase, Variable>,
pub unnamed_vars: ImMap<VarId, Variable>,
pub wildcards: Vec<Variable>,
}
pub fn to_type(solved_type: &SolvedType, free_vars: &mut FreeVars, var_store: &VarStore) -> Type {
fn to_type(solved_type: &SolvedType, free_vars: &mut FreeVars, var_store: &VarStore) -> Type {
use roc_types::solved_types::SolvedType::*;
match solved_type {
@ -196,7 +201,11 @@ pub fn to_type(solved_type: &SolvedType, free_vars: &mut FreeVars, var_store: &V
Type::Variable(var)
}
}
Wildcard => Type::Variable(var_store.fresh()),
Wildcard => {
let var = var_store.fresh();
free_vars.wildcards.push(var);
Type::Variable(var)
}
Record { fields, ext } => {
let mut new_fields = SendMap::default();

View file

@ -1,4 +1,5 @@
use crate::expr::{exists, exists_with_aliases, Info};
use roc_can::annotation::IntroducedVariables;
use roc_can::constraint::Constraint::{self, *};
use roc_can::constraint::LetConstraint;
use roc_can::def::{Declaration, Def};
@ -59,7 +60,7 @@ pub fn constrain_declaration(
pub fn constrain_decls(
home: ModuleId,
decls: &[Declaration],
aliases: SendMap<Symbol, Alias>,
mut aliases: SendMap<Symbol, Alias>,
var_store: &VarStore,
) -> Constraint {
let mut constraint = Constraint::SaveTheEnvironment;
@ -81,6 +82,8 @@ pub fn constrain_decls(
}
}
aliases_to_attr_type(var_store, &mut aliases);
for decl in decls.iter().rev() {
// NOTE: rigids are empty because they are not shared between top-level definitions
match decl {
@ -1635,14 +1638,15 @@ fn constrain_def(
let mut new_rigids = Vec::new();
let expr_con = match &def.annotation {
Some((annotation, free_vars, ann_def_aliases)) => {
Some((annotation, introduced_vars, ann_def_aliases)) => {
def_aliases = ann_def_aliases.clone();
let arity = annotation.arity();
let mut ftv = env.rigids.clone();
let annotation = instantiate_rigids(
var_store,
annotation,
&free_vars,
&introduced_vars,
&mut new_rigids,
&mut ftv,
&def.loc_pattern,
@ -1709,7 +1713,7 @@ fn constrain_def(
fn instantiate_rigids(
var_store: &VarStore,
annotation: &Type,
free_vars: &SendMap<Lowercase, Variable>,
introduced_vars: &IntroducedVariables,
new_rigids: &mut Vec<Variable>,
ftv: &mut ImMap<Lowercase, (Variable, Variable)>,
loc_pattern: &Located<Pattern>,
@ -1720,7 +1724,7 @@ fn instantiate_rigids(
let mut rigid_substitution: ImMap<Variable, Type> = ImMap::default();
for (name, var) in free_vars {
for (name, var) in introduced_vars.var_by_name.iter() {
if let Some((existing_rigid, existing_uvar)) = ftv.get(&name) {
rigid_substitution.insert(
*var,
@ -1764,6 +1768,7 @@ fn instantiate_rigids(
}
new_rigids.extend(uniq_vars);
new_rigids.extend(introduced_vars.wildcards.iter().cloned());
for (_, v) in new_rigid_pairs {
new_rigids.push(v);
@ -1854,7 +1859,7 @@ pub fn rec_defs_help(
flex_info.def_types.extend(pattern_state.headers);
}
Some((annotation, free_vars, ann_def_aliases)) => {
Some((annotation, introduced_vars, ann_def_aliases)) => {
for (symbol, alias) in ann_def_aliases.clone() {
def_aliases.insert(symbol, alias);
}
@ -1863,7 +1868,7 @@ pub fn rec_defs_help(
let annotation = instantiate_rigids(
var_store,
annotation,
&free_vars,
&introduced_vars,
&mut new_rigids,
&mut ftv,
&def.loc_pattern,

View file

@ -5,11 +5,11 @@ interface AStar
# a port of https://github.com/krisajenkins/elm-astar/blob/2.1.3/src/AStar/Generalised.elm
Model xyz :
{ evaluated : Set xyz
, openSet : Set xyz
, costs : Map.Map xyz Float
, cameFrom : Map.Map xyz xyz
Model position :
{ evaluated : Set position
, openSet : Set position
, costs : Map.Map position Float
, cameFrom : Map.Map position position
}

View file

@ -47,6 +47,31 @@ pub fn infer_expr(
(content, solved.into_inner())
}
/// Used in the with_larger_debug_stack() function, for tests that otherwise
/// run out of stack space in debug builds (but don't in --release builds)
#[allow(dead_code)]
const EXPANDED_STACK_SIZE: usize = 4 * 1024 * 1024;
/// Without this, some tests pass in `cargo test --release` but fail without
/// the --release flag because they run out of stack space. This increases
/// stack size for debug builds only, while leaving the stack space at the default
/// amount for release builds.
#[allow(dead_code)]
#[cfg(debug_assertions)]
pub fn with_larger_debug_stack<F>(run_test: F)
where
F: FnOnce() -> (),
F: Send,
F: 'static,
{
std::thread::Builder::new()
.stack_size(EXPANDED_STACK_SIZE)
.spawn(run_test)
.expect("Error while spawning expanded dev stack size thread")
.join()
.expect("Error while joining expanded dev stack size thread")
}
/// In --release builds, don't increase the stack size. Run the test normally.
/// This way, we find out if any of our tests are blowing the stack even after
/// optimizations in release builds.

View file

@ -37,7 +37,7 @@ mod test_infer {
assert_correct_variable_usage(&constraint);
for (var, name) in output.ftv {
for (var, name) in output.introduced_variables.name_by_var {
subs.rigid_var(var, name);
}

View file

@ -10,7 +10,9 @@ mod helpers;
#[cfg(test)]
mod test_infer_uniq {
use crate::helpers::{assert_correct_variable_usage, infer_expr, uniq_expr};
use crate::helpers::{
assert_correct_variable_usage, infer_expr, uniq_expr, with_larger_debug_stack,
};
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
// HELPERS
@ -21,7 +23,7 @@ mod test_infer_uniq {
assert_correct_variable_usage(&constraint);
for (var, name) in output.ftv {
for (var, name) in output.introduced_variables.name_by_var {
subs.rigid_var(var, name);
}
@ -2082,7 +2084,7 @@ mod test_infer_uniq {
reverse
"#
),
"Attr * (Attr * (List (Attr (a | b) c)) -> Attr (* | a | b) (List (Attr a c)))",
"Attr * (Attr * (List (Attr (a | b) c)) -> Attr (* | a | b) (List (Attr b c)))",
);
}
@ -2099,7 +2101,7 @@ mod test_infer_uniq {
}
#[test]
fn update_cost() {
fn use_correct_ext_var() {
infer_eq(
indoc!(
r#"
@ -2115,4 +2117,250 @@ mod test_infer_uniq {
"Attr * (Attr (* | a | b) { p : (Attr b *), q : (Attr a *) }* -> Attr * Int)",
);
}
#[test]
fn reconstruct_path() {
infer_eq(
indoc!(
r#"
reconstructPath : Map position position, position -> List position
reconstructPath = \cameFrom, goal ->
when Map.get cameFrom goal is
Err KeyNotFound ->
[]
Ok next ->
List.push (reconstructPath cameFrom next) goal
reconstructPath
"#
),
"Attr Shared (Attr Shared (Map (Attr Shared position) (Attr Shared position)), Attr Shared position -> Attr * (List (Attr Shared position)))"
);
}
#[test]
fn cheapest_open() {
infer_eq(
indoc!(
r#"
Model position : { evaluated : Set position
, openSet : Set position
, costs : Map.Map position Float
, cameFrom : Map.Map position position
}
cheapestOpen : (position -> Float), Model position -> Result position [ KeyNotFound ]*
cheapestOpen = \costFunction, model ->
folder = \position, resSmallestSoFar ->
when Map.get model.costs position is
Err e ->
Err e
Ok cost ->
positionCost = costFunction position
when resSmallestSoFar is
Err _ -> Ok { position, cost: cost + positionCost }
Ok smallestSoFar ->
if positionCost + cost < smallestSoFar.cost then
Ok { position, cost: cost + positionCost }
else
Ok smallestSoFar
Set.foldl model.openSet folder (Err KeyNotFound)
|> Result.map (\x -> x.position)
cheapestOpen
"#
),
"Attr * (Attr * (Attr Shared position -> Attr Shared Float), Attr * (Model (Attr Shared position)) -> Attr * (Result (Attr Shared position) (Attr * [ KeyNotFound ]*)))"
);
}
#[test]
fn update_cost() {
infer_eq(
indoc!(
r#"
Model position : { evaluated : Set position
, openSet : Set position
, costs : Map.Map position Float
, cameFrom : Map.Map position position
}
reconstructPath : Map position position, position -> List position
reconstructPath = \cameFrom, goal ->
when Map.get cameFrom goal is
Err KeyNotFound ->
[]
Ok next ->
List.push (reconstructPath cameFrom next) goal
updateCost : position, position, Model position -> Model position
updateCost = \current, neighbour, model ->
newCameFrom = Map.insert model.cameFrom neighbour current
newCosts = Map.insert model.costs neighbour distanceTo
distanceTo = reconstructPath newCameFrom neighbour
|> List.length
|> Num.toFloat
newModel = { model & costs : newCosts , cameFrom : newCameFrom }
when Map.get model.costs neighbour is
Err KeyNotFound ->
newModel
Ok previousDistance ->
if distanceTo < previousDistance then
newModel
else
model
updateCost
"#
),
"Attr * (Attr Shared position, Attr Shared position, Attr Shared (Model (Attr Shared position)) -> Attr Shared (Model (Attr Shared position)))"
);
}
#[test]
fn astar_full_code() {
with_larger_debug_stack(|| {
infer_eq(
indoc!(
r#"
Model position : { evaluated : Set position
, openSet : Set position
, costs : Map.Map position Float
, cameFrom : Map.Map position position
}
initialModel : position -> Model position
initialModel = \start ->
{ evaluated : Set.empty
, openSet : Set.singleton start
, costs : Map.singleton start 0.0
, cameFrom : Map.empty
}
cheapestOpen : (position -> Float), Model position -> Result position [ KeyNotFound ]*
cheapestOpen = \costFunction, model ->
folder = \position, resSmallestSoFar ->
when Map.get model.costs position is
Err e ->
Err e
Ok cost ->
positionCost = costFunction position
when resSmallestSoFar is
Err _ -> Ok { position, cost: cost + positionCost }
Ok smallestSoFar ->
if positionCost + cost < smallestSoFar.cost then
Ok { position, cost: cost + positionCost }
else
Ok smallestSoFar
Set.foldl model.openSet folder (Err KeyNotFound)
|> Result.map (\x -> x.position)
reconstructPath : Map position position, position -> List position
reconstructPath = \cameFrom, goal ->
when Map.get cameFrom goal is
Err KeyNotFound ->
[]
Ok next ->
List.push (reconstructPath cameFrom next) goal
updateCost : position, position, Model position -> Model position
updateCost = \current, neighbour, model ->
newCameFrom = Map.insert model.cameFrom neighbour current
newCosts = Map.insert model.costs neighbour distanceTo
distanceTo = reconstructPath newCameFrom neighbour
|> List.length
|> Num.toFloat
newModel = { model & costs : newCosts , cameFrom : newCameFrom }
when Map.get model.costs neighbour is
Err KeyNotFound ->
newModel
Ok previousDistance ->
if distanceTo < previousDistance then
newModel
else
model
findPath : { costFunction: (position, position -> Float), moveFunction: (position -> Set position), start : position, end : position } -> Result (List position) [ KeyNotFound ]*
findPath = \{ costFunction, moveFunction, start, end } ->
astar costFunction moveFunction end (initialModel start)
astar : (position, position -> Float), (position -> Set position), position, Model position -> [ Err [ KeyNotFound ]*, Ok (List position) ]*
astar = \costFn, moveFn, goal, model ->
when cheapestOpen (\position -> costFn goal position) model is
Err _ ->
Err KeyNotFound
Ok current ->
if current == goal then
Ok (reconstructPath model.cameFrom goal)
else
modelPopped = { model & openSet : Set.remove model.openSet current, evaluated : Set.insert model.evaluated current }
neighbours = moveFn current
newNeighbours = Set.diff neighbours modelPopped.evaluated
modelWithNeighbours = { modelPopped & openSet : Set.union modelPopped.openSet newNeighbours }
modelWithCosts = Set.foldl newNeighbours (\nb, md -> updateCost current nb md) modelWithNeighbours
astar costFn moveFn goal modelWithCosts
findPath
"#
),
"Attr * (Attr * { costFunction : (Attr Shared (Attr Shared position, Attr Shared position -> Attr Shared Float)), end : (Attr Shared position), moveFunction : (Attr Shared (Attr Shared position -> Attr * (Set (Attr Shared position)))), start : (Attr Shared position) } -> Attr * (Result (Attr * (List (Attr Shared position))) (Attr * [ KeyNotFound ]*)))"
)
});
}
#[test]
fn instantiated_alias() {
infer_eq(
indoc!(
r#"
Model a : { foo : Set a }
initialModel : position -> Model Int
initialModel = \_ -> { foo : Set.empty }
initialModel
"#
),
"Attr * (Attr * position -> Attr * (Model (Attr * Int)))",
);
}
}

View file

@ -222,6 +222,26 @@ mod test_uniqueness_load {
});
}
#[test]
fn load_astar() {
test_async(async {
let subs_by_module = MutMap::default();
let loaded_module = load_fixture("interface_with_deps", "AStar", subs_by_module).await;
expect_types(
loaded_module,
hashmap! {
"findPath" => "Attr * (Attr * { costFunction : (Attr Shared (Attr Shared position, Attr Shared position -> Attr Shared Float)), end : (Attr Shared position), moveFunction : (Attr Shared (Attr Shared position -> Attr * (Set (Attr Shared position)))), start : (Attr Shared position) } -> Attr * (Result (Attr * (List (Attr Shared position))) (Attr * [ KeyNotFound ]*)))",
"initialModel" => "Attr * (Attr Shared position -> Attr * (Model (Attr Shared position)))",
"reconstructPath" => "Attr Shared (Attr Shared (Map (Attr Shared position) (Attr Shared position)), Attr Shared position -> Attr * (List (Attr Shared position)))",
"updateCost" => "Attr * (Attr Shared position, Attr Shared position, Attr Shared (Model (Attr Shared position)) -> Attr Shared (Model (Attr Shared position)))",
"cheapestOpen" => "Attr * (Attr * (Attr Shared position -> Attr Shared Float), Attr * (Model (Attr Shared position)) -> Attr * (Result (Attr Shared position) (Attr * [ KeyNotFound ]*)))",
"astar" => "Attr Shared (Attr Shared (Attr Shared position, Attr Shared position -> Attr Shared Float), Attr Shared (Attr Shared position -> Attr * (Set (Attr Shared position))), Attr Shared position, Attr Shared (Model (Attr Shared position)) -> Attr * [ Err (Attr * [ KeyNotFound ]*), Ok (Attr * (List (Attr Shared position))) ]*)",
},
);
});
}
#[test]
fn load_and_typecheck_quicksort() {
test_async(async {