no more boolean algebra

This commit is contained in:
Folkert 2021-04-04 21:22:59 +02:00
parent ea7af0f7e7
commit fa7dec2997
13 changed files with 10 additions and 2059 deletions

File diff suppressed because it is too large Load Diff

View File

@ -49,7 +49,6 @@ impl Scope {
typ,
hidden_variables: MutSet::default(),
vars: variables,
uniqueness: None,
};
aliases.insert(symbol, alias);
@ -186,7 +185,6 @@ impl Scope {
region,
vars,
hidden_variables,
uniqueness: None,
typ,
};

View File

@ -3469,7 +3469,7 @@ pub fn with_hole<'a>(
}
List {
list_var,
list_var: _,
elem_var,
loc_elems,
} => {
@ -3488,7 +3488,7 @@ pub fn with_hole<'a>(
elems: arg_symbols,
};
let mode = crate::layout::mode_from_var(list_var, env.subs);
let mode = MemoryMode::Refcounted;
let stmt = Stmt::Let(
assigned,

View File

@ -1090,31 +1090,6 @@ fn layout_from_flat_type<'a>(
Symbol::STR_STR => Ok(Layout::Builtin(Builtin::Str)),
Symbol::LIST_LIST => list_layout_from_elem(env, args[0]),
Symbol::DICT_DICT => dict_layout_from_key_value(env, args[0], args[1]),
Symbol::ATTR_ATTR => {
debug_assert_eq!(args.len(), 2);
// The first argument is the uniqueness info;
// second is the base type
let wrapped_var = args[1];
// correct the memory mode of unique lists
match Layout::from_var(env, wrapped_var)? {
Layout::Builtin(Builtin::List(_ignored, elem_layout)) => {
let uniqueness_var = args[0];
let uniqueness_content =
subs.get_without_compacting(uniqueness_var).content;
let mode = if uniqueness_content.is_unique(subs) {
MemoryMode::Unique
} else {
MemoryMode::Refcounted
};
Ok(Layout::Builtin(Builtin::List(mode, elem_layout)))
}
other => Ok(other),
}
}
Symbol::SET_SET => dict_layout_from_key_value(env, args[0], Variable::EMPTY_RECORD),
_ => {
panic!("TODO layout_from_flat_type for {:?}", Apply(symbol, args));
@ -1270,9 +1245,6 @@ fn layout_from_flat_type<'a>(
EmptyTagUnion => {
panic!("TODO make Layout for empty Tag Union");
}
Boolean(_) => {
panic!("TODO make Layout for Boolean");
}
Erroneous(_) => Err(LayoutProblem::Erroneous),
EmptyRecord => Ok(Layout::Struct(&[])),
}
@ -1999,24 +1971,6 @@ pub fn list_layout_from_elem<'a>(
}
}
pub fn mode_from_var(var: Variable, subs: &Subs) -> MemoryMode {
match subs.get_without_compacting(var).content {
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, args)) => {
debug_assert_eq!(args.len(), 2);
let uvar = *args.get(0).unwrap();
let content = subs.get_without_compacting(uvar).content;
if content.is_unique(subs) {
MemoryMode::Unique
} else {
MemoryMode::Refcounted
}
}
_ => MemoryMode::Refcounted,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LayoutId(u32);

View File

