From d7a42c6423fc565deac0c60acb61b488e73e5a18 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 27 Feb 2020 19:46:39 +0100 Subject: [PATCH] implement Access --- src/uniqueness/boolean_algebra.rs | 5 + src/uniqueness/mod.rs | 82 ++++++- src/uniqueness/sharing.rs | 96 ++++++-- tests/test_uniqueness_infer.rs | 12 +- tests/test_usage_analysis.rs | 393 ++++++++++++++++++------------ 5 files changed, 406 insertions(+), 182 deletions(-) diff --git a/src/uniqueness/boolean_algebra.rs b/src/uniqueness/boolean_algebra.rs index 80ba9d4483..ee05e8c21c 100644 --- a/src/uniqueness/boolean_algebra.rs +++ b/src/uniqueness/boolean_algebra.rs @@ -47,6 +47,11 @@ impl Bool { Bool(Variable(variable), atom_set) } + pub fn from_parts(free: Atom, rest: Vec) -> Self { + let atom_set: SendSet = rest.into_iter().collect(); + Bool(free, atom_set) + } + pub fn variables(&self) -> SendSet { let mut result = SendSet::default(); diff --git a/src/uniqueness/mod.rs b/src/uniqueness/mod.rs index f3ce4d2784..a451214ec4 100644 --- a/src/uniqueness/mod.rs +++ b/src/uniqueness/mod.rs @@ -19,7 +19,7 @@ use crate::types::Reason; use crate::types::Type::{self, *}; use crate::uniqueness::boolean_algebra::{Atom, Bool}; use crate::uniqueness::builtins::{attr_type, list_type, str_type}; -use crate::uniqueness::sharing::{FieldAccess, Mark, Usage, VarUsage}; +use crate::uniqueness::sharing::{Container, FieldAccess, Mark, Usage, VarUsage}; pub use crate::can::expr::Expr::*; @@ -1186,9 +1186,11 @@ fn constrain_var( let mut variables = Vec::new(); let (free, rest, inner_type) = - constrain_field_access(var_store, &field_access, &mut variables); + constrain_by_usage(&usage.expect("wut"), var_store, &mut variables); - let record_type = attr_type(Bool::with_free(free, rest), inner_type); + dbg!(&free, &rest, &inner_type); + + let record_type = attr_type(Bool::from_parts(free, rest), inner_type); // NOTE breaking the expectation up like this REALLY matters! let new_expected = Expected::NoExpectation(record_type.clone()); @@ -1205,6 +1207,80 @@ fn constrain_var( } } +fn constrain_by_usage( + usage: &Usage, + var_store: &VarStore, + introduced: &mut Vec, +) -> (Atom, Vec, Type) { + use Container::*; + use Mark::*; + use Usage::*; + + match usage { + Simple(Shared) => { + let var = var_store.fresh(); + + introduced.push(var); + + (Atom::Zero, vec![], Type::Variable(var)) + } + Simple(Seen) | Simple(Unique) => { + let var = var_store.fresh(); + let uvar = var_store.fresh(); + + introduced.push(var); + introduced.push(uvar); + + (Atom::Variable(uvar), vec![], Type::Variable(var)) + } + Usage::Update(Container::Record, _, fields) => todo!(), + Usage::Access(Container::Record, mark, fields) => { + let (record_uniq_var, atoms, ext_type) = + constrain_by_usage(&Simple(*mark), var_store, introduced); + debug_assert!(atoms.is_empty()); + + let mut field_types = SendMap::default(); + + if fields.is_empty() { + ( + record_uniq_var, + vec![], + Type::Record(field_types, Box::new(ext_type)), + ) + } else { + let mut uniq_vars = Vec::with_capacity(fields.len()); + + for (lowercase, nested_usage) in fields.clone().into_iter() { + let (uvar, atoms, nested_type) = + constrain_by_usage(&nested_usage, var_store, introduced); + + for atom in &atoms { + uniq_vars.push(*atom); + } + uniq_vars.push(uvar); + + let field_type = attr_type(Bool::from_parts(uvar, atoms), nested_type); + + field_types.insert(lowercase.clone(), field_type); + } + ( + record_uniq_var, + uniq_vars, + Type::Record( + field_types, + // TODO can we avoid doing Box::new on every single one of these? + // For example, could we have a single lazy_static global Box they + // could all share? + Box::new(ext_type), + ), + ) + } + } + Usage::Update(Container::List, _, fields) => todo!(), + Usage::Access(Container::List, mark, fields) => todo!(), + } +} + fn constrain_field_access( var_store: &VarStore, field_access: &FieldAccess, diff --git a/src/uniqueness/sharing.rs b/src/uniqueness/sharing.rs index c686510a65..544d2098a6 100644 --- a/src/uniqueness/sharing.rs +++ b/src/uniqueness/sharing.rs @@ -16,6 +16,15 @@ pub struct FieldAccess { fields: ImMap, } +impl IntoIterator for FieldAccess { + type Item = (Lowercase, Usage); + type IntoIter = im_rc::hashmap::ConsumingIter<(Lowercase, Usage)>; + + fn into_iter(self) -> Self::IntoIter { + self.fields.into_iter() + } +} + #[derive(Debug, Clone, PartialEq)] pub enum Usage { Simple(Mark), @@ -29,7 +38,7 @@ pub enum Container { List, } -trait Composable { +pub trait Composable { fn sequential(&mut self, other: &Self); fn parallel(&mut self, other: &Self); } @@ -59,6 +68,7 @@ impl Composable for FieldAccess { } fn parallel(&mut self, other: &Self) { + dbg!(&self, &other); for (field_name, other_usage) in other.fields.clone() { if self.fields.contains_key(&field_name) { if let Some(self_usage) = self.fields.get_mut(&field_name) { @@ -76,6 +86,7 @@ impl Composable for Usage { use Mark::*; use Usage::*; + println!("{:?} ~ {:?}", &self, &other); match (&self, other) { // Shared always wins (Simple(Shared), _) | (_, Simple(Shared)) => { @@ -109,7 +120,7 @@ impl Composable for Usage { let mut fa = fa1.clone(); fa.sequential(fa2); - let mut m = m1.clone(); + let mut m = *m1; m.sequential(m2); *self = Access(*c1, m, fa); @@ -117,10 +128,30 @@ impl Composable for Usage { (Access(c1, m, fa1), Simple(Unique)) => { let mut copy = Access(*c1, *m, fa1.clone()); make_subtree_shared(&mut copy); - *self = copy; + + // correct the mark of the top-level access + *self = if let Access(c, _, fa) = copy { + let mut m = *m; + m.sequential(&Unique); + + Access(c, m, fa) + } else { + unreachable!() + }; } - (Simple(Unique), Access(_, _, _)) => { - *self = Simple(Shared); + (Simple(Unique), Access(c, m, fa)) => { + let mut copy = Access(*c, *m, fa.clone()); + make_subtree_shared(&mut copy); + + // correct the mark of the top-level access + *self = if let Access(c, _, fa) = copy { + let mut m = *m; + m.sequential(&Unique); + + Access(c, m, fa) + } else { + unreachable!() + }; } (Simple(m1 @ Seen), Access(c1, m2, fa)) => { @@ -150,7 +181,7 @@ impl Composable for Usage { match (&self, other) { (Simple(s1), Simple(s2)) => { let mut s = s1.clone(); - s.sequential(s2); + s.parallel(s2); *self = Simple(s); } @@ -190,15 +221,22 @@ impl Composable for Usage { } (Access(c1, m1, fa1), Access(c2, m2, fa2)) => { + println!("Access - Access"); debug_assert_eq!(c1, c2); let mut m = m1.clone(); m.parallel(m2); let mut fa = fa1.clone(); fa.parallel(fa2); + println!("Done"); *self = Access(*c1, m, fa) } - (Access(_, _, _), Simple(Unique)) | (Access(_, _, _), Simple(Seen)) => { + (Access(c, m, fa), Simple(Unique)) => { + let mut m = *m; + m.parallel(&Unique); + *self = Access(*c, m, fa.clone()); + } + (Access(_, _, _), Simple(Seen)) => { // *self = Access(*c1, *m, fa.clone()); } (Simple(m1 @ Unique), Access(c1, m2, fa)) | (Simple(m1 @ Seen), Access(c1, m2, fa)) => { @@ -291,7 +329,10 @@ fn make_subtree_shared(usage: &mut Usage) { for nested in fa.fields.iter_mut() { make_subtree_shared(nested); } - *m = Shared; + *m = match &m { + Seen => Seen, + _ => Shared, + }; } } } @@ -327,7 +368,9 @@ impl VarUsage { } }; - self.usage.insert(symbol.clone(), value); + println!("REGISTER {:?} {:?}", symbol, &value); + + self.usage.insert(symbol, value); } pub fn register_shared(&mut self, symbol: Symbol) { @@ -411,6 +454,17 @@ impl Usage { accum } + + pub fn parallel_chain(&mut self, access_chain: Vec) { + let other = Self::from_chain(access_chain); + self.parallel(&other); + } + + pub fn sequential_chain(&mut self, access_chain: Vec) { + let other = Self::from_chain(access_chain); + dbg!(&other); + self.sequential(&other); + } } impl FieldAccess { @@ -419,22 +473,24 @@ impl FieldAccess { field_usage.parallel(constraint); } } -} -// pub fn parallel(&mut self, access_chain: Vec) { -// let other = Self::from_chain(access_chain); -// self.parallel_merge(&other); -// } -// -// pub fn sequential(&mut self, access_chain: Vec) { -// let other = Self::from_chain(access_chain); -// self.sequential_merge(&other); -// } + pub fn new(fields: ImMap) -> Self { + FieldAccess { fields } + } + + pub fn is_empty(&self) -> bool { + self.fields.is_empty() + } + + pub fn len(&self) -> usize { + self.fields.len() + } +} pub fn annotate_usage(expr: &Expr, usage: &mut VarUsage) { use Expr::*; - match expr { + match dbg!(expr) { RuntimeError(_) | Int(_, _) | Float(_, _) diff --git a/tests/test_uniqueness_infer.rs b/tests/test_uniqueness_infer.rs index 656339cf29..95118ac9df 100644 --- a/tests/test_uniqueness_infer.rs +++ b/tests/test_uniqueness_infer.rs @@ -1239,12 +1239,13 @@ mod test_infer_uniq { v = r.x w = r.y - p = r + # force alias after access (nested let-block) + (p = r - p + p) "# ), - "Attr * (Attr Shared { x : (Attr Shared a), y : (Attr Shared b) }c -> Attr Shared { x : (Attr Shared a), y : (Attr Shared b) }c)" + "Attr * (Attr a { x : (Attr Shared b), y : (Attr Shared c) }d -> Attr a { x : (Attr Shared b), y : (Attr Shared c) }d)" ); } @@ -1306,7 +1307,7 @@ mod test_infer_uniq { r "# ), - "Attr * (Attr (a | b) { foo : (Attr b { bar : (Attr Shared c) }d) }e -> Attr (a | b) { foo : (Attr b { bar : (Attr Shared c) }d) }e)" + "Attr * (Attr (a | b) { foo : (Attr a { bar : (Attr Shared c) }d) }e -> Attr (a | b) { foo : (Attr a { bar : (Attr Shared c) }d) }e)" ); } @@ -1341,7 +1342,8 @@ mod test_infer_uniq { r.tic.tac.toe "# ), - "Attr * (Attr (* | a | b | c | d | e) { foo : (Attr (b | c | e) { bar : (Attr (b | e) { baz : (Attr b f) }*) }*), tic : (Attr (a | b | d) { tac : (Attr (b | d) { toe : (Attr b f) }*) }*) }* -> Attr b f)" + "Attr * (Attr (* | a | b | c | d | e) { foo : (Attr (b | d | e) { bar : (Attr (b | d) { baz : (Attr b f) }*) }*), tic : (Attr (a | b | c) { tac : (Attr (b | c) { toe : (Attr b f) }*) }*) }* -> Attr b f)" + // "Attr * (Attr (* | a | b | c | d | e) { foo : (Attr (b | c | e) { bar : (Attr (b | e) { baz : (Attr b f) }*) }*), tic : (Attr (a | b | d) { tac : (Attr (b | d) { toe : (Attr b f) }*) }*) }* -> Attr b f)" // "Attr * (Attr (* | a | b | c | d | e) { foo : (Attr (a | c | d) { bar : (Attr (a | c) { baz : (Attr c f) }*) }*), tic : (Attr (b | c | e) { tac : (Attr (c | e) { toe : (Attr c f) }*) }*) }* -> Attr c f)" // "Attr * (Attr (* | a | b | c | d | e) { foo : (Attr (a | c | d) { bar : (Attr (c | d) { baz : (Attr d f) }*) }*), tic : (Attr (b | d | e) { tac : (Attr (b | d) { toe : (Attr d f) }*) }*) }* -> Attr d f)" ); diff --git a/tests/test_usage_analysis.rs b/tests/test_usage_analysis.rs index 5e2cfc4771..166efcea12 100644 --- a/tests/test_usage_analysis.rs +++ b/tests/test_usage_analysis.rs @@ -16,54 +16,91 @@ mod test_usage_analysis { use roc::can::ident::Lowercase; use roc::collections::{ImMap, ImSet}; use roc::module::symbol::Interns; + use roc::uniqueness::sharing::Composable; use roc::uniqueness::sharing::FieldAccess; - use roc::uniqueness::sharing::ReferenceCount::{self, *}; use roc::uniqueness::sharing::VarUsage; + use roc::uniqueness::sharing::{Container, Mark, Usage}; + + use Container::*; + use Mark::*; + use Usage::*; fn field_access_seq( accesses: Vec>, - expected: std::collections::HashMap<&str, ReferenceCount>, + expected_ref: std::collections::HashMap<&str, Usage>, ) { - let mut state = FieldAccess::default(); + use Mark::*; + use Usage::*; + + let mut usage = Simple(Seen); for access in accesses { let temp: Vec = access.into_iter().map(|v| v.into()).collect(); - state.sequential(temp); + dbg!(&usage); + usage.sequential_chain(temp); } + dbg!(&usage); - let mut im_expected: std::collections::HashMap = - std::collections::HashMap::default(); + match usage { + Usage::Access(_, _, fields) => { + let mut actual: std::collections::HashMap = + std::collections::HashMap::default(); + for (k, v) in fields.into_iter() { + actual.insert(k, v); + } - for (k, v) in expected { - im_expected.insert(k.into(), v); + let mut expected = std::collections::HashMap::default(); + for (k, v) in expected_ref { + expected.insert(k.into(), v); + } + + assert_eq!(actual, expected); + } + _ => panic!("Not an access, but {:?}", usage), } - - let actual: std::collections::HashMap = state.into(); - - assert_eq!(actual, im_expected); } fn field_access_par( accesses: Vec>, - expected: std::collections::HashMap<&str, ReferenceCount>, + expected_ref: std::collections::HashMap<&str, Usage>, ) { - let mut state = FieldAccess::default(); + use Mark::*; + use Usage::*; + + let mut usage = Simple(Seen); for access in accesses { let temp: Vec = access.into_iter().map(|v| v.into()).collect(); - state.parallel(temp); + usage.parallel_chain(temp); } - let mut im_expected: std::collections::HashMap = - std::collections::HashMap::default(); + match usage { + Usage::Access(_, _, fields) => { + let mut actual: std::collections::HashMap = + std::collections::HashMap::default(); + for (k, v) in fields.into_iter() { + actual.insert(k, v); + } - for (k, v) in expected { - im_expected.insert(k.into(), v); + let mut expected = std::collections::HashMap::default(); + for (k, v) in expected_ref { + expected.insert(k.into(), v); + } + + assert_eq!(actual, expected); + } + _ => panic!("Not an access, but {:?}", usage), + } + } + + fn field_access(fields: std::collections::HashMap<&str, Usage>) -> FieldAccess { + let mut new_fields = ImMap::default(); + + for (k, v) in fields { + new_fields.insert(k.into(), v); } - let actual: std::collections::HashMap = state.into(); - - assert_eq!(actual, im_expected); + FieldAccess::new(new_fields) } #[test] @@ -71,16 +108,16 @@ mod test_usage_analysis { field_access_seq( vec![vec!["foo"], vec!["bar"]], hashmap![ - "foo" => Unique, - "bar" => Unique, + "foo" => Simple(Unique), + "bar" => Simple(Unique), ], ); field_access_par( vec![vec!["foo"], vec!["bar"]], hashmap![ - "foo" => Unique, - "bar" => Unique, + "foo" => Simple(Unique), + "bar" => Simple(Unique), ], ); } @@ -89,9 +126,7 @@ mod test_usage_analysis { fn usage_access_repeated_field_seq() { field_access_seq( vec![vec!["foo"], vec!["foo"]], - hashmap![ - "foo" => Shared, - ], + hashmap![ "foo" => Simple(Shared) ], ); } @@ -100,7 +135,7 @@ mod test_usage_analysis { field_access_par( vec![vec!["foo"], vec!["foo"]], hashmap![ - "foo" => Unique, + "foo" => Simple(Unique), ], ); } @@ -110,15 +145,14 @@ mod test_usage_analysis { field_access_seq( vec![vec!["foo", "bar"], vec!["foo"]], hashmap![ - "foo" => Unique, - "foo.bar" => Shared, + "foo" => Access(Record, Unique, field_access(hashmap![ "bar" => Simple(Shared) ])) ], ); + field_access_seq( vec![vec!["foo"], vec!["foo", "bar"]], hashmap![ - "foo" => Unique, - "foo.bar" => Shared, + "foo" => Access(Record, Unique, field_access(hashmap![ "bar" => Simple(Shared) ])) ], ); } @@ -127,15 +161,13 @@ mod test_usage_analysis { field_access_par( vec![vec!["foo", "bar"], vec!["foo"]], hashmap![ - "foo" => Unique, - "foo.bar" => Unique, + "foo" => Access(Record, Unique, field_access(hashmap![ "bar" => Simple(Unique) ])) ], ); field_access_par( vec![vec!["foo"], vec!["foo", "bar"]], hashmap![ - "foo" => Unique, - "foo.bar" => Unique, + "foo" => Access(Record, Unique, field_access(hashmap![ "bar" => Simple(Unique) ])) ], ); } @@ -145,20 +177,17 @@ mod test_usage_analysis { field_access_seq( vec![vec!["foo", "bar", "baz"], vec!["foo", "bar"]], hashmap![ - "foo" => Seen, - "foo.bar" => Unique, - "foo.bar.baz" => Shared, + "foo" => Access(Record, Seen, field_access(hashmap![ "bar" => Access(Record, Unique, field_access(hashmap![ "baz" => Simple(Shared) ]))])) ], ); field_access_seq( vec![vec!["foo", "bar"], vec!["foo", "bar", "baz"]], hashmap![ - "foo" => Seen, - "foo.bar" => Unique, - "foo.bar.baz" => Shared, + "foo" => Access(Record, Seen, field_access(hashmap![ "bar" => Access(Record, Unique, field_access(hashmap![ "baz" => Simple(Shared) ]))])) ], ); } + fn usage_eq(src: &str, get_expected: F) where F: FnOnce(Interns) -> VarUsage, @@ -192,9 +221,9 @@ mod test_usage_analysis { let home = test_home(); let mut usage = VarUsage::default(); - usage.register_with(interns.symbol(home, "m".into()), &Unique); - usage.register_with(interns.symbol(home, "n".into()), &Unique); - usage.register_with(interns.symbol(home, "factorial".into()), &Shared); + usage.register_unique(interns.symbol(home, "m".into())); + usage.register_unique(interns.symbol(home, "n".into())); + usage.register_shared(interns.symbol(home, "factorial".into())); usage }, @@ -213,11 +242,14 @@ mod test_usage_analysis { |interns| { let home = test_home(); let mut usage = VarUsage::default(); - let fa = FieldAccess::from_chain(vec!["foo".into()]); usage.register_with( interns.symbol(home, "rec".into()), - &ReferenceCount::Access(fa), + &Access( + Record, + Seen, + field_access(hashmap![ "foo" => Simple(Unique) ]), + ), ); usage @@ -237,12 +269,15 @@ mod test_usage_analysis { |interns| { let home = test_home(); let mut usage = VarUsage::default(); - let fa = FieldAccess::from_chain(vec!["foo".into()]); let overwritten = hashset!["foo".into()].into(); usage.register_with( interns.symbol(home, "rec".into()), - &ReferenceCount::Update(overwritten, fa), + &Update( + Record, + overwritten, + field_access(hashmap![ "foo" => Simple(Unique) ]), + ), ); usage @@ -264,7 +299,7 @@ mod test_usage_analysis { |interns| { let home = test_home(); let mut usage = VarUsage::default(); - usage.register_with(interns.symbol(home, "rec".into()), &ReferenceCount::Shared); + usage.register_with(interns.symbol(home, "rec".into()), &Simple(Shared)); usage }, @@ -285,15 +320,14 @@ mod test_usage_analysis { |interns| { let home = test_home(); let mut usage = VarUsage::default(); - let mut fields = ImMap::default(); - fields.insert( - "foo".into(), - (ReferenceCount::Shared, FieldAccess::default()), - ); - let fa = FieldAccess { fields: fields }; + usage.register_with( interns.symbol(home, "rec".into()), - &ReferenceCount::Update(ImSet::default(), fa), + &Access( + Record, + Unique, + field_access(hashmap![ "foo" => Simple(Shared) ]), + ), ); usage @@ -310,17 +344,27 @@ mod test_usage_analysis { v = r.x w = r.y - p = r + # nested let-block. Force the assignement after the access + (p = r - p + p) "# ), |interns| { let home = test_home(); + println!("---- test -----"); let mut usage = VarUsage::default(); - usage.register_with(interns.symbol(home, "p".into()), &ReferenceCount::Unique); - usage.register_with(interns.symbol(home, "r".into()), &ReferenceCount::Shared); + let fa = field_access(hashmap![ + "x" => Simple(Shared), + "y" => Simple(Shared), + ]); + + usage.register_unique(interns.symbol(home, "p".into())); + usage.register_with( + interns.symbol(home, "r".into()), + &Access(Record, Unique, fa), + ); usage }, @@ -343,26 +387,17 @@ mod test_usage_analysis { let home = test_home(); let mut usage = VarUsage::default(); - let mut nested_fields = ImMap::default(); - nested_fields.insert( - "bar".into(), - (ReferenceCount::Shared, FieldAccess::default()), - ); - nested_fields.insert( - "baz".into(), - (ReferenceCount::Shared, FieldAccess::default()), - ); - let nested_fa = FieldAccess { - fields: nested_fields, - }; + let fa = field_access(hashmap![ + "foo" => + Access(Record, Seen, field_access(hashmap![ + "bar" => Simple(Shared), + "baz" => Simple(Shared), + ])) + ]); - let mut fields = ImMap::default(); - fields.insert("foo".into(), (ReferenceCount::Seen, nested_fa)); - - let fa = FieldAccess { fields: fields }; usage.register_with( interns.symbol(home, "r".into()), - &ReferenceCount::Update(ImSet::default(), fa), + &Access(Record, Unique, fa), ); usage @@ -388,24 +423,22 @@ mod test_usage_analysis { let home = test_home(); let mut usage = VarUsage::default(); - let mut fields = ImMap::default(); - fields.insert("x".into(), (ReferenceCount::Shared, FieldAccess::default())); - let fa = FieldAccess { fields: fields }; + let r = interns.symbol(home, "r".into()); + let s = interns.symbol(home, "s".into()); + let overwritten = hashset!["y".into()].into(); - usage.register_with( - interns.symbol(home, "r".into()), - &ReferenceCount::Update(overwritten, fa), - ); + let fa = field_access(hashmap![ + "x" => Simple(Shared), + ]); - let mut fields = ImMap::default(); - fields.insert("x".into(), (ReferenceCount::Unique, FieldAccess::default())); - fields.insert("y".into(), (ReferenceCount::Unique, FieldAccess::default())); - let fa = FieldAccess { fields: fields }; - usage.register_with( - interns.symbol(home, "s".into()), - &ReferenceCount::Access(fa), - ); + usage.register_with(r, &Update(Record, overwritten, fa)); + let fa = field_access(hashmap![ + "x" => Simple(Unique), + "y" => Simple(Unique), + ]); + + usage.register_with(s, &Access(Record, Seen, fa)); usage }, ); @@ -427,24 +460,24 @@ mod test_usage_analysis { ), |interns| { let home = test_home(); - // pub fields: ImMap, let mut usage = VarUsage::default(); - let fa = FieldAccess::from_chain(vec!["x".into()]); - let overwritten = hashset!["x".into(), "y".into()].into(); - usage.register_with( - interns.symbol(home, "r".into()), - &ReferenceCount::Update(overwritten, fa), - ); + let r = interns.symbol(home, "r".into()); + let s = interns.symbol(home, "s".into()); - let mut fields = ImMap::default(); - fields.insert("x".into(), (ReferenceCount::Unique, FieldAccess::default())); - fields.insert("y".into(), (ReferenceCount::Unique, FieldAccess::default())); - let fa = FieldAccess { fields: fields }; - usage.register_with( - interns.symbol(home, "s".into()), - &ReferenceCount::Access(fa), - ); + let overwritten = hashset!["x".into(), "y".into()].into(); + let fa = field_access(hashmap![ + "x" => Simple(Unique), + ]); + + usage.register_with(r, &Update(Record, overwritten, fa)); + + let fa = field_access(hashmap![ + "x" => Simple(Unique), + "y" => Simple(Unique), + ]); + + usage.register_with(s, &Access(Record, Seen, fa)); usage }, @@ -466,14 +499,14 @@ mod test_usage_analysis { // pub fields: ImMap, let mut usage = VarUsage::default(); - let mut fields = ImMap::default(); - fields.insert("x".into(), (ReferenceCount::Unique, FieldAccess::default())); - fields.insert("y".into(), (ReferenceCount::Unique, FieldAccess::default())); - let fa = FieldAccess { fields: fields }; - usage.register_with( - interns.symbol(home, "r".into()), - &ReferenceCount::Access(fa), - ); + let r = interns.symbol(home, "r".into()); + + let fa = field_access(hashmap![ + "x" => Simple(Unique), + "y" => Simple(Unique), + ]); + + usage.register_with(r, &Access(Record, Seen, fa)); usage }, @@ -492,16 +525,17 @@ mod test_usage_analysis { ), |interns| { let home = test_home(); - // pub fields: ImMap, let mut usage = VarUsage::default(); - let mut fields = ImMap::default(); - fields.insert("x".into(), (ReferenceCount::Shared, FieldAccess::default())); - let fa = FieldAccess { fields: fields }; - usage.register_with( - interns.symbol(home, "r".into()), - &ReferenceCount::Update(hashset!["y".into()].into(), fa), - ); + let r = interns.symbol(home, "r".into()); + + let fa = field_access(hashmap![ + "x" => Simple(Shared), + ]); + + let overwritten = hashset!["y".into()].into(); + + usage.register_with(r, &Update(Record, overwritten, fa)); usage }, @@ -520,18 +554,18 @@ mod test_usage_analysis { ), |interns| { let home = test_home(); - // pub fields: ImMap, let mut usage = VarUsage::default(); - let mut fields = ImMap::default(); - fields.insert("x".into(), (ReferenceCount::Unique, FieldAccess::default())); - fields.insert("y".into(), (ReferenceCount::Unique, FieldAccess::default())); - let fa = FieldAccess { fields: fields }; - usage.register_with( - interns.symbol(home, "r".into()), - &ReferenceCount::Access(fa), + let access = Access( + Record, + Seen, + field_access(hashmap![ + "x" => Simple(Unique), + "y" => Simple(Unique) + ]), ); + usage.register_with(interns.symbol(home, "r".into()), &access); usage }, ); @@ -549,8 +583,8 @@ mod test_usage_analysis { let home = test_home(); let mut usage = VarUsage::default(); - usage.register_with(Interns::from_index(home, 1), &ReferenceCount::Unique); - usage.register_with(Interns::from_index(home, 3), &ReferenceCount::Unique); + usage.register_unique(Interns::from_index(home, 1)); + usage.register_unique(Interns::from_index(home, 3)); usage }, @@ -575,19 +609,22 @@ mod test_usage_analysis { let mut usage = VarUsage::default(); let home = test_home(); - let mut fields = ImMap::default(); - fields.insert("y".into(), (ReferenceCount::Unique, FieldAccess::default())); - let fa_r = FieldAccess { fields: fields }; - - let mut fields = ImMap::default(); - fields.insert("x".into(), (ReferenceCount::Shared, FieldAccess::default())); - let fa_s = FieldAccess { fields: fields }; + let access_r = Access( + Record, + Seen, + field_access(hashmap![ "y" => Simple(Unique) ]), + ); + let access_s = Access( + Record, + Seen, + field_access(hashmap![ "x" => Simple(Shared) ]), + ); let r = interns.symbol(home, "r".into()); let s = interns.symbol(home, "s".into()); - usage.register_with(r, &ReferenceCount::Access(fa_r)); - usage.register_with(s, &ReferenceCount::Access(fa_s)); + usage.register_with(r, &access_r); + usage.register_with(s, &access_s); usage }, @@ -613,19 +650,67 @@ mod test_usage_analysis { let mut usage = VarUsage::default(); let home = test_home(); - let mut fields = ImMap::default(); - fields.insert("x".into(), (ReferenceCount::Unique, FieldAccess::default())); - let fa_r = FieldAccess { fields: fields }; - - let mut fields = ImMap::default(); - fields.insert("x".into(), (ReferenceCount::Shared, FieldAccess::default())); - let fa_s = FieldAccess { fields: fields }; + let access_r = Access( + Record, + Seen, + field_access(hashmap![ "x" => Simple(Unique) ]), + ); + let access_s = Access( + Record, + Seen, + field_access(hashmap![ "x" => Simple(Shared) ]), + ); let r = interns.symbol(home, "r".into()); let s = interns.symbol(home, "s".into()); - usage.register_with(r, &ReferenceCount::Access(fa_r)); - usage.register_with(s, &ReferenceCount::Access(fa_s)); + usage.register_with(r, &access_r); + usage.register_with(s, &access_s); + + usage + }, + ); + } + #[test] + fn record_update_is_safe() { + usage_eq( + indoc!( + r#" + \r -> + + s = { r & y: r.x } + + p = s.x + q = s.y + + s + "# + ), + |interns| { + let mut usage = VarUsage::default(); + let home = test_home(); + + let overwritten = hashset!["y".into()].into(); + let access_r = Update( + Record, + overwritten, + field_access(hashmap![ "x" => Simple(Shared) ]), + ); + + let access_s = Access( + Record, + Unique, + field_access(hashmap![ + "x" => Simple(Shared), + "y" => Simple(Shared) + ]), + ); + + let r = interns.symbol(home, "r".into()); + let s = interns.symbol(home, "s".into()); + + usage.register_with(r, &access_r); + usage.register_with(s, &access_s); usage },