diff --git a/compiler/constrain/src/uniq.rs b/compiler/constrain/src/uniq.rs index eeba780ec8..39e31f4cf9 100644 --- a/compiler/constrain/src/uniq.rs +++ b/compiler/constrain/src/uniq.rs @@ -16,7 +16,7 @@ use roc_types::types::AnnotationSource::{self, *}; use roc_types::types::Type::{self, *}; use roc_types::types::{Alias, Category, PReason, Reason}; use roc_uniq::builtins::{attr_type, empty_list_type, list_type, str_type}; -use roc_uniq::sharing::{self, Container, FieldAccess, Mark, Usage, VarUsage}; +use roc_uniq::sharing::{self, FieldAccess, Mark, Usage, VarUsage}; pub struct Env { /// Whenever we encounter a user-defined type variable (a "rigid" var for short), @@ -1507,9 +1507,8 @@ fn constrain_by_usage( Usage::ApplyAccess(mark, fields) => { let (list_bool, _ext_type) = constrain_by_usage(&Simple(*mark), var_store, introduced); - let field_usage = fields - .get(&sharing::LIST_ELEM.into()) - .expect("no LIST_ELEM key"); + // TODO reconsier this for multi-value applies + let field_usage = fields.get(0).expect("no LIST_ELEM key"); let (elem_bool, elem_type) = constrain_by_usage(field_usage, var_store, introduced); @@ -1548,9 +1547,8 @@ fn constrain_by_usage( let list_uvar = var_store.fresh(); introduced.push(list_uvar); - let field_usage = fields - .get(&sharing::LIST_ELEM.into()) - .expect("no LIST_ELEM key"); + // TODO reconsier this for multi-value applies + let field_usage = fields.get(0).expect("no LIST_ELEM key"); let (elem_bool, elem_type) = constrain_by_usage(field_usage, var_store, introduced); diff --git a/compiler/uniq/src/sharing.rs b/compiler/uniq/src/sharing.rs index 8837c23f02..cad34d3044 100644 --- a/compiler/uniq/src/sharing.rs +++ b/compiler/uniq/src/sharing.rs @@ -33,8 +33,8 @@ pub enum Usage { Simple(Mark), // Lists, Sets, ADTs - ApplyAccess(Mark, FieldAccess), - ApplyUpdate(ImSet, FieldAccess), + ApplyAccess(Mark, Vec), + ApplyUpdate(ImSet, Vec), // Records RecordAccess(Mark, FieldAccess), @@ -96,6 +96,58 @@ impl Composable for FieldAccess { } } +impl Composable for Vec { + fn sequential(&mut self, other: &Self) { + // NOTE we don't know they have the same length + + let mut it_other = other.iter(); + { + for (self_usage, other_usage) in self.iter_mut().zip(&mut it_other) { + self_usage.sequential(&other_usage); + + if *self_usage != Usage::Simple(Mark::Seen) { + // e.g. we access `rec.foo` and `rec.foo.bar`. + // Since a reference to `rec.foo` exists, there are at least two references to `foo.bar` + // (`foo.bar` itself and `.bar rec.foo`) + // Therefore fields of the subtrees must be shared! + + // TODO make this work? Seems to function well without it + // self_nested.or_subtree(&Usage::Shared); + // other_nested.or_subtree(&Usage::Shared); + // + // member function on FieldAccess + // fn or_subtree(&mut self, constraint: &Usage) { + // for field_usage in self.fields.iter_mut() { + // field_usage.parallel(constraint); + // } + // } + } + } + } + + // if there are remaining elements in other, push them onto self + for other_usage in it_other { + self.push(other_usage.clone()); + } + } + + fn parallel(&mut self, other: &Self) { + // NOTE we don't know they have the same length + + let mut it_other = other.iter(); + { + for (self_usage, other_usage) in self.iter_mut().zip(&mut it_other) { + self_usage.parallel(&other_usage); + } + } + + // if there are remaining elements in other, push them onto self + for other_usage in it_other { + self.push(other_usage.clone()); + } + } +} + impl Composable for Usage { fn sequential(&mut self, other: &Self) { use Mark::*; @@ -466,22 +518,24 @@ fn correct_overwritten( fn correct_overwritten_apply( mut mark1: Mark, - fa1: &FieldAccess, + fa1: &[Usage], mark2: Mark, - fa2: &FieldAccess, + fa2: &[Usage], overwritten: &ImSet, ) -> Usage { use Usage::*; - let mut fa1 = fa1.clone(); + let mut fa1 = fa1.to_owned(); + // TODO fix this cloning + // tricky because Composable is defined on Vec, not &[] + let fa2 = fa2.to_owned(); mark1.sequential(&mark2); - fa1.sequential(fa2); + fa1.sequential(&fa2); // fields that are accessed, but not overwritten in the update, must be shared! - for (label, usage) in fa1.fields.clone().keys().zip(fa1.fields.iter_mut()) { - // if !overwritten.contains(&label.clone()) { - if true { + for (index, usage) in fa1.iter_mut().enumerate() { + if !overwritten.contains(&index) { make_subtree_shared(usage); } } @@ -515,13 +569,13 @@ fn make_subtree_shared(usage: &mut Usage) { }; } ApplyUpdate(_, fa) => { - for nested in fa.fields.iter_mut() { + for nested in fa.iter_mut() { make_subtree_shared(nested); } } ApplyAccess(m, fa) => { - for nested in fa.fields.iter_mut() { + for nested in fa.iter_mut() { make_subtree_shared(nested); } *m = match &m { @@ -549,14 +603,7 @@ impl VarUsage { closure_signatures.insert( Symbol::LIST_GET, vec![ - // Usage::Apply(vec![Usage::Simple(Mark::Unique)]), - Usage::ApplyAccess(Mark::Seen, { - let mut result = FieldAccess::default(); - result - .fields - .insert(LIST_ELEM.into(), Usage::Simple(Mark::Unique)); - result - }), + Usage::ApplyAccess(Mark::Seen, vec![Usage::Simple(Mark::Unique)]), Usage::Simple(Mark::Seen), ], ); @@ -566,14 +613,7 @@ impl VarUsage { closure_signatures.insert( Symbol::LIST_SET, vec![ - // Usage::Apply(vec![Usage::Simple(Mark::Unique)]), - Usage::ApplyUpdate(ImSet::default(), { - let mut result = FieldAccess::default(); - result - .fields - .insert(LIST_ELEM.into(), Usage::Simple(Mark::Seen)); - result - }), + Usage::ApplyUpdate(ImSet::default(), vec![Usage::Simple(Mark::Seen)]), Usage::Simple(Mark::Seen), Usage::Simple(Mark::Unique), ], diff --git a/compiler/uniq/tests/test_usage_analysis.rs b/compiler/uniq/tests/test_usage_analysis.rs index 8ec68c310f..d7add10545 100644 --- a/compiler/uniq/tests/test_usage_analysis.rs +++ b/compiler/uniq/tests/test_usage_analysis.rs @@ -18,10 +18,9 @@ mod test_usage_analysis { use roc_collections::all::{ImMap, ImSet}; use roc_module::ident::Lowercase; use roc_module::symbol::{Interns, Symbol}; - use roc_uniq::sharing::{self, Container, FieldAccess, Mark, Usage, VarUsage}; + use roc_uniq::sharing::{FieldAccess, Mark, Usage, VarUsage}; use std::collections::HashMap; - use Container::*; use Mark::*; use Usage::*; @@ -686,12 +685,7 @@ mod test_usage_analysis { let mut usage = VarUsage::default(); let home = test_home(); - let access = ApplyAccess( - Unique, - field_access(hashmap![ - sharing::LIST_ELEM => Simple(Shared), - ]), - ); + let access = ApplyAccess(Unique, vec![Simple(Shared)]); let r = interns.symbol(home, "r".into()); @@ -723,12 +717,7 @@ mod test_usage_analysis { let mut usage = VarUsage::default(); let home = test_home(); - let access = ApplyUpdate( - ImSet::default(), - field_access(hashmap![ - sharing::LIST_ELEM => Simple(Shared), - ]), - ); + let access = ApplyUpdate(ImSet::default(), vec![Simple(Shared)]); let r = interns.symbol(home, "list".into()); let v = interns.symbol(home, "v".into()); @@ -763,12 +752,7 @@ mod test_usage_analysis { let mut usage = VarUsage::default(); let home = test_home(); - let access = ApplyUpdate( - ImSet::default(), - field_access(hashmap![ - sharing::LIST_ELEM => Simple(Seen), - ]), - ); + let access = ApplyUpdate(ImSet::default(), vec![Simple(Seen)]); let r = interns.symbol(home, "list".into());