Ferro 파서를 Ferro 로, 그리고 그것이 드러낸 네 가지
렉서 다음은 파서다. 노드는 한 배열에 살고 자식은 그 안의 인덱스다 -- 노드는 ^Node 를 들 수 없고(여럿이며 한 번씩 소유하지 않는다) &Node 도 들 수 없다(R4). 인덱스는 둘 다 아니다. 소스도 필드가 아니라 매 단계에 같이 다닌다. unit demo / fn answer @2 / let n = (+ 1 (* 2 3)) / return n / balanced 전위 표기로 다시 찍는 것이 시험의 요점이다. 1 + 2 * 3 이 어떻게 묶였는지는 그렇게만 보인다. 쓰면서 나온 컴파일러 버그 넷: 1. 다른 유닛의 타입을 필드로 쓰면 그 필드 타입이 영영 UNKNOWN 이었다. 필드 해석이 유닛마다 선언 직후에 돌아서, 아직 선언되지 않은 유닛의 타입을 찾다 실패하고 그 답을 굳혔다. 이제 모든 유닛이 선언을 마친 뒤에 한 번 푼다. 2. 그리고 그 해석은 타입을 선언한 유닛에서 해야 한다. 필드 타입은 그 유닛의 import 로 쓰였는데 아무 유닛에서나 풀고 있었다. 타입 계층에 enter/leave 콜백을 두고 체커가 그 자리로 데려간다. 3. cycle_state 를 재귀 검사와 크기 계산이 같이 썼다. 첫 번째가 보는 중인 구조체 가 두 번째에게는 다 끝난 것으로 보여서, 필드가 하나뿐인 것처럼 1 바이트로 자리를 잡았다 -- Parser 가 그래서 자기 토큰을 밟았다. layout_state 로 나눴다. 4. 다른 유닛의 상수(ast.NONE)를 lowering 이 필드 접근으로 봤다. 체커가 이미 링크 이름을 붙여두었으니 그것이 있으면 전역이다. 그리고 R1 을 실제로 지키게 했다: 소유자를 놓으면 그것이 가진 것도 놓는다. 전에는 자기 drop 이 있거나 자기가 owned 일 때만이어서, drop 을 가진 타입을 필드로 담은 구조체는 그것을 놓을 방법이 없었다(drop 은 손으로 못 부른다). 이제 release_at 이 drop 을 부르고 필드로 내려간다. 그 덕에 List/Arena/Map 의 drop 이 전부 필요 없어져서 지웠다 -- 버퍼가 owned 이니 R1 이 알아서 한다. 221/221, 29/29.
This commit is contained in:
+38
-20
@@ -171,6 +171,12 @@ Slot lower_expr_core(Lower *L, FeNode *n)
|
||||
if (base && (base->kind == FE_TYPE_REF ||
|
||||
base->kind == FE_TYPE_OWNED)) base = base->elem;
|
||||
field = fe_type_field(base, n->b && n->b->text ? n->b->text : "");
|
||||
/* `binding.name` is not a field of anything: it is a constant or a
|
||||
global in another unit, and the checker already turned it into a
|
||||
link name. */
|
||||
if (!field && n->cname)
|
||||
return slot_place(fe_ir_at_global(n->cname, 0), it,
|
||||
ir_size(t));
|
||||
if (!field) { fail(L, "an unresolved field", n); return slot_void(); }
|
||||
b = lower_expr(L, n->a);
|
||||
if (n->a->sem_type && (n->a->sem_type->kind == FE_TYPE_REF ||
|
||||
@@ -305,6 +311,37 @@ const char *drop_name(Lower *L, const FeType *t)
|
||||
return method->cname;
|
||||
}
|
||||
|
||||
/* Let go of one value sitting at `at`. A type that says how to let go of
|
||||
itself is asked first; then whatever it holds is let go of in turn, so a
|
||||
struct that owns a struct that owns a buffer settles all three without
|
||||
anyone writing a `drop` (SPEC 5 R1). */
|
||||
void release_at(Lower *L, const FeType *t, FeIrPlace at)
|
||||
{
|
||||
unsigned args[1];
|
||||
unsigned i;
|
||||
if (!t) return;
|
||||
if (t->has_drop) {
|
||||
const char *how = drop_name(L, t);
|
||||
args[0] = fe_ir_addr(L->m, L->b, at);
|
||||
if (how) fe_ir_call(L->m, L->b, FE_IR_VOID, how, args, 1);
|
||||
}
|
||||
if (t->kind == FE_TYPE_OWNED) {
|
||||
FeIrPlace p = at;
|
||||
if (t->elem && t->elem->kind == FE_TYPE_SLICE)
|
||||
p.offset += SLICE_PTR_OFFSET;
|
||||
args[0] = fe_ir_load(L->m, L->b, FE_IR_PTR, p);
|
||||
fe_ir_call(L->m, L->b, FE_IR_VOID, "fe_rt_free", args, 1);
|
||||
return;
|
||||
}
|
||||
if (t->kind == FE_TYPE_STRUCT)
|
||||
for (i = 0; i < t->field_count; ++i) {
|
||||
FeIrPlace p = at;
|
||||
if (!needs_release(t->fields[i].type)) continue;
|
||||
p.offset += (long)t->fields[i].offset;
|
||||
release_at(L, t->fields[i].type, p);
|
||||
}
|
||||
}
|
||||
|
||||
/* Settle what a scope owes, most recent first. A `return` in the middle of a
|
||||
function still owes everything, so every exit path calls this. */
|
||||
void run_deferred(Lower *L, unsigned from)
|
||||
@@ -321,29 +358,10 @@ void run_deferred(Lower *L, unsigned from)
|
||||
fe_ir_at_local(L->owed[i - 1].flag, 0));
|
||||
FeIrBlock *doit = new_block(L);
|
||||
FeIrBlock *skip = new_block(L);
|
||||
unsigned args[1];
|
||||
FeType *t = L->owed[i - 1].type;
|
||||
fe_ir_br(L->b, live, doit->id, skip->id);
|
||||
L->b = doit;
|
||||
if (t && t->kind == FE_TYPE_OWNED && t->elem &&
|
||||
t->elem->kind == FE_TYPE_SLICE) {
|
||||
FeIrPlace at = fe_ir_at_local(L->owed[i - 1].local,
|
||||
SLICE_PTR_OFFSET);
|
||||
args[0] = fe_ir_load(L->m, L->b, FE_IR_PTR, at);
|
||||
} else {
|
||||
args[0] = fe_ir_load(L->m, L->b, FE_IR_PTR,
|
||||
fe_ir_at_local(L->owed[i - 1].local, 0));
|
||||
}
|
||||
if (t && t->has_drop) {
|
||||
/* A type that says how to let go of itself is asked to; the
|
||||
name is the one its instance was given. */
|
||||
const char *how = drop_name(L, t);
|
||||
args[0] = fe_ir_addr(L->m, L->b,
|
||||
fe_ir_at_local(L->owed[i - 1].local, 0));
|
||||
if (how) fe_ir_call(L->m, L->b, FE_IR_VOID, how, args, 1);
|
||||
} else {
|
||||
fe_ir_call(L->m, L->b, FE_IR_VOID, "fe_rt_free", args, 1);
|
||||
}
|
||||
release_at(L, t, fe_ir_at_local(L->owed[i - 1].local, 0));
|
||||
fe_ir_jmp(L->b, skip->id);
|
||||
L->b = skip;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user