p.a 와 p.b 는 서로 다른 자리인데 한쪽을 대여하면 다른 쪽까지 잠겼다. 메서드가
하는 일의 대부분이 한 필드에 쓰면서 다른 필드를 읽는 것이라, std.map 의 keep 은
그것 때문에 함수 둘로 갈라져 있었고 파서도 같은 자리에서 걸렸다.
FeOwnState 가 필드별 칸을 넷 갖는다. 값으로 복사되는 구조체라 흐름 병합과
스냅샷은 손댈 것이 없었다. 전체를 대여하면 모든 필드와 충돌하고, 필드를
대여하면 전체 및 같은 필드와 충돌한다. 칸이 모자라면 전체 대여로 되돌아가
더 많이 보고할 뿐 놓치지 않는다.
읽기는 식별자에서 일어나는데 그 자리에서는 자기가 무엇의 밑동인지 알 수 없다.
그래서 투영이 내려가는 길에 어느 필드인지 적어두고 식별자가 그것을 집는다.
인덱스는 갈라지지 않는다 -- xs[i] 의 i 는 상수가 아닐 수 있고, 필드 이름은
상수다.
길에서 나온 것: mem.replace 가 목적지 대여를 가져가고 돌려주지 않았다. 일반
호출 인자는 문장 끝에 돌려주는데 intrinsic 경로에만 그것이 없었다. 전에는
그 자리가 어차피 거부돼서 드러나지 않았다.
var p = Pair{ a: 1, b: 2 };
let r = &mut p.a;
p.b = 3; // ok -- 전에는 에러
p.a = 3; // 에러
take(p); // 에러
SPEC §5 R6 을 고쳤고, 옛 규칙을 그대로 적어둔 문단과 예제를 갈아치웠다.
own/badrfld 는 이제 허용되는 코드였으므로 같은 필드를 건드리도록 다시 겨눴고
okrfld·badrall·badrsame·exec/fieldbrw 를 더했다.
228/228, 32/32.
82 lines
2.1 KiB
Plaintext
82 lines
2.1 KiB
Plaintext
// EXIT:0
|
|
// OUTPUT:a 40 b 3
|
|
// OUTPUT:used 5 room 8
|
|
// OUTPUT:first 9
|
|
// OUTPUT:balanced
|
|
unit fieldbrw;
|
|
|
|
import std.io;
|
|
import std.sys;
|
|
import std.mem;
|
|
|
|
// Borrowing is per field. `p.a` and `p.b` are different places, so lending one
|
|
// out has to leave the other readable -- otherwise a method cannot write
|
|
// through one field while reading another, which is most of what a method
|
|
// does.
|
|
|
|
struct Pair { a: i32, b: i32, }
|
|
|
|
fn scale(v: &mut i32, by: i32) -> void {
|
|
v.^ = v.^ * by;
|
|
return;
|
|
}
|
|
|
|
struct Box {
|
|
bytes: ^[]mut u8,
|
|
used: usize,
|
|
room: usize,
|
|
|
|
fn with_capacity(n: usize) -> !Self {
|
|
let room: ^[]mut u8 = try mem.alloc_slice(u8, n);
|
|
return Self{ bytes: room, used: 0, room: n };
|
|
}
|
|
|
|
/// Move to a bigger buffer, then go on reading the other fields. The
|
|
/// borrow that hands over the buffer covers `bytes` and nothing else.
|
|
fn grow(self: &mut Self, want: usize) -> !void {
|
|
let bigger: ^[]mut u8 = try mem.alloc_slice(u8, want);
|
|
var i: usize = 0;
|
|
while i < self.used {
|
|
bigger.^[i] = self.bytes.^[i];
|
|
i = i + 1;
|
|
}
|
|
let old: ^[]mut u8 = mem.replace(&mut self.bytes, bigger);
|
|
mem.destroy(old);
|
|
self.room = want;
|
|
return;
|
|
}
|
|
|
|
fn push(self: &mut Self, v: u8) -> !void {
|
|
if self.used == self.room { try self.grow(self.room * 2); }
|
|
self.bytes.^[self.used] = v;
|
|
self.used = self.used + 1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
fn run() -> !void {
|
|
var p: Pair = Pair{ a: 4, b: 2 };
|
|
let left: &mut i32 = &mut p.a;
|
|
// Writing another field while `a` is lent out.
|
|
p.b = 3;
|
|
scale(left, 10);
|
|
@print("a {} b {}\n", p.a, p.b);
|
|
|
|
var box: Box = try Box.with_capacity(4);
|
|
try box.push(9);
|
|
try box.push(8);
|
|
try box.push(7);
|
|
try box.push(6);
|
|
try box.push(5);
|
|
@print("used {} room {}\n", box.used, box.room);
|
|
@print("first {}\n", box.bytes.^[0]);
|
|
return;
|
|
}
|
|
|
|
fn main() -> i32 {
|
|
run() catch |e| { @print("failed\n"); return 1; };
|
|
if sys.allocs() == sys.frees() { @print("balanced\n"); }
|
|
else { @print("leaked\n"); }
|
|
return 0;
|
|
}
|