Skip to content

Commit fd72920

Browse files
Add tests for mutability (#574)
Adding tests to examine the boundaries of mutability and immutability, not all of these use `unsafe` but only the ones I listed as unsafe do we annotate unsafe directly: - mut_const.rs: A UB test that mutates the data under an immutable value through pointer arithmetic - interior-mut-fail.rs: Safely has interior mutability through `RefCell` (stuck on `Rvalue::AddressOf`) - interior-mut2-fail.rs: Safely has interior mutability through `Cell` (stuck on `Rvalue::AddressOf`) - interior-mut3-fail.rs: Unsafe but sound interior mutability through `UnsafeCell` (stuck on `Rvalue::AddressOf`) We discussed the possibility of adding tests that show demonstrate immutable field of mutable structs, but this is not possible in rust. There is a notion of private fields if structs come from other modules, but this is not the same as mutability. --------- Co-authored-by: devops <[email protected]>
1 parent 29aaa11 commit fd72920

12 files changed

+144
-4
lines changed

kmir/pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "kmir"
7-
version = "0.3.156"
7+
version = "0.3.157"
88
description = ""
99
requires-python = "~=3.10"
1010
dependencies = [

kmir/src/kmir/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from typing import Final
22

3-
VERSION: Final = '0.3.156'
3+
VERSION: Final = '0.3.157'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use std::cell::RefCell;
2+
3+
struct Counter {
4+
value: RefCell<i32>,
5+
}
6+
7+
impl Counter {
8+
fn new(start: i32) -> Self {
9+
Counter { value: RefCell::new(start) }
10+
}
11+
12+
fn increment(&self) {
13+
*self.value.borrow_mut() += 1;
14+
}
15+
16+
fn get(&self) -> i32 {
17+
*self.value.borrow()
18+
}
19+
}
20+
21+
fn main() {
22+
let counter = Counter::new(0);
23+
// println!("Before: {}", counter.get());
24+
25+
// We only have &counter, but can still mutate inside
26+
counter.increment();
27+
counter.increment();
28+
29+
assert!(2 == counter.get());
30+
// println!("After: {}", counter.get());
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use std::cell::Cell;
2+
3+
struct Counter {
4+
value: Cell<i32>,
5+
}
6+
7+
impl Counter {
8+
fn new(start: i32) -> Self {
9+
Counter { value: Cell::new(start) }
10+
}
11+
12+
fn increment(&self) {
13+
self.value.set(self.get() + 1);
14+
}
15+
16+
fn get(&self) -> i32 {
17+
self.value.get()
18+
}
19+
}
20+
21+
fn main() {
22+
let counter = Counter::new(0);
23+
// println!("Before: {}", counter.get());
24+
25+
// We only have &counter, but can still mutate inside
26+
counter.increment();
27+
counter.increment();
28+
29+
assert!(2 == counter.get());
30+
// println!("After: {}", counter.get());
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use std::cell::UnsafeCell;
2+
3+
fn main() {
4+
let data = UnsafeCell::new(0);
5+
6+
unsafe {
7+
*data.get() += 42;
8+
}
9+
10+
assert!(data.into_inner() == 42)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
┌─ 1 (root, init)
3+
│ #init ( symbol ( "interior_mut_fail" ) globalAllocEntry ( 2 , Memory ( allocatio
4+
│ function: main
5+
│ span: 288
6+
7+
│ (138 steps)
8+
└─ 3 (stuck, leaf)
9+
rvalueAddressOf ( mutabilityNot , place ( ... local: local ( 1 ) , projection: p
10+
span: 88
11+
12+
13+
┌─ 2 (root, leaf, target, terminal)
14+
│ #EndProgram
15+
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
┌─ 1 (root, init)
3+
│ #init ( symbol ( "interior_mut2_fail" ) globalAllocEntry ( 1 , Memory ( allocati
4+
│ function: main
5+
│ span: 120
6+
7+
│ (129 steps)
8+
└─ 3 (stuck, leaf)
9+
rvalueAddressOf ( mutabilityNot , place ( ... local: local ( 1 ) , projection: p
10+
span: 50
11+
12+
13+
┌─ 2 (root, leaf, target, terminal)
14+
│ #EndProgram
15+
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
┌─ 1 (root, init)
3+
│ #init ( symbol ( "interior_mut3_fail" ) globalAllocEntry ( 1 , Memory ( allocati
4+
│ function: main
5+
│ span: 67
6+
7+
│ (48 steps)
8+
└─ 3 (stuck, leaf)
9+
rvalueAddressOf ( mutabilityNot , place ( ... local: local ( 1 ) , projection: p
10+
span: 52
11+
12+
13+
┌─ 2 (root, leaf, target, terminal)
14+
│ #EndProgram
15+
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
fn main() {
2+
let mut prior = 1;
3+
let x = 10;
4+
5+
// println!("Before: {}", x);
6+
let _y = &x; // <-- need something else on the stack for the offset to work
7+
let prior_mut = &mut prior as *mut i32;
8+
9+
unsafe {
10+
let prior_alias = prior_mut.add(1);
11+
*prior_alias = 20;
12+
}
13+
14+
// println!("After: {}", x);
15+
assert!(x == 20);
16+
}

kmir/src/tests/integration/test_integration.py

+3
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,9 @@ def test_prove(spec: Path, tmp_path: Path, kmir: KMIR) -> None:
438438
PROVING_FILES = list(PROVING_DIR.glob('*.rs'))
439439
PROVE_RS_SHOW_SPECS = [
440440
'local-raw-fail',
441+
'interior-mut-fail',
442+
'interior-mut2-fail',
443+
'interior-mut3-fail',
441444
'assert_eq_exp-fail',
442445
'bitwise-fail',
443446
]

kmir/uv.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package/version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.3.156
1+
0.3.157

0 commit comments

Comments
 (0)