Merge branch 'trunk' of github.com:rtfeldman/roc into wasm-link-host-to-app-calls

This commit is contained in:
Brian Carroll 2022-06-06 12:00:16 +01:00
commit d045f3be7b
No known key found for this signature in database
GPG key ID: 5C7B2EC4101703C0
24 changed files with 575 additions and 2027 deletions

View file

@ -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")

View file

@ -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(),
},
},

View file

@ -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) {

View file

@ -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",
)
}

View file

@ -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"

View file

@ -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!");
}

View file

@ -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);

View file

@ -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!(),
}
}
}

View file

@ -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,