대여는 변수가 아니라 place 단위다

p.a 와 p.b 는 서로 다른 자리인데 한쪽을 대여하면 다른 쪽까지 잠겼다. 메서드가
하는 일의 대부분이 한 필드에 쓰면서 다른 필드를 읽는 것이라, std.map 의 keep 은
그것 때문에 함수 둘로 갈라져 있었고 파서도 같은 자리에서 걸렸다.

FeOwnState 가 필드별 칸을 넷 갖는다. 값으로 복사되는 구조체라 흐름 병합과
스냅샷은 손댈 것이 없었다. 전체를 대여하면 모든 필드와 충돌하고, 필드를
대여하면 전체 및 같은 필드와 충돌한다. 칸이 모자라면 전체 대여로 되돌아가
더 많이 보고할 뿐 놓치지 않는다.

읽기는 식별자에서 일어나는데 그 자리에서는 자기가 무엇의 밑동인지 알 수 없다.
그래서 투영이 내려가는 길에 어느 필드인지 적어두고 식별자가 그것을 집는다.
인덱스는 갈라지지 않는다 -- xs[i] 의 i 는 상수가 아닐 수 있고, 필드 이름은
상수다.

길에서 나온 것: mem.replace 가 목적지 대여를 가져가고 돌려주지 않았다. 일반
호출 인자는 문장 끝에 돌려주는데 intrinsic 경로에만 그것이 없었다. 전에는
그 자리가 어차피 거부돼서 드러나지 않았다.

  var p = Pair{ a: 1, b: 2 };
  let r = &mut p.a;
  p.b = 3;      // ok -- 전에는 에러
  p.a = 3;      // 에러
  take(p);      // 에러

SPEC §5 R6 을 고쳤고, 옛 규칙을 그대로 적어둔 문단과 예제를 갈아치웠다.
own/badrfld 는 이제 허용되는 코드였으므로 같은 필드를 건드리도록 다시 겨눴고
okrfld·badrall·badrsame·exec/fieldbrw 를 더했다.

