GOAL P1-4: 작은 규칙 일곱을 정하고, 어긋난 둘을 고친다

일곱 중 다섯은 구현이 이미 옳게 답하고 있었다 -- match 는 enum 전용이고,
enum payload 에 소유 타입을 담을 수 있고, undefined 배열은 원소 단위로
추적하지 않고, 다른 유닛에서 private 필드가 있는 리터럴은 거부되고,
by-value self 의 부분 이동도 mem.replace 가 필요하다. SPEC §7.9 에 적었다.

나머지 둘은 그냥 통과하고 있었다.

defer { return 1; } 이 받아들여졌다. 지연 블록은 함수가 무엇을 반환할지 이미
정한 뒤 스코프 정리 중에 도는데 거기서 return 이 뜻할 것이 없다.

for x in xs { xs[0] = 9; } 도 받아들여졌다. x 가 xs 안을 가리키는 참조이니
순회 몸통에서 xs 에 쓰는 것은 그 참조가 가리키는 것을 옮기는 일이다 (R6).
순회는 이제 도는 동안 대여한다. 읽기는 공유 순회에서 그대로 된다.

240/240, 35/35.
This commit is contained in:
2026-08-17 16:10:44 +09:00
parent 1a368dc13f
commit d9bced830c
6 changed files with 68 additions and 0 deletions
+14
View File
@@ -589,6 +589,20 @@ pub fn main() -> !void {
---
### 7.9 작은 규칙들
구현자가 임의로 정하면 갈라지는 것들. 각각 한 줄이면 끝나므로 여기 모아 둔다.
| | |
|---|---|
| `match` | **enum 값에만 쓴다.** 정수와 `char`에는 쓸 수 없으므로 완전성 검사가 배리언트 목록 하나로 정해진다. 정수 분기는 `if`/`else if`로 쓴다 |
| enum payload | `^T`처럼 소유하는 타입을 담을 수 있다. 다만 v0.1은 **활성 배리언트의 payload를 자동으로 놓아주지 않는다** — 담았다면 꺼내서 직접 놓아야 한다 |
| `for x in xs` | 순회는 `xs`**순회 동안 대여한다**. `x`가 그 안을 가리키는 참조이므로 몸통에서 `xs`에 쓰는 것은 R6 위반이다. 읽는 것은 공유 순회에서 허용된다 |
| `defer` 안의 `return` | **컴파일 에러.** 지연 블록은 함수가 무엇을 반환할지 이미 정한 뒤 스코프 정리 중에 돈다 |
| `undefined` 배열 | 초기화 추적은 **변수 단위이며 원소 단위가 아니다.** `undefined`로 선언한 배열은 선언 시점부터 쓰기 가능하고, 읽기 전에 무엇을 채웠는지는 검사하지 않는다. 슬라이스로 넘겨 채우는 것이 의도된 사용법이다 |
| 다른 유닛의 struct 리터럴 | 모든 필드를 명시해야 하므로 **`pub`이 아닌 필드가 하나라도 있으면 밖에서 리터럴을 쓸 수 없다.** 생성자 함수를 두어야 한다 |
| by-value `self` | `self`를 값으로 받아도 필드를 꺼내는 것은 R7 그대로 `mem.replace`가 필요하다. 예외는 자기 `drop` 안뿐이다 |
## 8. 유닛
파일 하나가 유닛 하나다. 유닛의 canonical identity는 fully-qualified dotted unit path이며
+5
View File
@@ -986,6 +986,11 @@ void check_stmt(FeCheckerState *s, FeNode *n)
m7_check_match_stmt(s,n);
break;
case FE_N_RETURN:
/* A deferred block runs during scope cleanup, on the way out of a
function that has already decided what it returns. There is nothing
for a `return` in there to mean. */
if (s->defer_depth != 0)
err(s->c, n->loc, "cannot return from inside defer");
expected=s->ret;
if (n->a)
stored=m7_check_expected(s,n->a,expected);
+16
View File
@@ -101,7 +101,18 @@ void check_for(FeCheckerState *s, FeNode *n)
item_cname,n);
n->cname=item_cname;
}
/* Walking a container borrows it for the length of the walk: the
item is a reference into it, so writing the container underneath
would move what that reference points at (SPEC 5 R6). */
if (iter_sym)
fe_own_access(s->c->diags,&iter_sym->own,
iter_mut ? FE_OWN_BORROW_MUT : FE_OWN_BORROW_SHARED,
n->loc);
check_stmt(s,n->b);
if (iter_sym) {
if (iter_mut) fe_own_release_exclusive(&iter_sym->own);
else fe_own_release_shared(&iter_sym->own);
}
s->scope=old;
return;
}
@@ -459,6 +470,11 @@ void check_stmt_core(FeCheckerState *s, FeNode *n)
if (!s->loop_depth) err(c,n->loc,"break or continue outside loop");
break;
case FE_N_RETURN:
/* A deferred block runs during scope cleanup, on the way out of a
function that has already decided what it returns. There is nothing
for a `return` in there to mean. */
if (s->defer_depth != 0)
err(c, n->loc, "cannot return from inside defer");
b = n->a ? check_expr(s, n->a) : fe_type_intern(&c->types, "void");
if (s->ret && fe_own_is_reference_like(s->ret) &&
!own_return_from_allowed_root(s,n->a))
+12
View File
@@ -0,0 +1,12 @@
// ERROR:9:borrow
unit badforwr;
// Walking a container borrows it: the item is a reference into it, so writing
// the container underneath moves what that reference points at.
fn bad(xs: []mut i32) -> void {
for x in xs {
xs[0] = 9;
}
return;
}
+10
View File
@@ -0,0 +1,10 @@
// ERROR:8:cannot return from inside defer
unit bdefret;
// A deferred block runs during scope cleanup, on the way out of a function
// that has already decided what it returns.
fn bad() -> i32 {
defer { return 1; }
return 0;
}
+11
View File
@@ -0,0 +1,11 @@
unit okforrd;
// A shared walk still allows reading -- of the item and of the container.
fn ok(xs: []i32) -> i32 {
var sum: i32 = 0;
for x in xs {
sum = sum + x.^ + (xs.n as i32);
}
return sum;
}