@ -1479,7 +1479,6 @@ pub fn to_doc<'b>(
ext_to_doc(alloc, ext),
)
}
Boolean(b) => alloc.string(format!("{:?}", b)),
}
}
@ -2476,7 +2475,6 @@ fn type_problem_to_pretty<'b>(
alloc.reflow(" value"),
]),
),
Boolean(_) => bad_rigid_var(x, alloc.reflow("a uniqueness attribute value")),
}
}
IntFloat => alloc.tip().append(alloc.concat(vec![

View File

@ -4,7 +4,6 @@ use roc_collections::all::{ImMap, MutMap};
use roc_module::ident::TagName;
use roc_module::symbol::Symbol;
use roc_region::all::{Located, Region};
use roc_types::boolean_algebra::{self, Bool};
use roc_types::solved_types::Solved;
use roc_types::subs::{Content, Descriptor, FlatType, Mark, OptVariable, Rank, Subs, Variable};
use roc_types::types::Type::{self, *};
@ -659,12 +658,6 @@ fn type_to_variable(
EmptyTagUnion => Variable::EMPTY_TAG_UNION,
// This case is important for the rank of boolean variables
Boolean(boolean_algebra::Bool::Container(cvar, mvars)) if mvars.is_empty() => *cvar,
Boolean(b) => {
let content = Content::Structure(FlatType::Boolean(b.clone()));
register(subs, rank, pools, content)
}
Function(args, closure_type, ret_type) => {
let mut arg_vars = Vec::with_capacity(args.len());
@ -972,28 +965,6 @@ fn check_for_infinite_type(
_ => circular_error(subs, problems, symbol, &loc_var),
}
}
Content::Structure(FlatType::Boolean(Bool::Container(_cvar, _mvars))) => {
// We have a loop in boolean attributes. The attributes can be seen as constraints
// too, so if we have
//
// Container( u1, { u2, u3 } )
//
// That means u1 >= u2 and u1 >= u3
//
// Now if u1 occurs in the definition of u2, then that's like saying u1 >= u2 >= u1,
// which can only be true if u1 == u2. So that's what we do with unify.
for var in chain {
if let Content::Structure(FlatType::Boolean(_)) =
subs.get_without_compacting(var).content
{
// this unify just makes new pools. is that bad?
let outcome = unify(subs, recursive, var);
debug_assert!(matches!(outcome, roc_unify::unify::Unified::Success(_)));
}
}
boolean_algebra::flatten(subs, recursive);
}
_other => circular_error(subs, problems, symbol, &loc_var),
}
@ -1285,18 +1256,6 @@ fn adjust_rank_content(
rank
}
Boolean(Bool::Shared) => Rank::toplevel(),
Boolean(Bool::Container(cvar, mvars)) => {
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, *cvar);
for var in mvars {
rank =
rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, *var));
}
rank
}
Erroneous(_) => group_rank,
}
}
@ -1473,12 +1432,6 @@ fn instantiate_rigids_help(
instantiate_rigids_help(subs, max_rank, pools, ext_var),
)
}
Boolean(b) => {
let mut mapper = |var| instantiate_rigids_help(subs, max_rank, pools, var);
Boolean(b.map_variables(&mut mapper))
}
};
subs.set(copy, make_descriptor(Structure(new_flat_type)));
@ -1665,12 +1618,6 @@ fn deep_copy_var_help(
deep_copy_var_help(subs, max_rank, pools, ext_var),
)
}
Boolean(b) => {
let mut mapper = |var| deep_copy_var_help(subs, max_rank, pools, var);
Boolean(b.map_variables(&mut mapper))
}
};
subs.set(copy, make_descriptor(Structure(new_flat_type)));

View File

