mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into wasm-link-host-to-app-calls
This commit is contained in:
commit
d045f3be7b
24 changed files with 575 additions and 2027 deletions
|
@ -361,11 +361,12 @@ pub fn build_swift_host_native(
|
|||
unimplemented!("Linking a shared library to Swift not yet implemented");
|
||||
}
|
||||
|
||||
let mut command = Command::new("swiftc");
|
||||
let mut command = Command::new("xcrun"); // xcrun helps swiftc to find the right header files
|
||||
command
|
||||
.env_clear()
|
||||
.env("PATH", &env_path)
|
||||
.env("HOME", &env_home)
|
||||
.arg("swiftc")
|
||||
.args(sources)
|
||||
.arg("-emit-object")
|
||||
.arg("-parse-as-library")
|
||||
|
|
|
@ -867,43 +867,41 @@ fn resolve_abilities<'a>(
|
|||
.iter()
|
||||
.partition(|av| av.ability == loc_ability_name.value);
|
||||
|
||||
let mut bad_has_clauses = false;
|
||||
|
||||
if variables_bound_to_ability.is_empty() {
|
||||
// There are no variables bound to the parent ability - then this member doesn't
|
||||
// need to be a part of the ability.
|
||||
env.problem(Problem::AbilityMemberMissingHasClause {
|
||||
member: member_sym,
|
||||
ability: loc_ability_name.value,
|
||||
region: name_region,
|
||||
});
|
||||
bad_has_clauses = true;
|
||||
}
|
||||
|
||||
if variables_bound_to_ability.len() > 1 {
|
||||
// There is more than one variable bound to the member signature, so something like
|
||||
// Eq has eq : a, b -> Bool | a has Eq, b has Eq
|
||||
// We have no way of telling what type implements a particular instance of Eq in
|
||||
// this case (a or b?), so disallow it.
|
||||
let span_has_clauses =
|
||||
Region::across_all(variables_bound_to_ability.iter().map(|v| &v.first_seen));
|
||||
let bound_var_names = variables_bound_to_ability
|
||||
.iter()
|
||||
.map(|v| v.name.clone())
|
||||
.collect();
|
||||
env.problem(Problem::AbilityMemberMultipleBoundVars {
|
||||
member: member_sym,
|
||||
ability: loc_ability_name.value,
|
||||
span_has_clauses,
|
||||
bound_var_names,
|
||||
});
|
||||
bad_has_clauses = true;
|
||||
}
|
||||
|
||||
if bad_has_clauses {
|
||||
// Pretend the member isn't a part of the ability
|
||||
continue;
|
||||
}
|
||||
let var_bound_to_ability = match variables_bound_to_ability.as_slice() {
|
||||
[one] => one.variable,
|
||||
[] => {
|
||||
// There are no variables bound to the parent ability - then this member doesn't
|
||||
// need to be a part of the ability.
|
||||
env.problem(Problem::AbilityMemberMissingHasClause {
|
||||
member: member_sym,
|
||||
ability: loc_ability_name.value,
|
||||
region: name_region,
|
||||
});
|
||||
// Pretend the member isn't a part of the ability
|
||||
continue;
|
||||
}
|
||||
[..] => {
|
||||
// There is more than one variable bound to the member signature, so something like
|
||||
// Eq has eq : a, b -> Bool | a has Eq, b has Eq
|
||||
// We have no way of telling what type implements a particular instance of Eq in
|
||||
// this case (a or b?), so disallow it.
|
||||
let span_has_clauses = Region::across_all(
|
||||
variables_bound_to_ability.iter().map(|v| &v.first_seen),
|
||||
);
|
||||
let bound_var_names = variables_bound_to_ability
|
||||
.iter()
|
||||
.map(|v| v.name.clone())
|
||||
.collect();
|
||||
env.problem(Problem::AbilityMemberMultipleBoundVars {
|
||||
member: member_sym,
|
||||
ability: loc_ability_name.value,
|
||||
span_has_clauses,
|
||||
bound_var_names,
|
||||
});
|
||||
// Pretend the member isn't a part of the ability
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// The introduced variables are good; add them to the output.
|
||||
output
|
||||
|
@ -918,6 +916,13 @@ fn resolve_abilities<'a>(
|
|||
flex_vars: iv.collect_flex(),
|
||||
};
|
||||
|
||||
let signature = {
|
||||
let mut signature = member_annot.typ;
|
||||
signature
|
||||
.instantiate_lambda_sets_as_unspecialized(var_bound_to_ability, member_sym);
|
||||
signature
|
||||
};
|
||||
|
||||
can_members.push((
|
||||
member_sym,
|
||||
AbilityMemberData {
|
||||
|
@ -925,7 +930,7 @@ fn resolve_abilities<'a>(
|
|||
region: name_region,
|
||||
typ: MemberTypeInfo::Local {
|
||||
variables,
|
||||
signature: member_annot.typ,
|
||||
signature,
|
||||
signature_var: var_store.fresh(),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1685,7 +1685,6 @@ impl LocalDefVarsVec<(Symbol, Loc<Variable>)> {
|
|||
}
|
||||
}
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::cell::RefCell;
|
||||
use std::ops::ControlFlow;
|
||||
std::thread_local! {
|
||||
|
@ -1888,6 +1887,18 @@ fn type_to_variable<'a>(
|
|||
|
||||
register_with_known_var(subs, destination, rank, pools, content)
|
||||
}
|
||||
UnspecializedLambdaSet(..) => {
|
||||
// TODO: instantiate properly!
|
||||
let union_lambdas =
|
||||
UnionLambdas::from_slices(SubsSlice::new(0, 0), SubsSlice::new(0, 0));
|
||||
|
||||
let content = Content::LambdaSet(subs::LambdaSet {
|
||||
solved: union_lambdas,
|
||||
recursion_var: OptVariable::NONE,
|
||||
});
|
||||
|
||||
register_with_known_var(subs, destination, rank, pools, content)
|
||||
}
|
||||
// This case is important for the rank of boolean variables
|
||||
Function(arguments, closure_type, ret_type) => {
|
||||
let new_arguments = VariableSubsSlice::reserve_into_subs(subs, arguments.len());
|
||||
|
@ -2379,12 +2390,12 @@ fn insert_tags_fast_path<'a>(
|
|||
rank: Rank,
|
||||
pools: &mut Pools,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
tags: &'a [(TagName, impl Borrow<[Type]>)],
|
||||
tags: &'a [(TagName, Vec<Type>)],
|
||||
stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>,
|
||||
) -> UnionTags {
|
||||
if let [(TagName(tag_name), arguments)] = tags {
|
||||
let variable_slice =
|
||||
register_tag_arguments(subs, rank, pools, arena, stack, arguments.borrow());
|
||||
register_tag_arguments(subs, rank, pools, arena, stack, arguments.as_slice());
|
||||
let new_variable_slices =
|
||||
SubsSlice::extend_new(&mut subs.variable_slices, [variable_slice]);
|
||||
|
||||
|
@ -2411,7 +2422,7 @@ fn insert_tags_fast_path<'a>(
|
|||
|
||||
for (variable_slice_index, (_, arguments)) in it {
|
||||
subs.variable_slices[variable_slice_index] =
|
||||
register_tag_arguments(subs, rank, pools, arena, stack, arguments.borrow());
|
||||
register_tag_arguments(subs, rank, pools, arena, stack, arguments.as_slice());
|
||||
}
|
||||
|
||||
UnionTags::from_slices(new_tag_names, new_variable_slices)
|
||||
|
@ -2425,7 +2436,7 @@ fn insert_tags_fast_path<'a>(
|
|||
|
||||
for ((variable_slice_index, tag_name_index), (tag_name, arguments)) in it {
|
||||
subs.variable_slices[variable_slice_index] =
|
||||
register_tag_arguments(subs, rank, pools, arena, stack, arguments.borrow());
|
||||
register_tag_arguments(subs, rank, pools, arena, stack, arguments.as_slice());
|
||||
|
||||
subs.tag_names[tag_name_index] = tag_name.clone();
|
||||
}
|
||||
|
@ -2440,12 +2451,12 @@ fn insert_tags_slow_path<'a>(
|
|||
rank: Rank,
|
||||
pools: &mut Pools,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
tags: &'a [(TagName, impl Borrow<[Type]>)],
|
||||
tags: &'a [(TagName, Vec<Type>)],
|
||||
mut tag_vars: bumpalo::collections::Vec<(TagName, VariableSubsSlice)>,
|
||||
stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>,
|
||||
) -> UnionTags {
|
||||
for (tag, tag_argument_types) in tags {
|
||||
let tag_argument_types: &[Type] = tag_argument_types.borrow();
|
||||
let tag_argument_types: &[Type] = tag_argument_types.as_slice();
|
||||
let new_slice = VariableSubsSlice::reserve_into_subs(subs, tag_argument_types.len());
|
||||
|
||||
for (i, arg) in (new_slice.indices()).zip(tag_argument_types) {
|
||||
|
@ -2466,7 +2477,7 @@ fn type_to_union_tags<'a>(
|
|||
rank: Rank,
|
||||
pools: &mut Pools,
|
||||
arena: &'_ bumpalo::Bump,
|
||||
tags: &'a [(TagName, impl Borrow<[Type]>)],
|
||||
tags: &'a [(TagName, Vec<Type>)],
|
||||
ext: &'a TypeExtension,
|
||||
stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>,
|
||||
) -> (UnionTags, Variable) {
|
||||
|
|
|
@ -2504,7 +2504,7 @@ mod solve_expr {
|
|||
{ numIdentity, x : numIdentity 42, y }
|
||||
"#
|
||||
),
|
||||
"{ numIdentity : Num a -> Num a, x : Num a, y : Float * }",
|
||||
"{ numIdentity : Num a -> Num a, x : Num b, y : Float * }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3395,7 +3395,7 @@ mod solve_expr {
|
|||
{ id1, id2 }
|
||||
"#
|
||||
),
|
||||
"{ id1 : q -> q, id2 : q -> q }",
|
||||
"{ id1 : q -> q, id2 : a -> a }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3910,7 +3910,7 @@ mod solve_expr {
|
|||
{ a, b }
|
||||
"#
|
||||
),
|
||||
"{ a : { x : I64, y : I64, z : Num c }, b : { blah : Str, x : I64, y : I64, z : Num c } }",
|
||||
"{ a : { x : I64, y : I64, z : Num c }, b : { blah : Str, x : I64, y : I64, z : Num a } }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3941,7 +3941,7 @@ mod solve_expr {
|
|||
{ a, b }
|
||||
"#
|
||||
),
|
||||
"{ a : { x : Num a, y : Float *, z : c }, b : { blah : Str, x : Num a, y : Float *, z : c } }",
|
||||
"{ a : { x : Num a, y : Float *, z : c }, b : { blah : Str, x : Num b, y : Float *, z : d } }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -6100,7 +6100,7 @@ mod solve_expr {
|
|||
hashEq = \x, y -> hash x == hash y
|
||||
"#
|
||||
),
|
||||
"a, a -> Bool | a has Hash",
|
||||
"a, b -> Bool | a has Hash, b has Hash",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,5 @@ roc_region = { path = "../region" }
|
|||
roc_module = { path = "../module" }
|
||||
roc_error_macros = {path="../../error_macros"}
|
||||
roc_debug_flags = {path="../debug_flags"}
|
||||
ven_ena = { path = "../../vendor/ena" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
static_assertions = "1.1.0"
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::subs::{
|
|||
UnionTags, Variable,
|
||||
};
|
||||
use crate::types::{name_type_var, RecordField};
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
|
@ -58,6 +58,7 @@ struct Env<'a> {
|
|||
/// We only care about whether it was a single time or multiple times,
|
||||
/// because single appearances get a wildcard (*) and multiple times
|
||||
/// get a generated letter ("a" etc).
|
||||
#[derive(Debug)]
|
||||
enum Appearances {
|
||||
Single,
|
||||
Multiple,
|
||||
|
@ -75,7 +76,7 @@ fn find_names_needed(
|
|||
subs: &mut Subs,
|
||||
roots: &mut Vec<Variable>,
|
||||
root_appearances: &mut MutMap<Variable, Appearances>,
|
||||
names_taken: &mut MutSet<Lowercase>,
|
||||
names_taken: &mut MutMap<Lowercase, Variable>,
|
||||
) {
|
||||
use crate::subs::Content::*;
|
||||
use crate::subs::FlatType::*;
|
||||
|
@ -151,19 +152,27 @@ fn find_names_needed(
|
|||
..
|
||||
}
|
||||
| FlexVar(Some(name_index))
|
||||
| FlexAbleVar(Some(name_index), _) => {
|
||||
// This root already has a name. Nothing more to do here!
|
||||
| FlexAbleVar(Some(name_index), _)
|
||||
| RigidVar(name_index)
|
||||
| RigidAbleVar(name_index, _) => {
|
||||
let root = subs.get_root_key_without_compacting(variable);
|
||||
|
||||
// User-defined names are already taken.
|
||||
// We must not accidentally generate names that collide with them!
|
||||
let name = subs.field_names[name_index.index as usize].clone();
|
||||
names_taken.insert(name);
|
||||
}
|
||||
RigidVar(name_index) | RigidAbleVar(name_index, _) => {
|
||||
// User-defined names are already taken.
|
||||
// We must not accidentally generate names that collide with them!
|
||||
let name = subs.field_names[name_index.index as usize].clone();
|
||||
names_taken.insert(name);
|
||||
match names_taken.get(&name) {
|
||||
Some(var) if *var == root => {}
|
||||
Some(_) => {
|
||||
if !root_appearances.contains_key(&root) {
|
||||
roots.push(root);
|
||||
}
|
||||
// We want a name, but the default name is already taken by another root.
|
||||
root_appearances.insert(root, Appearances::Multiple);
|
||||
}
|
||||
None => {
|
||||
names_taken.insert(name, root);
|
||||
}
|
||||
}
|
||||
}
|
||||
Structure(Apply(_, args)) => {
|
||||
for index in args.into_iter() {
|
||||
|
@ -255,7 +264,7 @@ fn name_all_type_vars(variable: Variable, subs: &mut Subs) -> NamedResult {
|
|||
let mut roots = Vec::new();
|
||||
let mut letters_used = 0;
|
||||
let mut appearances = MutMap::default();
|
||||
let mut taken = MutSet::default();
|
||||
let mut taken = MutMap::default();
|
||||
|
||||
// Populate names_needed
|
||||
find_names_needed(variable, subs, &mut roots, &mut appearances, &mut taken);
|
||||
|
@ -290,14 +299,14 @@ fn name_root(
|
|||
letters_used: u32,
|
||||
root: Variable,
|
||||
subs: &mut Subs,
|
||||
taken: &mut MutSet<Lowercase>,
|
||||
taken: &mut MutMap<Lowercase, Variable>,
|
||||
) -> u32 {
|
||||
let (generated_name, new_letters_used) =
|
||||
name_type_var(letters_used, &mut taken.iter(), |var, str| {
|
||||
name_type_var(letters_used, &mut taken.keys(), |var, str| {
|
||||
var.as_str() == str
|
||||
});
|
||||
|
||||
taken.insert(generated_name.clone());
|
||||
taken.insert(generated_name.clone(), root);
|
||||
|
||||
set_root_name(root, generated_name, subs);
|
||||
|
||||
|
@ -310,12 +319,12 @@ fn set_root_name(root: Variable, name: Lowercase, subs: &mut Subs) {
|
|||
let old_content = subs.get_content_without_compacting(root);
|
||||
|
||||
match old_content {
|
||||
FlexVar(None) => {
|
||||
FlexVar(_) => {
|
||||
let name_index = SubsIndex::push_new(&mut subs.field_names, name);
|
||||
let content = FlexVar(Some(name_index));
|
||||
subs.set_content(root, content);
|
||||
}
|
||||
&FlexAbleVar(None, ability) => {
|
||||
&FlexAbleVar(_, ability) => {
|
||||
let name_index = SubsIndex::push_new(&mut subs.field_names, name);
|
||||
let content = FlexAbleVar(Some(name_index), ability);
|
||||
subs.set_content(root, content);
|
||||
|
@ -335,8 +344,7 @@ fn set_root_name(root: Variable, name: Lowercase, subs: &mut Subs) {
|
|||
RecursionVar {
|
||||
opt_name: Some(_existing),
|
||||
..
|
||||
}
|
||||
| FlexVar(Some(_existing)) => {
|
||||
} => {
|
||||
panic!("TODO FIXME - make sure the generated name does not clash with any bound vars! In other words, if the user decided to name a type variable 'a', make sure we don't generate 'a' to name a different one!");
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ use roc_module::ident::{Lowercase, TagName, Uppercase};
|
|||
use roc_module::symbol::Symbol;
|
||||
use std::fmt;
|
||||
use std::iter::{once, Iterator, Map};
|
||||
use ven_ena::unify::UnifyKey;
|
||||
|
||||
use crate::unification_table::{Snapshot, UnificationTable};
|
||||
|
||||
|
@ -1206,22 +1205,6 @@ impl fmt::Debug for Variable {
|
|||
}
|
||||
}
|
||||
|
||||
impl UnifyKey for Variable {
|
||||
type Value = Descriptor;
|
||||
|
||||
fn index(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn from_index(index: u32) -> Self {
|
||||
Variable(index)
|
||||
}
|
||||
|
||||
fn tag() -> &'static str {
|
||||
"Variable"
|
||||
}
|
||||
}
|
||||
|
||||
/// Used in SolvedType
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct VarId(u32);
|
||||
|
|
|
@ -240,6 +240,7 @@ pub enum Type {
|
|||
name: Symbol,
|
||||
captures: Vec<Type>,
|
||||
},
|
||||
UnspecializedLambdaSet(Uls),
|
||||
DelayedAlias(AliasCommon),
|
||||
Alias {
|
||||
symbol: Symbol,
|
||||
|
@ -264,6 +265,17 @@ pub enum Type {
|
|||
Erroneous(Problem),
|
||||
}
|
||||
|
||||
/// A lambda set under an arrow in a ability member signature. For example, in
|
||||
/// Default has default : {} -> a | a has Default
|
||||
/// the unspecialized lambda set for the arrow "{} -> a" would be `a:default:1`.
|
||||
///
|
||||
/// Lambda sets in member signatures are never known until those members are specialized at a
|
||||
/// usage site. Unspecialized lambda sets aid us in recovering those lambda sets; when we
|
||||
/// instantiate `a` with a proper type `T`, we'll know to resolve the lambda set by extracting
|
||||
/// it at region "1" from the specialization of "default" for `T`.
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Uls(Variable, Symbol, u8);
|
||||
|
||||
static mut TYPE_CLONE_COUNT: std::sync::atomic::AtomicUsize =
|
||||
std::sync::atomic::AtomicUsize::new(0);
|
||||
|
||||
|
@ -297,6 +309,7 @@ impl Clone for Type {
|
|||
name: *name,
|
||||
captures: captures.clone(),
|
||||
},
|
||||
Self::UnspecializedLambdaSet(uls) => Self::UnspecializedLambdaSet(*uls),
|
||||
Self::DelayedAlias(arg0) => Self::DelayedAlias(arg0.clone()),
|
||||
Self::Alias {
|
||||
symbol,
|
||||
|
@ -367,6 +380,14 @@ impl TypeExtension {
|
|||
TypeExtension::Closed => true,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn iter_mut(&mut self) -> impl Iterator<Item = &mut Type> {
|
||||
match self {
|
||||
TypeExtension::Open(ext) => Some(ext.as_mut()).into_iter(),
|
||||
TypeExtension::Closed => None.into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a TypeExtension {
|
||||
|
@ -627,6 +648,9 @@ impl fmt::Debug for Type {
|
|||
Type::RangedNumber(typ, range_vars) => {
|
||||
write!(f, "Ranged({:?}, {:?})", typ, range_vars)
|
||||
}
|
||||
Type::UnspecializedLambdaSet(Uls(a, mem, region)) => {
|
||||
write!(f, "ULS({:?}:{:?}:{:?})", a, mem, region)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -766,6 +790,12 @@ impl Type {
|
|||
RangedNumber(typ, _) => {
|
||||
stack.push(typ);
|
||||
}
|
||||
UnspecializedLambdaSet(Uls(v, _, _)) => {
|
||||
debug_assert!(
|
||||
substitutions.get(v).is_none(),
|
||||
"unspecialized lambda sets should never be substituted before solving"
|
||||
);
|
||||
}
|
||||
|
||||
EmptyRec | EmptyTagUnion | Erroneous(_) => {}
|
||||
}
|
||||
|
@ -877,6 +907,12 @@ impl Type {
|
|||
RangedNumber(typ, _) => {
|
||||
stack.push(typ);
|
||||
}
|
||||
UnspecializedLambdaSet(Uls(v, _, _)) => {
|
||||
debug_assert!(
|
||||
substitutions.get(v).is_none(),
|
||||
"unspecialized lambda sets should never be substituted before solving"
|
||||
);
|
||||
}
|
||||
|
||||
EmptyRec | EmptyTagUnion | Erroneous(_) => {}
|
||||
}
|
||||
|
@ -974,6 +1010,7 @@ impl Type {
|
|||
Ok(())
|
||||
}
|
||||
RangedNumber(typ, _) => typ.substitute_alias(rep_symbol, rep_args, actual),
|
||||
UnspecializedLambdaSet(..) => Ok(()),
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
@ -1030,6 +1067,7 @@ impl Type {
|
|||
Apply(symbol, _, _) if *symbol == rep_symbol => true,
|
||||
Apply(_, args, _) => args.iter().any(|arg| arg.contains_symbol(rep_symbol)),
|
||||
RangedNumber(typ, _) => typ.contains_symbol(rep_symbol),
|
||||
UnspecializedLambdaSet(Uls(_, sym, _)) => *sym == rep_symbol,
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => false,
|
||||
}
|
||||
}
|
||||
|
@ -1055,6 +1093,7 @@ impl Type {
|
|||
ClosureTag { name: _, captures } => {
|
||||
captures.iter().any(|t| t.contains_variable(rep_variable))
|
||||
}
|
||||
UnspecializedLambdaSet(Uls(v, _, _)) => *v == rep_variable,
|
||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||
Self::contains_variable_ext(ext, rep_variable)
|
||||
|| tags
|
||||
|
@ -1344,10 +1383,19 @@ impl Type {
|
|||
RangedNumber(typ, _) => {
|
||||
typ.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables);
|
||||
}
|
||||
UnspecializedLambdaSet(..) => {}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instantiate_lambda_sets_as_unspecialized(
|
||||
&mut self,
|
||||
able_var: Variable,
|
||||
ability_member: Symbol,
|
||||
) {
|
||||
instantiate_lambda_sets_as_unspecialized(self, able_var, ability_member)
|
||||
}
|
||||
|
||||
pub fn is_tag_union_like(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
|
@ -1473,6 +1521,9 @@ fn symbols_help(initial: &Type) -> Vec<Symbol> {
|
|||
RangedNumber(typ, _) => {
|
||||
stack.push(typ);
|
||||
}
|
||||
UnspecializedLambdaSet(Uls(_, _sym, _)) => {
|
||||
// ignore the member symbol because unspecialized lambda sets are internal-only
|
||||
}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
||||
}
|
||||
}
|
||||
|
@ -1520,6 +1571,9 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
|||
variables_help(t, accum);
|
||||
}
|
||||
}
|
||||
UnspecializedLambdaSet(Uls(v, _, _)) => {
|
||||
accum.insert(*v);
|
||||
}
|
||||
TagUnion(tags, ext) => {
|
||||
for (_, args) in tags {
|
||||
for x in args {
|
||||
|
@ -1670,6 +1724,9 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
|||
variables_help_detailed(ext, accum);
|
||||
}
|
||||
}
|
||||
UnspecializedLambdaSet(Uls(var, _, _)) => {
|
||||
accum.type_variables.insert(*var);
|
||||
}
|
||||
RecursiveTagUnion(rec, tags, ext) => {
|
||||
for (_, args) in tags {
|
||||
for x in args {
|
||||
|
@ -2665,3 +2722,170 @@ pub fn gather_tags(subs: &Subs, other_fields: UnionTags, var: Variable) -> TagUn
|
|||
ext,
|
||||
}
|
||||
}
|
||||
|
||||
fn instantiate_lambda_sets_as_unspecialized(
|
||||
typ: &mut Type,
|
||||
able_var: Variable,
|
||||
ability_member: Symbol,
|
||||
) {
|
||||
// We want to pop and assign lambda sets pre-order for readability, so types
|
||||
// should be pushed onto the stack in post-order
|
||||
let mut stack = vec![typ];
|
||||
let mut region = 0;
|
||||
|
||||
let mut new_uls = || {
|
||||
region += 1;
|
||||
Type::UnspecializedLambdaSet(Uls(able_var, ability_member, region))
|
||||
};
|
||||
|
||||
while let Some(typ) = stack.pop() {
|
||||
match typ {
|
||||
Type::EmptyRec => {}
|
||||
Type::EmptyTagUnion => {}
|
||||
Type::Function(args, lambda_set, ret) => {
|
||||
debug_assert!(
|
||||
matches!(**lambda_set, Type::Variable(..)),
|
||||
"lambda set already bound"
|
||||
);
|
||||
|
||||
**lambda_set = new_uls();
|
||||
stack.push(ret);
|
||||
stack.extend(args.iter_mut().rev());
|
||||
}
|
||||
Type::Record(fields, ext) => {
|
||||
stack.extend(ext.iter_mut());
|
||||
for (_, x) in fields.iter_mut() {
|
||||
stack.push(x.as_inner_mut());
|
||||
}
|
||||
}
|
||||
Type::TagUnion(tags, ext) | Type::RecursiveTagUnion(_, tags, ext) => {
|
||||
stack.extend(ext.iter_mut());
|
||||
for (_, ts) in tags {
|
||||
for t in ts.iter_mut().rev() {
|
||||
stack.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
Type::FunctionOrTagUnion(_, _, ext) => {
|
||||
stack.extend(ext.iter_mut());
|
||||
}
|
||||
Type::ClosureTag { name: _, captures } => {
|
||||
stack.extend(captures.iter_mut().rev());
|
||||
}
|
||||
Type::UnspecializedLambdaSet(..) => {
|
||||
internal_error!("attempting to re-instantiate ULS")
|
||||
}
|
||||
Type::DelayedAlias(AliasCommon {
|
||||
symbol: _,
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
}) => {
|
||||
stack.extend(lambda_set_variables.iter_mut().rev().map(|ls| &mut ls.0));
|
||||
stack.extend(type_arguments.iter_mut().rev());
|
||||
}
|
||||
Type::Alias {
|
||||
symbol: _,
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
actual,
|
||||
kind: _,
|
||||
} => {
|
||||
stack.push(actual);
|
||||
stack.extend(lambda_set_variables.iter_mut().rev().map(|ls| &mut ls.0));
|
||||
stack.extend(type_arguments.iter_mut().rev().map(|t| &mut t.typ));
|
||||
}
|
||||
Type::HostExposedAlias {
|
||||
name: _,
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
actual_var: _,
|
||||
actual,
|
||||
} => {
|
||||
stack.push(actual);
|
||||
stack.extend(lambda_set_variables.iter_mut().rev().map(|ls| &mut ls.0));
|
||||
stack.extend(type_arguments.iter_mut().rev());
|
||||
}
|
||||
Type::Apply(_sym, args, _region) => {
|
||||
stack.extend(args.iter_mut().rev());
|
||||
}
|
||||
Type::Variable(_) => {}
|
||||
Type::RangedNumber(t, _) => {
|
||||
stack.push(t);
|
||||
}
|
||||
Type::Erroneous(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn instantiate_lambda_sets_as_unspecialized() {
|
||||
let mut var_store = VarStore::default();
|
||||
let l1 = Box::new(Type::Variable(var_store.fresh()));
|
||||
let l2 = Box::new(Type::Variable(var_store.fresh()));
|
||||
let l3 = Box::new(Type::Variable(var_store.fresh()));
|
||||
let mut typ = Type::Function(
|
||||
vec![Type::Function(vec![], l2, Box::new(Type::EmptyRec))],
|
||||
l1,
|
||||
Box::new(Type::TagUnion(
|
||||
vec![(
|
||||
TagName("A".into()),
|
||||
vec![Type::Function(vec![], l3, Box::new(Type::EmptyRec))],
|
||||
)],
|
||||
TypeExtension::Closed,
|
||||
)),
|
||||
);
|
||||
|
||||
let able_var = var_store.fresh();
|
||||
let member = Symbol::UNDERSCORE;
|
||||
typ.instantiate_lambda_sets_as_unspecialized(able_var, member);
|
||||
|
||||
macro_rules! check_uls {
|
||||
($typ:expr, $region:literal) => {{
|
||||
match $typ {
|
||||
Type::UnspecializedLambdaSet(Uls(var1, member1, $region)) => {
|
||||
assert!(var1 == able_var && member1 == member)
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
match typ {
|
||||
Type::Function(args, l1, ret) => {
|
||||
check_uls!(*l1, 1);
|
||||
|
||||
match args.as_slice() {
|
||||
[Type::Function(args, l2, ret)] => {
|
||||
check_uls!(**l2, 2);
|
||||
assert!(args.is_empty());
|
||||
assert!(matches!(**ret, Type::EmptyRec));
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
match *ret {
|
||||
Type::TagUnion(tags, TypeExtension::Closed) => match tags.as_slice() {
|
||||
[(name, args)] => {
|
||||
assert_eq!(name.0.as_str(), "A");
|
||||
match args.as_slice() {
|
||||
[Type::Function(args, l3, ret)] => {
|
||||
check_uls!(**l3, 3);
|
||||
assert!(args.is_empty());
|
||||
assert!(matches!(**ret, Type::EmptyRec));
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
_ => panic!(),
|
||||
},
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,13 +99,6 @@ bitflags! {
|
|||
///
|
||||
/// For example, t1 += [A Str] says we should "add" the tag "A Str" to the type of "t1".
|
||||
const PRESENT = 1 << 1;
|
||||
/// Instructs the unifier to treat rigids exactly like flex vars.
|
||||
/// Usually rigids can only unify with flex vars, because rigids are named and bound
|
||||
/// explicitly.
|
||||
/// However, when checking type ranges, as we do for `RangedNumber` types, we must loosen
|
||||
/// this restriction because otherwise an admissible range will appear inadmissible.
|
||||
/// For example, Int * is in the range <I8, U8, ...>.
|
||||
const RIGID_AS_FLEX = 1 << 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,11 +119,7 @@ impl Mode {
|
|||
|
||||
#[cfg(debug_assertions)]
|
||||
fn pretty_print(&self) -> &str {
|
||||
if self.contains(Mode::EQ | Mode::RIGID_AS_FLEX) {
|
||||
"~*"
|
||||
} else if self.contains(Mode::PRESENT | Mode::RIGID_AS_FLEX) {
|
||||
"+=*"
|
||||
} else if self.contains(Mode::EQ) {
|
||||
if self.contains(Mode::EQ) {
|
||||
"~"
|
||||
} else if self.contains(Mode::PRESENT) {
|
||||
"+="
|
||||
|
@ -364,14 +353,10 @@ fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
|
|||
// This #[allow] is needed in release builds, where `result` is no longer used.
|
||||
#[allow(clippy::let_and_return)]
|
||||
let result = match &ctx.first_desc.content {
|
||||
FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, None, &ctx.second_desc.content),
|
||||
FlexAbleVar(opt_name, ability) => unify_flex(
|
||||
subs,
|
||||
&ctx,
|
||||
opt_name,
|
||||
Some(*ability),
|
||||
&ctx.second_desc.content,
|
||||
),
|
||||
FlexVar(opt_name) => unify_flex(subs, &ctx, opt_name, &ctx.second_desc.content),
|
||||
FlexAbleVar(opt_name, ability) => {
|
||||
unify_flex_able(subs, &ctx, opt_name, *ability, &ctx.second_desc.content)
|
||||
}
|
||||
RecursionVar {
|
||||
opt_name,
|
||||
structure,
|
||||
|
@ -383,9 +368,9 @@ fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
|
|||
*structure,
|
||||
&ctx.second_desc.content,
|
||||
),
|
||||
RigidVar(name) => unify_rigid(subs, &ctx, name, None, &ctx.second_desc.content),
|
||||
RigidVar(name) => unify_rigid(subs, &ctx, name, &ctx.second_desc.content),
|
||||
RigidAbleVar(name, ability) => {
|
||||
unify_rigid(subs, &ctx, name, Some(*ability), &ctx.second_desc.content)
|
||||
unify_rigid_able(subs, &ctx, name, *ability, &ctx.second_desc.content)
|
||||
}
|
||||
Structure(flat_type) => {
|
||||
unify_structure(subs, pool, &ctx, flat_type, &ctx.second_desc.content)
|
||||
|
@ -1916,7 +1901,6 @@ fn unify_rigid(
|
|||
subs: &mut Subs,
|
||||
ctx: &Context,
|
||||
name: &SubsIndex<Lowercase>,
|
||||
opt_able_bound: Option<Symbol>,
|
||||
other: &Content,
|
||||
) -> Outcome {
|
||||
match other {
|
||||
|
@ -1925,68 +1909,60 @@ fn unify_rigid(
|
|||
merge(subs, ctx, RigidVar(*name))
|
||||
}
|
||||
FlexAbleVar(_, other_ability) => {
|
||||
match opt_able_bound {
|
||||
Some(ability) => {
|
||||
if ability == *other_ability {
|
||||
// The ability bounds are the same, so rigid wins!
|
||||
merge(subs, ctx, RigidAbleVar(*name, ability))
|
||||
} else {
|
||||
// Mismatch for now.
|
||||
// TODO check ability hierarchies.
|
||||
mismatch!(
|
||||
%not_able, ctx.second, ability,
|
||||
"RigidAble {:?} with ability {:?} not compatible with ability {:?}",
|
||||
ctx.first,
|
||||
ability,
|
||||
other_ability
|
||||
)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Mismatch - Rigid can unify with FlexAble only when the Rigid has an ability
|
||||
// bound as well, otherwise the user failed to correctly annotate the bound.
|
||||
mismatch!(
|
||||
%not_able, ctx.first, *other_ability,
|
||||
"Rigid {:?} with FlexAble {:?}", ctx.first, other
|
||||
)
|
||||
}
|
||||
}
|
||||
// Mismatch - Rigid can unify with FlexAble only when the Rigid has an ability
|
||||
// bound as well, otherwise the user failed to correctly annotate the bound.
|
||||
mismatch!(
|
||||
%not_able, ctx.first, *other_ability,
|
||||
"Rigid {:?} with FlexAble {:?}", ctx.first, other
|
||||
)
|
||||
}
|
||||
|
||||
RigidVar(_)
|
||||
| RigidAbleVar(..)
|
||||
| RecursionVar { .. }
|
||||
| Structure(_)
|
||||
| Alias(_, _, _, _)
|
||||
| Alias(..)
|
||||
| RangedNumber(..)
|
||||
| LambdaSet(..)
|
||||
if ctx.mode.contains(Mode::RIGID_AS_FLEX) =>
|
||||
{
|
||||
// Usually rigids can only unify with flex, but the mode indicates we are treating
|
||||
// rigid vars as flex, so admit this.
|
||||
match (opt_able_bound, other) {
|
||||
(None, other) => merge(subs, ctx, *other),
|
||||
(Some(ability), Alias(opaque_name, vars, _real_var, AliasKind::Opaque))
|
||||
if vars.is_empty() =>
|
||||
{
|
||||
let mut output = merge(subs, ctx, *other);
|
||||
let must_implement_ability = MustImplementAbility {
|
||||
typ: Obligated::Opaque(*opaque_name),
|
||||
ability,
|
||||
};
|
||||
output.must_implement_ability.push(must_implement_ability);
|
||||
output
|
||||
}
|
||||
| LambdaSet(..) => {
|
||||
// Type mismatch! Rigid can only unify with flex, even if the
|
||||
// rigid names are the same.
|
||||
mismatch!("Rigid {:?} with {:?}", ctx.first, &other)
|
||||
}
|
||||
|
||||
// these have underscores because they're unused in --release builds
|
||||
(Some(_ability), _other) => {
|
||||
// For now, only allow opaque types with no type variables to implement abilities.
|
||||
mismatch!(
|
||||
%not_able, ctx.second, _ability,
|
||||
"RigidAble {:?} with non-opaque or opaque with type variables {:?}",
|
||||
ctx.first,
|
||||
&_other
|
||||
)
|
||||
}
|
||||
Error => {
|
||||
// Error propagates.
|
||||
merge(subs, ctx, Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn unify_rigid_able(
|
||||
subs: &mut Subs,
|
||||
ctx: &Context,
|
||||
name: &SubsIndex<Lowercase>,
|
||||
ability: Symbol,
|
||||
other: &Content,
|
||||
) -> Outcome {
|
||||
match other {
|
||||
FlexVar(_) => {
|
||||
// If the other is flex, rigid wins!
|
||||
merge(subs, ctx, RigidVar(*name))
|
||||
}
|
||||
FlexAbleVar(_, other_ability) => {
|
||||
if ability == *other_ability {
|
||||
// The ability bounds are the same, so rigid wins!
|
||||
merge(subs, ctx, RigidAbleVar(*name, ability))
|
||||
} else {
|
||||
// Mismatch for now.
|
||||
// TODO check ability hierarchies.
|
||||
mismatch!(
|
||||
%not_able, ctx.second, ability,
|
||||
"RigidAble {:?} with ability {:?} not compatible with ability {:?}",
|
||||
ctx.first,
|
||||
ability,
|
||||
other_ability
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2014,45 +1990,19 @@ fn unify_flex(
|
|||
subs: &mut Subs,
|
||||
ctx: &Context,
|
||||
opt_name: &Option<SubsIndex<Lowercase>>,
|
||||
opt_able_bound: Option<Symbol>,
|
||||
other: &Content,
|
||||
) -> Outcome {
|
||||
match other {
|
||||
FlexVar(other_opt_name) => {
|
||||
// Prefer using right's name.
|
||||
let opt_name = opt_name.or(*other_opt_name);
|
||||
match opt_able_bound {
|
||||
Some(ability) => merge(subs, ctx, FlexAbleVar(opt_name, ability)),
|
||||
None => merge(subs, ctx, FlexVar(opt_name)),
|
||||
}
|
||||
merge(subs, ctx, FlexVar(opt_name))
|
||||
}
|
||||
|
||||
FlexAbleVar(opt_other_name, other_ability) => {
|
||||
// Prefer the right's name when possible.
|
||||
FlexAbleVar(opt_other_name, ability) => {
|
||||
// Prefer using right's name.
|
||||
let opt_name = (opt_other_name).or(*opt_name);
|
||||
|
||||
match opt_able_bound {
|
||||
Some(ability) => {
|
||||
if ability == *other_ability {
|
||||
// The ability bounds are the same! Keep the name around if it exists.
|
||||
merge(subs, ctx, FlexAbleVar(opt_name, ability))
|
||||
} else {
|
||||
// Ability names differ; mismatch for now.
|
||||
// TODO check ability hierarchies.
|
||||
mismatch!(
|
||||
%not_able, ctx.second, ability,
|
||||
"FlexAble {:?} with ability {:?} not compatible with ability {:?}",
|
||||
ctx.first,
|
||||
ability,
|
||||
other_ability
|
||||
)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Right has an ability bound, but left might have the name. Combine them.
|
||||
merge(subs, ctx, FlexAbleVar(opt_name, *other_ability))
|
||||
}
|
||||
}
|
||||
merge(subs, ctx, FlexAbleVar(opt_name, *ability))
|
||||
}
|
||||
|
||||
RigidVar(_)
|
||||
|
@ -2071,6 +2021,80 @@ fn unify_flex(
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn unify_flex_able(
|
||||
subs: &mut Subs,
|
||||
ctx: &Context,
|
||||
opt_name: &Option<SubsIndex<Lowercase>>,
|
||||
ability: Symbol,
|
||||
other: &Content,
|
||||
) -> Outcome {
|
||||
match other {
|
||||
FlexVar(opt_other_name) => {
|
||||
// Prefer using right's name.
|
||||
let opt_name = (opt_other_name).or(*opt_name);
|
||||
merge(subs, ctx, FlexAbleVar(opt_name, ability))
|
||||
}
|
||||
|
||||
FlexAbleVar(opt_other_name, other_ability) => {
|
||||
// Prefer the right's name when possible.
|
||||
let opt_name = (opt_other_name).or(*opt_name);
|
||||
|
||||
if ability == *other_ability {
|
||||
merge(subs, ctx, FlexAbleVar(opt_name, ability))
|
||||
} else {
|
||||
// Ability names differ; mismatch for now.
|
||||
// TODO check ability hierarchies.
|
||||
mismatch!(
|
||||
%not_able, ctx.second, ability,
|
||||
"FlexAble {:?} with ability {:?} not compatible with ability {:?}",
|
||||
ctx.first,
|
||||
ability,
|
||||
other_ability
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
RigidAbleVar(_, other_ability) => {
|
||||
if ability == *other_ability {
|
||||
merge(subs, ctx, *other)
|
||||
} else {
|
||||
mismatch!(%not_able, ctx.second, ability, "RigidAble {:?} vs {:?}", ability, other_ability)
|
||||
}
|
||||
}
|
||||
|
||||
RigidVar(_) => mismatch!("FlexAble can never unify with non-able Rigid"),
|
||||
RecursionVar { .. } => mismatch!("FlexAble with RecursionVar"),
|
||||
LambdaSet(..) => mismatch!("FlexAble with LambdaSet"),
|
||||
|
||||
Alias(name, args, _real_var, AliasKind::Opaque) => {
|
||||
if args.is_empty() {
|
||||
// Opaque type wins
|
||||
let mut outcome = merge(subs, ctx, *other);
|
||||
outcome.must_implement_ability.push(MustImplementAbility {
|
||||
typ: Obligated::Opaque(*name),
|
||||
ability,
|
||||
});
|
||||
outcome
|
||||
} else {
|
||||
mismatch!("FlexAble vs Opaque with type vars")
|
||||
}
|
||||
}
|
||||
|
||||
Structure(_) | Alias(_, _, _, AliasKind::Structural) | RangedNumber(..) => {
|
||||
// Structural type wins.
|
||||
let mut outcome = merge(subs, ctx, *other);
|
||||
outcome.must_implement_ability.push(MustImplementAbility {
|
||||
typ: Obligated::Adhoc(ctx.second),
|
||||
ability,
|
||||
});
|
||||
outcome
|
||||
}
|
||||
|
||||
Error => merge(subs, ctx, Error),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn unify_recursion(
|
||||
subs: &mut Subs,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue