From 7fd575859a6e75287c1261071ff8ca44319c18c2 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:44:51 +0900 Subject: [PATCH 1/5] extract M5 ownership state helpers --- fec/src/own.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 fec/src/own.h diff --git a/fec/src/own.h b/fec/src/own.h new file mode 100644 index 0000000..eb0ff65 --- /dev/null +++ b/fec/src/own.h @@ -0,0 +1,24 @@ +#ifndef FE_OWN_H +#define FE_OWN_H + +#include "types.h" +#include "diag.h" + +#define FE_OWN_NODE_CONSUMED 0x100U +#define FE_OWN_NODE_DEFER_CAPTURE 0x200U + +enum FeOwnMoveState { + FE_OWN_AVAILABLE = 0, + FE_OWN_MOVED = 1, + FE_OWN_MAYBE_MOVED = 2 +}; + +int fe_own_is_copy_type(FeType *type); +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); +int fe_own_merge_move(int left, int right); +int fe_own_loop_entry(int before, int after); +int fe_own_loop_exit(int state, int after); + +#endif From 89cd035011edc22654a01d452280533d6995a45f 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:45:02 +0900 Subject: [PATCH 2/5] extract M5 ownership state helpers --- fec/src/own.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 fec/src/own.c diff --git a/fec/src/own.c b/fec/src/own.c new file mode 100644 index 0000000..0d90817 --- /dev/null +++ b/fec/src/own.c @@ -0,0 +1,80 @@ +#include "own.h" + +int fe_own_is_copy_type(FeType *type) +{ + unsigned i; + if (!type) return 1; + if (type->kind == FE_TYPE_OWNED) return 0; + if (type->kind == FE_TYPE_REF || type->kind == FE_TYPE_SLICE) + return !type->ref_mut; + if (type->kind == FE_TYPE_ARRAY) + return fe_own_is_copy_type(type->elem); + if (type->kind == FE_TYPE_STRUCT) { + if (type->has_drop) return 0; + for (i = 0; i < type->field_count; ++i) + if (!fe_own_is_copy_type(type->fields[i].type)) return 0; + } + if (type->kind == FE_TYPE_ENUM) { + for (i = 0; i < type->variant_count; ++i) { + unsigned j; + for (j = 0; j < type->variants[i].field_count; ++j) + if (!fe_own_is_copy_type(type->variants[i].fields[j].type)) + return 0; + } + } + return 1; +} + +void fe_own_mark_consumed(FeDiags *diags, int *state, FeNode *decl, + FeNode *expr, FeType *type, int in_defer) +{ + if (!expr || !type || fe_own_is_copy_type(type)) return; + if (expr->kind == FE_N_INDEX && type->kind == FE_TYPE_SLICE && + (expr->c || !expr->b)) + return; + if (expr->kind == FE_N_MEMBER || expr->kind == FE_N_INDEX) { + fe_diag_error(diags, expr->loc, + "cannot move a non-Copy value out of a projection; use mem.replace"); + return; + } + if (expr->kind != FE_N_IDENT || !state) return; + + if (in_defer) { + if (decl) decl->flags |= FE_OWN_NODE_DEFER_CAPTURE; + return; + } + + *state = FE_OWN_MOVED; + /* The consuming use owns the live-flag transition. The declaration must + stay live on control-flow paths where the move did not execute. */ + expr->flags |= FE_OWN_NODE_CONSUMED; +} + +void fe_own_check_use(FeDiags *diags, int state, FeLoc loc) +{ + if (state == FE_OWN_MOVED) + fe_diag_error(diags, loc, "use of moved value"); + else if (state == FE_OWN_MAYBE_MOVED) + fe_diag_error(diags, loc, "use of possibly moved value"); +} + +int fe_own_merge_move(int left, int right) +{ + if (left == FE_OWN_MOVED && right == FE_OWN_MOVED) + return FE_OWN_MOVED; + if (left != FE_OWN_AVAILABLE || right != FE_OWN_AVAILABLE) + return FE_OWN_MAYBE_MOVED; + return FE_OWN_AVAILABLE; +} + +int fe_own_loop_entry(int before, int after) +{ + if (before == after) return before; + return FE_OWN_MAYBE_MOVED; +} + +int fe_own_loop_exit(int state, int after) +{ + if (after != FE_OWN_AVAILABLE) return FE_OWN_MAYBE_MOVED; + return state; +} From accc7dd94ab2d5f6252bd87ead1077a9f55ffd75 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:45:09 +0900 Subject: [PATCH 3/5] wire ownership helper into compiler build --- fec/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fec/Makefile b/fec/Makefile index 17626e4..ae23bb3 100644 --- a/fec/Makefile +++ b/fec/Makefile @@ -1,7 +1,7 @@ CC ?= cc CFLAGS ?= -O2 -Wall -Wextra -std=c89 CPPFLAGS ?= -Isrc -SRC = src/arena.c src/diag.c src/lexer.c src/ast.c src/parser.c src/types.c src/check.c src/emit_c.c src/driver.c +SRC = src/arena.c src/diag.c src/lexer.c src/ast.c src/parser.c src/types.c src/own.c src/check.c src/emit_c.c src/driver.c OBJ = $(SRC:.c=.o) .PHONY: all clean dos-build From 5c88872ef05e2ff813906b11037455a6e6b032f4 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:45:20 +0900 Subject: [PATCH 4/5] wire ownership helper into DOS build --- fec/build-dos.bat | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fec/build-dos.bat b/fec/build-dos.bat index 38300c2..fbb7e5c 100644 --- a/fec/build-dos.bat +++ b/fec/build-dos.bat @@ -26,6 +26,8 @@ wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=parser.obj src\parser.c if errorlevel 1 goto build_fail wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=types.obj src\types.c if errorlevel 1 goto build_fail +wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=own.obj src\own.c +if errorlevel 1 goto build_fail wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=check.obj src\check.c if errorlevel 1 goto build_fail rem Use an unambiguous short object name for the emit_c source. From db51611225c12d79341eef872a0ce801921670a7 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:47:19 +0900 Subject: [PATCH 5/5] route M5 ownership state through own.c --- fec/src/check.c | 67 +++++++++++-------------------------------------- 1 file changed, 15 insertions(+), 52 deletions(-) diff --git a/fec/src/check.c b/fec/src/check.c index ad5abb3..88d6588 100644 --- a/fec/src/check.c +++ b/fec/src/check.c @@ -1,4 +1,5 @@ #include "check.h" +#include "own.h" #include #include @@ -49,50 +50,15 @@ static int known(FeType *t) return t && t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_ERROR; } -static int is_copy_type(FeType *t) -{ - unsigned i; - if (!t) return 1; - if (t->kind==FE_TYPE_OWNED) return 0; - if (t->kind==FE_TYPE_REF || t->kind==FE_TYPE_SLICE) return !t->ref_mut; - if (t->kind==FE_TYPE_ARRAY) return is_copy_type(t->elem); - if (t->kind==FE_TYPE_STRUCT) { - if (t->has_drop) return 0; - for (i=0;ifield_count;i++) if (!is_copy_type(t->fields[i].type)) return 0; - } - if (t->kind==FE_TYPE_ENUM) - for (i=0;ivariant_count;i++) { - unsigned j; - for (j=0;jvariants[i].field_count;j++) - if (!is_copy_type(t->variants[i].fields[j].type)) return 0; - } - return 1; -} - static void mark_moved(FeCheckerState *s, FeNode *n, FeType *t) { - FeSym *sym; - if (!n || !t || is_copy_type(t)) return; - if(n->kind==FE_N_INDEX && t->kind==FE_TYPE_SLICE && - (n->c || !n->b)) return; - if(n->kind==FE_N_MEMBER || n->kind==FE_N_INDEX) { - err(s->c,n->loc, - "cannot move a non-Copy value out of a projection; use mem.replace"); - return; - } - if(n->kind!=FE_N_IDENT) return; - sym=find_symbol(s->scope,n->text ? n->text : ""); - if (sym) { - if (s->defer_depth) { - if (sym->decl) sym->decl->flags |= 0x200U; - } else { - sym->moved=1; - /* Mark this consuming expression, not the declaration. Branches - may move conditionally; the declaration's runtime live flag - must remain available to guard cleanup on the other path. */ - n->flags |= 0x100U; - } - } + FeSym *sym=0; + if (n && n->kind==FE_N_IDENT) + sym=find_symbol(s->scope,n->text ? n->text : ""); + fe_own_mark_consumed(s->c->diags, + sym ? &sym->moved : 0, + sym ? sym->decl : 0, + n,t,s->defer_depth != 0); } static int compatible(FeType *want, FeType *got, FeNode *value) @@ -244,7 +210,7 @@ static FeSym *add_symbol(FeCheckerState *s, FeScope *scope, sym->fn = fn; sym->mutable = mutable; sym->initialized = initialized; - sym->moved = 0; + sym->moved = FE_OWN_AVAILABLE; sym->decl = decl; if (decl) { decl->cname = cname; @@ -329,9 +295,8 @@ static void flow_merge(FeFlowSlot *base, FeFlowSlot *left, FeFlowSlot *right, { unsigned i; for (i=0; imoved = left[i].moved==1 && right[i].moved==1 ? 1 : - (left[i].moved || right[i].moved ? 2 : 0); - base[i].sym->initialized = left[i].initialized && right[i].initialized; + base[i].sym->moved=fe_own_merge_move(left[i].moved,right[i].moved); + base[i].sym->initialized=left[i].initialized && right[i].initialized; } } @@ -579,8 +544,7 @@ static FeType *check_identifier(FeCheckerState *s, FeNode *n, int read) } n->cname = sym->cname; n->sem_type = sym->type; - if (sym->moved == 1) err(s->c,n->loc,"use of moved value"); - else if (sym->moved == 2) err(s->c,n->loc,"use of possibly moved value"); + fe_own_check_use(s->c->diags,sym->moved,n->loc); if (read && !sym->initialized && !sym->fn) err(s->c, n->loc, "use of uninitialized variable"); return sym->type; @@ -1007,8 +971,7 @@ static void check_match(FeCheckerState *s, FeNode *n) have_merged=1; } else { for(i=0;iscope,body,flow_count); for (i=0;iloop_depth) --s->loop_depth; flow_capture(s->scope,body,flow_count); for(i=0;i