mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-22 08:17:40 +03:00
implement Access
This commit is contained in:
parent
5be62b14b8
commit
d7a42c6423
@ -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();
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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(_, _)
|
||||
|
@ -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)"
|
||||
);
|
||||
|
@ -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
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user