From d9bced830c88a5374142532781933c62a9451b9b Mon Sep 17 00:00:00 2001 From: Sebastian Jeong Date: Mon, 17 Aug 2026 16:10:44 +0900 Subject: [PATCH] =?UTF-8?q?GOAL=20P1-4:=20=EC=9E=91=EC=9D=80=20=EA=B7=9C?= =?UTF-8?q?=EC=B9=99=20=EC=9D=BC=EA=B3=B1=EC=9D=84=20=EC=A0=95=ED=95=98?= =?UTF-8?q?=EA=B3=A0,=20=EC=96=B4=EA=B8=8B=EB=82=9C=20=EB=91=98=EC=9D=84?= =?UTF-8?q?=20=EA=B3=A0=EC=B9=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 일곱 중 다섯은 구현이 이미 옳게 답하고 있었다 -- 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. --- SPEC.md | 14 ++++++++++++++ fec/src/checkcal.c | 5 +++++ fec/src/checkstm.c | 16 ++++++++++++++++ fec/tests/own/badforwr.fe | 12 ++++++++++++ fec/tests/own/bdefret.fe | 10 ++++++++++ fec/tests/own/okforrd.fe | 11 +++++++++++ 6 files changed, 68 insertions(+) create mode 100644 fec/tests/own/badforwr.fe create mode 100644 fec/tests/own/bdefret.fe create mode 100644 fec/tests/own/okforrd.fe diff --git a/SPEC.md b/SPEC.md index c0982be..9d4a4b8 100644 --- a/SPEC.md +++ b/SPEC.md @@ -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이며 diff --git a/fec/src/checkcal.c b/fec/src/checkcal.c index 8454791..d1f5c47 100644 --- a/fec/src/checkcal.c +++ b/fec/src/checkcal.c @@ -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); diff --git a/fec/src/checkstm.c b/fec/src/checkstm.c index f8761ff..89eac2f 100644 --- a/fec/src/checkstm.c +++ b/fec/src/checkstm.c @@ -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)) diff --git a/fec/tests/own/badforwr.fe b/fec/tests/own/badforwr.fe new file mode 100644 index 0000000..f3553e9 --- /dev/null +++ b/fec/tests/own/badforwr.fe @@ -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; +} diff --git a/fec/tests/own/bdefret.fe b/fec/tests/own/bdefret.fe new file mode 100644 index 0000000..045b296 --- /dev/null +++ b/fec/tests/own/bdefret.fe @@ -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; +} diff --git a/fec/tests/own/okforrd.fe b/fec/tests/own/okforrd.fe new file mode 100644 index 0000000..3451761 --- /dev/null +++ b/fec/tests/own/okforrd.fe @@ -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; +}