From f5bffec7dd065d97b958c3647c7e59e4b836a749 Mon Sep 17 00:00:00 2001 From: Sebastian Jeong Date: Mon, 17 Aug 2026 04:49:05 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EA=B0=99=EC=9D=80=20=EC=9C=84=EB=B0=98?= =?UTF-8?q?=EC=9D=84=20=EB=91=90=20=EB=B2=88=20=EB=B3=B4=EA=B3=A0=ED=95=98?= =?UTF-8?q?=EB=8D=98=20=EB=91=90=20=EA=B2=BD=EB=A1=9C=EB=A5=BC=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이동한 값을 쓰면 진단이 두 번 나왔다. 원인이 둘이다. 식별자를 읽으면 FE_OWN_READ 가 이미 보고하는데 mark_moved 가 FE_OWN_MOVE 로 같은 자리를 다시 보고했고, member lvalue 는 check_lvalue 가 base 를 검사한 뒤 check_lvalue_core 가 또 검사했다. M6/M7 두 검사기를 합칠 때 남은 자국이다. 러너는 진단의 첫 줄만 마커와 대조하므로 fixture 188개가 이것을 잡지 못했다. --- fec/src/check.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/fec/src/check.c b/fec/src/check.c index 37e4370..031a415 100644 --- a/fec/src/check.c +++ b/fec/src/check.c @@ -84,6 +84,11 @@ static void mark_moved(FeCheckerState *s, FeNode *n, FeType *t) "cannot move a non-Copy value out of a projection; use mem.replace"); return; } + /* Reaching an identifier already ran FE_OWN_READ over it, and that + read reported the value as gone if it was. Running the move as well + reports the same sentence at the same column a second time, so stop + at the state the read left behind. */ + if (sym->own.move != FE_OWN_AVAILABLE) return; if (fe_own_access(s->c->diags,&sym->own,FE_OWN_MOVE,n->loc)) { sym->moved=sym->own.move; /* Keep the existing emitter contract: ownership-consuming AST @@ -304,7 +309,8 @@ static void check_match(FeCheckerState *s, FeNode *n); static void check_stmt(FeCheckerState *s, FeNode *n); static FeType *check_expr_core(FeCheckerState *s, FeNode *n); static void check_stmt_core(FeCheckerState *s, FeNode *n); -static FeType *check_lvalue_core(FeCheckerState *s, FeNode *n, int read); +static FeType *check_lvalue_core(FeCheckerState *s, FeNode *n, int read, + FeType *base_in); static FeType *check_lvalue(FeCheckerState *s, FeNode *n, int read); static FeType *check_call(FeCheckerState *s, FeNode *n); @@ -1163,7 +1169,11 @@ static FeType *check_expr_core(FeCheckerState *s, FeNode *n) return unknown(c); } -static FeType *check_lvalue_core(FeCheckerState *s, FeNode *n, int read) +/* `base_in` is the already-checked type of a member expression's base. The M7 + lvalue path looks at that base before delegating here, and checking it a + second time reports any ownership violation on it a second time too. */ +static FeType *check_lvalue_core(FeCheckerState *s, FeNode *n, int read, + FeType *base_in) { FeSym *sym; FeType *base; @@ -1189,7 +1199,7 @@ static FeType *check_lvalue_core(FeCheckerState *s, FeNode *n, int read) return sym->type; } if (n && n->kind == FE_N_MEMBER) { - base=check_expr(s,n->a); + base=base_in ? base_in : check_expr(s,n->a); if (base && base->kind == FE_TYPE_REF && n->b && n->b->text && strcmp(n->b->text,"^")==0) { if (!base->ref_mut) @@ -2223,7 +2233,7 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) static FeType *check_lvalue(FeCheckerState *s, FeNode *n, int read) { - FeType *base; + FeType *base=0; FeFieldType *field; FeType *owner; if (!n) return unknown(s->c); @@ -2262,7 +2272,7 @@ static FeType *check_lvalue(FeCheckerState *s, FeNode *n, int read) return field->type; } } - return check_lvalue_core(s,n,read); + return check_lvalue_core(s,n,read,n->kind==FE_N_MEMBER ? base : 0); } static FeType *m7_pattern_binding_type(FeCheckerState *s, FeType *payload,