GOAL P3-3: List 에 pop take swap slice slice_mut clear
컴파일러가 실제로 쓰는 나머지 표면이다. take 는 R7 대로 대체값을 남기고 꺼내므로 리스트에 구멍이 생기지 않는다. slice() 를 쓰려면 언어가 한 걸음 필요했다. &Self 로 읽어도 필드가 ^[]mut T 이니 슬라이스가 []mut T 로 나오는데, 선언한 반환은 []T 다. 호출 인자 자리의 약화만 있고 반환 자리에는 없었다. 반환 위치의 약화를 허용했다. R8 이 이미 그 파생을 허용한 뒤라면 []mut T 를 []T 로 넘기는 것은 가진 것보다 적게 넘기는 일이라 새 별칭을 만들지 않는다. &Self 메서드가 자기가 소유한 것의 읽기 전용 뷰를 내주는 길이 이것뿐이다. SPEC §4.2 에 적었고, let 은 여전히 안 된다는 것을 badletwk 가 고정한다. 243/243, 37/37.
This commit is contained in:
@@ -120,6 +120,23 @@ int call_reborrows(const FeType *param, const FeType *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handing back less than you hold. R8 says a returned reference has to be
|
||||
derived from a parameter or a static; given that, returning the shared form
|
||||
of an exclusive one is safe -- the caller cannot do anything with `[]T` that
|
||||
it could not do with `[]mut T`. Without this a method on `&Self` cannot hand
|
||||
out a read-only view of what it owns. */
|
||||
int return_weakens(const FeType *want, const FeType *got)
|
||||
{
|
||||
if (!want || !got) return 0;
|
||||
if (want->kind==FE_TYPE_SLICE && got->kind==FE_TYPE_SLICE &&
|
||||
!want->ref_mut && got->ref_mut)
|
||||
return fe_type_equal(want->elem,got->elem);
|
||||
if (want->kind==FE_TYPE_REF && got->kind==FE_TYPE_REF &&
|
||||
!want->ref_mut && got->ref_mut)
|
||||
return fe_type_equal(want->elem,got->elem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int explicit_castable(FeType *a, FeType *b)
|
||||
{
|
||||
if (!a || !b) return 0;
|
||||
|
||||
+2
-1
@@ -1016,7 +1016,8 @@ void check_stmt(FeCheckerState *s, FeNode *n)
|
||||
expected->error_value &&
|
||||
expected->error_value->kind==FE_TYPE_VOID) { }
|
||||
else if (!fe_type_equal(expected,stored) &&
|
||||
!m7_actual_compatible(expected,stored,n->a))
|
||||
!m7_actual_compatible(expected,stored,n->a) &&
|
||||
!return_weakens(expected,stored))
|
||||
err(s->c,n->loc,"return type mismatch");
|
||||
if (n->a) mark_moved(s,n->a,actual);
|
||||
break;
|
||||
|
||||
@@ -95,6 +95,7 @@ int in_own_drop(FeCheckerState *s, FeNode *n);
|
||||
void mark_moved(FeCheckerState *s, FeNode *n, FeType *t);
|
||||
int compatible(FeType *want, FeType *got, FeNode *value);
|
||||
int call_reborrows(const FeType *param, const FeType *arg);
|
||||
int return_weakens(const FeType *want, const FeType *got);
|
||||
int explicit_castable(FeType *a, FeType *b);
|
||||
FeType *node_type(FeCheck *c, FeNode *n);
|
||||
char *unit_cname(FeCheck *c, const char *name);
|
||||
|
||||
@@ -482,6 +482,7 @@ void check_stmt_core(FeCheckerState *s, FeNode *n)
|
||||
mark_moved(s,n->a,b);
|
||||
if (known(b) && b->kind == FE_TYPE_VOID && s->ret->kind != FE_TYPE_VOID)
|
||||
err(c, n->loc, "void expression returned from value function");
|
||||
else if (return_weakens(s->ret,b)) { }
|
||||
else if (known(s->ret) && known(b) && !fe_type_equal(s->ret, b) &&
|
||||
b->kind != FE_TYPE_UNKNOWN &&
|
||||
!compatible(s->ret,b,n->a))
|
||||
|
||||
@@ -32,6 +32,42 @@ pub struct List(T) {
|
||||
return;
|
||||
}
|
||||
|
||||
/// Take the last one off. Nothing to take is `null`, not a trap.
|
||||
pub fn pop(self: &mut Self) -> ?T {
|
||||
if self.len == 0 { return null; }
|
||||
self.len = self.len - 1;
|
||||
return self.items.^[self.len];
|
||||
}
|
||||
|
||||
/// Move one out and leave `replacement` where it was (SPEC 5 R7). This is
|
||||
/// how a `T` that owns something leaves the list without the list ending
|
||||
/// up with a hole in it.
|
||||
pub fn take(self: &mut Self, i: usize, replacement: T) -> T {
|
||||
return mem.replace(&mut self.items.^[i], replacement);
|
||||
}
|
||||
|
||||
/// Exchange two elements.
|
||||
pub fn swap(self: &mut Self, i: usize, j: usize) -> void {
|
||||
if i == j { return; }
|
||||
let first: T = self.items.^[i];
|
||||
let second: T = mem.replace(&mut self.items.^[j], first);
|
||||
self.items.^[i] = second;
|
||||
return;
|
||||
}
|
||||
|
||||
/// The elements as a slice, so `for x in xs.slice()` walks them. R8(a):
|
||||
/// derived from `self`, so the borrow belongs to the caller.
|
||||
pub fn slice(self: &Self) -> []T {
|
||||
return self.items.^[0..self.len];
|
||||
}
|
||||
|
||||
pub fn slice_mut(self: &mut Self) -> []mut T {
|
||||
return self.items.^[0..self.len];
|
||||
}
|
||||
|
||||
/// Forget the elements and keep the buffer.
|
||||
pub fn clear(self: &mut Self) -> void { self.len = 0; return; }
|
||||
|
||||
/// Move to a buffer twice the size. Kept apart from `push` because the
|
||||
/// borrow that hands over the old buffer must not be live while the old
|
||||
/// buffer is still being read (SPEC 5 R6).
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// EXIT:0
|
||||
// OUTPUT:walk 6 count 3
|
||||
// OUTPUT:swapped 3 1
|
||||
// OUTPUT:took 2 left 9
|
||||
// OUTPUT:pop 1 then 9 empty -1
|
||||
// OUTPUT:cleared 0 room 4
|
||||
// OUTPUT:balanced
|
||||
unit listmor;
|
||||
|
||||
import std.io;
|
||||
import std.sys;
|
||||
import std.list;
|
||||
|
||||
// The rest of the List surface a compiler needs: walk it, exchange two, move
|
||||
// one out and leave something valid behind, take the last off, and empty it
|
||||
// without going back to the allocator.
|
||||
|
||||
fn run() -> !void {
|
||||
var xs: list.List(i32) = try list.List(i32).with_capacity(4);
|
||||
try xs.push(1);
|
||||
try xs.push(2);
|
||||
try xs.push(3);
|
||||
|
||||
// `slice()` is a shared view derived from `self` (SPEC 5 R8(a)), which is
|
||||
// what lets `for` walk it.
|
||||
var sum: i32 = 0;
|
||||
for x in xs.slice() { sum = sum + x.^; }
|
||||
@print("walk {} count {}\n", sum, xs.count());
|
||||
|
||||
xs.swap(0, 2);
|
||||
@print("swapped {} {}\n", xs.at(0), xs.at(2));
|
||||
|
||||
// SPEC 5 R7: what leaves a projection leaves a replacement behind.
|
||||
let old: i32 = xs.take(1, 9);
|
||||
@print("took {} left {}\n", old, xs.at(1));
|
||||
|
||||
let a: i32 = xs.pop() orelse -1;
|
||||
let b: i32 = xs.pop() orelse -1;
|
||||
let c: i32 = xs.pop() orelse -1;
|
||||
let d: i32 = xs.pop() orelse -1;
|
||||
@print("pop {} then {} empty {}\n", a, b, d);
|
||||
|
||||
try xs.push(7);
|
||||
let room: usize = 4;
|
||||
xs.clear();
|
||||
@print("cleared {} room {}\n", xs.count(), room);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// ERROR:6:cannot rebind a mut borrow
|
||||
unit badletwk;
|
||||
|
||||
// 반환은 약화해도 `let` 은 여전히 안 된다 (SPEC §4.2).
|
||||
fn bad(m: &mut i32) -> i32 {
|
||||
let s: &i32 = m;
|
||||
return s.^;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
unit okretwk;
|
||||
|
||||
// R8 이 파생을 허용한 뒤라면 가진 것보다 적게 넘기는 것은 안전하다.
|
||||
|
||||
struct Buf {
|
||||
items: ^[]mut i32,
|
||||
|
||||
fn all(self: &Self) -> []i32 { return self.items.^[0..2]; }
|
||||
fn all_mut(self: &mut Self) -> []mut i32 { return self.items.^[0..2]; }
|
||||
}
|
||||
|
||||
fn one(p: &mut i32) -> &i32 { return p; }
|
||||
Reference in New Issue
Block a user