From deafd27939221c8387c5b19cfcb2c7c74318c5e1 Mon Sep 17 00:00:00 2001 From: Sebastian Jeong Date: Sun, 16 Aug 2026 18:48:35 +0900 Subject: [PATCH] feat: complete M5 ownership and cleanup --- AGENTS.md | 10 +- fec/src/check.c | 209 +++++++++++++++++++++++++++++++++++---- fec/src/emit_c.c | 191 ++++++++++++++++++++++++++++------- fec/src/types.c | 15 ++- fec/src/types.h | 2 + fec/std/mem.fe | 6 +- fec/test-dos.bat | 12 ++- fec/tests/m5/bad-clos.fe | 13 +++ fec/tests/m5/bad-loop.fe | 7 ++ fec/tests/m5/bad-proj.fe | 8 ++ fec/tests/m5/owned.fe | 4 +- fec/tests/m5/runtime.c | 64 ++++++++++-- fec/tests/m5/runtime.fe | 85 ++++++++++++++-- 13 files changed, 545 insertions(+), 81 deletions(-) create mode 100644 fec/tests/m5/bad-clos.fe create mode 100644 fec/tests/m5/bad-loop.fe create mode 100644 fec/tests/m5/bad-proj.fe diff --git a/AGENTS.md b/AGENTS.md index d48bfeb..629b919 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -63,12 +63,8 @@ VM 안에서 반복해서 물렸던 것들. 어기면 원인 찾기 어려운 ## 현재 상태 -- M1~M4 완료 및 QEMU/Open Watcom 검증됨. -- M5(`^T`, drop, defer, 이동 검사) 진행 중. 남은 것: 모든 경로에서 정확히 1회 - cleanup, defer와 drop의 선언 역순 병합, `try` 전파 경로 cleanup, `MaybeMoved` - 런타임 live flag, struct drop과 필드 역순 drop, 분기/루프 상태 합류, - 누수·이중해제 카운터 harness. -- `own.c/h`가 아직 없고 소유권 로직이 `check.c`/`emit_c.c`에 들어가 있다. R1~R8 - 전체를 다루는 **M6 착수 시점에 분리한다** (`SPEC.md` §11.3). +- M1~M5 완료 및 QEMU/Open Watcom 검증됨. +- 다음은 M6(R1~R8 대여 검사)다. 착수할 때 소유권 로직을 `check.c`/`emit_c.c`에서 + `own.c/h`로 분리한다 (`SPEC.md` §11.3). - v0.1.6에서 R8(파생 반환), R6(마지막 사용까지 대여), R10(전역 대여 금지)이 바뀌었다. 셋 다 own.c의 상태 기계를 건드리므로 분리 이후에 함께 구현한다. diff --git a/fec/src/check.c b/fec/src/check.c index 711159d..ad5abb3 100644 --- a/fec/src/check.c +++ b/fec/src/check.c @@ -72,7 +72,15 @@ static int is_copy_type(FeType *t) static void mark_moved(FeCheckerState *s, FeNode *n, FeType *t) { FeSym *sym; - if (!n || !t || is_copy_type(t) || n->kind!=FE_N_IDENT) return; + 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) { @@ -258,6 +266,31 @@ void fe_check_init(FeCheck *c, FeAst *ast, FeDiags *diags, } static FeType *check_expr(FeCheckerState *s, FeNode *n); + +static FeNode *find_method(FeCheck *c, FeType *owner, const char *name) +{ + FeNode *decl; + FeNode *method; + if(!owner || !name) return 0; + for(decl=c->ast->root ? c->ast->root->children : 0; decl; decl=decl->next) + if(decl->kind==FE_N_STRUCT && decl->text && + strcmp(decl->text,owner->name)==0) + for(method=decl->children; method; method=method->next) + if(method->kind==FE_N_FN && method->text && + strcmp(method->text,name)==0) return method; + return 0; +} + +static FeType *method_type(FeCheck *c, FeNode *node, FeType *owner) +{ + if(node && node->kind==FE_N_TYPE && node->text && + strcmp(node->text,"Self")==0) return owner; + if(node && node->kind==FE_N_TYPE && node->text && + (strcmp(node->text,"&")==0 || strcmp(node->text,"&mut")==0) && + node->a && node->a->text && strcmp(node->a->text,"Self")==0) + return fe_type_ref(&c->types,owner,strcmp(node->text,"&mut")==0); + return node_type(c,node); +} static void check_match(FeCheckerState *s, FeNode *n); static void check_stmt(FeCheckerState *s, FeNode *n); @@ -666,14 +699,44 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) return n->sem_type; } if (strcmp(n->a->b->text,"create")==0) { - if (!arg || arg->next || arg->kind!=FE_N_IDENT) - err(c,n->loc,"mem.create requires exactly one type argument"); - a=arg && arg->kind==FE_N_IDENT ? - fe_type_owned(&c->types,fe_type_intern(&c->types,arg->text)) : - fe_type_owned(&c->types,unknown(c)); + if (!arg || arg->next) + err(c,n->loc,"mem.create requires exactly one value"); + a=arg ? check_expr(s,arg) : unknown(c); + if(arg) mark_moved(s,arg,a); + a=fe_type_owned(&c->types,a); n->sem_type=fe_type_error_union(&c->types,a); return n->sem_type; } + if (strcmp(n->a->b->text,"alloc_slice")==0) { + FeNode *count=arg ? arg->next : 0; + FeType *item; + if(!arg || arg->kind!=FE_N_IDENT || !count || count->next) + err(c,n->loc,"mem.alloc_slice requires a type and length"); + item=arg && arg->kind==FE_N_IDENT ? + fe_type_intern(&c->types,arg->text) : unknown(c); + b=count ? check_expr(s,count) : unknown(c); + if(known(b) && !fe_type_is_integer(b)) + err(c,count->loc,"slice length must be an integer"); + a=fe_type_owned(&c->types,fe_type_slice(&c->types,item)); + n->sem_type=fe_type_error_union(&c->types,a); + return n->sem_type; + } + if (strcmp(n->a->b->text,"replace")==0) { + FeNode *value=arg ? arg->next : 0; + if(!arg || !value || value->next) + err(c,n->loc,"mem.replace requires destination and value"); + a=arg ? check_expr(s,arg) : unknown(c); + if(!a || a->kind!=FE_TYPE_REF || !a->ref_mut || + !arg->a || !lvalue_writable(s,arg->a)) + err(c,n->loc,"mem.replace destination must be a mutable place"); + b=value ? check_expr(s,value) : unknown(c); + if(a && a->kind==FE_TYPE_REF && !compatible(a->elem,b,value)) + err(c,value->loc,"mem.replace value type mismatch"); + if(value) mark_moved(s,value,b); + n->sem_type=a && a->kind==FE_TYPE_REF ? a->elem : unknown(c); + fe_type_require_replace(&c->types,n->sem_type); + return n->sem_type; + } } if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && n->a->a->kind==FE_N_IDENT && n->a->a->text && @@ -701,7 +764,39 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) n->sem_type=fe_type_intern(&c->types,"usize"); return n->sem_type; } if (n->a && n->a->kind == FE_N_MEMBER) { + FeNode *method; + FeNode *self_param; et=check_expr(s,n->a->a); + method=et && et->kind==FE_TYPE_STRUCT ? + find_method(c,et,n->a->b ? n->a->b->text : "") : 0; + if(method) { + self_param=method->a ? method->a->children : 0; + if(!self_param) { + err(c,n->loc,"method requires self parameter"); + return unknown(c); + } + a=method_type(c,self_param->a,et); + if(a->kind==FE_TYPE_REF && a->ref_mut && + !lvalue_writable(s,n->a->a)) + err(c,n->loc,"mutable method requires a mutable receiver"); + if(a->kind!=FE_TYPE_REF) mark_moved(s,n->a->a,et); + param=self_param->next; + arg=n->children; + while(param && arg) { + a=check_expr(s,arg); + b=method_type(c,param->a,et); + if(!compatible(b,a,arg) && a->kind!=FE_TYPE_UNKNOWN) + err(c,arg->loc,"method argument type mismatch"); + mark_moved(s,arg,a); + param=param->next; + arg=arg->next; + } + if(param || arg) err(c,n->loc,"wrong number of method arguments"); + n->sem_decl=method; + n->sem_type=method->b ? method_type(c,method->b,et) : + fe_type_intern(&c->types,"void"); + return n->sem_type; + } variant=et && et->kind==FE_TYPE_ENUM ? fe_type_variant(et,n->a->b ? n->a->b->text : "") : 0; arg=n->children; @@ -761,6 +856,13 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) n->sem_type=a->elem; return a->elem; } + if(a->kind==FE_TYPE_REF && a->elem && + a->elem->kind==FE_TYPE_STRUCT) { + field=fe_type_field(a->elem,n->b ? n->b->text : ""); + if(!field) { err(c,n->loc,"unknown struct field"); return unknown(c); } + n->sem_type=field->type; + return field->type; + } if (a->kind == FE_TYPE_OWNED && n->b && n->b->text && strcmp(n->b->text,"^")==0) { n->sem_type=a->elem; @@ -816,6 +918,15 @@ static FeType *check_lvalue(FeCheckerState *s, FeNode *n, int read) n->sem_type=base->elem; return base->elem; } + if(base && base->kind==FE_TYPE_REF && base->elem && + base->elem->kind==FE_TYPE_STRUCT) { + if(!base->ref_mut) + err(s->c,n->loc,"cannot write through shared reference"); + field=fe_type_field(base->elem,n->b ? n->b->text : ""); + if(!field) { err(s->c,n->loc,"assignment requires a valid struct field"); return unknown(s->c); } + n->sem_type=field->type; + return field->type; + } if (base && base->kind == FE_TYPE_OWNED && n->b && n->b->text && strcmp(n->b->text,"^")==0) { n->sem_type=base->elem; @@ -855,12 +966,17 @@ static void check_match(FeCheckerState *s, FeNode *n) FeVariantType *variant; int seen[256]; int wildcard=0; + FeFlowSlot base[64], merged[64], current[64]; + unsigned flow_count; + int have_merged=0; unsigned i; for(i=0;i<256U;i++) seen[i]=0; value=check_expr(s,n->a); if(!value || value->kind!=FE_TYPE_ENUM) { err(s->c,n->loc,"match requires an enum value"); return; } + flow_count=flow_capture(s->scope,base,64); for(arm=n->children;arm;arm=arm->next) { FeScope *old=s->scope; + flow_restore(base,flow_count); if(arm->text && strcmp(arm->text,"_")==0) wildcard=1; else { variant=fe_type_variant(value,arm->text); @@ -885,7 +1001,19 @@ static void check_match(FeCheckerState *s, FeNode *n) if(arm->a && arm->a->kind==FE_N_BLOCK) check_stmt(s,arm->a); else if(arm->a) check_expr(s,arm->a); s->scope=old; + flow_capture(s->scope,current,flow_count); + if(!have_merged) { + for(i=0;ivariant_count && i<256U;i++) if(!seen[i]) err(s->c,n->loc,"non-exhaustive match"); } @@ -1073,12 +1201,12 @@ static void check_stmt(FeCheckerState *s, FeNode *n) --s->defer_depth; break; case FE_N_IF: { - FeFlowSlot base[128], left[128], right[128]; + FeFlowSlot base[64], left[64], right[64]; unsigned flow_count; a = check_expr(s, n->a); if (known(a) && a->kind != FE_TYPE_BOOL) err(c, n->loc, "if condition must be bool"); - flow_count=flow_capture(s->scope,base,128); + flow_count=flow_capture(s->scope,base,64); check_stmt(s, n->b); flow_capture(s->scope,left,flow_count); flow_restore(base,flow_count); @@ -1092,25 +1220,32 @@ static void check_stmt(FeCheckerState *s, FeNode *n) break; } case FE_N_WHILE: { - FeFlowSlot base[128], body[128]; + FeFlowSlot base[64], body[64], entry2[64]; unsigned flow_count; + unsigned i; a = check_expr(s, n->a); if (known(a) && a->kind != FE_TYPE_BOOL) err(c, n->loc, "while condition must be bool"); - flow_count=flow_capture(s->scope,base,128); + flow_count=flow_capture(s->scope,base,64); if (s->loop_depth < 255U) ++s->loop_depth; check_stmt(s, n->b); if (s->loop_depth) --s->loop_depth; flow_capture(s->scope,body,flow_count); - flow_restore(base,flow_count); - { - unsigned i; - for (i=0;iloop_depth < 255U) ++s->loop_depth; + check_stmt(s,n->b); + if (s->loop_depth) --s->loop_depth; + flow_capture(s->scope,body,flow_count); + for(i=0;ib ? method_type(c,fn->b,owner) : fe_type_intern(&c->types,"void"); + s.loop_depth=0; + s.defer_depth=0; + fn->sem_type=s.ret; + for(x=fn->a ? fn->a->children : 0; x; x=x->next) { + t=method_type(c,x->a,owner); + x->sem_type=t; + add_symbol(&s,s.scope,x->text,t,0,1,1, + local_cname(c,x->text ? x->text : "arg"),x); + } + if(fn->c) check_stmt(&s,fn->c); +} + int fe_check_program(FeCheck *c) { FeCheckerState s; @@ -1189,6 +1346,15 @@ int fe_check_program(FeCheck *c) check_type_cycles(c); fe_type_layout_all(&c->types); for (n = c->ast->root ? c->ast->root->children : 0; n; n = n->next) { + if(n->kind==FE_N_STRUCT) { + FeNode *m; + char method_name[128]; + for(m=n->children; m; m=m->next) if(m->kind==FE_N_FN) { + sprintf(method_name,"%s_%s",n->text ? n->text : "Type", + m->text ? m->text : "method"); + m->cname=unit_cname(c,method_name); + } + } if (n->kind == FE_N_GLOBAL || n->kind == FE_N_CONST) { t = n->a ? node_type(c, n->a) : unknown(c); add_symbol(&s, s.globals, n->text, t, 0, @@ -1221,6 +1387,13 @@ int fe_check_program(FeCheck *c) } for (n = c->ast->root ? c->ast->root->children : 0; n; n = n->next) if (n->kind == FE_N_FN) check_fn(c, n, s.globals); + for (n = c->ast->root ? c->ast->root->children : 0; n; n = n->next) + if(n->kind==FE_N_STRUCT) { + FeNode *m; + t=fe_type_intern(&c->types,n->text); + for(m=n->children; m; m=m->next) + if(m->kind==FE_N_FN) check_method(c,m,s.globals,t); + } fe_type_layout_all(&c->types); return c->diags->errors == 0; } diff --git a/fec/src/emit_c.c b/fec/src/emit_c.c index 0df0575..0a053dc 100644 --- a/fec/src/emit_c.c +++ b/fec/src/emit_c.c @@ -63,6 +63,8 @@ static void emit_one_type(FeEmitter *e, FeType *t) if (!t || t->emit_state || (t->kind != FE_TYPE_STRUCT && t->kind != FE_TYPE_ENUM && t->kind != FE_TYPE_ARRAY && t->kind != FE_TYPE_SLICE && + !(t->kind == FE_TYPE_OWNED && t->elem && + t->elem->kind == FE_TYPE_SLICE) && t->kind != FE_TYPE_ERROR_UNION) || (t->kind == FE_TYPE_ERROR_UNION && (!t->error_value || t->error_value->kind == FE_TYPE_VOID))) return; @@ -79,6 +81,15 @@ static void emit_one_type(FeEmitter *e, FeType *t) if(!t->ref_mut) fputs("const ",e->out); fputs(fe_type_c_name(t->elem,e->pointer_bits),e->out); fputs(" *p; unsigned long n; } ",e->out); fputs(t->cname,e->out); fputs(";\n",e->out); fprintf(e->out,"static %s %s(%s%s *p, unsigned long n) { %s s; s.p=p; s.n=n; return s; }\n",t->cname,t->maker,t->ref_mut ? "" : "const ",fe_type_c_name(t->elem,e->pointer_bits),t->cname); + } else if(t->kind==FE_TYPE_OWNED && t->elem && + t->elem->kind==FE_TYPE_SLICE) { + FeType *item=t->elem->elem; + fputs("typedef struct { ",e->out); + fputs(fe_type_c_name(item,e->pointer_bits),e->out); + fputs(" *p; unsigned long n; } ",e->out); fputs(t->cname,e->out); + fputs(";\n",e->out); + fprintf(e->out,"static %s %s(%s *p, unsigned long n) { %s s; s.p=p; s.n=n; return s; }\n", + t->cname,t->maker,fe_type_c_name(item,e->pointer_bits),t->cname); } else if(t->kind==FE_TYPE_ERROR_UNION) { fputs(t->cname,e->out); fputs(" { unsigned short e; ",e->out); fputs(fe_type_c_name(t->error_value,e->pointer_bits),e->out); @@ -128,13 +139,21 @@ static void emit_drop_fields(FeEmitter *e, FeType *t) if (!type_needs_drop(ft)) continue; if (ft->kind==FE_TYPE_OWNED) { fputs("if (self->",e->out); fputs(t->fields[i-1].name,e->out); + if(ft->elem && ft->elem->kind==FE_TYPE_SLICE) fputs(".p",e->out); fputs(") { ",e->out); - if (ft->elem && type_needs_drop(ft->elem) && ft->elem->drop_cname) { + if(ft->elem && ft->elem->kind==FE_TYPE_SLICE) { + fputs("free(self->",e->out); fputs(t->fields[i-1].name,e->out); + fputs(".p); self->",e->out); fputs(t->fields[i-1].name,e->out); + fputs(".p=0; ",e->out); + } else if (ft->elem && type_needs_drop(ft->elem) && ft->elem->drop_cname) { fprintf(e->out,"%s(self->%s); ",ft->elem->drop_cname,t->fields[i-1].name); } - fputs("free(self->",e->out); fputs(t->fields[i-1].name,e->out); - fputs("); self->",e->out); fputs(t->fields[i-1].name,e->out); - fputs("=0; }\n",e->out); + if(!(ft->elem && ft->elem->kind==FE_TYPE_SLICE)) { + fputs("free(self->",e->out); fputs(t->fields[i-1].name,e->out); + fputs("); self->",e->out); fputs(t->fields[i-1].name,e->out); + fputs("=0; ",e->out); + } + fputs("}\n",e->out); } else if (ft->kind==FE_TYPE_STRUCT && ft->drop_cname) { fprintf(e->out,"%s(&self->%s);\n",ft->drop_cname,t->fields[i-1].name); } else if (ft->kind==FE_TYPE_ARRAY && ft->drop_cname) { @@ -147,7 +166,10 @@ static void emit_drop_helpers(FeEmitter *e) { FeType *t; FeNode *method; - FeNode *param; + for (t=e->check->types.types; t; t=t->next) + if(t->kind==FE_TYPE_STRUCT && (method=find_drop_method(e,t->name))!=0) + fprintf(e->out,"void %s(%s *self);\n", + cname(method,"fe_drop_method"),t->cname); for (t=e->check->types.types; t; t=t->next) if ((t->kind==FE_TYPE_STRUCT || t->kind==FE_TYPE_ARRAY) && type_needs_drop(t) && t->drop_cname) @@ -157,10 +179,7 @@ static void emit_drop_helpers(FeEmitter *e) fprintf(e->out,"static void %s(%s *self) {\n",t->drop_cname,t->cname); method=find_drop_method(e,t->name); if (method) { - param=method->a ? method->a->children : 0; - if (param) param->cname="self"; - if (method->c) emit_block(e,method->c); - fputc('\n',e->out); + fprintf(e->out,"%s(self);\n",cname(method,"fe_drop_method")); } emit_drop_fields(e,t); fputs("}\n",e->out); @@ -189,14 +208,29 @@ static void emit_type_helpers(FeEmitter *e) t->error_value->kind!=FE_TYPE_VOID) { fprintf(e->out,"static %s %s(unsigned short e, %s v) { %s r; r.e=e; r.v=v; return r; }\n", t->cname,t->maker,fe_type_c_name(t->error_value,e->pointer_bits),t->cname); - if (t->error_value->kind==FE_TYPE_OWNED) - fprintf(e->out,"static %s %s(void) { %s r; r.v=(%s)malloc(sizeof(%s)); r.e=r.v ? 0 : 1; return r; }\n", + if (t->error_value->kind==FE_TYPE_OWNED && + t->error_value->elem && + t->error_value->elem->kind==FE_TYPE_SLICE) { + FeType *item=t->error_value->elem->elem; + fprintf(e->out,"static %s %s(unsigned long n) { %s r; r.v.p=(%s*)malloc(sizeof(%s)*n); r.v.n=n; r.e=(r.v.p || !n) ? 0 : 1; return r; }\n", t->cname,t->alloc_cname,t->cname, + fe_type_c_name(item,e->pointer_bits), + fe_type_c_name(item,e->pointer_bits)); + } else if (t->error_value->kind==FE_TYPE_OWNED) { + fprintf(e->out,"static %s %s(%s v) { %s r; r.v=(%s)malloc(sizeof(%s)); if(r.v) *r.v=v; r.e=r.v ? 0 : 1; return r; }\n", + t->cname,t->alloc_cname, + fe_type_c_name(t->error_value->elem,e->pointer_bits),t->cname, fe_type_c_name(t->error_value,e->pointer_bits), fe_type_c_name(t->error_value->elem,e->pointer_bits)); + } } } for(t=e->check->types.types;t;t=t->next) { + if(t->replace_cname) { + const char *ct=fe_type_c_name(t,e->pointer_bits); + fprintf(e->out,"static %s %s(%s *dst, %s value) { %s old=*dst; *dst=value; return old; }\n", + ct,t->replace_cname,ct,ct,ct); + } if(t->kind==FE_TYPE_STRUCT && t->maker) { fprintf(e->out,"static %s %s(",t->cname,t->maker); for(i=0;ifield_count;i++) { if(i) fputs(", ",e->out); fputs(fe_type_c_name(t->fields[i].type,e->pointer_bits),e->out); fprintf(e->out," p%u",i); } @@ -599,6 +633,10 @@ static void emit_lvalue(FeEmitter *e, FeNode *n) } else if (n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_OWNED && n->b && n->b->text && strcmp(n->b->text,"^")==0) { fputs("(*",e->out); emit_expr(e,n->a); fputs(")",e->out); + } else if(n->a && n->a->sem_type && + n->a->sem_type->kind==FE_TYPE_REF) { + emit_expr(e,n->a); fputs("->",e->out); + fputs(n->b ? n->b->text : "member",e->out); } else { emit_lvalue(e,n->a); fputc('.',e->out); fputs(n->b ? n->b->text : "member",e->out); } return; } @@ -612,10 +650,17 @@ static void emit_lvalue(FeEmitter *e, FeNode *n) static void emit_destroy_expr(FeEmitter *e, FeNode *n) { - fputs("(free(",e->out); emit_expr(e,n); fputs(")",e->out); + fputs("(free(",e->out); emit_expr(e,n); + if(n && n->sem_type && n->sem_type->kind==FE_TYPE_OWNED && + n->sem_type->elem && n->sem_type->elem->kind==FE_TYPE_SLICE) + fputs(".p",e->out); + fputs(")",e->out); if (n && n->kind==FE_N_IDENT) { fputs(", ",e->out); emit_lvalue(e,n); - fputs("=0, fe_live_",e->out); fputs(cname(n,"owned"),e->out); + if(n->sem_type && n->sem_type->elem && + n->sem_type->elem->kind==FE_TYPE_SLICE) fputs(".p=0",e->out); + else fputs("=0",e->out); + fputs(", fe_live_",e->out); fputs(cname(n,"owned"),e->out); fputs("=0",e->out); } else { fputs(", ",e->out); emit_lvalue(e,n); fputs("=0",e->out); @@ -698,7 +743,12 @@ static void emit_expr(FeEmitter *e, FeNode *n) } switch (n->kind) { case FE_N_IDENT: - fputs(cname(n, "fe_missing"), e->out); + if((n->flags & 0x100U) && n->sem_type && + type_needs_drop(n->sem_type)) { + fputs("(fe_live_",e->out); fputs(cname(n,"owned"),e->out); + fputs("=0, ",e->out); fputs(cname(n,"fe_missing"),e->out); + fputc(')',e->out); + } else fputs(cname(n, "fe_missing"), e->out); break; case FE_N_LITERAL: if (n->text && strcmp(n->text, "true") == 0) fputs("1", e->out); @@ -760,7 +810,7 @@ static void emit_expr(FeEmitter *e, FeNode *n) if (strcmp(op, "not") == 0) fputs("(!", e->out); else { fputc('(', e->out); - fputs(op, e->out); + fputs(strcmp(op,"&mut")==0 ? "&" : op, e->out); } emit_expr(e, n->a); fputc(')', e->out); @@ -801,19 +851,55 @@ static void emit_expr(FeEmitter *e, FeNode *n) n->a->a->kind==FE_N_IDENT && n->a->a->text && strcmp(n->a->a->text,"mem")==0 && n->a->b && n->a->b->text && strcmp(n->a->b->text,"create")==0 && - n->children && n->children->kind==FE_N_IDENT) { - FeType *created=fe_type_intern(&e->check->types,n->children->text); + n->children) { + FeType *created=n->children->sem_type; FeType *owned=fe_type_owned(&e->check->types,created); FeType *result=fe_type_error_union(&e->check->types,owned); if (result->alloc_cname) fputs(result->alloc_cname,e->out); else fputs("fe_bad_alloc",e->out); - fputs("()",e->out); + fputc('(',e->out); emit_expr(e,n->children); fputc(')',e->out); + special=1; + } + else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && + n->a->a->kind==FE_N_IDENT && n->a->a->text && + strcmp(n->a->a->text,"mem")==0 && n->a->b && + n->a->b->text && strcmp(n->a->b->text,"alloc_slice")==0 && + n->children && n->children->next) { + FeType *result=n->sem_type; + if(result && result->alloc_cname) fputs(result->alloc_cname,e->out); + else fputs("fe_bad_slice_alloc",e->out); + fputc('(',e->out); emit_expr(e,n->children->next); fputc(')',e->out); + special=1; + } + else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && + n->a->a->kind==FE_N_IDENT && n->a->a->text && + strcmp(n->a->a->text,"mem")==0 && n->a->b && + n->a->b->text && strcmp(n->a->b->text,"replace")==0 && + n->children && n->children->next && n->sem_type) { + fputs(n->sem_type->replace_cname ? n->sem_type->replace_cname : + "fe_bad_replace",e->out); + fputc('(',e->out); emit_expr(e,n->children); fputs(", ",e->out); + emit_expr(e,n->children->next); fputc(')',e->out); special=1; } else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && n->a->a->kind==FE_N_IDENT && n->a->a->text && strcmp(n->a->a->text,"io")==0 && n->a->b && n->a->b->text && strcmp(n->a->b->text,"null_writer")==0) { fputs("fe_m4_null_writer()",e->out); special=1; } + else if(n->a && n->a->kind==FE_N_MEMBER && n->sem_decl && + n->sem_decl->kind==FE_N_FN) { + FeNode *mp=n->sem_decl->a ? n->sem_decl->a->children : 0; + FeNode *ma; + fputs(cname(n->sem_decl,"fe_method"),e->out); fputc('(',e->out); + if(mp && mp->sem_type && mp->sem_type->kind==FE_TYPE_REF) { + fputc('&',e->out); emit_lvalue(e,n->a->a); + } else emit_expr(e,n->a->a); + for(ma=n->children; ma; ma=ma->next) { + fputs(", ",e->out); emit_expr(e,ma); + } + fputc(')',e->out); + special=1; + } else if(!n->a && n->text && strcmp(n->text,"@size_of")==0 && n->children && n->children->kind==FE_N_IDENT) { fprintf(e->out,"%lu",fe_type_size(fe_type_intern(&e->check->types,n->children->text))); special=1; } else if(!n->a && n->text && strcmp(n->text,"@align_of")==0 && n->children && n->children->kind==FE_N_IDENT) { fprintf(e->out,"%u",fe_type_align(fe_type_intern(&e->check->types,n->children->text))); special=1; } else if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && n->a->a->sem_type && n->a->a->sem_type->kind==FE_TYPE_ENUM) { @@ -860,7 +946,10 @@ static void emit_expr(FeEmitter *e, FeNode *n) n->b && n->b->text && strcmp(n->b->text,"^")==0) { fputs("(*",e->out); emit_expr(e,n->a); fputs(")",e->out); } else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_ENUM) { v=fe_type_variant(n->a->sem_type,n->b ? n->b->text : ""); if(v) fputs(v->maker,e->out); else fputs("0",e->out); if(v)fputs("()",e->out); } - else { emit_expr(e, n->a); fputc('.', e->out); if (n->b) fputs(n->b->text ? n->b->text : "member", e->out); } + else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_REF) { + emit_expr(e,n->a); fputs("->",e->out); + if(n->b) fputs(n->b->text ? n->b->text : "member",e->out); + } else { emit_expr(e, n->a); fputc('.', e->out); if (n->b) fputs(n->b->text ? n->b->text : "member", e->out); } break; } default: @@ -887,7 +976,7 @@ static void emit_decl(FeEmitter *e, FeNode *n) } fputs(";\n", e->out); if ((n->kind==FE_N_LET || n->kind==FE_N_VAR) && n->sem_type && - n->sem_type->kind==FE_TYPE_OWNED) { + type_needs_drop(n->sem_type)) { pad(e); fputs("unsigned char fe_live_",e->out); fputs(cname(n,"owned"),e->out); fputs("=0;\n",e->out); } @@ -895,7 +984,7 @@ static void emit_decl(FeEmitter *e, FeNode *n) static void emit_owned_live(FeEmitter *e, FeNode *n, int value) { - if (n && n->sem_type && n->sem_type->kind==FE_TYPE_OWNED) { + if (n && n->sem_type && type_needs_drop(n->sem_type)) { pad(e); fputs("fe_live_",e->out); fputs(cname(n,"owned"),e->out); fprintf(e->out,"=%d;\n",value); } @@ -909,14 +998,21 @@ static void emit_value_drop(FeEmitter *e, FeNode *n) if (t->kind==FE_TYPE_OWNED) { pad(e); fputs("if (fe_live_",e->out); fputs(cname(n,"owned"),e->out); fputs(") { ",e->out); - if (t->elem && type_needs_drop(t->elem) && t->elem->drop_cname) { + if(t->elem && t->elem->kind==FE_TYPE_SLICE) { + fputs("free(",e->out); fputs(cname(n,"owned"),e->out); + fputs(".p); ",e->out); + } else if (t->elem && type_needs_drop(t->elem) && t->elem->drop_cname) { fprintf(e->out,"%s(%s); ",t->elem->drop_cname,cname(n,"owned")); + fputs("free(",e->out); fputs(cname(n,"owned"),e->out); fputs("); ",e->out); + } else { + fputs("free(",e->out); fputs(cname(n,"owned"),e->out); fputs("); ",e->out); } - fputs("free(",e->out); fputs(cname(n,"owned"),e->out); - fputs("); fe_live_",e->out); fputs(cname(n,"owned"),e->out); + fputs("fe_live_",e->out); fputs(cname(n,"owned"),e->out); fputs("=0; }\n",e->out); } else if (t->drop_cname) { - pad(e); fprintf(e->out,"%s(&%s);\n",t->drop_cname,cname(n,"local")); + pad(e); fputs("if (fe_live_",e->out); fputs(cname(n,"local"),e->out); + fprintf(e->out,") { %s(&%s); fe_live_",t->drop_cname,cname(n,"local")); + fputs(cname(n,"local"),e->out); fputs("=0; }\n",e->out); } } @@ -1006,7 +1102,7 @@ static void emit_block(FeEmitter *e, FeNode *n) 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) { + if (param->sem_type && type_needs_drop(param->sem_type)) { pad(e); fputs("unsigned char fe_live_",e->out); fputs(cname(param,"owned"),e->out); fputs("=1;\n",e->out); } @@ -1126,12 +1222,26 @@ static void emit_stmt(FeEmitter *e, FeNode *n) n->a->sem_type->kind==FE_TYPE_OWNED) { pad(e); fputs("if (fe_live_",e->out); fputs(cname(n->a,"owned"),e->out); fputs(") { ",e->out); - if (n->a->sem_type->elem && type_needs_drop(n->a->sem_type->elem) && + if(n->a->sem_type->elem && + n->a->sem_type->elem->kind==FE_TYPE_SLICE) { + fputs("free(",e->out); fputs(cname(n->a,"owned"),e->out); + fputs(".p); ",e->out); + } else if (n->a->sem_type->elem && type_needs_drop(n->a->sem_type->elem) && n->a->sem_type->elem->drop_cname) fprintf(e->out,"%s(%s); ",n->a->sem_type->elem->drop_cname,cname(n->a,"owned")); - fputs("free(",e->out); fputs(cname(n->a,"owned"),e->out); - fputs("); fe_live_",e->out); fputs(cname(n->a,"owned"),e->out); + if(!(n->a->sem_type->elem && + n->a->sem_type->elem->kind==FE_TYPE_SLICE)) { + fputs("free(",e->out); fputs(cname(n->a,"owned"),e->out); fputs("); ",e->out); + } + fputs("fe_live_",e->out); fputs(cname(n->a,"owned"),e->out); fputs("=0; }\n",e->out); + } else if(n->a && n->a->kind==FE_N_IDENT && n->a->sem_type && + type_needs_drop(n->a->sem_type) && + n->a->sem_type->drop_cname) { + pad(e); fputs("if (fe_live_",e->out); fputs(cname(n->a,"local"),e->out); + fprintf(e->out,") { %s(&%s); fe_live_", + n->a->sem_type->drop_cname,cname(n->a,"local")); + fputs(cname(n->a,"local"),e->out); fputs("=0; }\n",e->out); } pad(e); emit_lvalue(e, n->a); @@ -1153,12 +1263,12 @@ static void emit_stmt(FeEmitter *e, FeNode *n) pad(e); if (n->a && n->a->kind==FE_N_UNARY && n->a->text && strcmp(n->a->text,"try")==0 && n->a->a) { - fputs("if ((fe_m4_error = ",e->out); + fputs("if ((fe_error_temp = ",e->out); emit_expr(e,n->a->a); fputs(") != 0) {\n",e->out); ++e->indent; emit_cleanup_all(e); - pad(e); fputs("return fe_m4_error;\n",e->out); + pad(e); fputs("return fe_error_temp;\n",e->out); --e->indent; pad(e); fputs("}\n",e->out); } else { @@ -1275,7 +1385,8 @@ static void emit_fn(FeEmitter *e, FeNode *fn, int prototype) if (!p) fputs("void", e->out); while (p) { if (p != fn->a->children) fputs(", ", e->out); - fputs(ctype(e, p->a), e->out); + fputs(p->sem_type ? fe_type_c_name(p->sem_type,e->pointer_bits) : + ctype(e,p->a),e->out); fputc(' ', e->out); fputs(cname(p, "fe_arg"), e->out); p = p->next; @@ -1343,7 +1454,7 @@ void fe_emit_c_program(FeEmitter *e) fputs("typedef char fe_assert_usize[(sizeof(unsigned short)==2) ? 1 : -1];\n",e->out); else fputs("typedef char fe_assert_usize[(sizeof(unsigned long)==4) ? 1 : -1];\n",e->out); - fputs("static void fe_trap_bounds(void) { abort(); }\n\n", e->out); + fputs("static void fe_trap_bounds(void) { abort(); }\nstatic unsigned short fe_error_temp;\n\n", e->out); emit_type_defs(e); if (need_m4) emit_m4_runtime(e); emit_type_helpers(e); @@ -1373,10 +1484,24 @@ void fe_emit_c_program(FeEmitter *e) emit_fn(e, n, 1); if (n->text && strcmp(n->text, "main") == 0) main_fn = n; } + for (n = e->check->ast->root ? e->check->ast->root->children : 0; + n; n = n->next) if(n->kind==FE_N_STRUCT) { + FeNode *m; + for(m=n->children; m; m=m->next) + if(m->kind==FE_N_FN) + emit_fn(e,m,1); + } fputc('\n', e->out); for (n = e->check->ast->root ? e->check->ast->root->children : 0; n; n = n->next) if (n->kind == FE_N_FN) emit_fn(e, n, 0); + for (n = e->check->ast->root ? e->check->ast->root->children : 0; + n; n = n->next) if(n->kind==FE_N_STRUCT) { + FeNode *m; + for(m=n->children; m; m=m->next) + if(m->kind==FE_N_FN) + emit_fn(e,m,0); + } if (main_fn) { fputc('\n', e->out); emit_main_wrapper(e, main_fn); diff --git a/fec/src/types.c b/fec/src/types.c index c471dcf..542df00 100644 --- a/fec/src/types.c +++ b/fec/src/types.c @@ -21,6 +21,7 @@ static FeType *new_type(FeTypeCtx *ctx, const char *name, FeTypeKind kind) t->tail_slicer = 0; t->drop_cname = 0; t->alloc_cname = 0; + t->replace_cname = 0; t->bits = 0; t->is_unsigned = 0; t->packed = 0; @@ -200,6 +201,10 @@ FeType *fe_type_owned(FeTypeCtx *ctx, FeType *elem) if(t->kind==FE_TYPE_UNKNOWN) { t->kind=FE_TYPE_OWNED; t->elem=elem; + if(elem && elem->kind==FE_TYPE_SLICE) { + t->cname=generated_name(ctx,"fe_owned_slice_","type"); + t->maker=generated_name(ctx,"fe_make_owned_slice_","type"); + } } return t; } @@ -222,6 +227,12 @@ FeType *fe_type_error_union(FeTypeCtx *ctx, FeType *value) return t; } +void fe_type_require_replace(FeTypeCtx *ctx, FeType *type) +{ + if(type && !type->replace_cname) + type->replace_cname=generated_name(ctx,"fe_replace_","type"); +} + FeType *fe_type_declare_struct(FeTypeCtx *ctx, const FeNode *node, int packed) { FeType *t; @@ -410,7 +421,9 @@ static void layout_type(FeTypeCtx *ctx, FeType *t) t->cycle_state = 2; return; } if (t->kind == FE_TYPE_OWNED) { - t->size = ctx->pointer_bits == 16 ? 2UL : 4UL; + t->size = t->elem && t->elem->kind==FE_TYPE_SLICE ? + (ctx->pointer_bits == 16 ? 4UL : 8UL) : + (ctx->pointer_bits == 16 ? 2UL : 4UL); t->align = ctx->pointer_bits == 16 ? 1U : 4U; t->cycle_state = 2; return; } diff --git a/fec/src/types.h b/fec/src/types.h index fae31ae..1974a46 100644 --- a/fec/src/types.h +++ b/fec/src/types.h @@ -39,6 +39,7 @@ struct FeType { char *tail_slicer; char *drop_cname; char *alloc_cname; + char *replace_cname; unsigned bits; int is_unsigned; int packed; @@ -77,6 +78,7 @@ FeType *fe_type_mut_slice(FeTypeCtx *ctx, FeType *elem); FeType *fe_type_ref(FeTypeCtx *ctx, FeType *elem, int mutable); FeType *fe_type_owned(FeTypeCtx *ctx, FeType *elem); FeType *fe_type_error_union(FeTypeCtx *ctx, FeType *value); +void fe_type_require_replace(FeTypeCtx *ctx, FeType *type); FeType *fe_type_declare_struct(FeTypeCtx *ctx, const FeNode *node, int packed); FeType *fe_type_declare_enum(FeTypeCtx *ctx, const FeNode *node); FeType *fe_type_declare_error(FeTypeCtx *ctx, const FeNode *node); diff --git a/fec/std/mem.fe b/fec/std/mem.fe index cec54f5..4fe0cd7 100644 --- a/fec/std/mem.fe +++ b/fec/std/mem.fe @@ -1,8 +1,10 @@ unit mem; -pub fn create(T: type) -> !^T; +pub fn create(value: T) -> !^T; pub fn destroy(p: *void); -pub fn copy(dst: []u8, src: []u8); +pub fn alloc_slice(T: type, n: usize) -> !^[]T; +pub fn replace(dst: &mut T, value: T) -> T; +pub fn copy(dst: []mut u8, src: []u8); pub struct Arena { ptr: *void, pub fn init() -> Arena { return Arena{ ptr: null }; } diff --git a/fec/test-dos.bat b/fec/test-dos.bat index 60acb3e..bdd2d15 100644 --- a/fec/test-dos.bat +++ b/fec/test-dos.bat @@ -259,14 +259,20 @@ fec.exe --target=bits32 --emit-c TESTS\M5\BAD-DBL.FE -o TESTS\M5\BAD-DBL.C > nul if not errorlevel 1 goto test_fail fec.exe --target=bits32 --emit-c TESTS\M5\BAD-COND.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 +fec.exe --target=bits32 --emit-c TESTS\M5\BAD-PROJ.FE -o TESTS\M5\BAD-PROJ.C > nul +if not errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M5\BAD-CLOS.FE -o TESTS\M5\BAD-CLOS.C > nul +if not errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M5\BAD-LOOP.FE -o TESTS\M5\BAD-LOOP.C > nul +if not errorlevel 1 goto test_fail +if exist TESTS\M5\RUNT-G.C del TESTS\M5\RUNT-G.C if exist TESTS\M5\RUNTIME.O del TESTS\M5\RUNTIME.O if exist TESTS\M5\RUNTIME.EXE del TESTS\M5\RUNTIME.EXE -fec.exe --target=bits32 --emit-c TESTS\M5\RUNTIME.FE -o TESTS\M5\RUNTIME-G.C > nul +fec.exe --target=bits32 --emit-c TESTS\M5\RUNTIME.FE -o TESTS\M5\RUNT-G.C > nul 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 -dmalloc=m5_malloc -dfree=m5_free -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\RUNT-G.C TESTS\M5\RUNTIME.C if errorlevel 1 goto test_fail TESTS\M5\RUNTIME.EXE if errorlevel 1 goto test_fail diff --git a/fec/tests/m5/bad-clos.fe b/fec/tests/m5/bad-clos.fe new file mode 100644 index 0000000..530696b --- /dev/null +++ b/fec/tests/m5/bad-clos.fe @@ -0,0 +1,13 @@ +unit m5_bad_consuming_close; + +struct FileLike { + handle: i32, + fn close(self: Self) -> !void { self.handle = 0; } + fn drop(self: &mut Self) { self.handle = 0; } +} + +fn bad() -> !void { + let file: FileLike = FileLike{ handle: 7 }; + try file.close(); + file.close(); +} diff --git a/fec/tests/m5/bad-loop.fe b/fec/tests/m5/bad-loop.fe new file mode 100644 index 0000000..091d253 --- /dev/null +++ b/fec/tests/m5/bad-loop.fe @@ -0,0 +1,7 @@ +unit m5_bad_loop_move; + +fn take(p: ^i32) -> void { mem.destroy(p); } + +fn bad(p: ^i32, again: bool) -> void { + while again { take(p); } +} diff --git a/fec/tests/m5/bad-proj.fe b/fec/tests/m5/bad-proj.fe new file mode 100644 index 0000000..bf928ea --- /dev/null +++ b/fec/tests/m5/bad-proj.fe @@ -0,0 +1,8 @@ +unit m5_bad_projection_move; + +struct Holder { p: ^i32 } +fn take(p: ^i32) -> void { mem.destroy(p); } + +fn bad(h: Holder) -> void { + take(h.p); +} diff --git a/fec/tests/m5/owned.fe b/fec/tests/m5/owned.fe index 7f7a553..e3f44f6 100644 --- a/fec/tests/m5/owned.fe +++ b/fec/tests/m5/owned.fe @@ -1,8 +1,8 @@ unit m5_owned; fn main() -> void { - var p: ^i32 = try mem.create(i32); - p = try mem.create(i32); + var p: ^i32 = try mem.create(0); + p = try mem.create(0); p.^ = 7; let value: i32 = p.^; defer { mem.destroy(p); } diff --git a/fec/tests/m5/runtime.c b/fec/tests/m5/runtime.c index 04d5c10..43e51e0 100644 --- a/fec/tests/m5/runtime.c +++ b/fec/tests/m5/runtime.c @@ -4,21 +4,43 @@ #undef malloc #undef free +extern void *malloc(size_t size); +extern void free(void *p); + 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); +extern unsigned short fe_m5_runtime_conditional(unsigned char flag); +extern unsigned short fe_m5_runtime_argument_cleanup(void); +extern unsigned short fe_m5_runtime_owned_slice(unsigned long n); +extern unsigned short fe_m5_runtime_replace_field(void); +extern unsigned short fe_m5_runtime_loop_cleanup(void); +extern unsigned short fe_m5_runtime_try_cleanup(void); +extern unsigned short fe_m5_runtime_field_order(void); +extern unsigned short fe_m5_runtime_defer_order(void); +extern unsigned short fe_m5_runtime_match_cleanup(unsigned char flag); +extern unsigned short fe_m5_runtime_close_once(void); +extern unsigned short fe_m5_runtime_reassign_struct(void); static void *live_ptrs[64]; static unsigned live_count; static unsigned alloc_count; static unsigned free_count; static unsigned double_free_count; +static long fail_after = -1; +static unsigned malloc_attempts; +static int track_order; +static void *order_ptrs[2]; +static unsigned order_allocs; +static unsigned order_frees; +static unsigned order_bad; void *m5_malloc(size_t size) { - void *p = malloc(size); + void *p; + if (fail_after >= 0 && (long)malloc_attempts++ == fail_after) return 0; + p = malloc(size); if (p && live_count < 64) live_ptrs[live_count++] = p; if (p) ++alloc_count; + if (p && track_order && order_allocs < 2) order_ptrs[order_allocs++] = p; return p; } @@ -28,6 +50,9 @@ void m5_free(void *p) if (!p) return; for (i = 0; i < live_count; ++i) { if (live_ptrs[i] == p) { + if (track_order && order_frees < 2 && + p != order_ptrs[1-order_frees]) ++order_bad; + if (track_order && order_frees < 2) ++order_frees; live_ptrs[i] = live_ptrs[--live_count]; ++free_count; free(p); @@ -42,11 +67,32 @@ 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; + if (fe_m5_runtime_conditional(0) != 0) return 4; + if (fe_m5_runtime_conditional(1) != 0) return 5; + if (fe_m5_runtime_argument_cleanup() != 0) return 6; + if (fe_m5_runtime_owned_slice(17) != 0) return 7; + if (fe_m5_runtime_replace_field() != 0) return 8; + if (fe_m5_runtime_loop_cleanup() != 0) return 9; + fail_after=1; + malloc_attempts=0; + if (fe_m5_runtime_try_cleanup() == 0) return 10; + fail_after=-1; + track_order=1; + order_allocs=order_frees=order_bad=0; + if (fe_m5_runtime_field_order() != 0) return 11; + track_order=0; + if (order_allocs != 2 || order_frees != 2 || order_bad != 0) return 12; + track_order=1; + order_allocs=order_frees=order_bad=0; + if (fe_m5_runtime_defer_order() != 0) return 13; + track_order=0; + if (order_allocs != 2 || order_frees != 2 || order_bad != 0) return 14; + if (fe_m5_runtime_match_cleanup(0) != 0) return 15; + if (fe_m5_runtime_match_cleanup(1) != 0) return 16; + if (fe_m5_runtime_close_once() != 0) return 17; + if (fe_m5_runtime_reassign_struct() != 0) return 18; + if (double_free_count != 0) return 19; + if (live_count != 0) return 20; + if (alloc_count != free_count) return 21; return 0; } diff --git a/fec/tests/m5/runtime.fe b/fec/tests/m5/runtime.fe index 7df03b6..38a35da 100644 --- a/fec/tests/m5/runtime.fe +++ b/fec/tests/m5/runtime.fe @@ -3,11 +3,11 @@ unit m5_runtime; fn take(p: ^i32) -> void { mem.destroy(p); } pub fn run(mode: i32) -> i32 { - var p: ^i32 = try mem.create(i32); + var p: ^i32 = try mem.create(0); defer { mem.destroy(p); } p.^ = 7; if mode == 1 { - p = try mem.create(i32); + p = try mem.create(0); p.^ = 9; return p.^; } @@ -16,12 +16,85 @@ pub fn run(mode: i32) -> i32 { return p.^ - 7; } -pub fn conditional(flag: bool) -> void { - var p: ^i32 = try mem.create(i32); +pub fn conditional(flag: bool) -> !void { + var p: ^i32 = try mem.create(0); if flag { take(p); } } -pub fn argument_cleanup() -> void { - let p: ^i32 = try mem.create(i32); +pub fn argument_cleanup() -> !void { + let p: ^i32 = try mem.create(0); take(p); } + +pub fn owned_slice(n: usize) -> !void { + let bytes: ^[]u8 = try mem.alloc_slice(u8, n); +} + +struct Holder { p: ^i32 } + +pub fn replace_field() -> !void { + let first: ^i32 = try mem.create(1); + var h: Holder = Holder{ p: first }; + let second: ^i32 = try mem.create(2); + let old: ^i32 = mem.replace(&mut h.p, second); + mem.destroy(old); +} + +pub fn loop_cleanup() -> !void { + var i: i32 = 0; + while i < 2 { + let p: ^i32 = try mem.create(i); + i += 1; + if i == 1 { continue; } + break; + } +} + +pub fn try_cleanup() -> !void { + let first: ^i32 = try mem.create(1); + let second: ^i32 = try mem.create(2); +} + +struct PairOwners { first: ^i32, second: ^i32 } + +pub fn field_order() -> !void { + let first: ^i32 = try mem.create(1); + let second: ^i32 = try mem.create(2); + let pair: PairOwners = PairOwners{ first: first, second: second }; +} + +pub fn defer_order() -> !void { + let first: ^i32 = try mem.create(1); + defer { mem.destroy(first); } + let second: ^i32 = try mem.create(2); +} + +enum Choice { A, B } + +pub fn match_cleanup(flag: bool) -> !void { + var choice: Choice = Choice.A; + if flag { choice = Choice.B; } + let p: ^i32 = try mem.create(1); + match choice { + A => { take(p); } + B => { take(p); } + } +} + +struct FileLike { + handle: i32, + fn close(self: Self) -> !void { self.handle = 0; } + fn drop(self: &mut Self) { self.handle = 0; } +} + +pub fn close_once() -> !void { + let file: FileLike = FileLike{ handle: 7 }; + try file.close(); +} + +pub fn reassign_struct() -> !void { + let first: ^i32 = try mem.create(1); + var owner: Holder = Holder{ p: first }; + let second: ^i32 = try mem.create(2); + owner = Holder{ p: second }; +}