-
Notifications
You must be signed in to change notification settings - Fork 0
/
mutable_aliasing.rs
127 lines (119 loc) · 4.5 KB
/
mutable_aliasing.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! Problem: given a list of immutable and mutable references to variables,
//! determine which variables violate Rust's mutability XOR aliasing requirement.
//!
//! (Posted with modifications to
//! https://codegolf.stackexchange.com/questions/274829/is-there-mutable-aliasing-in-this-list-of-variable-references.)
use std::{
collections::{HashMap, HashSet},
hash::Hash,
};
/// Whether a variable reference is immutable or mutable.
#[derive(Clone, Copy)]
pub enum Mutability {
Immutable,
Mutable,
}
/// A reference to a variable, simulating Rust's `&x` and `&mut x` references.
pub struct Reference<T> {
// The exact representation of a variable is flexible, and can be of various types.
variable: T,
mutability: Mutability,
}
/// A classification of a given variable's set of references,
/// based on the number of immutable and mutable references.
#[derive(Clone, Copy, PartialEq, Eq)]
enum ReferenceSetType {
/// No references.
Empty,
/// One or more immutable references.
Aliased,
/// Exactly one mutable reference.
Mutable,
/// Two or more references, at least one of which is mutable.
MutablyAliased,
}
/// Returns the set of variables that are mutably aliased
/// (have two or more references, at least one of which is mutable)
/// in the given list of references.
pub fn mutable_aliasing_violations<T: Copy + Eq + Hash>(references: &[Reference<T>]) -> HashSet<T> {
use Mutability as Mut;
use ReferenceSetType as RST;
let mut var_to_type = HashMap::new();
for reference in references {
let ref_set_type = var_to_type.entry(reference.variable).or_insert(RST::Empty);
*ref_set_type = match (*ref_set_type, reference.mutability) {
(RST::Empty, Mut::Immutable) => RST::Aliased,
(RST::Empty, Mut::Mutable) => RST::Mutable,
(RST::Aliased, Mut::Immutable) => RST::Aliased,
(RST::Aliased, Mut::Mutable) => RST::MutablyAliased,
(RST::Mutable, _) => RST::MutablyAliased,
(RST::MutablyAliased, _) => RST::MutablyAliased,
};
}
var_to_type
.iter()
.filter_map(|(&var, &ref_set_type)| (ref_set_type == RST::MutablyAliased).then_some(var))
.collect()
}
#[cfg(test)]
mod tests {
use crate::mutable_aliasing::*;
use rstest::rstest;
/// Creates a list of references to variables represented by `&str` names.
///
/// Syntax:
/// ```text
/// refs![<reference in the form `&x` or `&mut x`>, ...]
/// ```
macro_rules! refs {
(@ref $var:ident) => {
Reference {
variable: stringify!($var),
mutability: Mutability::Immutable,
}
};
(@ref mut $var:ident) => {
Reference {
variable: stringify!($var),
mutability: Mutability::Mutable,
}
};
($(&$($tokens:ident)*),*) => {
[$(refs!(@ref $($tokens)*)),*]
};
}
#[rstest]
#[case(&refs![], [])]
#[case(&refs![&a], [])]
#[case(&refs![&mut a], [])]
#[case(&refs![&b, &b], [])]
#[case(&refs![&mut b, &b], ["b"])]
#[case(&refs![&b, &mut b], ["b"])]
#[case(&refs![&mut b, &mut b], ["b"])]
#[case(&refs![&c, &d], [])]
#[case(&refs![&mut c, &mut d], [])]
#[case(&refs![&a, &mut d, &mut d], ["d"])]
#[case(&refs![&mut a, &d, &mut d], ["d"])]
#[case(&refs![&mut a, &mut d, &mut d, &mut a], ["a", "d"])]
#[case(&refs![&a, &mut d, &d, &a], ["d"])]
#[case(&refs![&c, &mut g, &c, &c], [])]
#[case(&refs![&c, &mut g, &mut c, &c], ["c"])]
#[case(&refs![&a, &b, &c, &d, &e], [])]
#[case(&refs![&mut f, &e, &e, &mut d], [])]
#[case(&refs![&f, &e, &mut e, &d], ["e"])]
#[case(&refs![&a, &mut g, &b, &a, &b, &b, &mut f], [])]
#[case(&refs![&mut a, &g, &b, &a, &b, &mut b, &f], ["a", "b"])]
#[case(&refs![&a, &a, &a, &a, &a, &a, &a], [])]
#[case(&refs![&a, &a, &a, &mut a, &a, &a, &a], ["a"])]
#[case(&refs![&mut a, &mut a, &mut a, &mut a, &mut a, &mut a, &mut a], ["a"])]
#[case(&refs![&mut g, &mut g, &mut g, &mut g, &mut g, &mut g, &mut g], ["g"])]
#[case(&refs![&a, &b, &mut c, &mut d, &e, &mut f, &g], [])]
#[case(&refs![&a, &b, &c, &mut a, &mut b, &mut c], ["a", "b", "c"])]
#[case(&refs![&a, &mut a, &a, &mut g, &g, &mut g], ["a", "g"])]
fn tests<const N: usize>(#[case] references: &[Reference<&str>], #[case] expected: [&str; N]) {
assert_eq!(
mutable_aliasing_violations(references),
HashSet::from(expected)
);
}
}