@ -1,214 +0,0 @@
use self::Bool::*;
use crate::subs::{Content, FlatType, Subs, Variable};
use roc_collections::all::SendSet;
/// Uniqueness types
///
/// Roc uses uniqueness types to optimize programs. Uniqueness inference tries to find values that
/// are guaranteed to be unique (i.e. have a reference count of at most 1) at compile time.
///
/// Such unique values can be updated in-place, something otherwise very unsafe in a pure
/// functional language.
///
/// So how does that all work? Instead of inferring normal types like `Int`, we infer types
/// `Attr u a`. The `a` could be any "normal" type (it's called a base type), like `Int` or `Str`.
/// The `u` is the uniqueness attribute. It stores a value of type `Bool` (see definition below).
///
/// Before doing type inference, variables are tagged as either exclusive or shared. A variable is
/// exclusive if we can be sure it's not duplicated. That's always true when the variable is used
/// just once, but also e.g. `foo.x + foo.y` does not duplicate references to `foo`.
///
/// Next comes actual inference. Variables marked as shared always get the `Shared` uniqueness attribute.
/// For exclusive variables, the uniqueness attribute is initially an unbound type variable.
///
/// An important detail is that there is no `Unique` annotation. Instead, uniqueness variables that
/// are unbound after type inference and monomorphization are interpreted as unique. This makes inference
/// easier and ensures we can never get type errors caused by uniqueness attributes.
///
/// Besides normal type inference rules (e.g. in `f a` if `a : t` then it must be that `f : t -> s`),
/// uniqueness attributes must respect the container rule:
///
/// > Container rule: to extract a unique value from a container, the container must itself be unique
///
/// In this context a container can be a record, tag, built-in data structure (List, Set, etc) or
/// a function closure.
///
/// Thus in the case of `List.get`, it must "check" the container rule. It's type is
///
/// > Attr (Container(w, { u })) (List (Attr u a)), Int -> Result _ _
///
/// The container attribute means that the uniqueness of the container (variable w) is at least
/// uniqueness u. Unique is "more unique" than Shared. So if the elements are unique, the list must be unique. But if the list is
/// unique, the elements can be shared.
///
/// As mentioned, we then use monomorphization to find values that are actually unique (those with
/// an unbound uniqueness attribute). Those values are treated differently. They don't have to be
/// reference counted, and can be mutated in-place.
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Bool {
Shared,
Container(Variable, SendSet<Variable>),
}
pub fn var_is_shared(subs: &Subs, var: Variable) -> bool {
matches!(
subs.get_without_compacting(var).content,
Content::Structure(FlatType::Boolean(Bool::Shared))
)
}
/// Given the Subs
///
/// 0 |-> Container (Var 1, { Var 2, Var 3 })
/// 1 |-> Flex 'a'
/// 2 |-> Container(Var 4, { Var 5, Var 6 })
/// 3 |-> Flex 'b'
/// 4 |-> Flex 'c'
/// 5 |-> Flex 'd'
/// 6 |-> Shared
///
/// `flatten(subs, Var 0)` will rewrite it to
///
/// 0 |-> Container (Var 1, { Var 4, Var 5, Var 3 })
///
/// So containers are "inlined", and Shared variables are discarded
pub fn flatten(subs: &mut Subs, var: Variable) {
match subs.get_without_compacting(var).content {
Content::Structure(FlatType::Boolean(Bool::Container(cvar, mvars))) => {
let flattened_mvars = var_to_variables(subs, cvar, &mvars);
let content =
Content::Structure(FlatType::Boolean(Bool::Container(cvar, flattened_mvars)));
subs.set_content(var, content);
}
Content::Structure(FlatType::Boolean(Bool::Shared)) => {
// do nothing
}
_ => {
// do nothing
}
}
}
/// For a Container(cvar, start_vars), find (transitively) all the flex/rigid vars that are
/// occur in start_vars.
///
/// Because type aliases in Roc can be recursive, we have to be a bit careful to not get stuck in
/// an infinite loop.
fn var_to_variables(
subs: &Subs,
cvar: Variable,
start_vars: &SendSet<Variable>,
) -> SendSet<Variable> {
let mut stack: Vec<_> = start_vars.into_iter().copied().collect();
let mut seen = SendSet::default();
seen.insert(cvar);
let mut result = SendSet::default();
while let Some(var) = stack.pop() {
if seen.contains(&var) {
continue;
}
seen.insert(var);
match subs.get_without_compacting(var).content {
Content::Structure(FlatType::Boolean(Bool::Container(cvar, mvars))) => {
let it = std::iter::once(cvar).chain(mvars.into_iter());
for v in it {
if !seen.contains(&v) {
stack.push(v);
}
}
}
Content::Structure(FlatType::Boolean(Bool::Shared)) => {
// do nothing
}
_other => {
result.insert(var);
}
}
}
result
}
impl Bool {
pub fn shared() -> Self {
Bool::Shared
}
pub fn container<I>(cvar: Variable, mvars: I) -> Self
where
I: IntoIterator<Item = Variable>,
{
Bool::Container(cvar, mvars.into_iter().collect())
}
pub fn variable(var: Variable) -> Self {
Bool::Container(var, SendSet::default())
}
pub fn is_fully_simplified(&self, subs: &Subs) -> bool {
match self {
Shared => true,
Container(cvar, mvars) => {
!var_is_shared(subs, *cvar)
&& !(mvars.iter().any(|mvar| var_is_shared(subs, *mvar)))
}
}
}
pub fn is_unique(&self, subs: &Subs) -> bool {
!matches!(self.simplify(subs), Shared)
}
pub fn variables(&self) -> SendSet<Variable> {
match self {
Shared => SendSet::default(),
Container(cvar, mvars) => {
let mut mvars = mvars.clone();
mvars.insert(*cvar);
mvars
}
}
}
pub fn map_variables<F>(&self, f: &mut F) -> Self
where
F: FnMut(Variable) -> Variable,
{
match self {
Bool::Shared => Bool::Shared,
Bool::Container(cvar, mvars) => {
let new_cvar = f(*cvar);
let new_mvars = mvars.iter().map(|var| f(*var)).collect();
Bool::Container(new_cvar, new_mvars)
}
}
}
pub fn simplify(&self, subs: &Subs) -> Self {
match self {
Bool::Container(cvar, mvars) => {
let flattened_mvars = var_to_variables(subs, *cvar, mvars);
// find the parent variable, to remove distinct variables that all have the same
// parent. This prevents the signature from rendering as e.g. `( a | b | b)` and
// instead makes it `( a | b )`.
let parent_mvars = flattened_mvars
.into_iter()
.map(|v| subs.get_root_key_without_compacting(v))
.collect();
Bool::Container(*cvar, parent_mvars)
}
Bool::Shared => Bool::Shared,
}
}
}

