mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Merge branch 'explicit-closed-tag-record' into delay-instantiating-aliases
This commit is contained in:
commit
a3b00fbf55
29 changed files with 691 additions and 223 deletions
|
@ -2,3 +2,9 @@
|
||||||
test-gen-llvm = "test -p test_gen"
|
test-gen-llvm = "test -p test_gen"
|
||||||
test-gen-dev = "test -p roc_gen_dev -p test_gen --no-default-features --features gen-dev"
|
test-gen-dev = "test -p roc_gen_dev -p test_gen --no-default-features --features gen-dev"
|
||||||
test-gen-wasm = "test -p roc_gen_wasm -p test_gen --no-default-features --features gen-wasm"
|
test-gen-wasm = "test -p roc_gen_wasm -p test_gen --no-default-features --features gen-wasm"
|
||||||
|
|
||||||
|
[target.wasm32-unknown-unknown]
|
||||||
|
# Rust compiler flags for minimum-sized .wasm binary in the web REPL
|
||||||
|
# opt-level=s Optimizations should focus more on size than speed
|
||||||
|
# lto=fat Spend extra effort on link-time optimization across crates
|
||||||
|
rustflags = ["-Copt-level=s", "-Clto=fat"]
|
||||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3762,6 +3762,7 @@ dependencies = [
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
|
"roc_types",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -7,7 +7,9 @@ use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||||
use roc_parse::ast::{AssignedField, Pattern, Tag, TypeAnnotation, TypeHeader};
|
use roc_parse::ast::{AssignedField, Pattern, Tag, TypeAnnotation, TypeHeader};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::{Alias, AliasCommon, AliasKind, LambdaSet, Problem, RecordField, Type};
|
use roc_types::types::{
|
||||||
|
Alias, AliasCommon, AliasKind, LambdaSet, Problem, RecordField, Type, TypeExtension,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Annotation {
|
pub struct Annotation {
|
||||||
|
@ -29,25 +31,27 @@ pub struct IntroducedVariables {
|
||||||
// But then between annotations, the same name can occur multiple times,
|
// But then between annotations, the same name can occur multiple times,
|
||||||
// but a variable can only have one name. Therefore
|
// but a variable can only have one name. Therefore
|
||||||
// `ftv : SendMap<Variable, Lowercase>`.
|
// `ftv : SendMap<Variable, Lowercase>`.
|
||||||
pub wildcards: Vec<Variable>,
|
pub wildcards: Vec<Loc<Variable>>,
|
||||||
pub lambda_sets: Vec<Variable>,
|
pub lambda_sets: Vec<Variable>,
|
||||||
pub inferred: Vec<Variable>,
|
pub inferred: Vec<Loc<Variable>>,
|
||||||
pub var_by_name: SendMap<Lowercase, Variable>,
|
// NB: A mapping of a -> Loc<v1> in this map has the region of the first-seen var, but there
|
||||||
|
// may be multiple occurrences of it!
|
||||||
|
pub var_by_name: SendMap<Lowercase, Loc<Variable>>,
|
||||||
pub name_by_var: SendMap<Variable, Lowercase>,
|
pub name_by_var: SendMap<Variable, Lowercase>,
|
||||||
pub host_exposed_aliases: MutMap<Symbol, Variable>,
|
pub host_exposed_aliases: MutMap<Symbol, Variable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntroducedVariables {
|
impl IntroducedVariables {
|
||||||
pub fn insert_named(&mut self, name: Lowercase, var: Variable) {
|
pub fn insert_named(&mut self, name: Lowercase, var: Loc<Variable>) {
|
||||||
self.var_by_name.insert(name.clone(), var);
|
self.var_by_name.insert(name.clone(), var);
|
||||||
self.name_by_var.insert(var, name);
|
self.name_by_var.insert(var.value, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_wildcard(&mut self, var: Variable) {
|
pub fn insert_wildcard(&mut self, var: Loc<Variable>) {
|
||||||
self.wildcards.push(var);
|
self.wildcards.push(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_inferred(&mut self, var: Variable) {
|
pub fn insert_inferred(&mut self, var: Loc<Variable>) {
|
||||||
self.inferred.push(var);
|
self.inferred.push(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +74,7 @@ impl IntroducedVariables {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn var_by_name(&self, name: &Lowercase) -> Option<&Variable> {
|
pub fn var_by_name(&self, name: &Lowercase) -> Option<&Variable> {
|
||||||
self.var_by_name.get(name)
|
self.var_by_name.get(name).map(|v| &v.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name_by_var(&self, var: Variable) -> Option<&Lowercase> {
|
pub fn name_by_var(&self, var: Variable) -> Option<&Lowercase> {
|
||||||
|
@ -286,7 +290,7 @@ fn can_annotation_help(
|
||||||
let ret = can_annotation_help(
|
let ret = can_annotation_help(
|
||||||
env,
|
env,
|
||||||
&return_type.value,
|
&return_type.value,
|
||||||
region,
|
return_type.region,
|
||||||
scope,
|
scope,
|
||||||
var_store,
|
var_store,
|
||||||
introduced_variables,
|
introduced_variables,
|
||||||
|
@ -314,7 +318,7 @@ fn can_annotation_help(
|
||||||
let arg_ann = can_annotation_help(
|
let arg_ann = can_annotation_help(
|
||||||
env,
|
env,
|
||||||
&arg.value,
|
&arg.value,
|
||||||
region,
|
arg.region,
|
||||||
scope,
|
scope,
|
||||||
var_store,
|
var_store,
|
||||||
introduced_variables,
|
introduced_variables,
|
||||||
|
@ -389,7 +393,7 @@ fn can_annotation_help(
|
||||||
None => {
|
None => {
|
||||||
let var = var_store.fresh();
|
let var = var_store.fresh();
|
||||||
|
|
||||||
introduced_variables.insert_named(name, var);
|
introduced_variables.insert_named(name, Loc::at(region, var));
|
||||||
|
|
||||||
Type::Variable(var)
|
Type::Variable(var)
|
||||||
}
|
}
|
||||||
|
@ -453,7 +457,8 @@ fn can_annotation_help(
|
||||||
} else {
|
} else {
|
||||||
let var = var_store.fresh();
|
let var = var_store.fresh();
|
||||||
|
|
||||||
introduced_variables.insert_named(var_name.clone(), var);
|
introduced_variables
|
||||||
|
.insert_named(var_name.clone(), Loc::at(loc_var.region, var));
|
||||||
vars.push((var_name.clone(), Type::Variable(var)));
|
vars.push((var_name.clone(), Type::Variable(var)));
|
||||||
|
|
||||||
lowercase_vars.push(Loc::at(loc_var.region, (var_name, var)));
|
lowercase_vars.push(Loc::at(loc_var.region, (var_name, var)));
|
||||||
|
@ -560,7 +565,7 @@ fn can_annotation_help(
|
||||||
// just `a` does not mean the same as `{}a`, so even
|
// just `a` does not mean the same as `{}a`, so even
|
||||||
// if there are no fields, still make this a `Record`,
|
// if there are no fields, still make this a `Record`,
|
||||||
// not an EmptyRec
|
// not an EmptyRec
|
||||||
Type::Record(Default::default(), Box::new(ext_type))
|
Type::Record(Default::default(), TypeExtension::from_type(ext_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
None => Type::EmptyRec,
|
None => Type::EmptyRec,
|
||||||
|
@ -577,7 +582,7 @@ fn can_annotation_help(
|
||||||
references,
|
references,
|
||||||
);
|
);
|
||||||
|
|
||||||
Type::Record(field_types, Box::new(ext_type))
|
Type::Record(field_types, TypeExtension::from_type(ext_type))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TagUnion { tags, ext, .. } => {
|
TagUnion { tags, ext, .. } => {
|
||||||
|
@ -598,7 +603,7 @@ fn can_annotation_help(
|
||||||
// just `a` does not mean the same as `{}a`, so even
|
// just `a` does not mean the same as `{}a`, so even
|
||||||
// if there are no fields, still make this a `Record`,
|
// if there are no fields, still make this a `Record`,
|
||||||
// not an EmptyRec
|
// not an EmptyRec
|
||||||
Type::TagUnion(Default::default(), Box::new(ext_type))
|
Type::TagUnion(Default::default(), TypeExtension::from_type(ext_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
None => Type::EmptyTagUnion,
|
None => Type::EmptyTagUnion,
|
||||||
|
@ -620,7 +625,7 @@ fn can_annotation_help(
|
||||||
// in theory we save a lot of time by sorting once here
|
// in theory we save a lot of time by sorting once here
|
||||||
insertion_sort_by(&mut tag_types, |a, b| a.0.cmp(&b.0));
|
insertion_sort_by(&mut tag_types, |a, b| a.0.cmp(&b.0));
|
||||||
|
|
||||||
Type::TagUnion(tag_types, Box::new(ext_type))
|
Type::TagUnion(tag_types, TypeExtension::from_type(ext_type))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SpaceBefore(nested, _) | SpaceAfter(nested, _) => can_annotation_help(
|
SpaceBefore(nested, _) | SpaceAfter(nested, _) => can_annotation_help(
|
||||||
|
@ -636,7 +641,7 @@ fn can_annotation_help(
|
||||||
Wildcard => {
|
Wildcard => {
|
||||||
let var = var_store.fresh();
|
let var = var_store.fresh();
|
||||||
|
|
||||||
introduced_variables.insert_wildcard(var);
|
introduced_variables.insert_wildcard(Loc::at(region, var));
|
||||||
|
|
||||||
Type::Variable(var)
|
Type::Variable(var)
|
||||||
}
|
}
|
||||||
|
@ -645,7 +650,7 @@ fn can_annotation_help(
|
||||||
// make a fresh unconstrained variable, and let the type solver fill it in for us 🤠
|
// make a fresh unconstrained variable, and let the type solver fill it in for us 🤠
|
||||||
let var = var_store.fresh();
|
let var = var_store.fresh();
|
||||||
|
|
||||||
introduced_variables.insert_inferred(var);
|
introduced_variables.insert_inferred(Loc::at(region, var));
|
||||||
|
|
||||||
Type::Variable(var)
|
Type::Variable(var)
|
||||||
}
|
}
|
||||||
|
@ -655,7 +660,7 @@ fn can_annotation_help(
|
||||||
|
|
||||||
let var = var_store.fresh();
|
let var = var_store.fresh();
|
||||||
|
|
||||||
introduced_variables.insert_wildcard(var);
|
introduced_variables.insert_wildcard(Loc::at(region, var));
|
||||||
|
|
||||||
Type::Variable(var)
|
Type::Variable(var)
|
||||||
}
|
}
|
||||||
|
@ -721,7 +726,7 @@ fn can_extension_type<'a>(
|
||||||
|
|
||||||
let var = var_store.fresh();
|
let var = var_store.fresh();
|
||||||
|
|
||||||
introduced_variables.insert_inferred(var);
|
introduced_variables.insert_inferred(Loc::at_zero(var));
|
||||||
|
|
||||||
Type::Variable(var)
|
Type::Variable(var)
|
||||||
}
|
}
|
||||||
|
@ -913,7 +918,10 @@ fn can_assigned_fields<'a>(
|
||||||
Type::Variable(*var)
|
Type::Variable(*var)
|
||||||
} else {
|
} else {
|
||||||
let field_var = var_store.fresh();
|
let field_var = var_store.fresh();
|
||||||
introduced_variables.insert_named(field_name.clone(), field_var);
|
introduced_variables.insert_named(
|
||||||
|
field_name.clone(),
|
||||||
|
Loc::at(loc_field_name.region, field_var),
|
||||||
|
);
|
||||||
Type::Variable(field_var)
|
Type::Variable(field_var)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -294,14 +294,12 @@ pub fn canonicalize_defs<'a>(
|
||||||
let mut can_vars: Vec<Loc<(Lowercase, Variable)>> = Vec::with_capacity(vars.len());
|
let mut can_vars: Vec<Loc<(Lowercase, Variable)>> = Vec::with_capacity(vars.len());
|
||||||
let mut is_phantom = false;
|
let mut is_phantom = false;
|
||||||
|
|
||||||
|
let mut var_by_name = can_ann.introduced_variables.var_by_name.clone();
|
||||||
for loc_lowercase in vars.iter() {
|
for loc_lowercase in vars.iter() {
|
||||||
if let Some(var) = can_ann
|
if let Some(var) = var_by_name.remove(&loc_lowercase.value) {
|
||||||
.introduced_variables
|
|
||||||
.var_by_name(&loc_lowercase.value)
|
|
||||||
{
|
|
||||||
// This is a valid lowercase rigid var for the type def.
|
// This is a valid lowercase rigid var for the type def.
|
||||||
can_vars.push(Loc {
|
can_vars.push(Loc {
|
||||||
value: (loc_lowercase.value.clone(), *var),
|
value: (loc_lowercase.value.clone(), var.value),
|
||||||
region: loc_lowercase.region,
|
region: loc_lowercase.region,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -320,6 +318,33 @@ pub fn canonicalize_defs<'a>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let IntroducedVariables {
|
||||||
|
wildcards,
|
||||||
|
inferred,
|
||||||
|
..
|
||||||
|
} = can_ann.introduced_variables;
|
||||||
|
let num_unbound = var_by_name.len() + wildcards.len() + inferred.len();
|
||||||
|
if num_unbound > 0 {
|
||||||
|
let one_occurrence = var_by_name
|
||||||
|
.iter()
|
||||||
|
.map(|(_, v)| v)
|
||||||
|
.chain(wildcards.iter())
|
||||||
|
.chain(inferred.iter())
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.region;
|
||||||
|
|
||||||
|
env.problems.push(Problem::UnboundTypeVariable {
|
||||||
|
typ: symbol,
|
||||||
|
num_unbound,
|
||||||
|
one_occurrence,
|
||||||
|
kind,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bail out
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let alias = create_alias(
|
let alias = create_alias(
|
||||||
symbol,
|
symbol,
|
||||||
name.region,
|
name.region,
|
||||||
|
|
|
@ -10,7 +10,7 @@ use roc_module::ident::TagName;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::{AliasKind, Type};
|
use roc_types::types::{AliasKind, Type, TypeExtension};
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy)]
|
#[derive(Default, Clone, Copy)]
|
||||||
pub(crate) struct HostedGeneratedFunctions {
|
pub(crate) struct HostedGeneratedFunctions {
|
||||||
|
@ -210,7 +210,7 @@ fn build_effect_always(
|
||||||
let signature = {
|
let signature = {
|
||||||
// Effect.always : a -> Effect a
|
// Effect.always : a -> Effect a
|
||||||
let var_a = var_store.fresh();
|
let var_a = var_store.fresh();
|
||||||
introduced_variables.insert_named("a".into(), var_a);
|
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||||
|
|
||||||
let effect_a = build_effect_alias(
|
let effect_a = build_effect_alias(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
|
@ -223,7 +223,7 @@ fn build_effect_always(
|
||||||
);
|
);
|
||||||
|
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(closure_var);
|
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||||
|
|
||||||
Type::Function(
|
Type::Function(
|
||||||
vec![Type::Variable(var_a)],
|
vec![Type::Variable(var_a)],
|
||||||
|
@ -402,8 +402,8 @@ fn build_effect_map(
|
||||||
let var_a = var_store.fresh();
|
let var_a = var_store.fresh();
|
||||||
let var_b = var_store.fresh();
|
let var_b = var_store.fresh();
|
||||||
|
|
||||||
introduced_variables.insert_named("a".into(), var_a);
|
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||||
introduced_variables.insert_named("b".into(), var_b);
|
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
||||||
|
|
||||||
let effect_a = build_effect_alias(
|
let effect_a = build_effect_alias(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
|
@ -426,7 +426,7 @@ fn build_effect_map(
|
||||||
);
|
);
|
||||||
|
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(closure_var);
|
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||||
let a_to_b = {
|
let a_to_b = {
|
||||||
Type::Function(
|
Type::Function(
|
||||||
vec![Type::Variable(var_a)],
|
vec![Type::Variable(var_a)],
|
||||||
|
@ -436,7 +436,7 @@ fn build_effect_map(
|
||||||
};
|
};
|
||||||
|
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(closure_var);
|
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||||
Type::Function(
|
Type::Function(
|
||||||
vec![effect_a, a_to_b],
|
vec![effect_a, a_to_b],
|
||||||
Box::new(Type::Variable(closure_var)),
|
Box::new(Type::Variable(closure_var)),
|
||||||
|
@ -571,8 +571,8 @@ fn build_effect_after(
|
||||||
let var_a = var_store.fresh();
|
let var_a = var_store.fresh();
|
||||||
let var_b = var_store.fresh();
|
let var_b = var_store.fresh();
|
||||||
|
|
||||||
introduced_variables.insert_named("a".into(), var_a);
|
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||||
introduced_variables.insert_named("b".into(), var_b);
|
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
||||||
|
|
||||||
let effect_a = build_effect_alias(
|
let effect_a = build_effect_alias(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
|
@ -595,7 +595,7 @@ fn build_effect_after(
|
||||||
);
|
);
|
||||||
|
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(closure_var);
|
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||||
let a_to_effect_b = Type::Function(
|
let a_to_effect_b = Type::Function(
|
||||||
vec![Type::Variable(var_a)],
|
vec![Type::Variable(var_a)],
|
||||||
Box::new(Type::Variable(closure_var)),
|
Box::new(Type::Variable(closure_var)),
|
||||||
|
@ -603,7 +603,7 @@ fn build_effect_after(
|
||||||
);
|
);
|
||||||
|
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(closure_var);
|
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||||
Type::Function(
|
Type::Function(
|
||||||
vec![effect_a, a_to_effect_b],
|
vec![effect_a, a_to_effect_b],
|
||||||
Box::new(Type::Variable(closure_var)),
|
Box::new(Type::Variable(closure_var)),
|
||||||
|
@ -831,8 +831,8 @@ fn build_effect_forever(
|
||||||
let var_a = var_store.fresh();
|
let var_a = var_store.fresh();
|
||||||
let var_b = var_store.fresh();
|
let var_b = var_store.fresh();
|
||||||
|
|
||||||
introduced_variables.insert_named("a".into(), var_a);
|
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||||
introduced_variables.insert_named("b".into(), var_b);
|
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
||||||
|
|
||||||
let effect_a = build_effect_alias(
|
let effect_a = build_effect_alias(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
|
@ -855,7 +855,7 @@ fn build_effect_forever(
|
||||||
);
|
);
|
||||||
|
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(closure_var);
|
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||||
|
|
||||||
Type::Function(
|
Type::Function(
|
||||||
vec![effect_a],
|
vec![effect_a],
|
||||||
|
@ -1089,8 +1089,8 @@ fn build_effect_loop(
|
||||||
let var_a = var_store.fresh();
|
let var_a = var_store.fresh();
|
||||||
let var_b = var_store.fresh();
|
let var_b = var_store.fresh();
|
||||||
|
|
||||||
introduced_variables.insert_named("a".into(), var_a);
|
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||||
introduced_variables.insert_named("b".into(), var_b);
|
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
||||||
|
|
||||||
let effect_b = build_effect_alias(
|
let effect_b = build_effect_alias(
|
||||||
effect_symbol,
|
effect_symbol,
|
||||||
|
@ -1111,13 +1111,13 @@ fn build_effect_loop(
|
||||||
(step_tag_name, vec![Type::Variable(var_a)]),
|
(step_tag_name, vec![Type::Variable(var_a)]),
|
||||||
(done_tag_name, vec![Type::Variable(var_b)]),
|
(done_tag_name, vec![Type::Variable(var_b)]),
|
||||||
],
|
],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let effect_state_type = {
|
let effect_state_type = {
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(closure_var);
|
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||||
|
|
||||||
let actual = {
|
let actual = {
|
||||||
Type::TagUnion(
|
Type::TagUnion(
|
||||||
|
@ -1129,7 +1129,7 @@ fn build_effect_loop(
|
||||||
Box::new(state_type.clone()),
|
Box::new(state_type.clone()),
|
||||||
)],
|
)],
|
||||||
)],
|
)],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1145,7 +1145,7 @@ fn build_effect_loop(
|
||||||
};
|
};
|
||||||
|
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(closure_var);
|
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||||
|
|
||||||
let step_type = Type::Function(
|
let step_type = Type::Function(
|
||||||
vec![Type::Variable(var_a)],
|
vec![Type::Variable(var_a)],
|
||||||
|
@ -1154,7 +1154,7 @@ fn build_effect_loop(
|
||||||
);
|
);
|
||||||
|
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(closure_var);
|
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||||
|
|
||||||
Type::Function(
|
Type::Function(
|
||||||
vec![Type::Variable(var_a), step_type],
|
vec![Type::Variable(var_a), step_type],
|
||||||
|
@ -1559,7 +1559,7 @@ fn build_effect_alias(
|
||||||
introduced_variables: &mut IntroducedVariables,
|
introduced_variables: &mut IntroducedVariables,
|
||||||
) -> Type {
|
) -> Type {
|
||||||
let closure_var = var_store.fresh();
|
let closure_var = var_store.fresh();
|
||||||
introduced_variables.insert_wildcard(closure_var);
|
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||||
|
|
||||||
let actual = {
|
let actual = {
|
||||||
Type::TagUnion(
|
Type::TagUnion(
|
||||||
|
@ -1571,7 +1571,7 @@ fn build_effect_alias(
|
||||||
Box::new(a_type),
|
Box::new(a_type),
|
||||||
)],
|
)],
|
||||||
)],
|
)],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1600,7 +1600,7 @@ pub fn build_effect_actual(
|
||||||
Box::new(a_type),
|
Box::new(a_type),
|
||||||
)],
|
)],
|
||||||
)],
|
)],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -259,7 +259,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
for var in output.introduced_variables.wildcards {
|
for var in output.introduced_variables.wildcards {
|
||||||
rigid_variables.wildcards.insert(var);
|
rigid_variables.wildcards.insert(var.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut referenced_values = MutSet::default();
|
let mut referenced_values = MutSet::default();
|
||||||
|
|
|
@ -6,9 +6,9 @@ use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
use roc_types::types::Reason;
|
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{AliasKind, Category};
|
use roc_types::types::{AliasKind, Category};
|
||||||
|
use roc_types::types::{Reason, TypeExtension};
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -190,7 +190,7 @@ pub fn num_floatingpoint(range: Type) -> Type {
|
||||||
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
|
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
|
||||||
vec![range.clone()],
|
vec![range.clone()],
|
||||||
)],
|
)],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
);
|
);
|
||||||
|
|
||||||
builtin_alias(
|
builtin_alias(
|
||||||
|
@ -209,7 +209,7 @@ pub fn num_u32() -> Type {
|
||||||
fn num_unsigned32() -> Type {
|
fn num_unsigned32() -> Type {
|
||||||
let alias_content = Type::TagUnion(
|
let alias_content = Type::TagUnion(
|
||||||
vec![(TagName::Private(Symbol::NUM_AT_UNSIGNED32), vec![])],
|
vec![(TagName::Private(Symbol::NUM_AT_UNSIGNED32), vec![])],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
);
|
);
|
||||||
|
|
||||||
builtin_alias(Symbol::NUM_UNSIGNED32, vec![], Box::new(alias_content))
|
builtin_alias(Symbol::NUM_UNSIGNED32, vec![], Box::new(alias_content))
|
||||||
|
@ -219,7 +219,7 @@ fn num_unsigned32() -> Type {
|
||||||
pub fn num_binary64() -> Type {
|
pub fn num_binary64() -> Type {
|
||||||
let alias_content = Type::TagUnion(
|
let alias_content = Type::TagUnion(
|
||||||
vec![(TagName::Private(Symbol::NUM_AT_BINARY64), vec![])],
|
vec![(TagName::Private(Symbol::NUM_AT_BINARY64), vec![])],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
);
|
);
|
||||||
|
|
||||||
builtin_alias(Symbol::NUM_BINARY64, vec![], Box::new(alias_content))
|
builtin_alias(Symbol::NUM_BINARY64, vec![], Box::new(alias_content))
|
||||||
|
@ -238,7 +238,7 @@ pub fn num_int(range: Type) -> Type {
|
||||||
pub fn num_signed64() -> Type {
|
pub fn num_signed64() -> Type {
|
||||||
let alias_content = Type::TagUnion(
|
let alias_content = Type::TagUnion(
|
||||||
vec![(TagName::Private(Symbol::NUM_AT_SIGNED64), vec![])],
|
vec![(TagName::Private(Symbol::NUM_AT_SIGNED64), vec![])],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
);
|
);
|
||||||
|
|
||||||
builtin_alias(Symbol::NUM_SIGNED64, vec![], Box::new(alias_content))
|
builtin_alias(Symbol::NUM_SIGNED64, vec![], Box::new(alias_content))
|
||||||
|
@ -251,7 +251,7 @@ pub fn num_integer(range: Type) -> Type {
|
||||||
TagName::Private(Symbol::NUM_AT_INTEGER),
|
TagName::Private(Symbol::NUM_AT_INTEGER),
|
||||||
vec![range.clone()],
|
vec![range.clone()],
|
||||||
)],
|
)],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
);
|
);
|
||||||
|
|
||||||
builtin_alias(
|
builtin_alias(
|
||||||
|
@ -265,7 +265,7 @@ pub fn num_integer(range: Type) -> Type {
|
||||||
pub fn num_num(typ: Type) -> Type {
|
pub fn num_num(typ: Type) -> Type {
|
||||||
let alias_content = Type::TagUnion(
|
let alias_content = Type::TagUnion(
|
||||||
vec![(TagName::Private(Symbol::NUM_AT_NUM), vec![typ.clone()])],
|
vec![(TagName::Private(Symbol::NUM_AT_NUM), vec![typ.clone()])],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
);
|
);
|
||||||
|
|
||||||
builtin_alias(
|
builtin_alias(
|
||||||
|
|
|
@ -16,7 +16,9 @@ use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{AliasKind, AnnotationSource, Category, PReason, Reason, RecordField};
|
use roc_types::types::{
|
||||||
|
AliasKind, AnnotationSource, Category, PReason, Reason, RecordField, TypeExtension,
|
||||||
|
};
|
||||||
|
|
||||||
/// This is for constraining Defs
|
/// This is for constraining Defs
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
@ -119,13 +121,7 @@ pub fn constrain_expr(
|
||||||
rec_constraints.push(field_con);
|
rec_constraints.push(field_con);
|
||||||
}
|
}
|
||||||
|
|
||||||
let record_type = Type::Record(
|
let record_type = Type::Record(field_types, TypeExtension::Closed);
|
||||||
field_types,
|
|
||||||
// TODO can we avoid doing Box::new on every single one of these?
|
|
||||||
// We can put `static EMPTY_REC: Type = Type::EmptyRec`, but that requires a
|
|
||||||
// lifetime parameter on `Type`
|
|
||||||
Box::new(Type::EmptyRec),
|
|
||||||
);
|
|
||||||
|
|
||||||
let record_con = constraints.equal_types_with_storage(
|
let record_con = constraints.equal_types_with_storage(
|
||||||
record_type,
|
record_type,
|
||||||
|
@ -165,7 +161,8 @@ pub fn constrain_expr(
|
||||||
cons.push(con);
|
cons.push(con);
|
||||||
}
|
}
|
||||||
|
|
||||||
let fields_type = Type::Record(fields, Box::new(Type::Variable(*ext_var)));
|
let fields_type =
|
||||||
|
Type::Record(fields, TypeExtension::from_type(Type::Variable(*ext_var)));
|
||||||
let record_type = Type::Variable(*record_var);
|
let record_type = Type::Variable(*record_var);
|
||||||
|
|
||||||
// NOTE from elm compiler: fields_type is separate so that Error propagates better
|
// NOTE from elm compiler: fields_type is separate so that Error propagates better
|
||||||
|
@ -720,7 +717,7 @@ pub fn constrain_expr(
|
||||||
let label = field.clone();
|
let label = field.clone();
|
||||||
rec_field_types.insert(label, RecordField::Demanded(field_type));
|
rec_field_types.insert(label, RecordField::Demanded(field_type));
|
||||||
|
|
||||||
let record_type = Type::Record(rec_field_types, Box::new(ext_type));
|
let record_type = Type::Record(rec_field_types, TypeExtension::from_type(ext_type));
|
||||||
let record_expected = Expected::NoExpectation(record_type);
|
let record_expected = Expected::NoExpectation(record_type);
|
||||||
|
|
||||||
let category = Category::Access(field.clone());
|
let category = Category::Access(field.clone());
|
||||||
|
@ -767,7 +764,7 @@ pub fn constrain_expr(
|
||||||
let mut field_types = SendMap::default();
|
let mut field_types = SendMap::default();
|
||||||
let label = field.clone();
|
let label = field.clone();
|
||||||
field_types.insert(label, RecordField::Demanded(field_type.clone()));
|
field_types.insert(label, RecordField::Demanded(field_type.clone()));
|
||||||
let record_type = Type::Record(field_types, Box::new(ext_type));
|
let record_type = Type::Record(field_types, TypeExtension::from_type(ext_type));
|
||||||
|
|
||||||
let category = Category::Accessor(field.clone());
|
let category = Category::Accessor(field.clone());
|
||||||
|
|
||||||
|
@ -905,7 +902,7 @@ pub fn constrain_expr(
|
||||||
let union_con = constraints.equal_types_with_storage(
|
let union_con = constraints.equal_types_with_storage(
|
||||||
Type::TagUnion(
|
Type::TagUnion(
|
||||||
vec![(name.clone(), types)],
|
vec![(name.clone(), types)],
|
||||||
Box::new(Type::Variable(*ext_var)),
|
TypeExtension::from_type(Type::Variable(*ext_var)),
|
||||||
),
|
),
|
||||||
expected.clone(),
|
expected.clone(),
|
||||||
Category::TagApply {
|
Category::TagApply {
|
||||||
|
@ -951,7 +948,7 @@ pub fn constrain_expr(
|
||||||
Type::FunctionOrTagUnion(
|
Type::FunctionOrTagUnion(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
*closure_name,
|
*closure_name,
|
||||||
Box::new(Type::Variable(*ext_var)),
|
TypeExtension::from_type(Type::Variable(*ext_var)),
|
||||||
),
|
),
|
||||||
expected.clone(),
|
expected.clone(),
|
||||||
Category::TagApply {
|
Category::TagApply {
|
||||||
|
@ -1621,7 +1618,7 @@ fn constrain_closure_size(
|
||||||
let tag_name = TagName::Closure(name);
|
let tag_name = TagName::Closure(name);
|
||||||
Type::TagUnion(
|
Type::TagUnion(
|
||||||
vec![(tag_name, tag_arguments)],
|
vec![(tag_name, tag_arguments)],
|
||||||
Box::new(Type::Variable(closure_ext_var)),
|
TypeExtension::from_type(Type::Variable(closure_ext_var)),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1651,7 +1648,7 @@ fn instantiate_rigids(
|
||||||
headers: &mut SendMap<Symbol, Loc<Type>>,
|
headers: &mut SendMap<Symbol, Loc<Type>>,
|
||||||
) -> InstantiateRigids {
|
) -> InstantiateRigids {
|
||||||
let mut annotation = annotation.clone();
|
let mut annotation = annotation.clone();
|
||||||
let mut new_rigid_variables = Vec::new();
|
let mut new_rigid_variables: Vec<Variable> = Vec::new();
|
||||||
|
|
||||||
let mut rigid_substitution: ImMap<Variable, Type> = ImMap::default();
|
let mut rigid_substitution: ImMap<Variable, Type> = ImMap::default();
|
||||||
for (name, var) in introduced_vars.var_by_name.iter() {
|
for (name, var) in introduced_vars.var_by_name.iter() {
|
||||||
|
@ -1660,23 +1657,24 @@ fn instantiate_rigids(
|
||||||
match ftv.entry(name.clone()) {
|
match ftv.entry(name.clone()) {
|
||||||
Occupied(occupied) => {
|
Occupied(occupied) => {
|
||||||
let existing_rigid = occupied.get();
|
let existing_rigid = occupied.get();
|
||||||
rigid_substitution.insert(*var, Type::Variable(*existing_rigid));
|
rigid_substitution.insert(var.value, Type::Variable(*existing_rigid));
|
||||||
}
|
}
|
||||||
Vacant(vacant) => {
|
Vacant(vacant) => {
|
||||||
// It's possible to use this rigid in nested defs
|
// It's possible to use this rigid in nested defs
|
||||||
vacant.insert(*var);
|
vacant.insert(var.value);
|
||||||
new_rigid_variables.push(*var);
|
new_rigid_variables.push(var.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// wildcards are always freshly introduced in this annotation
|
// wildcards are always freshly introduced in this annotation
|
||||||
new_rigid_variables.extend(introduced_vars.wildcards.iter().copied());
|
new_rigid_variables.extend(introduced_vars.wildcards.iter().map(|v| v.value));
|
||||||
|
|
||||||
// lambda set vars are always freshly introduced in this annotation
|
// lambda set vars are always freshly introduced in this annotation
|
||||||
new_rigid_variables.extend(introduced_vars.lambda_sets.iter().copied());
|
new_rigid_variables.extend(introduced_vars.lambda_sets.iter().copied());
|
||||||
|
|
||||||
let new_infer_variables = introduced_vars.inferred.clone();
|
let new_infer_variables: Vec<Variable> =
|
||||||
|
introduced_vars.inferred.iter().map(|v| v.value).collect();
|
||||||
|
|
||||||
// Instantiate rigid variables
|
// Instantiate rigid variables
|
||||||
if !rigid_substitution.is_empty() {
|
if !rigid_substitution.is_empty() {
|
||||||
|
|
|
@ -9,7 +9,9 @@ use roc_module::ident::Lowercase;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
use roc_types::types::{AliasKind, Category, PReason, PatternCategory, Reason, RecordField, Type};
|
use roc_types::types::{
|
||||||
|
AliasKind, Category, PReason, PatternCategory, Reason, RecordField, Type, TypeExtension,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct PatternState {
|
pub struct PatternState {
|
||||||
|
@ -391,7 +393,7 @@ pub fn constrain_pattern(
|
||||||
state.vars.push(*var);
|
state.vars.push(*var);
|
||||||
}
|
}
|
||||||
|
|
||||||
let record_type = Type::Record(field_types, Box::new(ext_type));
|
let record_type = Type::Record(field_types, TypeExtension::from_type(ext_type));
|
||||||
|
|
||||||
let whole_con = constraints.equal_types(
|
let whole_con = constraints.equal_types(
|
||||||
Type::Variable(*whole_var),
|
Type::Variable(*whole_var),
|
||||||
|
|
|
@ -36,7 +36,7 @@ use roc_solve::solve;
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use roc_types::solved_types::Solved;
|
use roc_types::solved_types::Solved;
|
||||||
use roc_types::subs::{Subs, VarStore, Variable};
|
use roc_types::subs::{Subs, VarStore, Variable};
|
||||||
use roc_types::types::{Alias, AliasCommon};
|
use roc_types::types::{Alias, AliasCommon, TypeExtension};
|
||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -4123,7 +4123,7 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
||||||
TagName::Private(Symbol::NUM_AT_NUM),
|
TagName::Private(Symbol::NUM_AT_NUM),
|
||||||
vec![Type::Variable(tvar)],
|
vec![Type::Variable(tvar)],
|
||||||
)],
|
)],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
);
|
);
|
||||||
|
|
||||||
let alias = Alias {
|
let alias = Alias {
|
||||||
|
@ -4148,7 +4148,7 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
||||||
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
|
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
|
||||||
vec![Type::Variable(tvar)],
|
vec![Type::Variable(tvar)],
|
||||||
)],
|
)],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
);
|
);
|
||||||
|
|
||||||
let alias = Alias {
|
let alias = Alias {
|
||||||
|
@ -4231,7 +4231,7 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
||||||
TagName::Private(Symbol::NUM_AT_INTEGER),
|
TagName::Private(Symbol::NUM_AT_INTEGER),
|
||||||
vec![Type::Variable(tvar)],
|
vec![Type::Variable(tvar)],
|
||||||
)],
|
)],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
);
|
);
|
||||||
|
|
||||||
let alias = Alias {
|
let alias = Alias {
|
||||||
|
@ -4256,7 +4256,7 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
||||||
(TagName::Global("Ok".into()), vec![Type::Variable(tvar1)]),
|
(TagName::Global("Ok".into()), vec![Type::Variable(tvar1)]),
|
||||||
(TagName::Global("Err".into()), vec![Type::Variable(tvar2)]),
|
(TagName::Global("Err".into()), vec![Type::Variable(tvar2)]),
|
||||||
],
|
],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
);
|
);
|
||||||
|
|
||||||
let alias = Alias {
|
let alias = Alias {
|
||||||
|
@ -4277,7 +4277,7 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
||||||
let mut unit_function = |alias_name: Symbol, at_tag_name: Symbol| {
|
let mut unit_function = |alias_name: Symbol, at_tag_name: Symbol| {
|
||||||
let typ = Type::TagUnion(
|
let typ = Type::TagUnion(
|
||||||
vec![(TagName::Private(at_tag_name), vec![])],
|
vec![(TagName::Private(at_tag_name), vec![])],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
);
|
);
|
||||||
|
|
||||||
let alias = Alias {
|
let alias = Alias {
|
||||||
|
|
|
@ -112,6 +112,11 @@ pub struct PartialProcs<'a> {
|
||||||
/// maps a function name (symbol) to an index
|
/// maps a function name (symbol) to an index
|
||||||
symbols: Vec<'a, Symbol>,
|
symbols: Vec<'a, Symbol>,
|
||||||
|
|
||||||
|
/// An entry (a, b) means `a` directly references the lambda value of `b`,
|
||||||
|
/// i.e. this came from a `let a = b in ...` where `b` was defined as a
|
||||||
|
/// lambda earlier.
|
||||||
|
references: Vec<'a, (Symbol, Symbol)>,
|
||||||
|
|
||||||
partial_procs: Vec<'a, PartialProc<'a>>,
|
partial_procs: Vec<'a, PartialProc<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +124,7 @@ impl<'a> PartialProcs<'a> {
|
||||||
fn new_in(arena: &'a Bump) -> Self {
|
fn new_in(arena: &'a Bump) -> Self {
|
||||||
Self {
|
Self {
|
||||||
symbols: Vec::new_in(arena),
|
symbols: Vec::new_in(arena),
|
||||||
|
references: Vec::new_in(arena),
|
||||||
partial_procs: Vec::new_in(arena),
|
partial_procs: Vec::new_in(arena),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,7 +132,16 @@ impl<'a> PartialProcs<'a> {
|
||||||
self.symbol_to_id(symbol).is_some()
|
self.symbol_to_id(symbol).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn symbol_to_id(&self, symbol: Symbol) -> Option<PartialProcId> {
|
fn symbol_to_id(&self, mut symbol: Symbol) -> Option<PartialProcId> {
|
||||||
|
while let Some(real_symbol) = self
|
||||||
|
.references
|
||||||
|
.iter()
|
||||||
|
.find(|(alias, _)| *alias == symbol)
|
||||||
|
.map(|(_, real)| real)
|
||||||
|
{
|
||||||
|
symbol = *real_symbol;
|
||||||
|
}
|
||||||
|
|
||||||
self.symbols
|
self.symbols
|
||||||
.iter()
|
.iter()
|
||||||
.position(|s| *s == symbol)
|
.position(|s| *s == symbol)
|
||||||
|
@ -157,6 +172,21 @@ impl<'a> PartialProcs<'a> {
|
||||||
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn insert_alias(&mut self, alias: Symbol, real_symbol: Symbol) {
|
||||||
|
debug_assert!(
|
||||||
|
!self.contains_key(alias),
|
||||||
|
"{:?} is inserted as a partial proc twice: that's a bug!",
|
||||||
|
alias,
|
||||||
|
);
|
||||||
|
debug_assert!(
|
||||||
|
self.contains_key(real_symbol),
|
||||||
|
"{:?} is not a partial proc or another alias: that's a bug!",
|
||||||
|
real_symbol,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.references.push((alias, real_symbol));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -6680,10 +6710,10 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise we're dealing with an alias to something that doesn't need to be specialized, or
|
// Otherwise we're dealing with an alias to something that doesn't need to be specialized, or
|
||||||
// whose usages will already be specialized in the rest of the program. Let's just build the
|
// whose usages will already be specialized in the rest of the program.
|
||||||
// rest of the program now to get our hole.
|
|
||||||
let mut result = build_rest(env, procs, layout_cache);
|
|
||||||
if procs.is_imported_module_thunk(right) {
|
if procs.is_imported_module_thunk(right) {
|
||||||
|
let result = build_rest(env, procs, layout_cache);
|
||||||
|
|
||||||
// if this is an imported symbol, then we must make sure it is
|
// if this is an imported symbol, then we must make sure it is
|
||||||
// specialized, and wrap the original in a function pointer.
|
// specialized, and wrap the original in a function pointer.
|
||||||
add_needed_external(procs, env, variable, right);
|
add_needed_external(procs, env, variable, right);
|
||||||
|
@ -6693,25 +6723,25 @@ where
|
||||||
|
|
||||||
force_thunk(env, right, layout, left, env.arena.alloc(result))
|
force_thunk(env, right, layout, left, env.arena.alloc(result))
|
||||||
} else if env.is_imported_symbol(right) {
|
} else if env.is_imported_symbol(right) {
|
||||||
|
let result = build_rest(env, procs, layout_cache);
|
||||||
|
|
||||||
// if this is an imported symbol, then we must make sure it is
|
// if this is an imported symbol, then we must make sure it is
|
||||||
// specialized, and wrap the original in a function pointer.
|
// specialized, and wrap the original in a function pointer.
|
||||||
add_needed_external(procs, env, variable, right);
|
add_needed_external(procs, env, variable, right);
|
||||||
|
|
||||||
// then we must construct its closure; since imported symbols have no closure, we use the empty struct
|
// then we must construct its closure; since imported symbols have no closure, we use the empty struct
|
||||||
let_empty_struct(left, env.arena.alloc(result))
|
let_empty_struct(left, env.arena.alloc(result))
|
||||||
|
} else if procs.partial_procs.contains_key(right) {
|
||||||
|
// This is an alias to a function defined in this module.
|
||||||
|
// Attach the alias, then build the rest of the module, so that we reference and specialize
|
||||||
|
// the correct proc.
|
||||||
|
procs.partial_procs.insert_alias(left, right);
|
||||||
|
build_rest(env, procs, layout_cache)
|
||||||
} else {
|
} else {
|
||||||
|
// This should be a fully specialized value. Replace the alias with the original symbol.
|
||||||
|
let mut result = build_rest(env, procs, layout_cache);
|
||||||
substitute_in_exprs(env.arena, &mut result, left, right);
|
substitute_in_exprs(env.arena, &mut result, left, right);
|
||||||
|
result
|
||||||
// if the substituted variable is a function, make sure we specialize it
|
|
||||||
reuse_function_symbol(
|
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
Some(variable),
|
|
||||||
right,
|
|
||||||
result,
|
|
||||||
right,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1994,7 +1994,15 @@ pub fn union_sorted_tags<'a>(
|
||||||
|
|
||||||
let mut tags_vec = std::vec::Vec::new();
|
let mut tags_vec = std::vec::Vec::new();
|
||||||
let result = match roc_types::pretty_print::chase_ext_tag_union(subs, var, &mut tags_vec) {
|
let result = match roc_types::pretty_print::chase_ext_tag_union(subs, var, &mut tags_vec) {
|
||||||
Ok(()) | Err((_, Content::FlexVar(_))) | Err((_, Content::RecursionVar { .. })) => {
|
Ok(())
|
||||||
|
// Admit type variables in the extension for now. This may come from things that never got
|
||||||
|
// monomorphized, like in
|
||||||
|
// x : [ A ]*
|
||||||
|
// x = A
|
||||||
|
// x
|
||||||
|
// In such cases it's fine to drop the variable. We may be proven wrong in the future...
|
||||||
|
| Err((_, Content::FlexVar(_) | Content::RigidVar(_)))
|
||||||
|
| Err((_, Content::RecursionVar { .. })) => {
|
||||||
let opt_rec_var = get_recursion_var(subs, var);
|
let opt_rec_var = get_recursion_var(subs, var);
|
||||||
union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, target_info)
|
union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, target_info)
|
||||||
}
|
}
|
||||||
|
@ -2592,7 +2600,7 @@ pub fn ext_var_is_empty_tag_union(subs: &Subs, ext_var: Variable) -> bool {
|
||||||
// the ext_var is empty
|
// the ext_var is empty
|
||||||
let mut ext_fields = std::vec::Vec::new();
|
let mut ext_fields = std::vec::Vec::new();
|
||||||
match roc_types::pretty_print::chase_ext_tag_union(subs, ext_var, &mut ext_fields) {
|
match roc_types::pretty_print::chase_ext_tag_union(subs, ext_var, &mut ext_fields) {
|
||||||
Ok(()) | Err((_, Content::FlexVar(_))) => ext_fields.is_empty(),
|
Ok(()) | Err((_, Content::FlexVar(_) | Content::RigidVar(_))) => ext_fields.is_empty(),
|
||||||
Err(content) => panic!("invalid content in ext_var: {:?}", content),
|
Err(content) => panic!("invalid content in ext_var: {:?}", content),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,4 +9,5 @@ edition = "2018"
|
||||||
roc_collections = { path = "../collections" }
|
roc_collections = { path = "../collections" }
|
||||||
roc_region = { path = "../region" }
|
roc_region = { path = "../region" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
|
roc_types = { path = "../types" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
|
|
|
@ -5,6 +5,7 @@ use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_parse::ast::Base;
|
use roc_parse::ast::Base;
|
||||||
use roc_parse::pattern::PatternType;
|
use roc_parse::pattern::PatternType;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
|
use roc_types::types::AliasKind;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct CycleEntry {
|
pub struct CycleEntry {
|
||||||
|
@ -43,6 +44,12 @@ pub enum Problem {
|
||||||
variable_region: Region,
|
variable_region: Region,
|
||||||
variable_name: Lowercase,
|
variable_name: Lowercase,
|
||||||
},
|
},
|
||||||
|
UnboundTypeVariable {
|
||||||
|
typ: Symbol,
|
||||||
|
num_unbound: usize,
|
||||||
|
one_occurrence: Region,
|
||||||
|
kind: AliasKind,
|
||||||
|
},
|
||||||
DuplicateRecordFieldValue {
|
DuplicateRecordFieldValue {
|
||||||
field_name: Lowercase,
|
field_name: Lowercase,
|
||||||
record_region: Region,
|
record_region: Region,
|
||||||
|
|
|
@ -14,6 +14,7 @@ use roc_types::subs::{
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{
|
use roc_types::types::{
|
||||||
gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, PatternCategory,
|
gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, PatternCategory,
|
||||||
|
TypeExtension,
|
||||||
};
|
};
|
||||||
use roc_unify::unify::{unify, Mode, Unified::*};
|
use roc_unify::unify::{unify, Mode, Unified::*};
|
||||||
|
|
||||||
|
@ -1111,7 +1112,7 @@ fn solve(
|
||||||
let actual = type_to_var(subs, rank, pools, aliases, typ);
|
let actual = type_to_var(subs, rank, pools, aliases, typ);
|
||||||
let tag_ty = Type::TagUnion(
|
let tag_ty = Type::TagUnion(
|
||||||
vec![(tag_name.clone(), tys.to_vec())],
|
vec![(tag_name.clone(), tys.to_vec())],
|
||||||
Box::new(Type::EmptyTagUnion),
|
TypeExtension::Closed,
|
||||||
);
|
);
|
||||||
let includes = type_to_var(subs, rank, pools, aliases, &tag_ty);
|
let includes = type_to_var(subs, rank, pools, aliases, &tag_ty);
|
||||||
|
|
||||||
|
@ -1425,7 +1426,7 @@ fn type_to_variable<'a>(
|
||||||
Record(fields, ext) => {
|
Record(fields, ext) => {
|
||||||
// An empty fields is inefficient (but would be correct)
|
// An empty fields is inefficient (but would be correct)
|
||||||
// If hit, try to turn the value into an EmptyRecord in canonicalization
|
// If hit, try to turn the value into an EmptyRecord in canonicalization
|
||||||
debug_assert!(!fields.is_empty() || !ext.is_empty_record());
|
debug_assert!(!fields.is_empty() || !ext.is_closed());
|
||||||
|
|
||||||
let mut field_vars = Vec::with_capacity_in(fields.len(), arena);
|
let mut field_vars = Vec::with_capacity_in(fields.len(), arena);
|
||||||
|
|
||||||
|
@ -1442,7 +1443,10 @@ fn type_to_variable<'a>(
|
||||||
field_vars.push((field.clone(), field_var));
|
field_vars.push((field.clone(), field_var));
|
||||||
}
|
}
|
||||||
|
|
||||||
let temp_ext_var = helper!(ext);
|
let temp_ext_var = match ext {
|
||||||
|
TypeExtension::Open(ext) => helper!(ext),
|
||||||
|
TypeExtension::Closed => Variable::EMPTY_RECORD,
|
||||||
|
};
|
||||||
|
|
||||||
let (it, new_ext_var) =
|
let (it, new_ext_var) =
|
||||||
gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var)
|
gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var)
|
||||||
|
@ -1465,7 +1469,7 @@ fn type_to_variable<'a>(
|
||||||
TagUnion(tags, ext) => {
|
TagUnion(tags, ext) => {
|
||||||
// An empty tags is inefficient (but would be correct)
|
// An empty tags is inefficient (but would be correct)
|
||||||
// If hit, try to turn the value into an EmptyTagUnion in canonicalization
|
// If hit, try to turn the value into an EmptyTagUnion in canonicalization
|
||||||
debug_assert!(!tags.is_empty() || !ext.is_empty_tag_union());
|
debug_assert!(!tags.is_empty() || !ext.is_closed());
|
||||||
|
|
||||||
let (union_tags, ext) =
|
let (union_tags, ext) =
|
||||||
type_to_union_tags(subs, rank, pools, arena, tags, ext, &mut stack);
|
type_to_union_tags(subs, rank, pools, arena, tags, ext, &mut stack);
|
||||||
|
@ -1474,7 +1478,10 @@ fn type_to_variable<'a>(
|
||||||
register_with_known_var(subs, destination, rank, pools, content)
|
register_with_known_var(subs, destination, rank, pools, content)
|
||||||
}
|
}
|
||||||
FunctionOrTagUnion(tag_name, symbol, ext) => {
|
FunctionOrTagUnion(tag_name, symbol, ext) => {
|
||||||
let temp_ext_var = helper!(ext);
|
let temp_ext_var = match ext {
|
||||||
|
TypeExtension::Open(ext) => helper!(ext),
|
||||||
|
TypeExtension::Closed => Variable::EMPTY_TAG_UNION,
|
||||||
|
};
|
||||||
|
|
||||||
let (it, ext) = roc_types::types::gather_tags_unsorted_iter(
|
let (it, ext) = roc_types::types::gather_tags_unsorted_iter(
|
||||||
subs,
|
subs,
|
||||||
|
@ -1496,7 +1503,7 @@ fn type_to_variable<'a>(
|
||||||
RecursiveTagUnion(rec_var, tags, ext) => {
|
RecursiveTagUnion(rec_var, tags, ext) => {
|
||||||
// An empty tags is inefficient (but would be correct)
|
// An empty tags is inefficient (but would be correct)
|
||||||
// If hit, try to turn the value into an EmptyTagUnion in canonicalization
|
// If hit, try to turn the value into an EmptyTagUnion in canonicalization
|
||||||
debug_assert!(!tags.is_empty() || !ext.is_empty_tag_union());
|
debug_assert!(!tags.is_empty() || !ext.is_closed());
|
||||||
|
|
||||||
let (union_tags, ext) =
|
let (union_tags, ext) =
|
||||||
type_to_union_tags(subs, rank, pools, arena, tags, ext, &mut stack);
|
type_to_union_tags(subs, rank, pools, arena, tags, ext, &mut stack);
|
||||||
|
@ -1681,7 +1688,7 @@ fn roc_result_to_var<'a>(
|
||||||
) -> Variable {
|
) -> Variable {
|
||||||
match result_type {
|
match result_type {
|
||||||
Type::TagUnion(tags, ext) => {
|
Type::TagUnion(tags, ext) => {
|
||||||
debug_assert!(ext.is_empty_tag_union());
|
debug_assert!(ext.is_closed());
|
||||||
debug_assert!(tags.len() == 2);
|
debug_assert!(tags.len() == 2);
|
||||||
|
|
||||||
if let [(err, err_args), (ok, ok_args)] = &tags[..] {
|
if let [(err, err_args), (ok, ok_args)] = &tags[..] {
|
||||||
|
@ -1925,14 +1932,15 @@ fn type_to_union_tags<'a>(
|
||||||
pools: &mut Pools,
|
pools: &mut Pools,
|
||||||
arena: &'_ bumpalo::Bump,
|
arena: &'_ bumpalo::Bump,
|
||||||
tags: &'a [(TagName, Vec<Type>)],
|
tags: &'a [(TagName, Vec<Type>)],
|
||||||
ext: &'a Type,
|
ext: &'a TypeExtension,
|
||||||
stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>,
|
stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>,
|
||||||
) -> (UnionTags, Variable) {
|
) -> (UnionTags, Variable) {
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
|
|
||||||
let sorted = tags.len() == 1 || sorted_no_duplicates(tags);
|
let sorted = tags.len() == 1 || sorted_no_duplicates(tags);
|
||||||
|
|
||||||
if ext.is_empty_tag_union() {
|
match ext {
|
||||||
|
TypeExtension::Closed => {
|
||||||
let ext = Variable::EMPTY_TAG_UNION;
|
let ext = Variable::EMPTY_TAG_UNION;
|
||||||
|
|
||||||
let union_tags = if sorted {
|
let union_tags = if sorted {
|
||||||
|
@ -1943,12 +1951,16 @@ fn type_to_union_tags<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
(union_tags, ext)
|
(union_tags, ext)
|
||||||
} else {
|
}
|
||||||
|
TypeExtension::Open(ext) => {
|
||||||
let mut tag_vars = Vec::with_capacity_in(tags.len(), arena);
|
let mut tag_vars = Vec::with_capacity_in(tags.len(), arena);
|
||||||
|
|
||||||
let temp_ext_var = RegisterVariable::with_stack(subs, rank, pools, arena, ext, stack);
|
let temp_ext_var = RegisterVariable::with_stack(subs, rank, pools, arena, ext, stack);
|
||||||
let (it, ext) =
|
let (it, ext) = roc_types::types::gather_tags_unsorted_iter(
|
||||||
roc_types::types::gather_tags_unsorted_iter(subs, UnionTags::default(), temp_ext_var);
|
subs,
|
||||||
|
UnionTags::default(),
|
||||||
|
temp_ext_var,
|
||||||
|
);
|
||||||
|
|
||||||
tag_vars.extend(it.map(|(n, v)| (n.clone(), v)));
|
tag_vars.extend(it.map(|(n, v)| (n.clone(), v)));
|
||||||
|
|
||||||
|
@ -1961,6 +1973,7 @@ fn type_to_union_tags<'a>(
|
||||||
(union_tags, ext)
|
(union_tags, ext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn check_for_infinite_type(
|
fn check_for_infinite_type(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
|
|
|
@ -1548,3 +1548,35 @@ fn issue_1162() {
|
||||||
u8
|
u8
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn polymorphic_tag() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x : [ Y U8 ]*
|
||||||
|
x = Y 3
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
3, // Y is a newtype, it gets unwrapped
|
||||||
|
u8
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn issue_2725_alias_polymorphic_lambda() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
wrap = \value -> Tag value
|
||||||
|
wrapIt = wrap
|
||||||
|
wrapIt 42
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
42, // Tag is a newtype, it gets unwrapped
|
||||||
|
i64
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
procedure Test.2 (Test.3):
|
||||||
|
ret Test.3;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.6 : I64 = 42i64;
|
||||||
|
let Test.5 : I64 = CallByName Test.2 Test.6;
|
||||||
|
ret Test.5;
|
|
@ -2,22 +2,20 @@ procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
let Test.12 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Test.12 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Test.12;
|
ret Test.12;
|
||||||
|
|
||||||
procedure Test.4 (Test.7, Test.8):
|
procedure Test.5 (Test.7, Test.8):
|
||||||
let Test.17 : U64 = 1i64;
|
|
||||||
ret Test.17;
|
|
||||||
|
|
||||||
procedure Test.4 (Test.7, Test.8):
|
|
||||||
let Test.18 : U64 = 1i64;
|
let Test.18 : U64 = 1i64;
|
||||||
ret Test.18;
|
ret Test.18;
|
||||||
|
|
||||||
|
procedure Test.6 (Test.7, Test.8):
|
||||||
|
let Test.15 : U64 = 1i64;
|
||||||
|
ret Test.15;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.4 : {} = Struct {};
|
let Test.16 : U8 = 100i64;
|
||||||
let Test.4 : {} = Struct {};
|
let Test.17 : U32 = 100i64;
|
||||||
let Test.15 : U8 = 100i64;
|
let Test.10 : U64 = CallByName Test.5 Test.16 Test.17;
|
||||||
let Test.16 : U32 = 100i64;
|
|
||||||
let Test.10 : U64 = CallByName Test.4 Test.15 Test.16;
|
|
||||||
let Test.13 : U32 = 100i64;
|
let Test.13 : U32 = 100i64;
|
||||||
let Test.14 : U8 = 100i64;
|
let Test.14 : U8 = 100i64;
|
||||||
let Test.11 : U64 = CallByName Test.4 Test.13 Test.14;
|
let Test.11 : U64 = CallByName Test.6 Test.13 Test.14;
|
||||||
let Test.9 : U64 = CallByName Num.22 Test.10 Test.11;
|
let Test.9 : U64 = CallByName Num.22 Test.10 Test.11;
|
||||||
ret Test.9;
|
ret Test.9;
|
||||||
|
|
|
@ -36,9 +36,9 @@ procedure Test.8 (Test.11, #Attr.12):
|
||||||
ret Test.11;
|
ret Test.11;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
|
let Test.5 : I64 = 2i64;
|
||||||
let Test.6 : Int1 = true;
|
let Test.6 : Int1 = true;
|
||||||
let Test.4 : I64 = 1i64;
|
let Test.4 : I64 = 1i64;
|
||||||
let Test.5 : I64 = 2i64;
|
|
||||||
joinpoint Test.22 Test.14:
|
joinpoint Test.22 Test.14:
|
||||||
let Test.15 : I64 = 42i64;
|
let Test.15 : I64 = 42i64;
|
||||||
let Test.13 : I64 = CallByName Test.1 Test.14 Test.15;
|
let Test.13 : I64 = CallByName Test.1 Test.14 Test.15;
|
||||||
|
|
|
@ -17,8 +17,8 @@ procedure Test.7 (Test.9, #Attr.12):
|
||||||
ret Test.20;
|
ret Test.20;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.4 : I64 = 1i64;
|
|
||||||
let Test.5 : I64 = 2i64;
|
let Test.5 : I64 = 2i64;
|
||||||
|
let Test.4 : I64 = 1i64;
|
||||||
let Test.12 : I64 = 42i64;
|
let Test.12 : I64 = 42i64;
|
||||||
joinpoint Test.19 Test.13:
|
joinpoint Test.19 Test.13:
|
||||||
let Test.14 : U8 = GetTagId Test.13;
|
let Test.14 : U8 = GetTagId Test.13;
|
||||||
|
|
|
@ -1265,6 +1265,17 @@ fn issue_2535_polymorphic_fields_referenced_in_list() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn issue_2725_alias_polymorphic_lambda() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
wrap = \value -> Tag value
|
||||||
|
wrapIt = wrap
|
||||||
|
wrapIt 42
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// #[ignore]
|
// #[ignore]
|
||||||
// #[mono_test]
|
// #[mono_test]
|
||||||
// fn static_str_closure() {
|
// fn static_str_closure() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::subs::{FlatType, GetSubsSlice, Subs, VarId, VarStore, Variable};
|
use crate::subs::{FlatType, GetSubsSlice, Subs, VarId, VarStore, Variable};
|
||||||
use crate::types::{AliasCommon, AliasKind, Problem, RecordField, Type};
|
use crate::types::{AliasCommon, AliasKind, Problem, RecordField, Type, TypeExtension};
|
||||||
use roc_collections::all::{ImMap, MutSet, SendMap};
|
use roc_collections::all::{ImMap, MutSet, SendMap};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
@ -113,7 +113,11 @@ impl SolvedType {
|
||||||
SolvedType::Func(solved_args, Box::new(solved_closure), Box::new(solved_ret))
|
SolvedType::Func(solved_args, Box::new(solved_closure), Box::new(solved_ret))
|
||||||
}
|
}
|
||||||
Record(fields, box_ext) => {
|
Record(fields, box_ext) => {
|
||||||
let solved_ext = Self::from_type(solved_subs, box_ext);
|
let solved_ext = match box_ext {
|
||||||
|
TypeExtension::Open(ext) => Self::from_type(solved_subs, ext),
|
||||||
|
TypeExtension::Closed => SolvedType::EmptyRecord,
|
||||||
|
};
|
||||||
|
|
||||||
let mut solved_fields = Vec::with_capacity(fields.len());
|
let mut solved_fields = Vec::with_capacity(fields.len());
|
||||||
|
|
||||||
for (label, field) in fields {
|
for (label, field) in fields {
|
||||||
|
@ -139,7 +143,11 @@ impl SolvedType {
|
||||||
SolvedType::TagUnion(solved_tags, Box::new(solved_ext))
|
SolvedType::TagUnion(solved_tags, Box::new(solved_ext))
|
||||||
}
|
}
|
||||||
TagUnion(tags, box_ext) => {
|
TagUnion(tags, box_ext) => {
|
||||||
let solved_ext = Self::from_type(solved_subs, box_ext);
|
let solved_ext = match box_ext {
|
||||||
|
TypeExtension::Open(ext) => Self::from_type(solved_subs, ext),
|
||||||
|
TypeExtension::Closed => SolvedType::EmptyTagUnion,
|
||||||
|
};
|
||||||
|
|
||||||
let mut solved_tags = Vec::with_capacity(tags.len());
|
let mut solved_tags = Vec::with_capacity(tags.len());
|
||||||
for (tag_name, types) in tags {
|
for (tag_name, types) in tags {
|
||||||
let mut solved_types = Vec::with_capacity(types.len());
|
let mut solved_types = Vec::with_capacity(types.len());
|
||||||
|
@ -155,11 +163,19 @@ impl SolvedType {
|
||||||
SolvedType::TagUnion(solved_tags, Box::new(solved_ext))
|
SolvedType::TagUnion(solved_tags, Box::new(solved_ext))
|
||||||
}
|
}
|
||||||
FunctionOrTagUnion(tag_name, symbol, box_ext) => {
|
FunctionOrTagUnion(tag_name, symbol, box_ext) => {
|
||||||
let solved_ext = Self::from_type(solved_subs, box_ext);
|
let solved_ext = match box_ext {
|
||||||
|
TypeExtension::Open(ext) => Self::from_type(solved_subs, ext),
|
||||||
|
TypeExtension::Closed => SolvedType::EmptyTagUnion,
|
||||||
|
};
|
||||||
|
|
||||||
SolvedType::FunctionOrTagUnion(tag_name.clone(), *symbol, Box::new(solved_ext))
|
SolvedType::FunctionOrTagUnion(tag_name.clone(), *symbol, Box::new(solved_ext))
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(rec_var, tags, box_ext) => {
|
RecursiveTagUnion(rec_var, tags, box_ext) => {
|
||||||
let solved_ext = Self::from_type(solved_subs, box_ext);
|
let solved_ext = match box_ext {
|
||||||
|
TypeExtension::Open(ext) => Self::from_type(solved_subs, ext),
|
||||||
|
TypeExtension::Closed => SolvedType::EmptyTagUnion,
|
||||||
|
};
|
||||||
|
|
||||||
let mut solved_tags = Vec::with_capacity(tags.len());
|
let mut solved_tags = Vec::with_capacity(tags.len());
|
||||||
for (tag_name, types) in tags {
|
for (tag_name, types) in tags {
|
||||||
let mut solved_types = Vec::with_capacity(types.len());
|
let mut solved_types = Vec::with_capacity(types.len());
|
||||||
|
@ -523,7 +539,12 @@ pub fn to_type(
|
||||||
new_fields.insert(label.clone(), field_val);
|
new_fields.insert(label.clone(), field_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::Record(new_fields, Box::new(to_type(ext, free_vars, var_store)))
|
let ext = match ext.as_ref() {
|
||||||
|
SolvedType::EmptyRecord => TypeExtension::Closed,
|
||||||
|
other => TypeExtension::Open(Box::new(to_type(other, free_vars, var_store))),
|
||||||
|
};
|
||||||
|
|
||||||
|
Type::Record(new_fields, ext)
|
||||||
}
|
}
|
||||||
EmptyRecord => Type::EmptyRec,
|
EmptyRecord => Type::EmptyRec,
|
||||||
EmptyTagUnion => Type::EmptyTagUnion,
|
EmptyTagUnion => Type::EmptyTagUnion,
|
||||||
|
@ -540,13 +561,21 @@ pub fn to_type(
|
||||||
new_tags.push((tag_name.clone(), new_args));
|
new_tags.push((tag_name.clone(), new_args));
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::TagUnion(new_tags, Box::new(to_type(ext, free_vars, var_store)))
|
let ext = match ext.as_ref() {
|
||||||
|
SolvedType::EmptyTagUnion => TypeExtension::Closed,
|
||||||
|
other => TypeExtension::Open(Box::new(to_type(other, free_vars, var_store))),
|
||||||
|
};
|
||||||
|
|
||||||
|
Type::TagUnion(new_tags, ext)
|
||||||
|
}
|
||||||
|
FunctionOrTagUnion(tag_name, symbol, ext) => {
|
||||||
|
let ext = match ext.as_ref() {
|
||||||
|
SolvedType::EmptyTagUnion => TypeExtension::Closed,
|
||||||
|
other => TypeExtension::Open(Box::new(to_type(other, free_vars, var_store))),
|
||||||
|
};
|
||||||
|
|
||||||
|
Type::FunctionOrTagUnion(tag_name.clone(), *symbol, ext)
|
||||||
}
|
}
|
||||||
FunctionOrTagUnion(tag_name, symbol, ext) => Type::FunctionOrTagUnion(
|
|
||||||
tag_name.clone(),
|
|
||||||
*symbol,
|
|
||||||
Box::new(to_type(ext, free_vars, var_store)),
|
|
||||||
),
|
|
||||||
RecursiveTagUnion(rec_var_id, tags, ext) => {
|
RecursiveTagUnion(rec_var_id, tags, ext) => {
|
||||||
let mut new_tags = Vec::with_capacity(tags.len());
|
let mut new_tags = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
|
@ -560,16 +589,17 @@ pub fn to_type(
|
||||||
new_tags.push((tag_name.clone(), new_args));
|
new_tags.push((tag_name.clone(), new_args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ext = match ext.as_ref() {
|
||||||
|
SolvedType::EmptyTagUnion => TypeExtension::Closed,
|
||||||
|
other => TypeExtension::Open(Box::new(to_type(other, free_vars, var_store))),
|
||||||
|
};
|
||||||
|
|
||||||
let rec_var = free_vars
|
let rec_var = free_vars
|
||||||
.unnamed_vars
|
.unnamed_vars
|
||||||
.get(rec_var_id)
|
.get(rec_var_id)
|
||||||
.expect("rec var not in unnamed vars");
|
.expect("rec var not in unnamed vars");
|
||||||
|
|
||||||
Type::RecursiveTagUnion(
|
Type::RecursiveTagUnion(*rec_var, new_tags, ext)
|
||||||
*rec_var,
|
|
||||||
new_tags,
|
|
||||||
Box::new(to_type(ext, free_vars, var_store)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
DelayedAlias(symbol, solved_type_variables, solved_lambda_sets) => {
|
DelayedAlias(symbol, solved_type_variables, solved_lambda_sets) => {
|
||||||
let mut type_variables = Vec::with_capacity(solved_type_variables.len());
|
let mut type_variables = Vec::with_capacity(solved_type_variables.len());
|
||||||
|
|
|
@ -193,9 +193,9 @@ pub enum Type {
|
||||||
EmptyTagUnion,
|
EmptyTagUnion,
|
||||||
/// A function. The types of its arguments, size of its closure, then the type of its return value.
|
/// A function. The types of its arguments, size of its closure, then the type of its return value.
|
||||||
Function(Vec<Type>, Box<Type>, Box<Type>),
|
Function(Vec<Type>, Box<Type>, Box<Type>),
|
||||||
Record(SendMap<Lowercase, RecordField<Type>>, Box<Type>),
|
Record(SendMap<Lowercase, RecordField<Type>>, TypeExtension),
|
||||||
TagUnion(Vec<(TagName, Vec<Type>)>, Box<Type>),
|
TagUnion(Vec<(TagName, Vec<Type>)>, TypeExtension),
|
||||||
FunctionOrTagUnion(TagName, Symbol, Box<Type>),
|
FunctionOrTagUnion(TagName, Symbol, TypeExtension),
|
||||||
/// A function name that is used in our defunctionalization algorithm
|
/// A function name that is used in our defunctionalization algorithm
|
||||||
ClosureTag {
|
ClosureTag {
|
||||||
name: Symbol,
|
name: Symbol,
|
||||||
|
@ -216,7 +216,7 @@ pub enum Type {
|
||||||
actual_var: Variable,
|
actual_var: Variable,
|
||||||
actual: Box<Type>,
|
actual: Box<Type>,
|
||||||
},
|
},
|
||||||
RecursiveTagUnion(Variable, Vec<(TagName, Vec<Type>)>, Box<Type>),
|
RecursiveTagUnion(Variable, Vec<(TagName, Vec<Type>)>, TypeExtension),
|
||||||
/// Applying a type to some arguments (e.g. Dict.Dict String Int)
|
/// Applying a type to some arguments (e.g. Dict.Dict String Int)
|
||||||
Apply(Symbol, Vec<Type>, Region),
|
Apply(Symbol, Vec<Type>, Region),
|
||||||
Variable(Variable),
|
Variable(Variable),
|
||||||
|
@ -288,6 +288,43 @@ impl Clone for Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone)]
|
||||||
|
pub enum TypeExtension {
|
||||||
|
Open(Box<Type>),
|
||||||
|
Closed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeExtension {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from_type(typ: Type) -> Self {
|
||||||
|
match typ {
|
||||||
|
Type::EmptyTagUnion | Type::EmptyRec => Self::Closed,
|
||||||
|
_ => Self::Open(Box::new(typ)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_closed(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
TypeExtension::Open(_) => false,
|
||||||
|
TypeExtension::Closed => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a TypeExtension {
|
||||||
|
type Item = &'a Type;
|
||||||
|
|
||||||
|
type IntoIter = std::option::IntoIter<Self::Item>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
match self {
|
||||||
|
TypeExtension::Open(ext) => Some(ext.as_ref()).into_iter(),
|
||||||
|
TypeExtension::Closed => None.into_iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Type {
|
impl fmt::Debug for Type {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
@ -422,12 +459,12 @@ impl fmt::Debug for Type {
|
||||||
|
|
||||||
write!(f, "}}")?;
|
write!(f, "}}")?;
|
||||||
|
|
||||||
match *ext.clone() {
|
match ext {
|
||||||
Type::EmptyRec => {
|
TypeExtension::Closed => {
|
||||||
// This is a closed record. We're done!
|
// This is a closed record. We're done!
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
other => {
|
TypeExtension::Open(other) => {
|
||||||
// This is an open record, so print the variable
|
// This is an open record, so print the variable
|
||||||
// right after the '}'
|
// right after the '}'
|
||||||
//
|
//
|
||||||
|
@ -463,12 +500,12 @@ impl fmt::Debug for Type {
|
||||||
|
|
||||||
write!(f, "]")?;
|
write!(f, "]")?;
|
||||||
|
|
||||||
match *ext.clone() {
|
match ext {
|
||||||
Type::EmptyTagUnion => {
|
TypeExtension::Closed => {
|
||||||
// This is a closed variant. We're done!
|
// This is a closed variant. We're done!
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
other => {
|
TypeExtension::Open(other) => {
|
||||||
// This is an open tag union, so print the variable
|
// This is an open tag union, so print the variable
|
||||||
// right after the ']'
|
// right after the ']'
|
||||||
//
|
//
|
||||||
|
@ -483,12 +520,12 @@ impl fmt::Debug for Type {
|
||||||
write!(f, "{:?}", tag_name)?;
|
write!(f, "{:?}", tag_name)?;
|
||||||
write!(f, "]")?;
|
write!(f, "]")?;
|
||||||
|
|
||||||
match *ext.clone() {
|
match ext {
|
||||||
Type::EmptyTagUnion => {
|
TypeExtension::Closed => {
|
||||||
// This is a closed variant. We're done!
|
// This is a closed variant. We're done!
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
other => {
|
TypeExtension::Open(other) => {
|
||||||
// This is an open tag union, so print the variable
|
// This is an open tag union, so print the variable
|
||||||
// right after the ']'
|
// right after the ']'
|
||||||
//
|
//
|
||||||
|
@ -533,19 +570,20 @@ impl fmt::Debug for Type {
|
||||||
|
|
||||||
write!(f, "]")?;
|
write!(f, "]")?;
|
||||||
|
|
||||||
match *ext.clone() {
|
match ext {
|
||||||
Type::EmptyTagUnion => {
|
TypeExtension::Closed => {
|
||||||
// This is a closed variant. We're done!
|
// This is a closed variant. We're done!
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
other => {
|
TypeExtension::Open(other) => {
|
||||||
// This is an open tag union, so print the variable
|
// This is an open tag union, so print the variable
|
||||||
// right after the ']'
|
// right after the ']'
|
||||||
//
|
//
|
||||||
// e.g. the "*" at the end of `[ Foo ]*`
|
// e.g. the "*" at the end of `[ Foo ]*`
|
||||||
// or the "r" at the end of `[ DivByZero ]r`
|
// or the "r" at the end of `[ DivByZero ]r`
|
||||||
other.fmt(f)?;
|
other.fmt(f)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}?;
|
||||||
|
|
||||||
write!(f, " as <{:?}>", rec)
|
write!(f, " as <{:?}>", rec)
|
||||||
}
|
}
|
||||||
|
@ -611,23 +649,34 @@ impl Type {
|
||||||
for (_, args) in tags {
|
for (_, args) in tags {
|
||||||
stack.extend(args.iter_mut());
|
stack.extend(args.iter_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
stack.push(ext);
|
stack.push(ext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
FunctionOrTagUnion(_, _, ext) => {
|
FunctionOrTagUnion(_, _, ext) => {
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
stack.push(ext);
|
stack.push(ext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
RecursiveTagUnion(_, tags, ext) => {
|
RecursiveTagUnion(_, tags, ext) => {
|
||||||
for (_, args) in tags {
|
for (_, args) in tags {
|
||||||
stack.extend(args.iter_mut());
|
stack.extend(args.iter_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
stack.push(ext);
|
stack.push(ext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Record(fields, ext) => {
|
Record(fields, ext) => {
|
||||||
for (_, x) in fields.iter_mut() {
|
for (_, x) in fields.iter_mut() {
|
||||||
stack.push(x.as_inner_mut());
|
stack.push(x.as_inner_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
stack.push(ext);
|
stack.push(ext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Type::DelayedAlias(AliasCommon {
|
Type::DelayedAlias(AliasCommon {
|
||||||
type_arguments,
|
type_arguments,
|
||||||
lambda_set_variables,
|
lambda_set_variables,
|
||||||
|
@ -650,6 +699,7 @@ impl Type {
|
||||||
for (_, value) in type_arguments.iter_mut() {
|
for (_, value) in type_arguments.iter_mut() {
|
||||||
stack.push(value);
|
stack.push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
for lambda_set in lambda_set_variables.iter_mut() {
|
for lambda_set in lambda_set_variables.iter_mut() {
|
||||||
stack.push(lambda_set.as_inner_mut());
|
stack.push(lambda_set.as_inner_mut());
|
||||||
}
|
}
|
||||||
|
@ -705,11 +755,16 @@ impl Type {
|
||||||
for (_, args) in tags {
|
for (_, args) in tags {
|
||||||
stack.extend(args.iter_mut());
|
stack.extend(args.iter_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
stack.push(ext);
|
stack.push(ext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
FunctionOrTagUnion(_, _, ext) => {
|
FunctionOrTagUnion(_, _, ext) => {
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
stack.push(ext);
|
stack.push(ext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
RecursiveTagUnion(rec_var, tags, ext) => {
|
RecursiveTagUnion(rec_var, tags, ext) => {
|
||||||
if let Some(replacement) = substitutions.get(rec_var) {
|
if let Some(replacement) = substitutions.get(rec_var) {
|
||||||
*rec_var = *replacement;
|
*rec_var = *replacement;
|
||||||
|
@ -718,14 +773,19 @@ impl Type {
|
||||||
for (_, args) in tags {
|
for (_, args) in tags {
|
||||||
stack.extend(args.iter_mut());
|
stack.extend(args.iter_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
stack.push(ext);
|
stack.push(ext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Record(fields, ext) => {
|
Record(fields, ext) => {
|
||||||
for (_, x) in fields.iter_mut() {
|
for (_, x) in fields.iter_mut() {
|
||||||
stack.push(x.as_inner_mut());
|
stack.push(x.as_inner_mut());
|
||||||
}
|
}
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
stack.push(ext);
|
stack.push(ext);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Type::DelayedAlias(AliasCommon {
|
Type::DelayedAlias(AliasCommon {
|
||||||
type_arguments,
|
type_arguments,
|
||||||
lambda_set_variables,
|
lambda_set_variables,
|
||||||
|
@ -800,20 +860,31 @@ impl Type {
|
||||||
closure.substitute_alias(rep_symbol, rep_args, actual)?;
|
closure.substitute_alias(rep_symbol, rep_args, actual)?;
|
||||||
ret.substitute_alias(rep_symbol, rep_args, actual)
|
ret.substitute_alias(rep_symbol, rep_args, actual)
|
||||||
}
|
}
|
||||||
FunctionOrTagUnion(_, _, ext) => ext.substitute_alias(rep_symbol, rep_args, actual),
|
FunctionOrTagUnion(_, _, ext) => match ext {
|
||||||
|
TypeExtension::Open(ext) => ext.substitute_alias(rep_symbol, rep_args, actual),
|
||||||
|
TypeExtension::Closed => Ok(()),
|
||||||
|
},
|
||||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||||
for (_, args) in tags {
|
for (_, args) in tags {
|
||||||
for x in args {
|
for x in args {
|
||||||
x.substitute_alias(rep_symbol, rep_args, actual)?;
|
x.substitute_alias(rep_symbol, rep_args, actual)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ext.substitute_alias(rep_symbol, rep_args, actual)
|
|
||||||
|
match ext {
|
||||||
|
TypeExtension::Open(ext) => ext.substitute_alias(rep_symbol, rep_args, actual),
|
||||||
|
TypeExtension::Closed => Ok(()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Record(fields, ext) => {
|
Record(fields, ext) => {
|
||||||
for (_, x) in fields.iter_mut() {
|
for (_, x) in fields.iter_mut() {
|
||||||
x.substitute_alias(rep_symbol, rep_args, actual)?;
|
x.substitute_alias(rep_symbol, rep_args, actual)?;
|
||||||
}
|
}
|
||||||
ext.substitute_alias(rep_symbol, rep_args, actual)
|
|
||||||
|
match ext {
|
||||||
|
TypeExtension::Open(ext) => ext.substitute_alias(rep_symbol, rep_args, actual),
|
||||||
|
TypeExtension::Closed => Ok(()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DelayedAlias(AliasCommon { type_arguments, .. }) => {
|
DelayedAlias(AliasCommon { type_arguments, .. }) => {
|
||||||
for (_, ta) in type_arguments {
|
for (_, ta) in type_arguments {
|
||||||
|
@ -862,6 +933,13 @@ impl Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn contains_symbol_ext(ext: &TypeExtension, rep_symbol: Symbol) -> bool {
|
||||||
|
match ext {
|
||||||
|
TypeExtension::Open(ext) => ext.contains_symbol(rep_symbol),
|
||||||
|
TypeExtension::Closed => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn contains_symbol(&self, rep_symbol: Symbol) -> bool {
|
pub fn contains_symbol(&self, rep_symbol: Symbol) -> bool {
|
||||||
use Type::*;
|
use Type::*;
|
||||||
|
|
||||||
|
@ -871,9 +949,9 @@ impl Type {
|
||||||
|| closure.contains_symbol(rep_symbol)
|
|| closure.contains_symbol(rep_symbol)
|
||||||
|| args.iter().any(|arg| arg.contains_symbol(rep_symbol))
|
|| args.iter().any(|arg| arg.contains_symbol(rep_symbol))
|
||||||
}
|
}
|
||||||
FunctionOrTagUnion(_, _, ext) => ext.contains_symbol(rep_symbol),
|
FunctionOrTagUnion(_, _, ext) => Self::contains_symbol_ext(ext, rep_symbol),
|
||||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||||
ext.contains_symbol(rep_symbol)
|
Self::contains_symbol_ext(ext, rep_symbol)
|
||||||
|| tags
|
|| tags
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| v.1.iter())
|
.map(|v| v.1.iter())
|
||||||
|
@ -882,7 +960,7 @@ impl Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
Record(fields, ext) => {
|
Record(fields, ext) => {
|
||||||
ext.contains_symbol(rep_symbol)
|
Self::contains_symbol_ext(ext, rep_symbol)
|
||||||
|| fields.values().any(|arg| arg.contains_symbol(rep_symbol))
|
|| fields.values().any(|arg| arg.contains_symbol(rep_symbol))
|
||||||
}
|
}
|
||||||
DelayedAlias(AliasCommon {
|
DelayedAlias(AliasCommon {
|
||||||
|
@ -910,6 +988,13 @@ impl Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn contains_variable_ext(ext: &TypeExtension, rep_variable: Variable) -> bool {
|
||||||
|
match ext {
|
||||||
|
TypeExtension::Open(ext) => ext.contains_variable(rep_variable),
|
||||||
|
TypeExtension::Closed => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn contains_variable(&self, rep_variable: Variable) -> bool {
|
pub fn contains_variable(&self, rep_variable: Variable) -> bool {
|
||||||
use Type::*;
|
use Type::*;
|
||||||
|
|
||||||
|
@ -920,9 +1005,9 @@ impl Type {
|
||||||
|| closure.contains_variable(rep_variable)
|
|| closure.contains_variable(rep_variable)
|
||||||
|| args.iter().any(|arg| arg.contains_variable(rep_variable))
|
|| args.iter().any(|arg| arg.contains_variable(rep_variable))
|
||||||
}
|
}
|
||||||
FunctionOrTagUnion(_, _, ext) => ext.contains_variable(rep_variable),
|
FunctionOrTagUnion(_, _, ext) => Self::contains_variable_ext(ext, rep_variable),
|
||||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||||
ext.contains_variable(rep_variable)
|
Self::contains_variable_ext(ext, rep_variable)
|
||||||
|| tags
|
|| tags
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| v.1.iter())
|
.map(|v| v.1.iter())
|
||||||
|
@ -931,7 +1016,7 @@ impl Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
Record(fields, ext) => {
|
Record(fields, ext) => {
|
||||||
ext.contains_variable(rep_variable)
|
Self::contains_variable_ext(ext, rep_variable)
|
||||||
|| fields
|
|| fields
|
||||||
.values()
|
.values()
|
||||||
.any(|arg| arg.contains_variable(rep_variable))
|
.any(|arg| arg.contains_variable(rep_variable))
|
||||||
|
@ -983,22 +1068,30 @@ impl Type {
|
||||||
ret.instantiate_aliases(region, aliases, var_store, introduced);
|
ret.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
FunctionOrTagUnion(_, _, ext) => {
|
FunctionOrTagUnion(_, _, ext) => {
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||||
for (_, args) in tags {
|
for (_, args) in tags {
|
||||||
for x in args {
|
for x in args {
|
||||||
x.instantiate_aliases(region, aliases, var_store, introduced);
|
x.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Record(fields, ext) => {
|
Record(fields, ext) => {
|
||||||
for (_, x) in fields.iter_mut() {
|
for (_, x) in fields.iter_mut() {
|
||||||
x.instantiate_aliases(region, aliases, var_store, introduced);
|
x.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
DelayedAlias(AliasCommon { .. }) => {
|
DelayedAlias(AliasCommon { .. }) => {
|
||||||
// do nothing, yay
|
// do nothing, yay
|
||||||
}
|
}
|
||||||
|
@ -1084,7 +1177,10 @@ impl Type {
|
||||||
for typ in tags.iter_mut().map(|v| v.1.iter_mut()).flatten() {
|
for typ in tags.iter_mut().map(|v| v.1.iter_mut()).flatten() {
|
||||||
typ.substitute(&substitution);
|
typ.substitute(&substitution);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = &mut ext {
|
||||||
ext.substitute(&substitution);
|
ext.substitute(&substitution);
|
||||||
|
}
|
||||||
|
|
||||||
actual = Type::RecursiveTagUnion(new_rec_var, tags, ext);
|
actual = Type::RecursiveTagUnion(new_rec_var, tags, ext);
|
||||||
}
|
}
|
||||||
|
@ -1145,14 +1241,17 @@ impl Type {
|
||||||
pub fn is_narrow(&self) -> bool {
|
pub fn is_narrow(&self) -> bool {
|
||||||
match self.shallow_dealias() {
|
match self.shallow_dealias() {
|
||||||
Type::TagUnion(tags, ext) | Type::RecursiveTagUnion(_, tags, ext) => {
|
Type::TagUnion(tags, ext) | Type::RecursiveTagUnion(_, tags, ext) => {
|
||||||
ext.is_empty_tag_union()
|
matches!(ext, TypeExtension::Closed)
|
||||||
&& tags.len() == 1
|
&& tags.len() == 1
|
||||||
&& tags[0].1.len() == 1
|
&& tags[0].1.len() == 1
|
||||||
&& tags[0].1[0].is_narrow()
|
&& tags[0].1[0].is_narrow()
|
||||||
}
|
}
|
||||||
Type::Record(fields, ext) => {
|
Type::Record(fields, ext) => match ext {
|
||||||
|
TypeExtension::Open(ext) => {
|
||||||
fields.values().all(|field| field.as_inner().is_narrow()) && ext.is_narrow()
|
fields.values().all(|field| field.as_inner().is_narrow()) && ext.is_narrow()
|
||||||
}
|
}
|
||||||
|
TypeExtension::Closed => fields.values().all(|field| field.as_inner().is_narrow()),
|
||||||
|
},
|
||||||
Type::Function(args, clos, ret) => {
|
Type::Function(args, clos, ret) => {
|
||||||
args.iter().all(|a| a.is_narrow()) && clos.is_narrow() && ret.is_narrow()
|
args.iter().all(|a| a.is_narrow()) && clos.is_narrow() && ret.is_narrow()
|
||||||
}
|
}
|
||||||
|
@ -1187,15 +1286,15 @@ fn symbols_help(initial: &Type) -> Vec<Symbol> {
|
||||||
stack.extend(args);
|
stack.extend(args);
|
||||||
}
|
}
|
||||||
FunctionOrTagUnion(_, _, ext) => {
|
FunctionOrTagUnion(_, _, ext) => {
|
||||||
stack.push(ext);
|
stack.extend(ext);
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||||
stack.push(ext);
|
stack.extend(ext);
|
||||||
stack.extend(tags.iter().map(|v| v.1.iter()).flatten());
|
stack.extend(tags.iter().map(|v| v.1.iter()).flatten());
|
||||||
}
|
}
|
||||||
|
|
||||||
Record(fields, ext) => {
|
Record(fields, ext) => {
|
||||||
stack.push(ext);
|
stack.extend(ext);
|
||||||
stack.extend(fields.values().map(|field| field.as_inner()));
|
stack.extend(fields.values().map(|field| field.as_inner()));
|
||||||
}
|
}
|
||||||
DelayedAlias(AliasCommon {
|
DelayedAlias(AliasCommon {
|
||||||
|
@ -1269,26 +1368,37 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
||||||
Demanded(x) => variables_help(x, accum),
|
Demanded(x) => variables_help(x, accum),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
variables_help(ext, accum);
|
variables_help(ext, accum);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
TagUnion(tags, ext) => {
|
TagUnion(tags, ext) => {
|
||||||
for (_, args) in tags {
|
for (_, args) in tags {
|
||||||
for x in args {
|
for x in args {
|
||||||
variables_help(x, accum);
|
variables_help(x, accum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
variables_help(ext, accum);
|
variables_help(ext, accum);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
FunctionOrTagUnion(_, _, ext) => {
|
FunctionOrTagUnion(_, _, ext) => {
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
variables_help(ext, accum);
|
variables_help(ext, accum);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
RecursiveTagUnion(rec, tags, ext) => {
|
RecursiveTagUnion(rec, tags, ext) => {
|
||||||
for (_, args) in tags {
|
for (_, args) in tags {
|
||||||
for x in args {
|
for x in args {
|
||||||
variables_help(x, accum);
|
variables_help(x, accum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
variables_help(ext, accum);
|
variables_help(ext, accum);
|
||||||
|
}
|
||||||
|
|
||||||
// just check that this is actually a recursive type
|
// just check that this is actually a recursive type
|
||||||
debug_assert!(accum.contains(rec));
|
debug_assert!(accum.contains(rec));
|
||||||
|
@ -1380,26 +1490,37 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
||||||
Demanded(x) => variables_help_detailed(x, accum),
|
Demanded(x) => variables_help_detailed(x, accum),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
variables_help_detailed(ext, accum);
|
variables_help_detailed(ext, accum);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
TagUnion(tags, ext) => {
|
TagUnion(tags, ext) => {
|
||||||
for (_, args) in tags {
|
for (_, args) in tags {
|
||||||
for x in args {
|
for x in args {
|
||||||
variables_help_detailed(x, accum);
|
variables_help_detailed(x, accum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
variables_help_detailed(ext, accum);
|
variables_help_detailed(ext, accum);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
FunctionOrTagUnion(_, _, ext) => {
|
FunctionOrTagUnion(_, _, ext) => {
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
variables_help_detailed(ext, accum);
|
variables_help_detailed(ext, accum);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
RecursiveTagUnion(rec, tags, ext) => {
|
RecursiveTagUnion(rec, tags, ext) => {
|
||||||
for (_, args) in tags {
|
for (_, args) in tags {
|
||||||
for x in args {
|
for x in args {
|
||||||
variables_help_detailed(x, accum);
|
variables_help_detailed(x, accum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let TypeExtension::Open(ext) = ext {
|
||||||
variables_help_detailed(ext, accum);
|
variables_help_detailed(ext, accum);
|
||||||
|
}
|
||||||
|
|
||||||
// just check that this is actually a recursive type
|
// just check that this is actually a recursive type
|
||||||
// debug_assert!(accum.type_variables.contains(rec));
|
// debug_assert!(accum.type_variables.contains(rec));
|
||||||
|
|
|
@ -26,7 +26,6 @@ roc_target = {path = "../compiler/roc_target"}
|
||||||
roc_types = {path = "../compiler/types"}
|
roc_types = {path = "../compiler/types"}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["console_error_panic_hook"]
|
|
||||||
wasmer = ["futures"]
|
wasmer = ["futures"]
|
||||||
|
|
||||||
[package.metadata.wasm-pack.profile.profiling]
|
[package.metadata.wasm-pack.profile.profiling]
|
||||||
|
|
|
@ -3,7 +3,7 @@ mod repl;
|
||||||
//
|
//
|
||||||
// Interface with external JS in the browser
|
// Interface with external JS in the browser
|
||||||
//
|
//
|
||||||
#[cfg(not(feature = "wasmer"))]
|
#[cfg(feature = "console_error_panic_hook")]
|
||||||
extern crate console_error_panic_hook;
|
extern crate console_error_panic_hook;
|
||||||
#[cfg(not(feature = "wasmer"))]
|
#[cfg(not(feature = "wasmer"))]
|
||||||
mod externs_js;
|
mod externs_js;
|
||||||
|
|
|
@ -155,7 +155,7 @@ impl<'a> ReplApp<'a> for WasmReplApp<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn entrypoint_from_js(src: String) -> Result<String, String> {
|
pub async fn entrypoint_from_js(src: String) -> Result<String, String> {
|
||||||
#[cfg(not(feature = "wasmer"))]
|
#[cfg(feature = "console_error_panic_hook")]
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
let arena = &Bump::new();
|
let arena = &Bump::new();
|
||||||
|
|
|
@ -26,11 +26,11 @@ cp -r repl_www/public/* $WWW_ROOT
|
||||||
if [ -n "${REPL_DEBUG:-}" ]
|
if [ -n "${REPL_DEBUG:-}" ]
|
||||||
then
|
then
|
||||||
# Leave out wasm-opt since it takes too long when debugging, and provide some debug options
|
# Leave out wasm-opt since it takes too long when debugging, and provide some debug options
|
||||||
cargo build --target wasm32-unknown-unknown -p roc_repl_wasm --release
|
cargo build --target wasm32-unknown-unknown -p roc_repl_wasm --release --features console_error_panic_hook
|
||||||
wasm-bindgen --target web --keep-debug target/wasm32-unknown-unknown/release/roc_repl_wasm.wasm --out-dir repl_wasm/pkg/
|
wasm-bindgen --target web --keep-debug target/wasm32-unknown-unknown/release/roc_repl_wasm.wasm --out-dir repl_wasm/pkg/
|
||||||
else
|
else
|
||||||
# A `--profiling` build is optimized and has debug info, so we get stack traces for compiler `todo!()`
|
# A `--profiling` build is optimized and has debug info, so we get stack traces for compiler `todo!()`
|
||||||
wasm-pack build --profiling --target web repl_wasm
|
wasm-pack build --profiling --target web repl_wasm -- --features console_error_panic_hook
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cp repl_wasm/pkg/*.wasm $WWW_ROOT
|
cp repl_wasm/pkg/*.wasm $WWW_ROOT
|
||||||
|
|
|
@ -5,6 +5,7 @@ use roc_problem::can::{
|
||||||
BadPattern, ExtensionTypeKind, FloatErrorKind, IntErrorKind, Problem, RuntimeError,
|
BadPattern, ExtensionTypeKind, FloatErrorKind, IntErrorKind, Problem, RuntimeError,
|
||||||
};
|
};
|
||||||
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Loc, Region};
|
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Loc, Region};
|
||||||
|
use roc_types::types::AliasKind;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::error::r#type::suggest;
|
use crate::error::r#type::suggest;
|
||||||
|
@ -17,6 +18,7 @@ const UNRECOGNIZED_NAME: &str = "UNRECOGNIZED NAME";
|
||||||
const UNUSED_DEF: &str = "UNUSED DEFINITION";
|
const UNUSED_DEF: &str = "UNUSED DEFINITION";
|
||||||
const UNUSED_IMPORT: &str = "UNUSED IMPORT";
|
const UNUSED_IMPORT: &str = "UNUSED IMPORT";
|
||||||
const UNUSED_ALIAS_PARAM: &str = "UNUSED TYPE ALIAS PARAMETER";
|
const UNUSED_ALIAS_PARAM: &str = "UNUSED TYPE ALIAS PARAMETER";
|
||||||
|
const UNBOUND_TYPE_VARIABLE: &str = "UNBOUND TYPE VARIABLE";
|
||||||
const UNUSED_ARG: &str = "UNUSED ARGUMENT";
|
const UNUSED_ARG: &str = "UNUSED ARGUMENT";
|
||||||
const MISSING_DEFINITION: &str = "MISSING DEFINITION";
|
const MISSING_DEFINITION: &str = "MISSING DEFINITION";
|
||||||
const UNKNOWN_GENERATES_WITH: &str = "UNKNOWN GENERATES FUNCTION";
|
const UNKNOWN_GENERATES_WITH: &str = "UNKNOWN GENERATES FUNCTION";
|
||||||
|
@ -252,6 +254,43 @@ pub fn can_problem<'b>(
|
||||||
title = UNUSED_ALIAS_PARAM.to_string();
|
title = UNUSED_ALIAS_PARAM.to_string();
|
||||||
severity = Severity::RuntimeError;
|
severity = Severity::RuntimeError;
|
||||||
}
|
}
|
||||||
|
Problem::UnboundTypeVariable {
|
||||||
|
typ: alias,
|
||||||
|
num_unbound,
|
||||||
|
one_occurrence,
|
||||||
|
kind,
|
||||||
|
} => {
|
||||||
|
let mut stack = Vec::with_capacity(4);
|
||||||
|
if num_unbound == 1 {
|
||||||
|
stack.push(alloc.concat(vec![
|
||||||
|
alloc.reflow("The definition of "),
|
||||||
|
alloc.symbol_unqualified(alias),
|
||||||
|
alloc.reflow(" has an unbound type variable:"),
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
stack.push(alloc.concat(vec![
|
||||||
|
alloc.reflow("The definition of "),
|
||||||
|
alloc.symbol_unqualified(alias),
|
||||||
|
alloc.reflow(" has "),
|
||||||
|
alloc.text(format!("{}", num_unbound)),
|
||||||
|
alloc.reflow(" unbound type variables."),
|
||||||
|
]));
|
||||||
|
stack.push(alloc.reflow("Here is one occurrence:"));
|
||||||
|
}
|
||||||
|
stack.push(alloc.region(lines.convert_region(one_occurrence)));
|
||||||
|
stack.push(alloc.tip().append(alloc.concat(vec![
|
||||||
|
alloc.reflow("Type variables must be bound before the "),
|
||||||
|
alloc.keyword(match kind {
|
||||||
|
AliasKind::Structural => ":",
|
||||||
|
AliasKind::Opaque => ":=",
|
||||||
|
}),
|
||||||
|
alloc.reflow(". Perhaps you intended to add a type parameter to this type?"),
|
||||||
|
])));
|
||||||
|
doc = alloc.stack(stack);
|
||||||
|
|
||||||
|
title = UNBOUND_TYPE_VARIABLE.to_string();
|
||||||
|
severity = Severity::RuntimeError;
|
||||||
|
}
|
||||||
Problem::BadRecursion(entries) => {
|
Problem::BadRecursion(entries) => {
|
||||||
doc = to_circular_def_doc(alloc, lines, &entries);
|
doc = to_circular_def_doc(alloc, lines, &entries);
|
||||||
title = CIRCULAR_DEF.to_string();
|
title = CIRCULAR_DEF.to_string();
|
||||||
|
|
|
@ -76,7 +76,7 @@ mod test_reporting {
|
||||||
}
|
}
|
||||||
|
|
||||||
for var in output.introduced_variables.wildcards {
|
for var in output.introduced_variables.wildcards {
|
||||||
subs.rigid_var(var, "*".into());
|
subs.rigid_var(var.value, "*".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut solve_aliases = roc_solve::solve::Aliases::default();
|
let mut solve_aliases = roc_solve::solve::Aliases::default();
|
||||||
|
@ -8739,4 +8739,136 @@ I need all branches in an `if` to have the same type!
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wildcard_in_alias() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
I : Int *
|
||||||
|
a : I
|
||||||
|
a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── UNBOUND TYPE VARIABLE ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
The definition of `I` has an unbound type variable:
|
||||||
|
|
||||||
|
1│ I : Int *
|
||||||
|
^
|
||||||
|
|
||||||
|
Tip: Type variables must be bound before the `:`. Perhaps you intended
|
||||||
|
to add a type parameter to this type?
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wildcard_in_opaque() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
I := Int *
|
||||||
|
a : I
|
||||||
|
a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── UNBOUND TYPE VARIABLE ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
The definition of `I` has an unbound type variable:
|
||||||
|
|
||||||
|
1│ I := Int *
|
||||||
|
^
|
||||||
|
|
||||||
|
Tip: Type variables must be bound before the `:=`. Perhaps you intended
|
||||||
|
to add a type parameter to this type?
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_wildcards_in_alias() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
I : [ A (Int *), B (Int *) ]
|
||||||
|
a : I
|
||||||
|
a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── UNBOUND TYPE VARIABLE ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
The definition of `I` has 2 unbound type variables.
|
||||||
|
|
||||||
|
Here is one occurrence:
|
||||||
|
|
||||||
|
1│ I : [ A (Int *), B (Int *) ]
|
||||||
|
^
|
||||||
|
|
||||||
|
Tip: Type variables must be bound before the `:`. Perhaps you intended
|
||||||
|
to add a type parameter to this type?
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inference_var_in_alias() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
I : Int _
|
||||||
|
a : I
|
||||||
|
a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── UNBOUND TYPE VARIABLE ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
The definition of `I` has an unbound type variable:
|
||||||
|
|
||||||
|
1│ I : Int _
|
||||||
|
^
|
||||||
|
|
||||||
|
Tip: Type variables must be bound before the `:`. Perhaps you intended
|
||||||
|
to add a type parameter to this type?
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unbound_var_in_alias() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
I : Int a
|
||||||
|
a : I
|
||||||
|
a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── UNBOUND TYPE VARIABLE ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
The definition of `I` has an unbound type variable:
|
||||||
|
|
||||||
|
1│ I : Int a
|
||||||
|
^
|
||||||
|
|
||||||
|
Tip: Type variables must be bound before the `:`. Perhaps you intended
|
||||||
|
to add a type parameter to this type?
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue