From 5981d2b5b12964883ca260fd5dea8265d9c03639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=8B=9C=EC=9B=90?= Date: Sun, 16 Aug 2026 19:56:15 +0900 Subject: [PATCH 1/4] add M6 ownership core interfaces --- fec/src/own.h | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/fec/src/own.h b/fec/src/own.h index eb0ff65..830341f 100644 --- a/fec/src/own.h +++ b/fec/src/own.h @@ -13,7 +13,80 @@ enum FeOwnMoveState { FE_OWN_MAYBE_MOVED = 2 }; +typedef enum FeOwnAccessKind { + FE_OWN_READ, + FE_OWN_WRITE, + FE_OWN_MOVE, + FE_OWN_BORROW_SHARED, + FE_OWN_BORROW_MUT, + FE_OWN_PROJECTION +} FeOwnAccessKind; + +typedef enum FeOwnProvenanceKind { + FE_OWN_PROV_INVALID, + FE_OWN_PROV_STATIC, + FE_OWN_PROV_PARAM +} FeOwnProvenanceKind; + +typedef struct FeOwnPlace { + FeNode *root; + const char *root_cname; + int projected; +} FeOwnPlace; + +typedef struct FeOwnState { + int move; + int initialized; + unsigned shared; + int exclusive; + int borrow_conflict; + FeLoc move_loc; + FeLoc borrow_loc; +} FeOwnState; + +typedef struct FeOwnProvenance { + FeOwnProvenanceKind kind; + unsigned param_index; +} FeOwnProvenance; + +typedef struct FeOwnLastUse { + const char *cname; + FeNode *decl; + FeNode *last_node; + unsigned long last_ordinal; + int defer_extended; +} FeOwnLastUse; + +typedef struct FeOwnLiveness { + FeArena *arena; + FeOwnLastUse *items; + unsigned count; + unsigned capacity; + unsigned long ordinal; +} FeOwnLiveness; + int fe_own_is_copy_type(FeType *type); +int fe_own_is_reference_like(FeType *type); +int fe_own_place_from_expr(FeNode *expr, FeOwnPlace *place); + +void fe_own_state_init(FeOwnState *state, int initialized); +int fe_own_access(FeDiags *diags, FeOwnState *state, + FeOwnAccessKind access, FeLoc loc); +void fe_own_release_shared(FeOwnState *state); +void fe_own_release_exclusive(FeOwnState *state); +FeOwnState fe_own_merge_state(FeOwnState left, FeOwnState right); +int fe_own_state_equal(const FeOwnState *left, const FeOwnState *right); + +FeOwnProvenance fe_own_provenance_static(void); +FeOwnProvenance fe_own_provenance_param(unsigned param_index); +FeOwnProvenance fe_own_merge_provenance(FeOwnProvenance left, + FeOwnProvenance right); + +void fe_own_liveness_init(FeOwnLiveness *live, FeArena *arena); +int fe_own_collect_last_uses(FeOwnLiveness *live, FeNode *fn); +const FeOwnLastUse *fe_own_last_use(const FeOwnLiveness *live, + const char *cname); + void fe_own_mark_consumed(FeDiags *diags, int *state, FeNode *decl, FeNode *expr, FeType *type, int in_defer); void fe_own_check_use(FeDiags *diags, int state, FeLoc loc); From db2b681f6b4ede22426f724de09afe91206fb57b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=8B=9C=EC=9B=90?= Date: Sun, 16 Aug 2026 19:57:15 +0900 Subject: [PATCH 2/4] implement M6 ownership core --- fec/src/own.c | 391 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) diff --git a/fec/src/own.c b/fec/src/own.c index 0d90817..862d330 100644 --- a/fec/src/own.c +++ b/fec/src/own.c @@ -1,4 +1,21 @@ #include "own.h" +#include + +static FeLoc fe_own_no_loc(void) +{ + FeLoc loc; + loc.file = 0; + loc.line = 0; + loc.col = 0; + return loc; +} + +static void fe_own_error_note(FeDiags *diags, FeLoc loc, const char *msg, + FeLoc note, const char *note_msg) +{ + fe_diag_error(diags, loc, msg); + if (note.file) fe_diag_note_src(diags, note, note_msg); +} int fe_own_is_copy_type(FeType *type) { @@ -25,6 +42,380 @@ int fe_own_is_copy_type(FeType *type) return 1; } +int fe_own_is_reference_like(FeType *type) +{ + return type && (type->kind == FE_TYPE_REF || + type->kind == FE_TYPE_SLICE || + type->kind == FE_TYPE_STR); +} + +static FeNode *fe_own_root_expr(FeNode *expr) +{ + if (!expr) return 0; + if (expr->kind == FE_N_IDENT) return expr; + if (expr->kind == FE_N_MEMBER || expr->kind == FE_N_INDEX) + return fe_own_root_expr(expr->a); + if (expr->kind == FE_N_UNARY && expr->text && + (strcmp(expr->text, "&") == 0 || strcmp(expr->text, "&mut") == 0)) + return fe_own_root_expr(expr->a); + return 0; +} + +int fe_own_place_from_expr(FeNode *expr, FeOwnPlace *place) +{ + FeNode *root; + if (!place) return 0; + place->root = 0; + place->root_cname = 0; + place->projected = 0; + root = fe_own_root_expr(expr); + if (!root) return 0; + place->root = root; + place->root_cname = root->cname ? root->cname : root->text; + place->projected = root != expr; + return place->root_cname != 0; +} + +void fe_own_state_init(FeOwnState *state, int initialized) +{ + if (!state) return; + state->move = FE_OWN_AVAILABLE; + state->initialized = initialized != 0; + state->shared = 0; + state->exclusive = 0; + state->borrow_conflict = 0; + state->move_loc = fe_own_no_loc(); + state->borrow_loc = fe_own_no_loc(); +} + +static int fe_own_require_value(FeDiags *diags, FeOwnState *state, FeLoc loc) +{ + if (!state->initialized) { + fe_diag_error(diags, loc, "use of uninitialized variable"); + return 0; + } + if (state->move == FE_OWN_MOVED) { + fe_own_error_note(diags, loc, "use of moved value", state->move_loc, + "value was moved here"); + return 0; + } + if (state->move == FE_OWN_MAYBE_MOVED) { + fe_diag_error(diags, loc, "use of possibly moved value"); + return 0; + } + return 1; +} + +static int fe_own_require_stable_borrow(FeDiags *diags, FeOwnState *state, + FeLoc loc) +{ + if (!state->borrow_conflict) return 1; + fe_own_error_note(diags, loc, + "incompatible borrow state across control-flow paths", + state->borrow_loc, "borrow originated here"); + return 0; +} + +int fe_own_access(FeDiags *diags, FeOwnState *state, + FeOwnAccessKind access, FeLoc loc) +{ + if (!state) return 0; + if (access == FE_OWN_PROJECTION) return 1; + + if (!fe_own_require_stable_borrow(diags, state, loc)) return 0; + + if (access == FE_OWN_WRITE) { + if (state->shared || state->exclusive) { + fe_own_error_note(diags, loc, "cannot write while value is borrowed", + state->borrow_loc, "borrow originated here"); + return 0; + } + state->move = FE_OWN_AVAILABLE; + state->initialized = 1; + state->move_loc = fe_own_no_loc(); + return 1; + } + + if (!fe_own_require_value(diags, state, loc)) return 0; + + switch (access) { + case FE_OWN_READ: + if (state->exclusive) { + fe_own_error_note(diags, loc, + "cannot read directly while value is mutably borrowed", + state->borrow_loc, "mutable borrow originated here"); + return 0; + } + return 1; + case FE_OWN_MOVE: + if (state->shared || state->exclusive) { + fe_own_error_note(diags, loc, "cannot move while value is borrowed", + state->borrow_loc, "borrow originated here"); + return 0; + } + state->move = FE_OWN_MOVED; + state->initialized = 0; + state->move_loc = loc; + return 1; + case FE_OWN_BORROW_SHARED: + if (state->exclusive) { + fe_own_error_note(diags, loc, + "cannot create shared borrow while mutable borrow is live", + state->borrow_loc, "mutable borrow originated here"); + return 0; + } + if (!state->shared) state->borrow_loc = loc; + ++state->shared; + return 1; + case FE_OWN_BORROW_MUT: + if (state->shared || state->exclusive) { + fe_own_error_note(diags, loc, + "cannot create mutable borrow while another borrow is live", + state->borrow_loc, "existing borrow originated here"); + return 0; + } + state->exclusive = 1; + state->borrow_loc = loc; + return 1; + default: + break; + } + return 1; +} + +void fe_own_release_shared(FeOwnState *state) +{ + if (!state || !state->shared) return; + --state->shared; + if (!state->shared && !state->exclusive) + state->borrow_loc = fe_own_no_loc(); +} + +void fe_own_release_exclusive(FeOwnState *state) +{ + if (!state) return; + state->exclusive = 0; + if (!state->shared) state->borrow_loc = fe_own_no_loc(); +} + +FeOwnState fe_own_merge_state(FeOwnState left, FeOwnState right) +{ + FeOwnState out; + out.move = left.move == right.move ? left.move : + fe_own_merge_move(left.move, right.move); + out.initialized = left.initialized && right.initialized; + out.shared = left.shared > right.shared ? left.shared : right.shared; + out.exclusive = left.exclusive || right.exclusive; + out.borrow_conflict = left.borrow_conflict || right.borrow_conflict || + (out.shared != 0 && out.exclusive != 0); + out.move_loc = left.move != FE_OWN_AVAILABLE ? left.move_loc : right.move_loc; + if (left.shared || left.exclusive || left.borrow_conflict) + out.borrow_loc = left.borrow_loc; + else + out.borrow_loc = right.borrow_loc; + return out; +} + +int fe_own_state_equal(const FeOwnState *left, const FeOwnState *right) +{ + if (!left || !right) return 0; + return left->move == right->move && + left->initialized == right->initialized && + left->shared == right->shared && + left->exclusive == right->exclusive && + left->borrow_conflict == right->borrow_conflict; +} + +FeOwnProvenance fe_own_provenance_static(void) +{ + FeOwnProvenance p; + p.kind = FE_OWN_PROV_STATIC; + p.param_index = 0; + return p; +} + +FeOwnProvenance fe_own_provenance_param(unsigned param_index) +{ + FeOwnProvenance p; + p.kind = FE_OWN_PROV_PARAM; + p.param_index = param_index; + return p; +} + +FeOwnProvenance fe_own_merge_provenance(FeOwnProvenance left, + FeOwnProvenance right) +{ + FeOwnProvenance invalid; + invalid.kind = FE_OWN_PROV_INVALID; + invalid.param_index = 0; + if (left.kind == FE_OWN_PROV_INVALID || right.kind == FE_OWN_PROV_INVALID) + return invalid; + if (left.kind == FE_OWN_PROV_STATIC) return right; + if (right.kind == FE_OWN_PROV_STATIC) return left; + if (left.param_index == right.param_index) return left; + return invalid; +} + +void fe_own_liveness_init(FeOwnLiveness *live, FeArena *arena) +{ + if (!live) return; + live->arena = arena; + live->items = 0; + live->count = 0; + live->capacity = 0; + live->ordinal = 0; +} + +static FeOwnLastUse *fe_own_live_find(FeOwnLiveness *live, const char *cname) +{ + unsigned i; + if (!live || !cname) return 0; + for (i = 0; i < live->count; ++i) + if (live->items[i].cname == cname || + strcmp(live->items[i].cname, cname) == 0) + return &live->items[i]; + return 0; +} + +static int fe_own_live_add(FeOwnLiveness *live, FeNode *decl) +{ + FeOwnLastUse *items; + FeOwnLastUse *slot; + unsigned capacity; + const char *cname; + if (!live || !decl || !live->arena || !fe_own_is_reference_like(decl->sem_type)) + return 1; + cname = decl->cname ? decl->cname : decl->text; + if (!cname || fe_own_live_find(live, cname)) return 1; + if (live->count == live->capacity) { + capacity = live->capacity ? live->capacity * 2U : 8U; + items = (FeOwnLastUse *)fe_arena_alloc(live->arena, + capacity * sizeof(FeOwnLastUse)); + if (!items) return 0; + if (live->items) + memcpy(items, live->items, live->count * sizeof(FeOwnLastUse)); + live->items = items; + live->capacity = capacity; + } + slot = &live->items[live->count++]; + slot->cname = cname; + slot->decl = decl; + slot->last_node = 0; + slot->last_ordinal = 0; + slot->defer_extended = 0; + return 1; +} + +static unsigned long fe_own_node_weight(FeNode *node); + +static unsigned long fe_own_list_weight(FeNode *node) +{ + unsigned long count; + count = 0; + while (node) { + count += fe_own_node_weight(node); + node = node->next; + } + return count; +} + +static unsigned long fe_own_node_weight(FeNode *node) +{ + unsigned long count; + if (!node) return 0; + count = 1; + count += fe_own_node_weight(node->a); + count += fe_own_node_weight(node->b); + count += fe_own_node_weight(node->c); + count += fe_own_list_weight(node->children); + return count; +} + +static int fe_own_live_visit(FeOwnLiveness *live, FeNode *node, + unsigned long block_end, + unsigned long defer_until); + +static int fe_own_live_visit_list(FeOwnLiveness *live, FeNode *node, + unsigned long block_end, + unsigned long defer_until) +{ + while (node) { + if (!fe_own_live_visit(live, node, block_end, defer_until)) return 0; + node = node->next; + } + return 1; +} + +static int fe_own_live_visit(FeOwnLiveness *live, FeNode *node, + unsigned long block_end, + unsigned long defer_until) +{ + FeOwnLastUse *slot; + unsigned long end; + unsigned long effective; + if (!node) return 1; + ++live->ordinal; + + if (node->kind == FE_N_IDENT) { + slot = fe_own_live_find(live, node->cname ? node->cname : node->text); + if (slot) { + effective = defer_until ? defer_until : live->ordinal; + if (effective >= slot->last_ordinal) { + slot->last_ordinal = effective; + slot->last_node = node; + if (defer_until) slot->defer_extended = 1; + } + } + return 1; + } + + if (node->kind == FE_N_BLOCK) { + end = live->ordinal + fe_own_list_weight(node->children); + return fe_own_live_visit_list(live, node->children, end, defer_until); + } + + if (node->kind == FE_N_DEFER) { + effective = defer_until ? defer_until : block_end; + return fe_own_live_visit(live, node->a, block_end, effective); + } + + if (!fe_own_live_visit(live, node->a, block_end, defer_until)) return 0; + if (!fe_own_live_visit(live, node->b, block_end, defer_until)) return 0; + if (!fe_own_live_visit(live, node->c, block_end, defer_until)) return 0; + if (!fe_own_live_visit_list(live, node->children, block_end, defer_until)) + return 0; + + if (node->kind == FE_N_LET || node->kind == FE_N_VAR || + node->kind == FE_N_CONST) + return fe_own_live_add(live, node); + return 1; +} + +int fe_own_collect_last_uses(FeOwnLiveness *live, FeNode *fn) +{ + FeNode *param; + if (!live || !fn || fn->kind != FE_N_FN) return 0; + live->items = 0; + live->count = 0; + live->capacity = 0; + live->ordinal = 0; + for (param = fn->a ? fn->a->children : 0; param; param = param->next) + if (!fe_own_live_add(live, param)) return 0; + return fe_own_live_visit(live, fn->c, 0, 0); +} + +const FeOwnLastUse *fe_own_last_use(const FeOwnLiveness *live, + const char *cname) +{ + unsigned i; + if (!live || !cname) return 0; + for (i = 0; i < live->count; ++i) + if (live->items[i].cname == cname || + strcmp(live->items[i].cname, cname) == 0) + return &live->items[i]; + return 0; +} + void fe_own_mark_consumed(FeDiags *diags, int *state, FeNode *decl, FeNode *expr, FeType *type, int in_defer) { From 8c4ec8a6e65bfefd2237eddb5a3e8f03d9168ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=8B=9C=EC=9B=90?= Date: Sun, 16 Aug 2026 19:58:18 +0900 Subject: [PATCH 3/4] complete M6 ownership core primitives --- fec/src/own.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fec/src/own.h b/fec/src/own.h index 830341f..927ff97 100644 --- a/fec/src/own.h +++ b/fec/src/own.h @@ -72,10 +72,13 @@ int fe_own_place_from_expr(FeNode *expr, FeOwnPlace *place); void fe_own_state_init(FeOwnState *state, int initialized); int fe_own_access(FeDiags *diags, FeOwnState *state, FeOwnAccessKind access, FeLoc loc); +int fe_own_call_shared_view(FeDiags *diags, FeOwnState *state, FeLoc loc); void fe_own_release_shared(FeOwnState *state); void fe_own_release_exclusive(FeOwnState *state); FeOwnState fe_own_merge_state(FeOwnState left, FeOwnState right); int fe_own_state_equal(const FeOwnState *left, const FeOwnState *right); +int fe_own_loop_merge_state(FeOwnState entry, FeOwnState backedge, + FeOwnState *merged); FeOwnProvenance fe_own_provenance_static(void); FeOwnProvenance fe_own_provenance_param(unsigned param_index); From 3bcf6a33f184e2137d7e4f04be95baaaad23c3bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=8B=9C=EC=9B=90?= Date: Sun, 16 Aug 2026 19:59:01 +0900 Subject: [PATCH 4/4] tighten M6 ownership core semantics --- fec/src/own.c | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/fec/src/own.c b/fec/src/own.c index 862d330..f40fd43 100644 --- a/fec/src/own.c +++ b/fec/src/own.c @@ -61,6 +61,16 @@ static FeNode *fe_own_root_expr(FeNode *expr) return 0; } +static int fe_own_expr_has_projection(FeNode *expr) +{ + if (!expr) return 0; + if (expr->kind == FE_N_MEMBER || expr->kind == FE_N_INDEX) return 1; + if (expr->kind == FE_N_UNARY && expr->text && + (strcmp(expr->text, "&") == 0 || strcmp(expr->text, "&mut") == 0)) + return fe_own_expr_has_projection(expr->a); + return 0; +} + int fe_own_place_from_expr(FeNode *expr, FeOwnPlace *place) { FeNode *root; @@ -72,7 +82,7 @@ int fe_own_place_from_expr(FeNode *expr, FeOwnPlace *place) if (!root) return 0; place->root = root; place->root_cname = root->cname ? root->cname : root->text; - place->projected = root != expr; + place->projected = fe_own_expr_has_projection(expr); return place->root_cname != 0; } @@ -90,10 +100,6 @@ void fe_own_state_init(FeOwnState *state, int initialized) static int fe_own_require_value(FeDiags *diags, FeOwnState *state, FeLoc loc) { - if (!state->initialized) { - fe_diag_error(diags, loc, "use of uninitialized variable"); - return 0; - } if (state->move == FE_OWN_MOVED) { fe_own_error_note(diags, loc, "use of moved value", state->move_loc, "value was moved here"); @@ -103,6 +109,10 @@ static int fe_own_require_value(FeDiags *diags, FeOwnState *state, FeLoc loc) fe_diag_error(diags, loc, "use of possibly moved value"); return 0; } + if (!state->initialized) { + fe_diag_error(diags, loc, "use of uninitialized variable"); + return 0; + } return 1; } @@ -183,6 +193,19 @@ int fe_own_access(FeDiags *diags, FeOwnState *state, return 1; } +int fe_own_call_shared_view(FeDiags *diags, FeOwnState *state, FeLoc loc) +{ + if (!state) return 0; + if (!fe_own_require_stable_borrow(diags, state, loc)) return 0; + if (!fe_own_require_value(diags, state, loc)) return 0; + if (!state->exclusive) { + fe_diag_error(diags, loc, + "read-only reborrow requires a live mutable borrow"); + return 0; + } + return 1; +} + void fe_own_release_shared(FeOwnState *state) { if (!state || !state->shared) return; @@ -226,6 +249,14 @@ int fe_own_state_equal(const FeOwnState *left, const FeOwnState *right) left->borrow_conflict == right->borrow_conflict; } +int fe_own_loop_merge_state(FeOwnState entry, FeOwnState backedge, + FeOwnState *merged) +{ + if (!merged) return 0; + *merged = fe_own_merge_state(entry, backedge); + return fe_own_state_equal(&entry, merged); +} + FeOwnProvenance fe_own_provenance_static(void) { FeOwnProvenance p;