View File

@ -1,7 +1,6 @@
#![warn(clippy::all, clippy::dbg_macro)]
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
pub mod boolean_algebra;
pub mod builtin_aliases;
pub mod pretty_print;
pub mod solved_types;

View File

@ -1,4 +1,3 @@
use crate::boolean_algebra::Bool;
use crate::subs::{Content, FlatType, Subs, Variable};
use crate::types::{name_type_var, RecordField};
use roc_collections::all::{MutMap, MutSet};
@ -98,9 +97,6 @@ fn find_names_needed(
let flat_type = FlatType::RecursiveTagUnion(rec_var, new_tags, ext_var);
subs.set_content(recursive, Content::Structure(flat_type));
}
Content::Structure(FlatType::Boolean(Bool::Container(_cvar, _mvars))) => {
crate::boolean_algebra::flatten(subs, recursive);
}
_ => panic!(
"unfixable recursive type in roc_types::pretty_print {:?} {:?} {:?}",
recursive, variable, content
@ -199,16 +195,6 @@ fn find_names_needed(
find_names_needed(ext_var, subs, roots, root_appearances, names_taken);
find_names_needed(rec_var, subs, roots, root_appearances, names_taken);
}
Structure(Boolean(b)) => match b {
Bool::Shared => {}
Bool::Container(cvar, mvars) => {
find_names_needed(cvar, subs, roots, root_appearances, names_taken);
for var in mvars {
find_names_needed(var, subs, roots, root_appearances, names_taken);
}
}
},
Alias(symbol, args, _actual) => {
if let Symbol::ATTR_ATTR = symbol {
find_names_needed(args[0].1, subs, roots, root_appearances, names_taken);
@ -590,9 +576,6 @@ fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String
parens,
)
}
Boolean(b) => {
write_boolean(env, b, subs, buf, Parens::InTypeParam);
}
Erroneous(problem) => {
buf.push_str(&format!("<Type Mismatch: {:?}>", problem));
}
@ -655,65 +638,6 @@ pub fn chase_ext_record(
}
}
fn write_boolean(env: &Env, boolean: Bool, subs: &Subs, buf: &mut String, parens: Parens) {
use crate::boolean_algebra::var_is_shared;
match boolean.simplify(subs) {
Bool::Shared => {
buf.push_str("Shared");
}
Bool::Container(cvar, mvars) if mvars.iter().all(|v| var_is_shared(subs, *v)) => {
debug_assert!(!var_is_shared(subs, cvar));
write_content(
env,
subs.get_without_compacting(cvar).content,
subs,
buf,
Parens::Unnecessary,
);
}
Bool::Container(cvar, mvars) => {
debug_assert!(!var_is_shared(subs, cvar));
let mut buffers = Vec::with_capacity(mvars.len());
for v in mvars {
// don't print shared in a container
if var_is_shared(subs, v) {
continue;
}
let mut inner_buf: String = "".to_string();
write_content(
env,
subs.get_without_compacting(v).content,
subs,
&mut inner_buf,
parens,
);
buffers.push(inner_buf);
}
// sort type variables alphabetically
buffers.sort();
let combined = buffers.join(" | ");
buf.push('(');
write_content(
env,
subs.get_without_compacting(cvar).content,
subs,
buf,
Parens::Unnecessary,
);
buf.push_str(" | ");
buf.push_str(&combined);
buf.push(')');
}
}
}
fn write_apply(
env: &Env,
symbol: Symbol,

View File

@ -1,4 +1,3 @@
use crate::boolean_algebra::{self, Bool};
use crate::subs::{FlatType, Subs, VarId, VarStore, Variable};
use crate::types::{Problem, RecordField, Type};
use roc_collections::all::{ImMap, MutSet, SendMap};
@ -85,7 +84,6 @@ fn hash_solved_type_help<H: Hasher>(
}
Rigid(name) => name.hash(state),
Erroneous(problem) => problem.hash(state),
Boolean(solved_bool) => solved_bool.hash(state),
Record { fields, ext } => {
for (name, x) in fields {
@ -189,44 +187,10 @@ pub enum SolvedType {
actual: Box<SolvedType>,
},
/// a boolean algebra Bool
Boolean(SolvedBool),
/// A type error
Error,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SolvedBool {
SolvedShared,
SolvedContainer(VarId, Vec<VarId>),
}
impl SolvedBool {
pub fn from_bool(boolean: &boolean_algebra::Bool, subs: &Subs) -> Self {
match boolean {
Bool::Shared => SolvedBool::SolvedShared,
Bool::Container(cvar, mvars) => {
match subs.get_without_compacting(*cvar).content {
crate::subs::Content::FlexVar(_) => {}
crate::subs::Content::Structure(FlatType::Boolean(Bool::Shared)) => {
return SolvedBool::SolvedShared;
}
other => panic!("Container var is not flex but {:?}", other),
}
SolvedBool::SolvedContainer(
VarId::from_var(*cvar, subs),
mvars
.iter()
.map(|mvar| VarId::from_var(*mvar, subs))
.collect(),
)
}
}
}
}
impl SolvedType {
pub fn new(solved_subs: &Solved<Subs>, var: Variable) -> Self {
Self::from_var(solved_subs.inner(), var)
@ -350,7 +314,6 @@ impl SolvedType {
actual: Box::new(solved_type),
}
}
Boolean(val) => SolvedType::Boolean(SolvedBool::from_bool(&val, solved_subs.inner())),
Variable(var) => Self::from_var(solved_subs.inner(), *var),
}
}
@ -485,7 +448,6 @@ impl SolvedType {
}
EmptyRecord => SolvedType::EmptyRecord,
EmptyTagUnion => SolvedType::EmptyTagUnion,
Boolean(val) => SolvedType::Boolean(SolvedBool::from_bool(&val, subs)),
Erroneous(problem) => SolvedType::Erroneous(problem),
}
}
@ -624,16 +586,6 @@ pub fn to_type(
Box::new(to_type(ext, free_vars, var_store)),
)
}
Boolean(SolvedBool::SolvedShared) => Type::Boolean(Bool::Shared),
Boolean(SolvedBool::SolvedContainer(solved_cvar, solved_mvars)) => {
let cvar = var_id_to_flex_var(*solved_cvar, free_vars, var_store);
let mvars = solved_mvars
.iter()
.map(|var_id| var_id_to_flex_var(*var_id, free_vars, var_store));
Type::Boolean(Bool::container(cvar, mvars))
}
Alias(symbol, solved_type_variables, solved_actual) => {
let mut type_variables = Vec::with_capacity(solved_type_variables.len());

View File

@ -1,4 +1,3 @@
use crate::boolean_algebra;
use crate::types::{name_type_var, ErrorType, Problem, RecordField, TypeExt};
use roc_collections::all::{ImMap, ImSet, MutMap, MutSet, SendMap};
use roc_module::ident::{Lowercase, TagName};
@ -561,14 +560,6 @@ impl Content {
)
}
pub fn is_unique(&self, subs: &Subs) -> bool {
match self {
Content::Structure(FlatType::Boolean(boolean)) => boolean.is_unique(subs),
Content::FlexVar(_) => true,
_ => false,
}
}
#[cfg(debug_assertions)]
#[allow(dead_code)]
pub fn dbg(self, subs: &Subs) -> Self {
@ -597,7 +588,6 @@ pub enum FlatType {
Erroneous(Problem),
EmptyRecord,
EmptyTagUnion,
Boolean(boolean_algebra::Bool),
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
@ -655,15 +645,6 @@ fn occurs(
let it = once(&ext_var).chain(tags.values().flatten());
short_circuit(subs, root_var, &new_seen, it)
}
Boolean(b) => {
for var in b.variables().iter() {
if let Some((v, mut vec)) = occurs(subs, &new_seen, *var) {
vec.push(root_var);
return Some((v, vec));
}
}
None
}
EmptyRecord | EmptyTagUnion | Erroneous(_) => None,
}
}
@ -783,8 +764,7 @@ fn explicit_substitute(
subs.set_content(in_var, Structure(Record(vars_by_field, new_ext_var)));
}
// NOTE assume we never substitute into a Boolean
EmptyRecord | EmptyTagUnion | Boolean(_) | Erroneous(_) => {}
EmptyRecord | EmptyTagUnion | Erroneous(_) => {}
}
in_var
@ -901,12 +881,6 @@ fn get_var_names(
taken_names
}
FlatType::Boolean(b) => b
.variables()
.into_iter()
.fold(taken_names, |answer, arg_var| {
get_var_names(subs, arg_var, answer)
}),
},
}
}
@ -1184,8 +1158,6 @@ fn flat_type_to_err_type(
}
}
Boolean(b) => ErrorType::Boolean(b),
Erroneous(problem) => {
state.problems.push(problem);
@ -1252,11 +1224,6 @@ fn restore_content(subs: &mut Subs, content: &Content) {
subs.restore(*rec_var);
}
Boolean(b) => {
for var in b.variables() {
subs.restore(var);
}
}
Erroneous(_) => (),
},
Alias(_, args, var) => {

View File

@ -1,4 +1,3 @@
use crate::boolean_algebra;
use crate::pretty_print::Parens;
use crate::subs::{Subs, VarStore, Variable};
use inlinable_string::InlinableString;
@ -152,8 +151,6 @@ pub enum Type {
RecursiveTagUnion(Variable, Vec<(TagName, Vec<Type>)>, Box<Type>),
/// Applying a type to some arguments (e.g. Dict.Dict String Int)
Apply(Symbol, Vec<Type>),
/// Boolean type used in uniqueness inference
Boolean(boolean_algebra::Bool),
Variable(Variable),
/// A type error, which will code gen to a runtime error
Erroneous(Problem),
@ -353,7 +350,6 @@ impl fmt::Debug for Type {
write!(f, " as <{:?}>", rec)
}
Type::Boolean(b) => write!(f, "{:?}", b),
}
}
}
@ -440,15 +436,6 @@ impl Type {
arg.substitute(substitutions);
}
}
Boolean(b) => {
let mut mapper = |var| match substitutions.get(&var) {
Some(Type::Variable(new_var)) => *new_var,
Some(_) => panic!("cannot substitute boolean var for Type"),
None => var,
};
*b = b.map_variables(&mut mapper)
}
EmptyRec | EmptyTagUnion | Erroneous(_) => {}
}
@ -503,7 +490,7 @@ impl Type {
arg.substitute_alias(rep_symbol, actual);
}
}
EmptyRec | EmptyTagUnion | Erroneous(_) | Variable(_) | Boolean(_) => {}
EmptyRec | EmptyTagUnion | Erroneous(_) | Variable(_) => {}
}
}
@ -537,7 +524,7 @@ impl Type {
}
Apply(symbol, _) if *symbol == rep_symbol => true,
Apply(_, args) => args.iter().any(|arg| arg.contains_symbol(rep_symbol)),
EmptyRec | EmptyTagUnion | Erroneous(_) | Variable(_) | Boolean(_) => false,
EmptyRec | EmptyTagUnion | Erroneous(_) | Variable(_) => false,
}
}
@ -569,7 +556,7 @@ impl Type {
Alias(_, _, actual_type) => actual_type.contains_variable(rep_variable),
HostExposedAlias { actual, .. } => actual.contains_variable(rep_variable),
Apply(_, args) => args.iter().any(|arg| arg.contains_variable(rep_variable)),
EmptyRec | EmptyTagUnion | Erroneous(_) | Boolean(_) => false,
EmptyRec | EmptyTagUnion | Erroneous(_) => false,
}
}
@ -632,35 +619,6 @@ impl Type {
actual_type.instantiate_aliases(region, aliases, var_store, introduced);
}
Apply(Symbol::ATTR_ATTR, attr_args) => {
use boolean_algebra::Bool;
debug_assert_eq!(attr_args.len(), 2);
let mut it = attr_args.iter_mut();
let uniqueness_type = it.next().unwrap();
let base_type = it.next().unwrap();
// instantiate the rest
base_type.instantiate_aliases(region, aliases, var_store, introduced);
// correct uniqueness type
// if this attr contains an alias of a recursive tag union, then the uniqueness
// attribute on the recursion variable must match the uniqueness of the whole tag
// union. We enforce that here.
if let Some(Bool::Container(unbound_cvar, mvars1)) =
find_rec_var_uniqueness(base_type, aliases)
{
if let Type::Boolean(Bool::Container(bound_cvar, mvars2)) = uniqueness_type {
debug_assert!(mvars1.is_empty());
debug_assert!(mvars2.is_empty());
let mut substitution = ImMap::default();
substitution.insert(unbound_cvar, Type::Variable(*bound_cvar));
base_type.substitute(&substitution);
}
}
}
Apply(symbol, args) => {
if let Some(alias) = aliases.get(symbol) {
if args.len() != alias.vars.len() {
@ -693,8 +651,6 @@ impl Type {
substitution.insert(*placeholder, filler);
}
use boolean_algebra::Bool;
// Instantiate "hidden" uniqueness variables
//
// Aliases can hide uniqueness variables: e.g. in
@ -725,12 +681,6 @@ impl Type {
//
// And now we must make sure the `a`s stay the same variable, i.e.
// don't re-instantiate it here.
if let Some(Bool::Container(unbound_cvar, _)) = alias.uniqueness {
if variable == unbound_cvar {
introduced.insert(variable);
continue;
}
}
let var = var_store.fresh();
substitution.insert(variable, Type::Variable(var));
@ -767,7 +717,7 @@ impl Type {
}
}
}
EmptyRec | EmptyTagUnion | Erroneous(_) | Variable(_) | Boolean(_) => {}
EmptyRec | EmptyTagUnion | Erroneous(_) | Variable(_) => {}
}
}
}
@ -813,7 +763,7 @@ fn symbols_help(tipe: &Type, accum: &mut ImSet<Symbol>) {
accum.insert(*symbol);
args.iter().for_each(|arg| symbols_help(arg, accum));
}
EmptyRec | EmptyTagUnion | Erroneous(_) | Variable(_) | Boolean(_) => {}
EmptyRec | EmptyTagUnion | Erroneous(_) | Variable(_) => {}
}
}
@ -822,11 +772,7 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
match tipe {
EmptyRec | EmptyTagUnion | Erroneous(_) => (),
Boolean(b) => {
for v in b.variables() {
accum.insert(v);
}
}
Variable(v) => {
accum.insert(*v);
}
@ -894,34 +840,6 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
}
}
/// We're looking for an alias whose actual type is a recursive tag union
/// if `base_type` is one, return the uniqueness variable of the alias.
fn find_rec_var_uniqueness(
base_type: &Type,
aliases: &ImMap<Symbol, Alias>,
) -> Option<boolean_algebra::Bool> {
use Type::*;
if let Alias(symbol, _, actual) = base_type {
match **actual {
Alias(_, _, _) => find_rec_var_uniqueness(actual, aliases),
RecursiveTagUnion(_, _, _) => {
if let Some(alias) = aliases.get(symbol) {
// alias with a recursive tag union must have its uniqueness set
debug_assert!(alias.uniqueness.is_some());
alias.uniqueness.clone()
} else {
unreachable!("aliases must be defined in the set of aliases!")
}
}
_ => None,
}
} else {
None
}
}
pub struct RecordStructure {
pub fields: MutMap<Lowercase, RecordField<Variable>>,
pub ext: Variable,
@ -1049,7 +967,6 @@ pub struct Alias {
/// hidden type variables, like the closure variable in `a -> b`
pub hidden_variables: MutSet<Variable>,
pub uniqueness: Option<boolean_algebra::Bool>,
pub typ: Type,
}
@ -1090,7 +1007,6 @@ pub enum ErrorType {
RecursiveTagUnion(Box<ErrorType>, SendMap<TagName, Vec<ErrorType>>, TypeExt),
Function(Vec<ErrorType>, Box<ErrorType>, Box<ErrorType>),
Alias(Symbol, Vec<(Lowercase, ErrorType)>, Box<ErrorType>),
Boolean(boolean_algebra::Bool),
Error,
}
@ -1416,11 +1332,6 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
write_debug_error_type_help(*rec, buf, Parens::Unnecessary);
}
Boolean(boolean_algebra::Bool::Shared) => buf.push_str("Shared"),
Boolean(boolean_algebra::Bool::Container(mvar, cvars)) => {
buf.push_str(&format!("Container({:?}, {:?})", mvar, cvars))
}
}
}