228/228, 32/32.
This commit is contained in:
2026-08-17 15:29:12 +09:00
parent 120a36eef5
commit f7e667652e
15 changed files with 510 additions and 40 deletions
+58 -7
View File
@@ -260,6 +260,7 @@ FeSym *add_symbol(FeCheckerState *s, FeScope *scope,
sym->decl = decl;
fe_own_state_init(&sym->own, initialized);
sym->borrow_root = 0;
sym->borrow_field = 0;
sym->borrow_mut = 0;
sym->borrow_defer = 0;
sym->owner = scope;
@@ -506,9 +507,47 @@ int own_is_global(FeCheckerState *s, FeSym *sym)
return 0;
}
/* Strip the `&`/`&mut` off an expression; the place underneath is what is
being reached. */
static FeNode *own_strip_ref(FeNode *e)
{
while (e && e->kind==FE_N_UNARY && e->text &&
(strcmp(e->text,"&")==0 || strcmp(e->text,"&mut")==0))
e=e->a;
return e;
}
/* The first field projected off the root of `expr`, and that root.
`self.bytes.^[i]` projects `bytes` off `self`. An index (`arr[i]`) and a
dereference (`p.^`) name no field, so they answer for the whole value --
which is what the checker did for everything before. */
const char *own_projected_field(FeNode *expr, FeNode **root_out)
{
FeNode *inner;
FeNode *outer=0;
if (root_out) *root_out=0;
inner=own_strip_ref(expr);
while (inner && (inner->kind==FE_N_MEMBER || inner->kind==FE_N_INDEX)) {
outer=inner;
inner=own_strip_ref(inner->a);
}
if (!inner || inner->kind!=FE_N_IDENT || !outer) return 0;
if (outer->kind!=FE_N_MEMBER) return 0;
/* A member node's own text is the token the postfix chain started at, not
the operator, so the spelling of the projection is what to look at:
`.?` carries nothing on the right and `.^` carries a caret. */
if (outer->text && (strcmp(outer->text,".?")==0 ||
strcmp(outer->text,".^")==0)) return 0;
if (!outer->b || !outer->b->text) return 0;
if (strcmp(outer->b->text,"^")==0) return 0;
if (root_out) *root_out=inner;
return outer->b->text;
}
void own_borrow_expr(FeCheckerState *s, FeNode *expr, int mutable)
{
FeSym *root=own_root_symbol(s,expr);
const char *field=own_projected_field(expr,0);
if (!root) return;
if (mutable && root->type && root->type->kind==FE_TYPE_REF &&
!root->type->ref_mut) {
@@ -521,9 +560,9 @@ void own_borrow_expr(FeCheckerState *s, FeNode *expr, int mutable)
err(s->c,expr->loc,"cannot borrow a mutable global");
return;
}
fe_own_access(s->c->diags,&root->own,
mutable ? FE_OWN_BORROW_MUT : FE_OWN_BORROW_SHARED,
expr->loc);
fe_own_access_field(s->c->diags,&root->own,field,
mutable ? FE_OWN_BORROW_MUT : FE_OWN_BORROW_SHARED,
expr->loc);
}
void own_release_temporary_borrow(FeCheckerState *s, FeNode *expr)
@@ -533,8 +572,12 @@ void own_release_temporary_borrow(FeCheckerState *s, FeNode *expr)
if (strcmp(expr->text,"&")!=0 && strcmp(expr->text,"&mut")!=0) return;
root=own_root_symbol(s,expr->a);
if (!root) return;
if (strcmp(expr->text,"&mut")==0) fe_own_release_exclusive(&root->own);
else fe_own_release_shared(&root->own);
{
const char *field=own_projected_field(expr->a,0);
if (strcmp(expr->text,"&mut")==0)
fe_own_release_exclusive_field(&root->own,field);
else fe_own_release_shared_field(&root->own,field);
}
}
/* Return-reference provenance is represented at call sites by retaining a
@@ -577,6 +620,7 @@ void own_bind_derived_call(FeCheckerState *s, FeSym *binding,
else
fe_own_access(s->c->diags,&root->own,FE_OWN_BORROW_SHARED,value->loc);
binding->borrow_root=root;
binding->borrow_field=0;
binding->borrow_mut=value->sem_type->kind==FE_TYPE_REF && value->sem_type->ref_mut;
}
@@ -631,9 +675,13 @@ void own_release_after_stmt(FeCheckerState *s, FeScope *scope,
ref->decl && ref->decl->text ? ref->decl->text : ref->name);
if (!scope_end && (ref->borrow_defer || !last || last->defer_extended ||
!own_contains_node(stmt,last->last_node))) continue;
if (ref->borrow_mut) fe_own_release_exclusive(&ref->borrow_root->own);
else fe_own_release_shared(&ref->borrow_root->own);
if (ref->borrow_mut)
fe_own_release_exclusive_field(&ref->borrow_root->own,
ref->borrow_field);
else fe_own_release_shared_field(&ref->borrow_root->own,
ref->borrow_field);
ref->borrow_root=0;
ref->borrow_field=0;
}
}
@@ -687,6 +735,7 @@ void flow_borrow_capture(FeFlowSlot *slots, FeFlowBorrow *states,
if (!states) return;
for (i=0;i<count;++i) {
states[i].root=slots[i].sym->borrow_root;
states[i].field=slots[i].sym->borrow_field;
states[i].mutable=slots[i].sym->borrow_mut;
}
}
@@ -698,6 +747,7 @@ void flow_borrow_restore(FeFlowSlot *slots, FeFlowBorrow *states,
if (!states) return;
for (i=0;i<count;++i) {
slots[i].sym->borrow_root=states[i].root;
slots[i].sym->borrow_field=states[i].field;
slots[i].sym->borrow_mut=states[i].mutable;
}
}
@@ -709,6 +759,7 @@ void flow_borrow_merge(FeFlowSlot *slots, FeFlowBorrow *left,
if (!left || !right) return;
for (i=0;i<count;++i) {
slots[i].sym->borrow_root=left[i].root ? left[i].root : right[i].root;
slots[i].sym->borrow_field=left[i].root ? left[i].field : right[i].field;
slots[i].sym->borrow_mut=left[i].mutable || right[i].mutable;
}
}