implement Access

This commit is contained in:
Folkert 2020-02-27 19:46:39 +01:00
parent 5be62b14b8
commit d7a42c6423
5 changed files with 406 additions and 182 deletions

View File

@ -47,6 +47,11 @@ impl Bool {
Bool(Variable(variable), atom_set)
}
pub fn from_parts(free: Atom, rest: Vec<Atom>) -> Self {
let atom_set: SendSet<Atom> = rest.into_iter().collect();
Bool(free, atom_set)
}
pub fn variables(&self) -> SendSet<Variable> {
let mut result = SendSet::default();

View File

@ -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<Variable>,
) -> (Atom, Vec<Atom>, 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,

View File

@ -16,6 +16,15 @@ pub struct FieldAccess {
fields: ImMap<Lowercase, Usage>,
}
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<Lowercase>) {
let other = Self::from_chain(access_chain);
self.parallel(&other);
}
pub fn sequential_chain(&mut self, access_chain: Vec<Lowercase>) {
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<Lowercase>) {
// let other = Self::from_chain(access_chain);
// self.parallel_merge(&other);
// }
//
// pub fn sequential(&mut self, access_chain: Vec<Lowercase>) {
// let other = Self::from_chain(access_chain);
// self.sequential_merge(&other);
// }
pub fn new(fields: ImMap<Lowercase, Usage>) -> 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(_, _)

View File

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

View File

@ -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<Vec<&str>>,
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<Lowercase> = 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<String, ReferenceCount> =
std::collections::HashMap::default();
match usage {
Usage::Access(_, _, fields) => {
let mut actual: std::collections::HashMap<Lowercase, Usage> =
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<String, ReferenceCount> = state.into();
assert_eq!(actual, im_expected);
}
fn field_access_par(
accesses: Vec<Vec<&str>>,
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<Lowercase> = access.into_iter().map(|v| v.into()).collect();
state.parallel(temp);
usage.parallel_chain(temp);
}
let mut im_expected: std::collections::HashMap<String, ReferenceCount> =
std::collections::HashMap::default();
match usage {
Usage::Access(_, _, fields) => {
let mut actual: std::collections::HashMap<Lowercase, Usage> =
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<String, ReferenceCount> = 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<F>(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<String, (ReferenceCount, FieldAccess)>,
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<String, (ReferenceCount, FieldAccess)>,
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<String, (ReferenceCount, FieldAccess)>,
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<String, (ReferenceCount, FieldAccess)>,
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
},