View File

@ -1,9 +1,6 @@
use roc_collections::all::{
default_hasher, get_shared, relative_complement, union, MutMap, SendSet,
};
use roc_collections::all::{default_hasher, get_shared, relative_complement, union, MutMap};
use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::Symbol;
use roc_types::boolean_algebra::Bool;
use roc_types::subs::Content::{self, *};
use roc_types::subs::{Descriptor, FlatType, Mark, OptVariable, Subs, Variable};
use roc_types::types::{gather_fields, ErrorType, Mismatch, RecordField, RecordStructure};
@ -971,75 +968,6 @@ fn unify_flat_type(
problems
}
(Boolean(b1), Boolean(b2)) => {
use Bool::*;
let b1 = b1.simplify(subs);
let b2 = b2.simplify(subs);
match (&b1, &b2) {
(Shared, Shared) => merge(subs, ctx, Structure(left.clone())),
(Shared, Container(cvar, mvars)) => {
let mut outcome = vec![];
// unify everything with shared
outcome.extend(unify_pool(subs, pool, ctx.first, *cvar));
for mvar in mvars {
outcome.extend(unify_pool(subs, pool, ctx.first, *mvar));
}
// set the first and second variables to Shared
let content = Content::Structure(FlatType::Boolean(Bool::Shared));
outcome.extend(merge(subs, ctx, content));
outcome
}
(Container(cvar, mvars), Shared) => {
let mut outcome = vec![];
// unify everything with shared
outcome.extend(unify_pool(subs, pool, ctx.second, *cvar));
for mvar in mvars {
outcome.extend(unify_pool(subs, pool, ctx.second, *mvar));
}
// set the first and second variables to Shared
let content = Content::Structure(FlatType::Boolean(Bool::Shared));
outcome.extend(merge(subs, ctx, content));
outcome
}
(Container(cvar1, mvars1), Container(cvar2, mvars2)) => {
let mut outcome = vec![];
// unify cvar1 and cvar2?
outcome.extend(unify_pool(subs, pool, *cvar1, *cvar2));
let mvars: SendSet<Variable> = mvars1
.into_iter()
.chain(mvars2.into_iter())
.copied()
.filter_map(|v| {
let root = subs.get_root_key(v);
if roc_types::boolean_algebra::var_is_shared(subs, root) {
None
} else {
Some(root)
}
})
.collect();
let content =
Content::Structure(FlatType::Boolean(Bool::Container(*cvar1, mvars)));
outcome.extend(merge(subs, ctx, content));
outcome
}
}
}
(Apply(l_symbol, l_args), Apply(r_symbol, r_args)) if l_symbol == r_symbol => {
let problems = unify_zip(subs, pool, l_args.iter(), r_args.iter());