mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-13 09:49:11 +03:00
no more boolean algebra
This commit is contained in:
parent
ea7af0f7e7
commit
fa7dec2997
File diff suppressed because it is too large
Load Diff
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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![
|
||||
|
@ -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)));
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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) => {
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user