wip: extend M5 ownership tests and cleanup emission
조건부 이동, 이중 destroy, 직접 drop 호출에 대한 실패 fixture를 추가하고 runtime harness와 test-dos.bat를 그에 맞춰 갱신한다. M5는 아직 완료가 아니다. 모든 경로에서 정확히 1회 cleanup, defer/drop의 선언 역순 병합, try 전파 경로 cleanup, MaybeMoved 런타임 live flag, struct drop과 필드 역순 drop, 분기/루프 상태 합류, 누수/이중해제 카운터 harness가 남아 있다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PQm6oAvWX4Lp3iSN5AHGT
This commit is contained in:
+4
-1
@@ -79,7 +79,10 @@ static void mark_moved(FeCheckerState *s, FeNode *n, FeType *t)
|
||||
if (sym->decl) sym->decl->flags |= 0x200U;
|
||||
} else {
|
||||
sym->moved=1;
|
||||
if (sym->decl) sym->decl->flags |= 0x100U;
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -932,9 +932,17 @@ static void emit_cleanup_to(FeEmitter *e, unsigned floor)
|
||||
for (i=e->block_depth; i>floor; --i) emit_cleanup_block(e,e->block_stack[i-1]);
|
||||
}
|
||||
|
||||
static void emit_param_cleanup(FeEmitter *e)
|
||||
{
|
||||
FeNode *p;
|
||||
if (!e->current_fn || !e->current_fn->a) return;
|
||||
for (p=e->current_fn->a->children; p; p=p->next) emit_value_drop(e,p);
|
||||
}
|
||||
|
||||
static void emit_cleanup_all(FeEmitter *e)
|
||||
{
|
||||
emit_cleanup_to(e,0);
|
||||
emit_param_cleanup(e);
|
||||
}
|
||||
|
||||
static void emit_error_return(FeEmitter *e, const char *error_expr)
|
||||
@@ -974,6 +982,14 @@ static void emit_block(FeEmitter *e, FeNode *n)
|
||||
for (x = n->children; x; x = x->next)
|
||||
if (x->kind == FE_N_LET || x->kind == FE_N_VAR ||
|
||||
x->kind == FE_N_CONST) emit_decl(e, x);
|
||||
if (e->current_fn && e->current_fn->c==n && e->current_fn->a) {
|
||||
FeNode *param;
|
||||
for (param=e->current_fn->a->children; param; param=param->next)
|
||||
if (param->sem_type && param->sem_type->kind==FE_TYPE_OWNED) {
|
||||
pad(e); fputs("unsigned char fe_live_",e->out);
|
||||
fputs(cname(param,"owned"),e->out); fputs("=1;\n",e->out);
|
||||
}
|
||||
}
|
||||
if (e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) {
|
||||
pad(e); fputs(fe_type_c_name(e->current_ret,e->pointer_bits),e->out);
|
||||
fputs(" fe_return_value;\n",e->out);
|
||||
@@ -988,6 +1004,7 @@ static void emit_block(FeEmitter *e, FeNode *n)
|
||||
}
|
||||
--e->indent;
|
||||
emit_cleanup_block(e,n);
|
||||
if (e->current_fn && e->current_fn->c==n) emit_param_cleanup(e);
|
||||
if (e->block_depth) --e->block_depth;
|
||||
if (e->fallthrough_block==n) {
|
||||
pad(e);
|
||||
@@ -1246,7 +1263,9 @@ static void emit_fn(FeEmitter *e, FeNode *fn, int prototype)
|
||||
if (prototype) fputs(";\n", e->out);
|
||||
else {
|
||||
FeType *old_ret=e->current_ret;
|
||||
FeNode *old_fn=e->current_fn;
|
||||
e->current_ret=fn->sem_type;
|
||||
e->current_fn=fn;
|
||||
fputs(" ", e->out);
|
||||
if (fn->sem_type && fn->sem_type->kind==FE_TYPE_ERROR_UNION &&
|
||||
fn->sem_type->error_value &&
|
||||
@@ -1254,6 +1273,7 @@ static void emit_fn(FeEmitter *e, FeNode *fn, int prototype)
|
||||
e->fallthrough_block=fn->c;
|
||||
emit_block(e, fn->c);
|
||||
e->current_ret=old_ret;
|
||||
e->current_fn=old_fn;
|
||||
fputc('\n', e->out);
|
||||
}
|
||||
}
|
||||
@@ -1285,6 +1305,7 @@ void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check,
|
||||
e->block_depth = 0;
|
||||
e->loop_depth = 0;
|
||||
e->current_ret = 0;
|
||||
e->current_fn = 0;
|
||||
}
|
||||
|
||||
void fe_emit_c_program(FeEmitter *e)
|
||||
|
||||
@@ -17,6 +17,7 @@ typedef struct FeEmitter {
|
||||
unsigned loop_floor[16];
|
||||
unsigned loop_depth;
|
||||
FeType *current_ret;
|
||||
FeNode *current_fn;
|
||||
} FeEmitter;
|
||||
|
||||
void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check,
|
||||
|
||||
+28
-22
@@ -15,9 +15,9 @@ fec.exe --dump-ast TESTS\PASS\BASIC.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast TESTS\PASS\LITERALS.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast TESTS\PASS\KEYWOR.FE > nul
|
||||
fec.exe --dump-ast TESTS\PASS\KEYWORDS-AND-BUILTINS.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast TESTS\PASS\V012-F.FE > nul
|
||||
fec.exe --dump-ast TESTS\PASS\V012-FORMS.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
|
||||
fec.exe --dump-ast STD\CORE.FE > nul
|
||||
@@ -37,11 +37,11 @@ if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast STD\SYS.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
|
||||
fec.exe --dump-ast TESTS\FAIL\MISSIN.FE > nul
|
||||
fec.exe --dump-ast TESTS\FAIL\MISSING-SEMI.FE > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast TESTS\FAIL\UNCLOS.FE > nul
|
||||
fec.exe --dump-ast TESTS\FAIL\UNCLOSED-COMMENT.FE > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast TESTS\FAIL\LOGICA.FE > nul
|
||||
fec.exe --dump-ast TESTS\FAIL\LOGICAL-SYMBOLS.FE > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
|
||||
if exist TESTS\M2\HELLO.C del TESTS\M2\HELLO.C
|
||||
@@ -67,30 +67,30 @@ TESTS\M2\SCOPES.EXE
|
||||
if errorlevel 1 goto test_fail
|
||||
|
||||
rem M2 bits16 regression path remains on compiler A (wcl).
|
||||
fec.exe --target=bits16 --emit-c TESTS\M2\CAST-W.FE -o TESTS\M2\CAST16.C > nul
|
||||
fec.exe --target=bits16 --emit-c TESTS\M2\CAST-WHILE.FE -o TESTS\M2\CAST16.C > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
wcl -q -za -bt=dos -fe=TESTS\M2\CAST16.EXE TESTS\M2\CAST16.C
|
||||
if errorlevel 1 goto test_fail
|
||||
TESTS\M2\CAST16.EXE
|
||||
if errorlevel 1 goto test_fail
|
||||
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-CO.FE -o TESTS\M2\BAD-CO.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-CONDITION.FE -o TESTS\M2\BAD-CO.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-CA.FE -o TESTS\M2\BAD-CA.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-CAST.FE -o TESTS\M2\BAD-CA.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-AS.FE -o TESTS\M2\BAD-AS.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-ASSIGN.FE -o TESTS\M2\BAD-AS.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-UN.FE -o TESTS\M2\BAD-UN.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-UNKNOWN.FE -o TESTS\M2\BAD-UN.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-AR.FE -o TESTS\M2\BAD-AR.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-ARITY.FE -o TESTS\M2\BAD-AR.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-TY.FE -o TESTS\M2\BAD-TY.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-TYPES.FE -o TESTS\M2\BAD-TY.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-RE.FE -o TESTS\M2\BAD-RE.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-RETURN.FE -o TESTS\M2\BAD-RE.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-UI.FE -o TESTS\M2\BAD-UI.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-UNINIT.FE -o TESTS\M2\BAD-UI.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-VO.FE -o TESTS\M2\BAD-VO.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M2\BAD-VOID.FE -o TESTS\M2\BAD-VO.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
|
||||
if exist TESTS\M3\STRUCT.C del TESTS\M3\STRUCT.C
|
||||
@@ -197,7 +197,7 @@ wcl386 -q -za -wx -wcd=202 -bt=dos -fe=TESTS\M4\FORMAT.EXE TESTS\M4\FORMAT.C
|
||||
if errorlevel 1 goto test_fail
|
||||
TESTS\M4\FORMAT.EXE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M4\TRY-FPR.FE -o TESTS\M4\TRY-FPR.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M4\TRY-FPRINT.FE -o TESTS\M4\TRY-FPR.C > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
wcl386 -q -za -wx -wcd=202 -bt=dos -fe=TESTS\M4\TRY-FPR.EXE TESTS\M4\TRY-FPR.C
|
||||
if errorlevel 1 goto test_fail
|
||||
@@ -209,17 +209,17 @@ wcl386 -q -za -wx -wcd=202 -bt=dos -fe=TESTS\M4\PROP.EXE TESTS\M4\PROPTEST.C
|
||||
if errorlevel 1 goto test_fail
|
||||
TESTS\M4\PROP.EXE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-ARI.FE -o TESTS\M4\BAD-ARI.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-ARITY.FE -o TESTS\M4\BAD-ARI.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-VERB.FE -o TESTS\M4\BAD-VERB.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-RUN.FE -o TESTS\M4\BAD-RUN.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-RUNTIME.FE -o TESTS\M4\BAD-RUN.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-TYP.FE -o TESTS\M4\BAD-TYP.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-TYPE.FE -o TESTS\M4\BAD-TYP.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-TRY.FE -o TESTS\M4\BAD-TRY.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-WRI.FE -o TESTS\M4\BAD-WRI.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-WRITER.FE -o TESTS\M4\BAD-WRI.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M4\BAD-MANY.FE -o TESTS\M4\BAD-MANY.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
@@ -233,7 +233,13 @@ fec.exe --target=bits32 --emit-c TESTS\M5\OWNED.FE -o TESTS\M5\OWNED.C > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M5\BAD-MOVE.FE -o TESTS\M5\BAD-MOVE.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M5\BAD-DES.FE -o TESTS\M5\BAD-DES.C > nul
|
||||
fec.exe --target=bits32 --emit-c TESTS\M5\BAD-DESTROY.FE -o TESTS\M5\BAD-DES.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M5\BAD-DROP.FE -o TESTS\M5\BAD-DROP.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M5\BAD-DOUBLE.FE -o TESTS\M5\BAD-DBL.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --target=bits32 --emit-c TESTS\M5\BAD-CONDITIONAL.FE -o TESTS\M5\BAD-COND.C > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
if exist TESTS\M5\RUNTIME-G.C del TESTS\M5\RUNTIME-G.C
|
||||
if exist TESTS\M5\RUNTIME.O del TESTS\M5\RUNTIME.O
|
||||
@@ -242,7 +248,7 @@ fec.exe --target=bits32 --emit-c TESTS\M5\RUNTIME.FE -o TESTS\M5\RUNTIME-G.C > n
|
||||
if errorlevel 1 goto test_fail
|
||||
rem Compile generated source and the C89 runtime harness in one WCL386 invocation
|
||||
rem so both objects use the same DOS/4GW startup and runtime library.
|
||||
wcl386 -q -za -bt=dos -fe=TESTS\M5\RUNTIME.EXE TESTS\M5\RUNTIME-G.C TESTS\M5\RUNTIME.C
|
||||
wcl386 -q -za -bt=dos -dmalloc=m5_malloc -dfree=m5_free -fe=TESTS\M5\RUNTIME.EXE TESTS\M5\RUNTIME-G.C TESTS\M5\RUNTIME.C
|
||||
if errorlevel 1 goto test_fail
|
||||
TESTS\M5\RUNTIME.EXE
|
||||
if errorlevel 1 goto test_fail
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
unit m5_bad_conditional;
|
||||
|
||||
fn take(p: ^i32) -> void { mem.destroy(p); }
|
||||
|
||||
fn bad(p: ^i32, flag: bool) -> void {
|
||||
if flag { take(p); }
|
||||
p.^ = 3;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
unit m5_bad_double;
|
||||
|
||||
fn bad(p: ^i32) -> void {
|
||||
mem.destroy(p);
|
||||
mem.destroy(p);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
unit m5_bad_drop;
|
||||
|
||||
struct Box {
|
||||
value: i32,
|
||||
fn drop(self: &mut Self) { self.value = 0; }
|
||||
}
|
||||
|
||||
fn bad() -> void {
|
||||
var b: Box = Box{ value: 1 };
|
||||
b.drop();
|
||||
}
|
||||
@@ -1,9 +1,52 @@
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#undef malloc
|
||||
#undef free
|
||||
|
||||
extern long fe_m5_runtime_run(long mode);
|
||||
extern void fe_m5_runtime_conditional(unsigned char flag);
|
||||
extern void fe_m5_runtime_argument_cleanup(void);
|
||||
|
||||
static void *live_ptrs[64];
|
||||
static unsigned live_count;
|
||||
static unsigned alloc_count;
|
||||
static unsigned free_count;
|
||||
static unsigned double_free_count;
|
||||
|
||||
void *m5_malloc(size_t size)
|
||||
{
|
||||
void *p = malloc(size);
|
||||
if (p && live_count < 64) live_ptrs[live_count++] = p;
|
||||
if (p) ++alloc_count;
|
||||
return p;
|
||||
}
|
||||
|
||||
void m5_free(void *p)
|
||||
{
|
||||
unsigned i;
|
||||
if (!p) return;
|
||||
for (i = 0; i < live_count; ++i) {
|
||||
if (live_ptrs[i] == p) {
|
||||
live_ptrs[i] = live_ptrs[--live_count];
|
||||
++free_count;
|
||||
free(p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
++double_free_count;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
if (fe_m5_runtime_run(0) != 0) return 1;
|
||||
if (fe_m5_runtime_run(1) != 9) return 2;
|
||||
if (fe_m5_runtime_run(2) != 0) return 3;
|
||||
fe_m5_runtime_conditional(0);
|
||||
fe_m5_runtime_conditional(1);
|
||||
fe_m5_runtime_argument_cleanup();
|
||||
if (double_free_count != 0) return 4;
|
||||
if (live_count != 0) return 5;
|
||||
if (alloc_count != free_count) return 6;
|
||||
return 0;
|
||||
}
|
||||
|
||||
+14
-6
@@ -1,5 +1,7 @@
|
||||
unit m5_runtime;
|
||||
|
||||
fn take(p: ^i32) -> void { mem.destroy(p); }
|
||||
|
||||
pub fn run(mode: i32) -> i32 {
|
||||
var p: ^i32 = try mem.create(i32);
|
||||
defer { mem.destroy(p); }
|
||||
@@ -9,11 +11,17 @@ pub fn run(mode: i32) -> i32 {
|
||||
p.^ = 9;
|
||||
return p.^;
|
||||
}
|
||||
while true {
|
||||
break;
|
||||
}
|
||||
if mode == 2 {
|
||||
return 0;
|
||||
}
|
||||
while true { break; }
|
||||
if mode == 2 { return 0; }
|
||||
return p.^ - 7;
|
||||
}
|
||||
|
||||
pub fn conditional(flag: bool) -> void {
|
||||
var p: ^i32 = try mem.create(i32);
|
||||
if flag { take(p); }
|
||||
}
|
||||
|
||||
pub fn argument_cleanup() -> void {
|
||||
let p: ^i32 = try mem.create(i32);
|
||||
take(p);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user