mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Merge remote-tracking branch 'origin/various-bugfixes' into trunk
This commit is contained in:
commit
d8cf402528
14 changed files with 784 additions and 115 deletions
|
@ -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,
|
||||
|
|
|
@ -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)]),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)))",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue