loads of things

This commit is contained in:
Folkert 2020-06-27 16:20:30 +02:00
parent ac18da302c
commit 79f01e0604
8 changed files with 382 additions and 74 deletions

View File

@ -1691,7 +1691,7 @@ fn constrain_def_pattern(
fn annotation_to_attr_type(
var_store: &mut VarStore,
ann: &Type,
rigids: &mut ImMap<Variable, Variable>,
rigids: &mut ImSet<Variable>,
change_var_kind: bool,
) -> (Vec<Variable>, Type) {
use roc_types::types::Type::*;
@ -1699,19 +1699,12 @@ fn annotation_to_attr_type(
match ann {
Variable(var) => {
if change_var_kind {
if let Some(uvar) = rigids.get(var) {
(
vec![],
attr_type(Bool::variable(*uvar), Type::Variable(*var)),
)
} else {
let uvar = var_store.fresh();
rigids.insert(*var, uvar);
(
vec![],
attr_type(Bool::variable(uvar), Type::Variable(*var)),
)
}
let uvar = var_store.fresh();
rigids.insert(uvar);
(
vec![],
attr_type(Bool::variable(uvar), Type::Variable(*var)),
)
} else {
(vec![], Type::Variable(*var))
}
@ -1900,7 +1893,7 @@ fn annotation_to_attr_type(
fn annotation_to_attr_type_many(
var_store: &mut VarStore,
anns: &[Type],
rigids: &mut ImMap<Variable, Variable>,
rigids: &mut ImSet<Variable>,
change_var_kind: bool,
) -> (Vec<Variable>, Vec<Type>) {
anns.iter()
@ -1926,7 +1919,7 @@ fn aliases_to_attr_type(var_store: &mut VarStore, aliases: &mut SendMap<Symbol,
//
// That would give a double attr wrapper on the type arguments.
// The `change_var_kind` flag set to false ensures type variables remain of kind *
let (_, new) = annotation_to_attr_type(var_store, &alias.typ, &mut ImMap::default(), false);
let (_, new) = annotation_to_attr_type(var_store, &alias.typ, &mut ImSet::default(), false);
// remove the outer Attr, because when this occurs in a signature it'll already be wrapped in one
match new {
Type::Apply(Symbol::ATTR_ATTR, args) => {
@ -1942,8 +1935,6 @@ fn aliases_to_attr_type(var_store: &mut VarStore, aliases: &mut SendMap<Symbol,
fix_mutual_recursive_alias(&mut alias.typ, b);
}
}
dbg!(&aliases);
}
fn constrain_def(
@ -2077,9 +2068,9 @@ fn instantiate_rigids(
annotation.substitute(&rigid_substitution);
}
let mut new_rigid_pairs = ImMap::default();
let mut new_uniqueness_rigids = ImSet::default();
let (mut uniq_vars, annotation) =
annotation_to_attr_type(var_store, &annotation, &mut new_rigid_pairs, true);
annotation_to_attr_type(var_store, &annotation, &mut new_uniqueness_rigids, true);
if let Pattern::Identifier(symbol) = loc_pattern.value {
headers.insert(symbol, Located::at(loc_pattern.region, annotation.clone()));
@ -2089,7 +2080,7 @@ fn instantiate_rigids(
) {
for (k, v) in new_headers {
let (new_uniq_vars, attr_annotation) =
annotation_to_attr_type(var_store, &v.value, &mut new_rigid_pairs, true);
annotation_to_attr_type(var_store, &v.value, &mut new_uniqueness_rigids, true);
uniq_vars.extend(new_uniq_vars);
@ -2099,10 +2090,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);
}
new_rigids.extend(new_uniqueness_rigids);
annotation
}

View File

@ -650,6 +650,7 @@ fn type_to_variable(
}
Alias(Symbol::BOOL_BOOL, _, _) => Variable::BOOL,
Alias(symbol, args, alias_type) => {
// TODO cache in uniqueness inference gives problems! all Int's get the same uniqueness var!
// Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
// different variables (once for each occurence). The recursion restriction is required
// for uniqueness types only: recursive aliases "introduce" an unbound uniqueness
@ -666,11 +667,13 @@ fn type_to_variable(
// TODO does caching work at all with uniqueness types? even Int then hides a uniqueness variable
let is_recursive = alias_type.is_recursive();
let no_args = args.is_empty();
/*
if no_args && !is_recursive {
if let Some(var) = cached.get(symbol) {
return *var;
}
}
*/
let mut arg_vars = Vec::with_capacity(args.len());
let mut new_aliases = ImMap::default();
@ -688,7 +691,7 @@ fn type_to_variable(
let result = register(subs, rank, pools, content);
if no_args && !is_recursive {
cached.insert(*symbol, result);
// cached.insert(*symbol, result);
}
result

View File

@ -1737,8 +1737,6 @@ mod test_uniq_solve {
);
}
// This snippet exhibits the rank issue. Seems to only occur when using recursive types with
// recursive functions.
#[test]
fn rigids_in_signature() {
infer_eq(
@ -1746,13 +1744,19 @@ mod test_uniq_solve {
r#"
ConsList a : [ Cons a (ConsList a), Nil ]
map : (p -> q), p -> ConsList q
map = \f, x -> map f x
map : (p -> q), ConsList p -> ConsList q
map = \f, list ->
when list is
Cons x xs ->
Cons (f x) (map f xs)
Nil ->
Nil
map
"#
),
"Attr Shared (Attr * (Attr a p -> Attr b q), Attr a p -> Attr * (ConsList (Attr b q)))",
"Attr Shared (Attr Shared (Attr a p -> Attr b q), Attr (* | a) (ConsList (Attr a p)) -> Attr * (ConsList (Attr b q)))",
);
}
@ -1773,7 +1777,7 @@ mod test_uniq_solve {
toEmpty
"#
),
"Attr * (Attr * (ConsList (Attr a p)) -> Attr * (ConsList (Attr a p)))",
"Attr * (Attr * (ConsList (Attr * p)) -> Attr * (ConsList (Attr * p)))",
);
}
@ -1794,7 +1798,7 @@ mod test_uniq_solve {
toEmpty
"#
),
"Attr Shared (Attr * (ConsList (Attr a p)) -> Attr * (ConsList (Attr a p)))",
"Attr Shared (Attr * (ConsList (Attr * p)) -> Attr * (ConsList (Attr * p)))",
);
}
@ -1830,33 +1834,61 @@ mod test_uniq_solve {
);
}
#[test]
fn typecheck_mutually_recursive_tag_union() {
infer_eq(
indoc!(
r#"
ListA a b : [ Cons a (ListB b a), Nil ]
ListB a b : [ Cons a (ListA b a), Nil ]
ConsList q : [ Cons q (ConsList q), Nil ]
toAs : (q -> p), ListA p q -> ConsList p
toAs =
\f, lista ->
when lista is
Nil -> Nil
Cons a listb ->
when listb is
Nil -> Nil
Cons b newLista ->
Cons a (Cons (f b) (toAs f newLista))
toAs
"#
),
"Attr Shared (Attr Shared (Attr a q -> Attr b p), Attr (* | a | b) (ListA (Attr b p) (Attr a q)) -> Attr * (ConsList (Attr b p)))"
);
}
// #[test]
// fn assoc_list_map() {
// infer_eq(
// indoc!(
// r#"
// ConsList a : [ Cons a (ConsList a), Nil ]
// AssocList a b : ConsList { key: a, value : b }
//
// map : (k, v -> v2), AssocList k v -> ConsList k v2
// map = \f, list ->
// when list is
// Cons { key, value } xs ->
// Cons (f key value) (map f xs)
// Nil ->
// Nil
//
// map
// "#
// ),
// "Attr Shared (Attr Shared (Attr a p -> Attr b q), Attr (* | a) (ConsList (Attr a p)) -> Attr * (ConsList (Attr b q)))",
// );
// }
//
//
// #[test]
// fn typecheck_mutually_recursive_tag_union() {
// infer_eq(
// indoc!(
// r#"
// ListA a b : [ Cons a (ListB b a), Nil ]
// ListB a b : [ Cons a (ListA b a), Nil ]
//
// ConsList q : [ Cons q (ConsList q), Nil ]
//
// toAs : (q -> p), ListA p q -> ConsList p
// toAs =
// \f, lista ->
// when lista is
// Nil -> Nil
// Cons a listb ->
// when listb is
// Nil -> Nil
// Cons b newLista ->
// Cons a (Cons (f b) (toAs f newLista))
//
// foo = \_ ->
// x = 4
// { a : x, b : x }.a
//
// toAs foo Nil
// "#
// ),
// "Attr Shared (Attr Shared (Attr a q -> Attr b p), Attr (* | a | b) (ListA (Attr b p) (Attr a q)) -> Attr * (ConsList (Attr b p)))"
// );
// }
#[test]
fn infer_mutually_recursive_tag_union() {
@ -2285,6 +2317,59 @@ mod test_uniq_solve {
);
}
#[test]
fn hidden_uniqueness_one() {
infer_eq(
indoc!(
r#"
Model : { foo : Int }
extract : Model -> Int
extract = \{ foo } -> foo
extract
"#
),
"Attr * (Attr (* | a) Model -> Attr a Int)",
);
}
#[test]
fn hidden_uniqueness_two() {
infer_eq(
indoc!(
r#"
Model : { foo : Int, bar : Int }
extract : Model -> Int
extract = \{ foo } -> foo
extract
"#
),
"Attr * (Attr (* | a) Model -> Attr a Int)",
);
}
#[test]
fn hidden_uniqueness_three() {
infer_eq(
indoc!(
r#"
Model : { foo : Int, bar : Int }
# extract : { foo : Int, bar : Int } -> Int
extract : Model -> Int
# extract = \r -> r.foo + r.bar
extract = \{foo, bar} -> foo + bar
extract
"#
),
"Attr * (Attr (* | * | *) Model -> Attr * Int)",
);
}
#[test]
fn peano_roc_is_empty() {
infer_eq(
@ -2319,7 +2404,7 @@ mod test_uniq_solve {
map
"#
),
"Attr * (Attr (* | c | d) (Result (Attr d a) (Attr c e)), Attr * (Attr d a -> Attr f b) -> Attr * (Result (Attr f b) (Attr c e)))"
"Attr * (Attr (* | c | d) (Result (Attr c a) (Attr d e)), Attr * (Attr c a -> Attr f b) -> Attr * (Result (Attr f b) (Attr d e)))"
);
}
@ -2386,6 +2471,21 @@ mod test_uniq_solve {
);
}
#[test]
fn int_abs_with_annotation() {
infer_eq(
indoc!(
r#"
foobar : Int -> Int
foobar = \x -> Num.abs x
foobar
"#
),
"Attr * (Attr * Int -> Attr * Int)",
);
}
#[test]
fn int_addition_without_annotation() {
infer_eq(
@ -2492,7 +2592,7 @@ mod test_uniq_solve {
reconstructPath
"#
),
"Attr Shared (Attr Shared (Map (Attr Shared position) (Attr Shared position)), Attr Shared position -> Attr * (List (Attr Shared position)))"
"Attr Shared (Attr Shared (Map (Attr * position) (Attr Shared position)), Attr Shared position -> Attr * (List (Attr Shared position)))"
);
}
@ -2508,7 +2608,7 @@ mod test_uniq_solve {
, cameFrom : Map.Map position position
}
# cheapestOpen : (position -> Float), Model position -> Result position [ KeyNotFound ]*
cheapestOpen : (position -> Float), Model position -> Result position [ KeyNotFound ]*
cheapestOpen = \costFunction, model ->
folder = \position, resSmallestSoFar ->
@ -2534,7 +2634,7 @@ mod test_uniq_solve {
cheapestOpen
"#
),
"Attr * (Attr * (Attr Shared position -> Attr Shared Float), Attr * (Model (Attr Shared position)) -> Attr * (Result (Attr Shared position) (Attr * [ KeyNotFound ]*)))"
"Attr * (Attr * (Attr Shared position -> Attr Shared Float), Attr (* | * | *) (Model (Attr Shared position)) -> Attr * (Result (Attr Shared position) (Attr * [ KeyNotFound ]*)))"
)
});
}
@ -2701,7 +2801,7 @@ mod test_uniq_solve {
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 ]*)))"
"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 * position)))), start : (Attr Shared position) } -> Attr * (Result (Attr * (List (Attr Shared position))) (Attr * [ KeyNotFound ]*)))"
)
});
}

View File

@ -164,7 +164,16 @@ impl Bool {
match self {
Bool::Container(cvar, mvars) => {
let flattened_mvars = var_to_variables(subs, *cvar, mvars);
Bool::Container(*cvar, flattened_mvars)
// find the parent variable, to remove distinct variables that all have the same
// parent. This prevents the signature from rendering as e.g. `( a | b | b)` and
// instead makes it `( a | b )`.
let parent_mvars = flattened_mvars
.into_iter()
.map(|v| subs.get_root_key_without_compacting(v))
.collect();
Bool::Container(*cvar, parent_mvars)
}
Bool::Shared => Bool::Shared,
}

View File

@ -333,6 +333,15 @@ fn write_content(env: &Env, content: Content, subs: &Subs, buf: &mut String, par
Parens::InTypeParam,
);
}
// useful for debugging
let write_out_alias = false;
if write_out_alias {
buf.push_str("[[ but really ");
let content = subs.get_without_compacting(_actual).content;
write_content(env, content, subs, buf, parens);
buf.push_str("]]");
}
}),
}
}
@ -603,7 +612,7 @@ fn write_boolean(env: &Env, boolean: Bool, subs: &Subs, buf: &mut String, parens
);
}
Bool::Container(cvar, mvars) => {
let mut buffers_set = ImSet::default();
let mut buffers = Vec::with_capacity(mvars.len());
for v in mvars {
if var_is_shared(subs, v) {
continue;
@ -617,10 +626,11 @@ fn write_boolean(env: &Env, boolean: Bool, subs: &Subs, buf: &mut String, parens
&mut inner_buf,
parens,
);
buffers_set.insert(inner_buf);
// buffers_set.insert(inner_buf);
buffers.push(inner_buf);
}
let mut buffers: Vec<String> = buffers_set.into_iter().collect();
// let mut buffers: Vec<String> = buffers_set.into_iter().collect();
buffers.sort();
let combined = buffers.join(" | ");

View File

@ -876,13 +876,26 @@ where
}
fn var_to_err_type(subs: &mut Subs, state: &mut ErrorTypeState, var: Variable) -> ErrorType {
let desc = subs.get(var);
let mut desc = subs.get(var);
if desc.mark == Mark::OCCURS {
ErrorType::Infinite
} else {
subs.set_mark(var, Mark::OCCURS);
if false {
// useful for debugging
match desc.content {
Content::FlexVar(_) => {
desc.content = Content::FlexVar(Some(format!("{:?}", var).into()));
}
Content::RigidVar(_) => {
desc.content = Content::RigidVar(format!("{:?}", var).into());
}
_ => {}
}
}
let err_type = content_to_err_type(subs, state, var, desc.content);
subs.set_mark(var, desc.mark);

View File

@ -79,7 +79,7 @@ impl fmt::Debug for Type {
}
// Sometimes it's useful to see the expansion of the alias
write!(f, "[ but actually {:?} ]", _actual)?;
// write!(f, "[ but actually {:?} ]", _actual)?;
Ok(())
}
@ -794,7 +794,7 @@ pub enum Mismatch {
CanonicalizationProblem,
}
#[derive(PartialEq, Eq, Debug, Clone)]
#[derive(PartialEq, Eq, Clone)]
pub enum ErrorType {
Infinite,
Type(Symbol, Vec<ErrorType>),
@ -809,6 +809,13 @@ pub enum ErrorType {
Error,
}
impl std::fmt::Debug for ErrorType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO remove clone
write!(f, "{:?}", write_debug_error_type(self.clone()))
}
}
impl ErrorType {
pub fn unwrap_alias(self) -> ErrorType {
match self {
@ -925,6 +932,174 @@ fn write_error_type_help(
}
}
pub fn write_debug_error_type(error_type: ErrorType) -> String {
let mut buf = String::new();
write_debug_error_type_help(error_type, &mut buf, Parens::Unnecessary);
buf
}
fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens: Parens) {
use ErrorType::*;
match error_type {
Infinite => buf.push_str(""),
Error => buf.push_str("?"),
FlexVar(name) => buf.push_str(name.as_str()),
RigidVar(name) => buf.push_str(name.as_str()),
Type(symbol, arguments) => {
let write_parens = parens == Parens::InTypeParam && !arguments.is_empty();
if write_parens {
buf.push('(');
}
buf.push_str(&format!("{:?}", symbol));
for arg in arguments {
buf.push(' ');
write_debug_error_type_help(arg, buf, Parens::InTypeParam);
}
if write_parens {
buf.push(')');
}
}
Alias(symbol, arguments, _actual) => {
let write_parens = parens == Parens::InTypeParam && !arguments.is_empty();
if write_parens {
buf.push('(');
}
buf.push_str(&format!("{:?}", symbol));
for arg in arguments {
buf.push(' ');
write_debug_error_type_help(arg.1, buf, Parens::InTypeParam);
}
// useful for debugging
let write_out_alias = true;
if write_out_alias {
buf.push_str("[[ but really ");
write_debug_error_type_help(*_actual, buf, Parens::Unnecessary);
buf.push_str("]]");
}
if write_parens {
buf.push(')');
}
}
Alias(Symbol::NUM_NUM, mut arguments, _actual) => {
debug_assert!(arguments.len() == 1);
let argument = arguments.remove(0).1;
match argument {
Type(Symbol::INT_INTEGER, _) => {
buf.push_str("Int");
}
Type(Symbol::FLOAT_FLOATINGPOINT, _) => {
buf.push_str("Float");
}
other => {
let write_parens = parens == Parens::InTypeParam;
if write_parens {
buf.push('(');
}
buf.push_str("Num ");
write_debug_error_type_help(other, buf, Parens::InTypeParam);
if write_parens {
buf.push(')');
}
}
}
}
Function(arguments, result) => {
let write_parens = parens != Parens::Unnecessary;
if write_parens {
buf.push('(');
}
let mut it = arguments.into_iter().peekable();
while let Some(arg) = it.next() {
write_debug_error_type_help(arg, buf, Parens::InFn);
if it.peek().is_some() {
buf.push_str(", ");
}
}
buf.push_str(" -> ");
write_debug_error_type_help(*result, buf, Parens::InFn);
if write_parens {
buf.push(')');
}
}
Record(fields, ext) => {
buf.push('{');
for (label, content) in fields {
buf.push_str(label.as_str());
buf.push_str(": ");
write_debug_error_type_help(content, buf, Parens::Unnecessary);
}
buf.push('}');
write_type_ext(ext, buf);
}
TagUnion(tags, ext) => {
buf.push('[');
let mut it = tags.into_iter().peekable();
while let Some((tag, args)) = it.next() {
buf.push_str(&format!("{:?}", tag));
for arg in args {
buf.push_str(" ");
write_debug_error_type_help(arg, buf, Parens::InTypeParam);
}
if it.peek().is_some() {
buf.push_str(", ");
}
}
buf.push(']');
write_type_ext(ext, buf);
}
RecursiveTagUnion(rec, tags, ext) => {
buf.push('[');
for (tag, args) in tags {
buf.push_str(&format!("{:?}", tag));
for arg in args {
buf.push_str(" ");
write_debug_error_type_help(arg, buf, Parens::Unnecessary);
}
}
buf.push(']');
write_type_ext(ext, buf);
buf.push_str(" as ");
write_debug_error_type_help(*rec, buf, Parens::Unnecessary);
}
Boolean(boolean_algebra::Bool::Shared) => buf.push_str("Shared"),
Boolean(boolean_algebra::Bool::Container(mvar, cvars)) => {
buf.push_str(&format!("Container({:?}, {:?})", mvar, cvars))
}
}
}
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum TypeExt {
Closed,

View File

@ -128,7 +128,16 @@ pub fn unify_pool(subs: &mut Subs, pool: &mut Pool, var1: Variable, var2: Variab
}
fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
// println!( "{:?} {:?} ~ {:?} {:?}", ctx.first, ctx.first_desc.content, ctx.second, ctx.second_desc.content,);
let print_debug_messages = false;
if print_debug_messages {
let (type1, _problems1) = subs.var_to_error_type(ctx.first);
let (type2, _problems2) = subs.var_to_error_type(ctx.second);
println!("\n --------------- \n");
dbg!(ctx.first, type1);
println!("\n --- \n");
dbg!(ctx.second, type2);
println!("\n --------------- \n");
}
match &ctx.first_desc.content {
FlexVar(opt_name) => unify_flex(subs, pool, &ctx, opt_name, &ctx.second_desc.content),
RigidVar(name) => unify_rigid(subs, &ctx, name, &ctx.second_desc.content),
@ -218,7 +227,8 @@ fn unify_structure(
}
RigidVar(name) => {
// Type mismatch! Rigid can only unify with flex.
mismatch!("trying to unify {:?} with rigid var {:?}", &flat_type, name)
mismatch!("trying to unify {:?} with rigid var {:?}", &flat_type, name);
panic!()
}
Structure(ref other_flat_type) => {