mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +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-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"
|
||||
|
||||
[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_parse",
|
||||
"roc_region",
|
||||
"roc_types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -7,7 +7,9 @@ use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
|||
use roc_parse::ast::{AssignedField, Pattern, Tag, TypeAnnotation, TypeHeader};
|
||||
use roc_region::all::{Loc, Region};
|
||||
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)]
|
||||
pub struct Annotation {
|
||||
|
@ -29,25 +31,27 @@ pub struct IntroducedVariables {
|
|||
// But then between annotations, the same name can occur multiple times,
|
||||
// but a variable can only have one name. Therefore
|
||||
// `ftv : SendMap<Variable, Lowercase>`.
|
||||
pub wildcards: Vec<Variable>,
|
||||
pub wildcards: Vec<Loc<Variable>>,
|
||||
pub lambda_sets: Vec<Variable>,
|
||||
pub inferred: Vec<Variable>,
|
||||
pub var_by_name: SendMap<Lowercase, Variable>,
|
||||
pub inferred: Vec<Loc<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 host_exposed_aliases: MutMap<Symbol, Variable>,
|
||||
}
|
||||
|
||||
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.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);
|
||||
}
|
||||
|
||||
pub fn insert_inferred(&mut self, var: Variable) {
|
||||
pub fn insert_inferred(&mut self, var: Loc<Variable>) {
|
||||
self.inferred.push(var);
|
||||
}
|
||||
|
||||
|
@ -70,7 +74,7 @@ impl IntroducedVariables {
|
|||
}
|
||||
|
||||
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> {
|
||||
|
@ -286,7 +290,7 @@ fn can_annotation_help(
|
|||
let ret = can_annotation_help(
|
||||
env,
|
||||
&return_type.value,
|
||||
region,
|
||||
return_type.region,
|
||||
scope,
|
||||
var_store,
|
||||
introduced_variables,
|
||||
|
@ -314,7 +318,7 @@ fn can_annotation_help(
|
|||
let arg_ann = can_annotation_help(
|
||||
env,
|
||||
&arg.value,
|
||||
region,
|
||||
arg.region,
|
||||
scope,
|
||||
var_store,
|
||||
introduced_variables,
|
||||
|
@ -389,7 +393,7 @@ fn can_annotation_help(
|
|||
None => {
|
||||
let var = var_store.fresh();
|
||||
|
||||
introduced_variables.insert_named(name, var);
|
||||
introduced_variables.insert_named(name, Loc::at(region, var));
|
||||
|
||||
Type::Variable(var)
|
||||
}
|
||||
|
@ -453,7 +457,8 @@ fn can_annotation_help(
|
|||
} else {
|
||||
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)));
|
||||
|
||||
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
|
||||
// if there are no fields, still make this a `Record`,
|
||||
// not an EmptyRec
|
||||
Type::Record(Default::default(), Box::new(ext_type))
|
||||
Type::Record(Default::default(), TypeExtension::from_type(ext_type))
|
||||
}
|
||||
|
||||
None => Type::EmptyRec,
|
||||
|
@ -577,7 +582,7 @@ fn can_annotation_help(
|
|||
references,
|
||||
);
|
||||
|
||||
Type::Record(field_types, Box::new(ext_type))
|
||||
Type::Record(field_types, TypeExtension::from_type(ext_type))
|
||||
}
|
||||
}
|
||||
TagUnion { tags, ext, .. } => {
|
||||
|
@ -598,7 +603,7 @@ fn can_annotation_help(
|
|||
// just `a` does not mean the same as `{}a`, so even
|
||||
// if there are no fields, still make this a `Record`,
|
||||
// not an EmptyRec
|
||||
Type::TagUnion(Default::default(), Box::new(ext_type))
|
||||
Type::TagUnion(Default::default(), TypeExtension::from_type(ext_type))
|
||||
}
|
||||
|
||||
None => Type::EmptyTagUnion,
|
||||
|
@ -620,7 +625,7 @@ fn can_annotation_help(
|
|||
// 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));
|
||||
|
||||
Type::TagUnion(tag_types, Box::new(ext_type))
|
||||
Type::TagUnion(tag_types, TypeExtension::from_type(ext_type))
|
||||
}
|
||||
}
|
||||
SpaceBefore(nested, _) | SpaceAfter(nested, _) => can_annotation_help(
|
||||
|
@ -636,7 +641,7 @@ fn can_annotation_help(
|
|||
Wildcard => {
|
||||
let var = var_store.fresh();
|
||||
|
||||
introduced_variables.insert_wildcard(var);
|
||||
introduced_variables.insert_wildcard(Loc::at(region, 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 🤠
|
||||
let var = var_store.fresh();
|
||||
|
||||
introduced_variables.insert_inferred(var);
|
||||
introduced_variables.insert_inferred(Loc::at(region, var));
|
||||
|
||||
Type::Variable(var)
|
||||
}
|
||||
|
@ -655,7 +660,7 @@ fn can_annotation_help(
|
|||
|
||||
let var = var_store.fresh();
|
||||
|
||||
introduced_variables.insert_wildcard(var);
|
||||
introduced_variables.insert_wildcard(Loc::at(region, var));
|
||||
|
||||
Type::Variable(var)
|
||||
}
|
||||
|
@ -721,7 +726,7 @@ fn can_extension_type<'a>(
|
|||
|
||||
let var = var_store.fresh();
|
||||
|
||||
introduced_variables.insert_inferred(var);
|
||||
introduced_variables.insert_inferred(Loc::at_zero(var));
|
||||
|
||||
Type::Variable(var)
|
||||
}
|
||||
|
@ -913,7 +918,10 @@ fn can_assigned_fields<'a>(
|
|||
Type::Variable(*var)
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
};
|
||||
|
|
|
@ -294,14 +294,12 @@ pub fn canonicalize_defs<'a>(
|
|||
let mut can_vars: Vec<Loc<(Lowercase, Variable)>> = Vec::with_capacity(vars.len());
|
||||
let mut is_phantom = false;
|
||||
|
||||
let mut var_by_name = can_ann.introduced_variables.var_by_name.clone();
|
||||
for loc_lowercase in vars.iter() {
|
||||
if let Some(var) = can_ann
|
||||
.introduced_variables
|
||||
.var_by_name(&loc_lowercase.value)
|
||||
{
|
||||
if let Some(var) = var_by_name.remove(&loc_lowercase.value) {
|
||||
// This is a valid lowercase rigid var for the type def.
|
||||
can_vars.push(Loc {
|
||||
value: (loc_lowercase.value.clone(), *var),
|
||||
value: (loc_lowercase.value.clone(), var.value),
|
||||
region: loc_lowercase.region,
|
||||
});
|
||||
} else {
|
||||
|
@ -320,6 +318,33 @@ pub fn canonicalize_defs<'a>(
|
|||
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(
|
||||
symbol,
|
||||
name.region,
|
||||
|
|
|
@ -10,7 +10,7 @@ use roc_module::ident::TagName;
|
|||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::{AliasKind, Type};
|
||||
use roc_types::types::{AliasKind, Type, TypeExtension};
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub(crate) struct HostedGeneratedFunctions {
|
||||
|
@ -210,7 +210,7 @@ fn build_effect_always(
|
|||
let signature = {
|
||||
// Effect.always : a -> Effect a
|
||||
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(
|
||||
effect_symbol,
|
||||
|
@ -223,7 +223,7 @@ fn build_effect_always(
|
|||
);
|
||||
|
||||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(closure_var);
|
||||
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||
|
||||
Type::Function(
|
||||
vec![Type::Variable(var_a)],
|
||||
|
@ -402,8 +402,8 @@ fn build_effect_map(
|
|||
let var_a = var_store.fresh();
|
||||
let var_b = var_store.fresh();
|
||||
|
||||
introduced_variables.insert_named("a".into(), var_a);
|
||||
introduced_variables.insert_named("b".into(), var_b);
|
||||
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
||||
|
||||
let effect_a = build_effect_alias(
|
||||
effect_symbol,
|
||||
|
@ -426,7 +426,7 @@ fn build_effect_map(
|
|||
);
|
||||
|
||||
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 = {
|
||||
Type::Function(
|
||||
vec![Type::Variable(var_a)],
|
||||
|
@ -436,7 +436,7 @@ fn build_effect_map(
|
|||
};
|
||||
|
||||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(closure_var);
|
||||
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||
Type::Function(
|
||||
vec![effect_a, a_to_b],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
|
@ -571,8 +571,8 @@ fn build_effect_after(
|
|||
let var_a = var_store.fresh();
|
||||
let var_b = var_store.fresh();
|
||||
|
||||
introduced_variables.insert_named("a".into(), var_a);
|
||||
introduced_variables.insert_named("b".into(), var_b);
|
||||
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
||||
|
||||
let effect_a = build_effect_alias(
|
||||
effect_symbol,
|
||||
|
@ -595,7 +595,7 @@ fn build_effect_after(
|
|||
);
|
||||
|
||||
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(
|
||||
vec![Type::Variable(var_a)],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
|
@ -603,7 +603,7 @@ fn build_effect_after(
|
|||
);
|
||||
|
||||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(closure_var);
|
||||
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||
Type::Function(
|
||||
vec![effect_a, a_to_effect_b],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
|
@ -831,8 +831,8 @@ fn build_effect_forever(
|
|||
let var_a = var_store.fresh();
|
||||
let var_b = var_store.fresh();
|
||||
|
||||
introduced_variables.insert_named("a".into(), var_a);
|
||||
introduced_variables.insert_named("b".into(), var_b);
|
||||
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
||||
|
||||
let effect_a = build_effect_alias(
|
||||
effect_symbol,
|
||||
|
@ -855,7 +855,7 @@ fn build_effect_forever(
|
|||
);
|
||||
|
||||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(closure_var);
|
||||
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||
|
||||
Type::Function(
|
||||
vec![effect_a],
|
||||
|
@ -1089,8 +1089,8 @@ fn build_effect_loop(
|
|||
let var_a = var_store.fresh();
|
||||
let var_b = var_store.fresh();
|
||||
|
||||
introduced_variables.insert_named("a".into(), var_a);
|
||||
introduced_variables.insert_named("b".into(), var_b);
|
||||
introduced_variables.insert_named("a".into(), Loc::at_zero(var_a));
|
||||
introduced_variables.insert_named("b".into(), Loc::at_zero(var_b));
|
||||
|
||||
let effect_b = build_effect_alias(
|
||||
effect_symbol,
|
||||
|
@ -1111,13 +1111,13 @@ fn build_effect_loop(
|
|||
(step_tag_name, vec![Type::Variable(var_a)]),
|
||||
(done_tag_name, vec![Type::Variable(var_b)]),
|
||||
],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
)
|
||||
};
|
||||
|
||||
let effect_state_type = {
|
||||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(closure_var);
|
||||
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||
|
||||
let actual = {
|
||||
Type::TagUnion(
|
||||
|
@ -1129,7 +1129,7 @@ fn build_effect_loop(
|
|||
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();
|
||||
introduced_variables.insert_wildcard(closure_var);
|
||||
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||
|
||||
let step_type = Type::Function(
|
||||
vec![Type::Variable(var_a)],
|
||||
|
@ -1154,7 +1154,7 @@ fn build_effect_loop(
|
|||
);
|
||||
|
||||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(closure_var);
|
||||
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||
|
||||
Type::Function(
|
||||
vec![Type::Variable(var_a), step_type],
|
||||
|
@ -1559,7 +1559,7 @@ fn build_effect_alias(
|
|||
introduced_variables: &mut IntroducedVariables,
|
||||
) -> Type {
|
||||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(closure_var);
|
||||
introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||
|
||||
let actual = {
|
||||
Type::TagUnion(
|
||||
|
@ -1571,7 +1571,7 @@ fn build_effect_alias(
|
|||
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(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -259,7 +259,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
}
|
||||
|
||||
for var in output.introduced_variables.wildcards {
|
||||
rigid_variables.wildcards.insert(var);
|
||||
rigid_variables.wildcards.insert(var.value);
|
||||
}
|
||||
|
||||
let mut referenced_values = MutSet::default();
|
||||
|
|
|
@ -6,9 +6,9 @@ use roc_module::ident::{Lowercase, TagName};
|
|||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::Region;
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::types::Reason;
|
||||
use roc_types::types::Type::{self, *};
|
||||
use roc_types::types::{AliasKind, Category};
|
||||
use roc_types::types::{Reason, TypeExtension};
|
||||
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
|
@ -190,7 +190,7 @@ pub fn num_floatingpoint(range: Type) -> Type {
|
|||
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
|
||||
vec![range.clone()],
|
||||
)],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
builtin_alias(
|
||||
|
@ -209,7 +209,7 @@ pub fn num_u32() -> Type {
|
|||
fn num_unsigned32() -> Type {
|
||||
let alias_content = Type::TagUnion(
|
||||
vec![(TagName::Private(Symbol::NUM_AT_UNSIGNED32), vec![])],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
builtin_alias(Symbol::NUM_UNSIGNED32, vec![], Box::new(alias_content))
|
||||
|
@ -219,7 +219,7 @@ fn num_unsigned32() -> Type {
|
|||
pub fn num_binary64() -> Type {
|
||||
let alias_content = Type::TagUnion(
|
||||
vec![(TagName::Private(Symbol::NUM_AT_BINARY64), vec![])],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
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 {
|
||||
let alias_content = Type::TagUnion(
|
||||
vec![(TagName::Private(Symbol::NUM_AT_SIGNED64), vec![])],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
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),
|
||||
vec![range.clone()],
|
||||
)],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
builtin_alias(
|
||||
|
@ -265,7 +265,7 @@ pub fn num_integer(range: Type) -> Type {
|
|||
pub fn num_num(typ: Type) -> Type {
|
||||
let alias_content = Type::TagUnion(
|
||||
vec![(TagName::Private(Symbol::NUM_AT_NUM), vec![typ.clone()])],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
builtin_alias(
|
||||
|
|
|
@ -16,7 +16,9 @@ use roc_module::symbol::{ModuleId, Symbol};
|
|||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::Variable;
|
||||
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
|
||||
#[derive(Default, Debug)]
|
||||
|
@ -119,13 +121,7 @@ pub fn constrain_expr(
|
|||
rec_constraints.push(field_con);
|
||||
}
|
||||
|
||||
let record_type = Type::Record(
|
||||
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_type = Type::Record(field_types, TypeExtension::Closed);
|
||||
|
||||
let record_con = constraints.equal_types_with_storage(
|
||||
record_type,
|
||||
|
@ -165,7 +161,8 @@ pub fn constrain_expr(
|
|||
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);
|
||||
|
||||
// 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();
|
||||
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 category = Category::Access(field.clone());
|
||||
|
@ -767,7 +764,7 @@ pub fn constrain_expr(
|
|||
let mut field_types = SendMap::default();
|
||||
let label = field.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());
|
||||
|
||||
|
@ -905,7 +902,7 @@ pub fn constrain_expr(
|
|||
let union_con = constraints.equal_types_with_storage(
|
||||
Type::TagUnion(
|
||||
vec![(name.clone(), types)],
|
||||
Box::new(Type::Variable(*ext_var)),
|
||||
TypeExtension::from_type(Type::Variable(*ext_var)),
|
||||
),
|
||||
expected.clone(),
|
||||
Category::TagApply {
|
||||
|
@ -951,7 +948,7 @@ pub fn constrain_expr(
|
|||
Type::FunctionOrTagUnion(
|
||||
name.clone(),
|
||||
*closure_name,
|
||||
Box::new(Type::Variable(*ext_var)),
|
||||
TypeExtension::from_type(Type::Variable(*ext_var)),
|
||||
),
|
||||
expected.clone(),
|
||||
Category::TagApply {
|
||||
|
@ -1621,7 +1618,7 @@ fn constrain_closure_size(
|
|||
let tag_name = TagName::Closure(name);
|
||||
Type::TagUnion(
|
||||
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>>,
|
||||
) -> InstantiateRigids {
|
||||
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();
|
||||
for (name, var) in introduced_vars.var_by_name.iter() {
|
||||
|
@ -1660,23 +1657,24 @@ fn instantiate_rigids(
|
|||
match ftv.entry(name.clone()) {
|
||||
Occupied(occupied) => {
|
||||
let existing_rigid = occupied.get();
|
||||
rigid_substitution.insert(*var, Type::Variable(*existing_rigid));
|
||||
rigid_substitution.insert(var.value, Type::Variable(*existing_rigid));
|
||||
}
|
||||
Vacant(vacant) => {
|
||||
// It's possible to use this rigid in nested defs
|
||||
vacant.insert(*var);
|
||||
new_rigid_variables.push(*var);
|
||||
vacant.insert(var.value);
|
||||
new_rigid_variables.push(var.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
if !rigid_substitution.is_empty() {
|
||||
|
|
|
@ -9,7 +9,9 @@ use roc_module::ident::Lowercase;
|
|||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
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)]
|
||||
pub struct PatternState {
|
||||
|
@ -391,7 +393,7 @@ pub fn constrain_pattern(
|
|||
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(
|
||||
Type::Variable(*whole_var),
|
||||
|
|
|
@ -36,7 +36,7 @@ use roc_solve::solve;
|
|||
use roc_target::TargetInfo;
|
||||
use roc_types::solved_types::Solved;
|
||||
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::{HashMap, HashSet};
|
||||
use std::io;
|
||||
|
@ -4123,7 +4123,7 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
|||
TagName::Private(Symbol::NUM_AT_NUM),
|
||||
vec![Type::Variable(tvar)],
|
||||
)],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
let alias = Alias {
|
||||
|
@ -4148,7 +4148,7 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
|||
TagName::Private(Symbol::NUM_AT_FLOATINGPOINT),
|
||||
vec![Type::Variable(tvar)],
|
||||
)],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
let alias = Alias {
|
||||
|
@ -4231,7 +4231,7 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
|||
TagName::Private(Symbol::NUM_AT_INTEGER),
|
||||
vec![Type::Variable(tvar)],
|
||||
)],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
let alias = Alias {
|
||||
|
@ -4256,7 +4256,7 @@ fn default_aliases() -> roc_solve::solve::Aliases {
|
|||
(TagName::Global("Ok".into()), vec![Type::Variable(tvar1)]),
|
||||
(TagName::Global("Err".into()), vec![Type::Variable(tvar2)]),
|
||||
],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
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 typ = Type::TagUnion(
|
||||
vec![(TagName::Private(at_tag_name), vec![])],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
|
||||
let alias = Alias {
|
||||
|
|
|
@ -112,6 +112,11 @@ pub struct PartialProcs<'a> {
|
|||
/// maps a function name (symbol) to an index
|
||||
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>>,
|
||||
}
|
||||
|
||||
|
@ -119,6 +124,7 @@ impl<'a> PartialProcs<'a> {
|
|||
fn new_in(arena: &'a Bump) -> Self {
|
||||
Self {
|
||||
symbols: Vec::new_in(arena),
|
||||
references: 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()
|
||||
}
|
||||
|
||||
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
|
||||
.iter()
|
||||
.position(|s| *s == symbol)
|
||||
|
@ -157,6 +172,21 @@ impl<'a> PartialProcs<'a> {
|
|||
|
||||
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)]
|
||||
|
@ -6680,10 +6710,10 @@ where
|
|||
}
|
||||
|
||||
// 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
|
||||
// rest of the program now to get our hole.
|
||||
let mut result = build_rest(env, procs, layout_cache);
|
||||
// whose usages will already be specialized in the rest of the program.
|
||||
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
|
||||
// specialized, and wrap the original in a function pointer.
|
||||
add_needed_external(procs, env, variable, right);
|
||||
|
@ -6693,25 +6723,25 @@ where
|
|||
|
||||
force_thunk(env, right, layout, left, env.arena.alloc(result))
|
||||
} 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
|
||||
// specialized, and wrap the original in a function pointer.
|
||||
add_needed_external(procs, env, variable, right);
|
||||
|
||||
// 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))
|
||||
} 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 {
|
||||
// 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);
|
||||
|
||||
// if the substituted variable is a function, make sure we specialize it
|
||||
reuse_function_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
Some(variable),
|
||||
right,
|
||||
result,
|
||||
right,
|
||||
)
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1994,7 +1994,15 @@ pub fn union_sorted_tags<'a>(
|
|||
|
||||
let mut tags_vec = std::vec::Vec::new();
|
||||
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);
|
||||
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
|
||||
let mut ext_fields = std::vec::Vec::new();
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,4 +9,5 @@ edition = "2018"
|
|||
roc_collections = { path = "../collections" }
|
||||
roc_region = { path = "../region" }
|
||||
roc_module = { path = "../module" }
|
||||
roc_types = { path = "../types" }
|
||||
roc_parse = { path = "../parse" }
|
||||
|
|
|
@ -5,6 +5,7 @@ use roc_module::symbol::{ModuleId, Symbol};
|
|||
use roc_parse::ast::Base;
|
||||
use roc_parse::pattern::PatternType;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::types::AliasKind;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct CycleEntry {
|
||||
|
@ -43,6 +44,12 @@ pub enum Problem {
|
|||
variable_region: Region,
|
||||
variable_name: Lowercase,
|
||||
},
|
||||
UnboundTypeVariable {
|
||||
typ: Symbol,
|
||||
num_unbound: usize,
|
||||
one_occurrence: Region,
|
||||
kind: AliasKind,
|
||||
},
|
||||
DuplicateRecordFieldValue {
|
||||
field_name: Lowercase,
|
||||
record_region: Region,
|
||||
|
|
|
@ -14,6 +14,7 @@ use roc_types::subs::{
|
|||
use roc_types::types::Type::{self, *};
|
||||
use roc_types::types::{
|
||||
gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, ErrorType, PatternCategory,
|
||||
TypeExtension,
|
||||
};
|
||||
use roc_unify::unify::{unify, Mode, Unified::*};
|
||||
|
||||
|
@ -1111,7 +1112,7 @@ fn solve(
|
|||
let actual = type_to_var(subs, rank, pools, aliases, typ);
|
||||
let tag_ty = Type::TagUnion(
|
||||
vec![(tag_name.clone(), tys.to_vec())],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
TypeExtension::Closed,
|
||||
);
|
||||
let includes = type_to_var(subs, rank, pools, aliases, &tag_ty);
|
||||
|
||||
|
@ -1425,7 +1426,7 @@ fn type_to_variable<'a>(
|
|||
Record(fields, ext) => {
|
||||
// An empty fields is inefficient (but would be correct)
|
||||
// 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);
|
||||
|
||||
|
@ -1442,7 +1443,10 @@ fn type_to_variable<'a>(
|
|||
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) =
|
||||
gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var)
|
||||
|
@ -1465,7 +1469,7 @@ fn type_to_variable<'a>(
|
|||
TagUnion(tags, ext) => {
|
||||
// An empty tags is inefficient (but would be correct)
|
||||
// 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) =
|
||||
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)
|
||||
}
|
||||
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(
|
||||
subs,
|
||||
|
@ -1496,7 +1503,7 @@ fn type_to_variable<'a>(
|
|||
RecursiveTagUnion(rec_var, tags, ext) => {
|
||||
// An empty tags is inefficient (but would be correct)
|
||||
// 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) =
|
||||
type_to_union_tags(subs, rank, pools, arena, tags, ext, &mut stack);
|
||||
|
@ -1681,7 +1688,7 @@ fn roc_result_to_var<'a>(
|
|||
) -> Variable {
|
||||
match result_type {
|
||||
Type::TagUnion(tags, ext) => {
|
||||
debug_assert!(ext.is_empty_tag_union());
|
||||
debug_assert!(ext.is_closed());
|
||||
debug_assert!(tags.len() == 2);
|
||||
|
||||
if let [(err, err_args), (ok, ok_args)] = &tags[..] {
|
||||
|
@ -1925,14 +1932,15 @@ fn type_to_union_tags<'a>(
|
|||
pools: &mut Pools,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
tags: &'a [(TagName, Vec<Type>)],
|
||||
ext: &'a Type,
|
||||
ext: &'a TypeExtension,
|
||||
stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>,
|
||||
) -> (UnionTags, Variable) {
|
||||
use bumpalo::collections::Vec;
|
||||
|
||||
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 union_tags = if sorted {
|
||||
|
@ -1943,12 +1951,16 @@ fn type_to_union_tags<'a>(
|
|||
};
|
||||
|
||||
(union_tags, ext)
|
||||
} else {
|
||||
}
|
||||
TypeExtension::Open(ext) => {
|
||||
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 (it, ext) =
|
||||
roc_types::types::gather_tags_unsorted_iter(subs, UnionTags::default(), temp_ext_var);
|
||||
let (it, ext) = roc_types::types::gather_tags_unsorted_iter(
|
||||
subs,
|
||||
UnionTags::default(),
|
||||
temp_ext_var,
|
||||
);
|
||||
|
||||
tag_vars.extend(it.map(|(n, v)| (n.clone(), v)));
|
||||
|
||||
|
@ -1961,6 +1973,7 @@ fn type_to_union_tags<'a>(
|
|||
(union_tags, ext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_infinite_type(
|
||||
subs: &mut Subs,
|
||||
|
|
|
@ -1548,3 +1548,35 @@ fn issue_1162() {
|
|||
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;
|
||||
ret Test.12;
|
||||
|
||||
procedure Test.4 (Test.7, Test.8):
|
||||
let Test.17 : U64 = 1i64;
|
||||
ret Test.17;
|
||||
|
||||
procedure Test.4 (Test.7, Test.8):
|
||||
procedure Test.5 (Test.7, Test.8):
|
||||
let Test.18 : U64 = 1i64;
|
||||
ret Test.18;
|
||||
|
||||
procedure Test.6 (Test.7, Test.8):
|
||||
let Test.15 : U64 = 1i64;
|
||||
ret Test.15;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.4 : {} = Struct {};
|
||||
let Test.4 : {} = Struct {};
|
||||
let Test.15 : U8 = 100i64;
|
||||
let Test.16 : U32 = 100i64;
|
||||
let Test.10 : U64 = CallByName Test.4 Test.15 Test.16;
|
||||
let Test.16 : U8 = 100i64;
|
||||
let Test.17 : U32 = 100i64;
|
||||
let Test.10 : U64 = CallByName Test.5 Test.16 Test.17;
|
||||
let Test.13 : U32 = 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;
|
||||
ret Test.9;
|
||||
|
|
|
@ -36,9 +36,9 @@ procedure Test.8 (Test.11, #Attr.12):
|
|||
ret Test.11;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.5 : I64 = 2i64;
|
||||
let Test.6 : Int1 = true;
|
||||
let Test.4 : I64 = 1i64;
|
||||
let Test.5 : I64 = 2i64;
|
||||
joinpoint Test.22 Test.14:
|
||||
let Test.15 : I64 = 42i64;
|
||||
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;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.4 : I64 = 1i64;
|
||||
let Test.5 : I64 = 2i64;
|
||||
let Test.4 : I64 = 1i64;
|
||||
let Test.12 : I64 = 42i64;
|
||||
joinpoint Test.19 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]
|
||||
// #[mono_test]
|
||||
// fn static_str_closure() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
|
@ -113,7 +113,11 @@ impl SolvedType {
|
|||
SolvedType::Func(solved_args, Box::new(solved_closure), Box::new(solved_ret))
|
||||
}
|
||||
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());
|
||||
|
||||
for (label, field) in fields {
|
||||
|
@ -139,7 +143,11 @@ impl SolvedType {
|
|||
SolvedType::TagUnion(solved_tags, Box::new(solved_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());
|
||||
for (tag_name, types) in tags {
|
||||
let mut solved_types = Vec::with_capacity(types.len());
|
||||
|
@ -155,11 +163,19 @@ impl SolvedType {
|
|||
SolvedType::TagUnion(solved_tags, Box::new(solved_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))
|
||||
}
|
||||
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());
|
||||
for (tag_name, types) in tags {
|
||||
let mut solved_types = Vec::with_capacity(types.len());
|
||||
|
@ -523,7 +539,12 @@ pub fn to_type(
|
|||
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,
|
||||
EmptyTagUnion => Type::EmptyTagUnion,
|
||||
|
@ -540,13 +561,21 @@ pub fn to_type(
|
|||
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) => {
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
.unnamed_vars
|
||||
.get(rec_var_id)
|
||||
.expect("rec var not in unnamed vars");
|
||||
|
||||
Type::RecursiveTagUnion(
|
||||
*rec_var,
|
||||
new_tags,
|
||||
Box::new(to_type(ext, free_vars, var_store)),
|
||||
)
|
||||
Type::RecursiveTagUnion(*rec_var, new_tags, ext)
|
||||
}
|
||||
DelayedAlias(symbol, solved_type_variables, solved_lambda_sets) => {
|
||||
let mut type_variables = Vec::with_capacity(solved_type_variables.len());
|
||||
|
|
|
@ -193,9 +193,9 @@ pub enum Type {
|
|||
EmptyTagUnion,
|
||||
/// 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>),
|
||||
Record(SendMap<Lowercase, RecordField<Type>>, Box<Type>),
|
||||
TagUnion(Vec<(TagName, Vec<Type>)>, Box<Type>),
|
||||
FunctionOrTagUnion(TagName, Symbol, Box<Type>),
|
||||
Record(SendMap<Lowercase, RecordField<Type>>, TypeExtension),
|
||||
TagUnion(Vec<(TagName, Vec<Type>)>, TypeExtension),
|
||||
FunctionOrTagUnion(TagName, Symbol, TypeExtension),
|
||||
/// A function name that is used in our defunctionalization algorithm
|
||||
ClosureTag {
|
||||
name: Symbol,
|
||||
|
@ -216,7 +216,7 @@ pub enum Type {
|
|||
actual_var: Variable,
|
||||
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)
|
||||
Apply(Symbol, Vec<Type>, Region),
|
||||
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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
|
@ -422,12 +459,12 @@ impl fmt::Debug for Type {
|
|||
|
||||
write!(f, "}}")?;
|
||||
|
||||
match *ext.clone() {
|
||||
Type::EmptyRec => {
|
||||
match ext {
|
||||
TypeExtension::Closed => {
|
||||
// This is a closed record. We're done!
|
||||
Ok(())
|
||||
}
|
||||
other => {
|
||||
TypeExtension::Open(other) => {
|
||||
// This is an open record, so print the variable
|
||||
// right after the '}'
|
||||
//
|
||||
|
@ -463,12 +500,12 @@ impl fmt::Debug for Type {
|
|||
|
||||
write!(f, "]")?;
|
||||
|
||||
match *ext.clone() {
|
||||
Type::EmptyTagUnion => {
|
||||
match ext {
|
||||
TypeExtension::Closed => {
|
||||
// This is a closed variant. We're done!
|
||||
Ok(())
|
||||
}
|
||||
other => {
|
||||
TypeExtension::Open(other) => {
|
||||
// This is an open tag union, so print the variable
|
||||
// right after the ']'
|
||||
//
|
||||
|
@ -483,12 +520,12 @@ impl fmt::Debug for Type {
|
|||
write!(f, "{:?}", tag_name)?;
|
||||
write!(f, "]")?;
|
||||
|
||||
match *ext.clone() {
|
||||
Type::EmptyTagUnion => {
|
||||
match ext {
|
||||
TypeExtension::Closed => {
|
||||
// This is a closed variant. We're done!
|
||||
Ok(())
|
||||
}
|
||||
other => {
|
||||
TypeExtension::Open(other) => {
|
||||
// This is an open tag union, so print the variable
|
||||
// right after the ']'
|
||||
//
|
||||
|
@ -533,19 +570,20 @@ impl fmt::Debug for Type {
|
|||
|
||||
write!(f, "]")?;
|
||||
|
||||
match *ext.clone() {
|
||||
Type::EmptyTagUnion => {
|
||||
match ext {
|
||||
TypeExtension::Closed => {
|
||||
// This is a closed variant. We're done!
|
||||
Ok(())
|
||||
}
|
||||
other => {
|
||||
TypeExtension::Open(other) => {
|
||||
// This is an open tag union, so print the variable
|
||||
// right after the ']'
|
||||
//
|
||||
// e.g. the "*" at the end of `[ Foo ]*`
|
||||
// or the "r" at the end of `[ DivByZero ]r`
|
||||
other.fmt(f)?;
|
||||
}
|
||||
other.fmt(f)
|
||||
}
|
||||
}?;
|
||||
|
||||
write!(f, " as <{:?}>", rec)
|
||||
}
|
||||
|
@ -611,23 +649,34 @@ impl Type {
|
|||
for (_, args) in tags {
|
||||
stack.extend(args.iter_mut());
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext) => {
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
RecursiveTagUnion(_, tags, ext) => {
|
||||
for (_, args) in tags {
|
||||
stack.extend(args.iter_mut());
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
Record(fields, ext) => {
|
||||
for (_, x) in fields.iter_mut() {
|
||||
stack.push(x.as_inner_mut());
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
Type::DelayedAlias(AliasCommon {
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
|
@ -650,6 +699,7 @@ impl Type {
|
|||
for (_, value) in type_arguments.iter_mut() {
|
||||
stack.push(value);
|
||||
}
|
||||
|
||||
for lambda_set in lambda_set_variables.iter_mut() {
|
||||
stack.push(lambda_set.as_inner_mut());
|
||||
}
|
||||
|
@ -705,11 +755,16 @@ impl Type {
|
|||
for (_, args) in tags {
|
||||
stack.extend(args.iter_mut());
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext) => {
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
RecursiveTagUnion(rec_var, tags, ext) => {
|
||||
if let Some(replacement) = substitutions.get(rec_var) {
|
||||
*rec_var = *replacement;
|
||||
|
@ -718,14 +773,19 @@ impl Type {
|
|||
for (_, args) in tags {
|
||||
stack.extend(args.iter_mut());
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
Record(fields, ext) => {
|
||||
for (_, x) in fields.iter_mut() {
|
||||
stack.push(x.as_inner_mut());
|
||||
}
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
Type::DelayedAlias(AliasCommon {
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
|
@ -800,20 +860,31 @@ impl Type {
|
|||
closure.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) => {
|
||||
for (_, args) in tags {
|
||||
for x in args {
|
||||
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) => {
|
||||
for (_, x) in fields.iter_mut() {
|
||||
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, .. }) => {
|
||||
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 {
|
||||
use Type::*;
|
||||
|
||||
|
@ -871,9 +949,9 @@ impl Type {
|
|||
|| closure.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) => {
|
||||
ext.contains_symbol(rep_symbol)
|
||||
Self::contains_symbol_ext(ext, rep_symbol)
|
||||
|| tags
|
||||
.iter()
|
||||
.map(|v| v.1.iter())
|
||||
|
@ -882,7 +960,7 @@ impl Type {
|
|||
}
|
||||
|
||||
Record(fields, ext) => {
|
||||
ext.contains_symbol(rep_symbol)
|
||||
Self::contains_symbol_ext(ext, rep_symbol)
|
||||
|| fields.values().any(|arg| arg.contains_symbol(rep_symbol))
|
||||
}
|
||||
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 {
|
||||
use Type::*;
|
||||
|
||||
|
@ -920,9 +1005,9 @@ impl Type {
|
|||
|| closure.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) => {
|
||||
ext.contains_variable(rep_variable)
|
||||
Self::contains_variable_ext(ext, rep_variable)
|
||||
|| tags
|
||||
.iter()
|
||||
.map(|v| v.1.iter())
|
||||
|
@ -931,7 +1016,7 @@ impl Type {
|
|||
}
|
||||
|
||||
Record(fields, ext) => {
|
||||
ext.contains_variable(rep_variable)
|
||||
Self::contains_variable_ext(ext, rep_variable)
|
||||
|| fields
|
||||
.values()
|
||||
.any(|arg| arg.contains_variable(rep_variable))
|
||||
|
@ -983,22 +1068,30 @@ impl Type {
|
|||
ret.instantiate_aliases(region, aliases, var_store, introduced);
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext) => {
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
||||
}
|
||||
}
|
||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||
for (_, args) in tags {
|
||||
for x in args {
|
||||
x.instantiate_aliases(region, aliases, var_store, introduced);
|
||||
}
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
||||
}
|
||||
}
|
||||
Record(fields, ext) => {
|
||||
for (_, x) in fields.iter_mut() {
|
||||
x.instantiate_aliases(region, aliases, var_store, introduced);
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
||||
}
|
||||
}
|
||||
DelayedAlias(AliasCommon { .. }) => {
|
||||
// do nothing, yay
|
||||
}
|
||||
|
@ -1084,7 +1177,10 @@ impl Type {
|
|||
for typ in tags.iter_mut().map(|v| v.1.iter_mut()).flatten() {
|
||||
typ.substitute(&substitution);
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = &mut ext {
|
||||
ext.substitute(&substitution);
|
||||
}
|
||||
|
||||
actual = Type::RecursiveTagUnion(new_rec_var, tags, ext);
|
||||
}
|
||||
|
@ -1145,14 +1241,17 @@ impl Type {
|
|||
pub fn is_narrow(&self) -> bool {
|
||||
match self.shallow_dealias() {
|
||||
Type::TagUnion(tags, ext) | Type::RecursiveTagUnion(_, tags, ext) => {
|
||||
ext.is_empty_tag_union()
|
||||
matches!(ext, TypeExtension::Closed)
|
||||
&& tags.len() == 1
|
||||
&& tags[0].1.len() == 1
|
||||
&& 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()
|
||||
}
|
||||
TypeExtension::Closed => fields.values().all(|field| field.as_inner().is_narrow()),
|
||||
},
|
||||
Type::Function(args, clos, ret) => {
|
||||
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);
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext) => {
|
||||
stack.push(ext);
|
||||
stack.extend(ext);
|
||||
}
|
||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||
stack.push(ext);
|
||||
stack.extend(ext);
|
||||
stack.extend(tags.iter().map(|v| v.1.iter()).flatten());
|
||||
}
|
||||
|
||||
Record(fields, ext) => {
|
||||
stack.push(ext);
|
||||
stack.extend(ext);
|
||||
stack.extend(fields.values().map(|field| field.as_inner()));
|
||||
}
|
||||
DelayedAlias(AliasCommon {
|
||||
|
@ -1269,26 +1368,37 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
|||
Demanded(x) => variables_help(x, accum),
|
||||
};
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
variables_help(ext, accum);
|
||||
}
|
||||
}
|
||||
TagUnion(tags, ext) => {
|
||||
for (_, args) in tags {
|
||||
for x in args {
|
||||
variables_help(x, accum);
|
||||
}
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
variables_help(ext, accum);
|
||||
}
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext) => {
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
variables_help(ext, accum);
|
||||
}
|
||||
}
|
||||
RecursiveTagUnion(rec, tags, ext) => {
|
||||
for (_, args) in tags {
|
||||
for x in args {
|
||||
variables_help(x, accum);
|
||||
}
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
variables_help(ext, accum);
|
||||
}
|
||||
|
||||
// just check that this is actually a recursive type
|
||||
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),
|
||||
};
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
variables_help_detailed(ext, accum);
|
||||
}
|
||||
}
|
||||
TagUnion(tags, ext) => {
|
||||
for (_, args) in tags {
|
||||
for x in args {
|
||||
variables_help_detailed(x, accum);
|
||||
}
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
variables_help_detailed(ext, accum);
|
||||
}
|
||||
}
|
||||
FunctionOrTagUnion(_, _, ext) => {
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
variables_help_detailed(ext, accum);
|
||||
}
|
||||
}
|
||||
RecursiveTagUnion(rec, tags, ext) => {
|
||||
for (_, args) in tags {
|
||||
for x in args {
|
||||
variables_help_detailed(x, accum);
|
||||
}
|
||||
}
|
||||
|
||||
if let TypeExtension::Open(ext) = ext {
|
||||
variables_help_detailed(ext, accum);
|
||||
}
|
||||
|
||||
// just check that this is actually a recursive type
|
||||
// debug_assert!(accum.type_variables.contains(rec));
|
||||
|
|
|
@ -26,7 +26,6 @@ roc_target = {path = "../compiler/roc_target"}
|
|||
roc_types = {path = "../compiler/types"}
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook"]
|
||||
wasmer = ["futures"]
|
||||
|
||||
[package.metadata.wasm-pack.profile.profiling]
|
||||
|
|
|
@ -3,7 +3,7 @@ mod repl;
|
|||
//
|
||||
// Interface with external JS in the browser
|
||||
//
|
||||
#[cfg(not(feature = "wasmer"))]
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
extern crate console_error_panic_hook;
|
||||
#[cfg(not(feature = "wasmer"))]
|
||||
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> {
|
||||
#[cfg(not(feature = "wasmer"))]
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let arena = &Bump::new();
|
||||
|
|
|
@ -26,11 +26,11 @@ cp -r repl_www/public/* $WWW_ROOT
|
|||
if [ -n "${REPL_DEBUG:-}" ]
|
||||
then
|
||||
# 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/
|
||||
else
|
||||
# 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
|
||||
|
||||
cp repl_wasm/pkg/*.wasm $WWW_ROOT
|
||||
|
|
|
@ -5,6 +5,7 @@ use roc_problem::can::{
|
|||
BadPattern, ExtensionTypeKind, FloatErrorKind, IntErrorKind, Problem, RuntimeError,
|
||||
};
|
||||
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Loc, Region};
|
||||
use roc_types::types::AliasKind;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::error::r#type::suggest;
|
||||
|
@ -17,6 +18,7 @@ const UNRECOGNIZED_NAME: &str = "UNRECOGNIZED NAME";
|
|||
const UNUSED_DEF: &str = "UNUSED DEFINITION";
|
||||
const UNUSED_IMPORT: &str = "UNUSED IMPORT";
|
||||
const UNUSED_ALIAS_PARAM: &str = "UNUSED TYPE ALIAS PARAMETER";
|
||||
const UNBOUND_TYPE_VARIABLE: &str = "UNBOUND TYPE VARIABLE";
|
||||
const UNUSED_ARG: &str = "UNUSED ARGUMENT";
|
||||
const MISSING_DEFINITION: &str = "MISSING DEFINITION";
|
||||
const UNKNOWN_GENERATES_WITH: &str = "UNKNOWN GENERATES FUNCTION";
|
||||
|
@ -252,6 +254,43 @@ pub fn can_problem<'b>(
|
|||
title = UNUSED_ALIAS_PARAM.to_string();
|
||||
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) => {
|
||||
doc = to_circular_def_doc(alloc, lines, &entries);
|
||||
title = CIRCULAR_DEF.to_string();
|
||||
|
|
|
@ -76,7 +76,7 @@ mod test_reporting {
|
|||
}
|
||||
|
||||
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();
|
||||
|
@ -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