From 631d2ebb481668b3bc9bac191ad87ce28b469f79 Mon Sep 17 00:00:00 2001 From: Sebastian Jeong Date: Sun, 16 Aug 2026 14:22:03 +0900 Subject: [PATCH] wip: advance M5 ownership cleanup --- .pi/agents/Explore.md | 36 ++++ fec/src/check.c | 182 +++++++++++++++- fec/src/emit_c.c | 402 +++++++++++++++++++++++++++++++++--- fec/src/emit_c.h | 6 + fec/src/types.c | 55 ++++- fec/src/types.h | 6 +- fec/test-dos.bat | 19 ++ fec/tests/m5/bad-destroy.fe | 5 + fec/tests/m5/bad-move.fe | 8 + fec/tests/m5/defer.fe | 5 + fec/tests/m5/owned.fe | 9 + fec/tests/m5/runtime.c | 9 + fec/tests/m5/runtime.fe | 19 ++ fec/tests/run-tests.sh | 11 + fec/vm-m1.bat | 13 ++ 15 files changed, 752 insertions(+), 33 deletions(-) create mode 100644 .pi/agents/Explore.md create mode 100644 fec/tests/m5/bad-destroy.fe create mode 100644 fec/tests/m5/bad-move.fe create mode 100644 fec/tests/m5/defer.fe create mode 100644 fec/tests/m5/owned.fe create mode 100644 fec/tests/m5/runtime.c create mode 100644 fec/tests/m5/runtime.fe diff --git a/.pi/agents/Explore.md b/.pi/agents/Explore.md new file mode 100644 index 0000000..801991f --- /dev/null +++ b/.pi/agents/Explore.md @@ -0,0 +1,36 @@ +--- +description: "Fast read-only search agent for locating code. Use it to find files by pattern (eg. \"src/components/**/*.tsx\"), grep for symbols or keywords (eg. \"API endpoints\"), or answer \"where is X defined / which files reference Y.\" Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: \"quick\" for a single targeted lookup, \"medium\" for moderate exploration, or \"very thorough\" to search across multiple locations and naming conventions." +display_name: Explore +tools: read, bash, grep, find, ls +model: gpt-5.6-luna +prompt_mode: replace +--- + +# CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS +You are a file search specialist. You excel at thoroughly navigating and exploring codebases. +Your role is EXCLUSIVELY to search and analyze existing code. You do NOT have access to file editing tools. + +You are STRICTLY PROHIBITED from: +- Creating new files +- Modifying existing files +- Deleting files +- Moving or copying files +- Creating temporary files anywhere, including /tmp +- Using redirect operators (>, >>, |) or heredocs to write to files +- Running ANY commands that change system state + +Use Bash ONLY for read-only operations: ls, git status, git log, git diff, find, cat, head, tail. + +# Tool Usage +- Use the find tool for file pattern matching (NOT the bash find command) +- Use the grep tool for content search (NOT bash grep/rg command) +- Use the read tool for reading files (NOT bash cat/head/tail) +- Use Bash ONLY for read-only operations +- Make independent tool calls in parallel for efficiency +- Adapt search approach based on thoroughness level specified + +# Output +- Use absolute file paths in all references +- Report findings as regular messages +- Do not use emojis +- Be thorough and precise \ No newline at end of file diff --git a/fec/src/check.c b/fec/src/check.c index 9563b18..28d95d9 100644 --- a/fec/src/check.c +++ b/fec/src/check.c @@ -12,6 +12,7 @@ struct FeSym { FeNode *fn; int mutable; int initialized; + int moved; FeNode *decl; }; @@ -22,11 +23,15 @@ struct FeScope { unsigned capacity; }; +static FeSym *find_symbol(FeScope *scope, const char *name); + typedef struct FeCheckerState { FeCheck *c; FeScope *scope; FeScope *globals; FeType *ret; + unsigned loop_depth; + unsigned defer_depth; } FeCheckerState; static FeType *unknown(FeCheck *c) @@ -44,6 +49,41 @@ 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) 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) || 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; + if (sym->decl) sym->decl->flags |= 0x100U; + } + } +} + static int compatible(FeType *want, FeType *got, FeNode *value) { FeNode *item; @@ -193,6 +233,7 @@ static FeSym *add_symbol(FeCheckerState *s, FeScope *scope, sym->fn = fn; sym->mutable = mutable; sym->initialized = initialized; + sym->moved = 0; sym->decl = decl; if (decl) { decl->cname = cname; @@ -217,6 +258,47 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n); static void check_match(FeCheckerState *s, FeNode *n); static void check_stmt(FeCheckerState *s, FeNode *n); +typedef struct FeFlowSlot { + FeSym *sym; + int moved; + int initialized; +} FeFlowSlot; + +static unsigned flow_capture(FeScope *scope, FeFlowSlot *slots, unsigned cap) +{ + unsigned count=0; + unsigned i; + FeScope *p; + for (p=scope; p && countparent) + for (i=0; icount && countitems[i]; + slots[count].moved=p->items[i].moved; + slots[count].initialized=p->items[i].initialized; + ++count; + } + return count; +} + +static void flow_restore(FeFlowSlot *slots, unsigned count) +{ + unsigned i; + for (i=0; imoved=slots[i].moved; + slots[i].sym->initialized=slots[i].initialized; + } +} + +static void flow_merge(FeFlowSlot *base, FeFlowSlot *left, FeFlowSlot *right, + unsigned count) +{ + 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; + } +} + static FeNode *find_const_node(FeCheck *c, const char *name) { FeNode *n; @@ -390,6 +472,7 @@ static FeType *check_struct_init(FeCheckerState *s, FeNode *n) } if (!field) { err(s->c,f->loc,"invalid enum payload field"); continue; } v=check_expr(s,f->a); + mark_moved(s,f->a,v); if (!compatible(field->type,v,f->a) && v->kind!=FE_TYPE_UNKNOWN) err(s->c,f->loc,"enum payload type mismatch"); } } else if (n->children) err(s->c,n->loc,"empty enum variant cannot have payload"); @@ -402,6 +485,7 @@ static FeType *check_struct_init(FeCheckerState *s, FeNode *n) field=fe_type_field(t,f->text); if(!field) { err(s->c,f->loc,"invalid struct field"); continue; } v=check_expr(s,f->a); + mark_moved(s,f->a,v); if(!compatible(field->type,v,f->a) && v->kind!=FE_TYPE_UNKNOWN) err(s->c,f->loc,"struct field type mismatch"); } for(i=0;ifield_count;i++) if(!has_field(n->children,t->fields[i].name)) err(s->c,n->loc,"missing struct field"); @@ -411,7 +495,7 @@ static FeType *check_struct_init(FeCheckerState *s, FeNode *n) static FeType *check_array_init(FeCheckerState *s, FeNode *n) { FeNode *x; FeType *elem=0; FeType *v; unsigned long count=0; - for(x=n->children;x;x=x->next) { v=check_expr(s,x); if(!elem) elem=v; else if(!compatible(elem,v,x)&&v->kind!=FE_TYPE_UNKNOWN) err(s->c,x->loc,"array element type mismatch"); ++count; } + for(x=n->children;x;x=x->next) { v=check_expr(s,x); mark_moved(s,x,v); if(!elem) elem=v; else if(!compatible(elem,v,x)&&v->kind!=FE_TYPE_UNKNOWN) err(s->c,x->loc,"array element type mismatch"); ++count; } if(!elem) elem=unknown(s->c); n->sem_type=fe_type_array(&s->c->types,count,elem); return n->sem_type; } @@ -446,6 +530,8 @@ 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"); if (read && !sym->initialized && !sym->fn) err(s->c, n->loc, "use of uninitialized variable"); return sym->type; @@ -545,6 +631,34 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) return a; } if (n->kind == FE_N_CALL) { + if (n->a && n->a->kind==FE_N_MEMBER && n->a->b && n->a->b->text && + strcmp(n->a->b->text,"drop")==0) { + err(c,n->loc,"drop may only be invoked by scope cleanup"); + return unknown(c); + } + 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) { + FeNode *arg=n->children; + if (strcmp(n->a->b->text,"destroy")==0) { + a=arg ? check_expr(s,arg) : unknown(c); + if (!arg || arg->next || !a || a->kind!=FE_TYPE_OWNED) + err(c,n->loc,"mem.destroy requires exactly one owned pointer"); + else + mark_moved(s,arg,a); + n->sem_type=fe_type_intern(&c->types,"void"); + 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)); + n->sem_type=fe_type_error_union(&c->types,a); + 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 && strcmp(n->a->a->text,"io")==0 && n->a->b && n->a->b->text && @@ -606,6 +720,7 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) arg = n->children; while (param && arg) { a = check_expr(s, arg); + mark_moved(s,arg,a); b = node_type(c, param->a); if (!compatible(b, a, arg) && a->kind != FE_TYPE_UNKNOWN) err(c, arg->loc, "argument type mismatch"); @@ -634,6 +749,11 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) n->sem_type=a->elem; return a->elem; } + if (a->kind == FE_TYPE_OWNED && n->b && n->b->text && + strcmp(n->b->text,"^")==0) { + n->sem_type=a->elem; + return a->elem; + } if(a->kind==FE_TYPE_STRUCT) { field=fe_type_field(a,n->b ? n->b->text : ""); if(!field) { err(c,n->loc,"unknown struct field"); return unknown(c); } @@ -684,6 +804,11 @@ static FeType *check_lvalue(FeCheckerState *s, FeNode *n, int read) n->sem_type=base->elem; return base->elem; } + if (base && base->kind == FE_TYPE_OWNED && n->b && n->b->text && + strcmp(n->b->text,"^")==0) { + n->sem_type=base->elem; + return base->elem; + } if (!lvalue_writable(s,n->a)) err(s->c,n->loc,"cannot assign through immutable value"); field=base && base->kind==FE_TYPE_STRUCT ? fe_type_field(base,n->b ? n->b->text : "") : 0; @@ -817,7 +942,7 @@ static void check_type_cycle(FeCheck *c, FeType *t) unsigned i; FeType *next; if (!t || t->kind == FE_TYPE_SLICE || t->kind == FE_TYPE_STR || - t->kind == FE_TYPE_REF || + t->kind == FE_TYPE_REF || t->kind == FE_TYPE_OWNED || t->kind == FE_TYPE_INT || t->kind == FE_TYPE_BOOL || t->kind == FE_TYPE_CHAR || t->kind == FE_TYPE_VOID || t->kind == FE_TYPE_UNKNOWN || t->kind == FE_TYPE_ERROR) return; @@ -889,6 +1014,7 @@ static void check_stmt(FeCheckerState *s, FeNode *n) err(c, n->loc, "initializer type mismatch"); if (b->kind == FE_TYPE_VOID) err(c, n->loc, "void expression cannot initialize a variable"); + mark_moved(s,n->b,b); add_symbol(s, s->scope, n->text, a, 0, 0, 1, local_cname(c, n->text ? n->text : "local"), n); break; @@ -904,6 +1030,7 @@ static void check_stmt(FeCheckerState *s, FeNode *n) err(c, n->loc, "initializer type mismatch"); if (b->kind == FE_TYPE_VOID) err(c, n->loc, "void expression cannot initialize a variable"); + mark_moved(s,n->b,b); initialized = n->b != 0; add_symbol(s, s->scope, n->text, a, 0, 1, initialized, local_cname(c, n->text ? n->text : "local"), n); @@ -913,6 +1040,7 @@ static void check_stmt(FeCheckerState *s, FeNode *n) a = check_lvalue(s, n->a, compound_operator(n->text)); if (!compatible(a, b, n->b) && b->kind != FE_TYPE_UNKNOWN) err(c, n->loc, "assignment type mismatch"); + mark_moved(s,n->b,b); sym = n->a && n->a->kind == FE_N_IDENT ? find_symbol(s->scope, n->a->text) : 0; if (sym && sym->mutable) sym->initialized = 1; @@ -924,27 +1052,67 @@ static void check_stmt(FeCheckerState *s, FeNode *n) (!s->ret || s->ret->kind!=FE_TYPE_ERROR_UNION)) err(c,n->loc,"try requires an enclosing error result"); break; - case FE_N_IF: + case FE_N_DEFER: + ++s->defer_depth; + check_stmt(s,n->a); + --s->defer_depth; + break; + case FE_N_IF: { + FeFlowSlot base[128], left[128], right[128]; + 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); check_stmt(s, n->b); - check_stmt(s, n->c); + flow_capture(s->scope,left,flow_count); + flow_restore(base,flow_count); + if (n->c) check_stmt(s, n->c); + if (n->c) flow_capture(s->scope,right,flow_count); + else { + unsigned i; + for (i=0;ia); 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); + 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_for(s,n); + if (s->loop_depth) --s->loop_depth; break; case FE_N_MATCH: check_match(s,n); break; + case FE_N_BREAK: + case FE_N_CONTINUE: + if (!s->loop_depth) err(c,n->loc,"break or continue outside loop"); + break; case FE_N_RETURN: b = n->a ? check_expr(s, n->a) : fe_type_intern(&c->types, "void"); + mark_moved(s,n->a,b); if (known(b) && b->kind == FE_TYPE_VOID && s->ret->kind != FE_TYPE_VOID) err(c, n->loc, "void expression returned from value function"); else if (known(s->ret) && known(b) && !fe_type_equal(s->ret, b) && @@ -970,6 +1138,8 @@ static void check_fn(FeCheck *c, FeNode *fn, FeScope *globals) s.globals = globals; s.scope = scope_new(&s, globals); s.ret = fn->b ? node_type(c, fn->b) : 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 = node_type(c, x->a); @@ -1047,5 +1217,7 @@ FeType *fe_check_expr_type(FeCheck *c, FeNode *n) s.scope = scope_new(&s, 0); s.globals = s.scope; s.ret = fe_type_intern(&c->types, "void"); + s.loop_depth=0; + s.defer_depth=0; return check_expr(&s, n); } diff --git a/fec/src/emit_c.c b/fec/src/emit_c.c index ff75623..ecba29f 100644 --- a/fec/src/emit_c.c +++ b/fec/src/emit_c.c @@ -2,6 +2,10 @@ #include #include +static void emit_expr(FeEmitter *e, FeNode *n); +static void emit_stmt(FeEmitter *e, FeNode *n); +static void emit_block(FeEmitter *e, FeNode *n); + static void pad(FeEmitter *e) { int i; @@ -24,6 +28,19 @@ static const char *cname(FeNode *n, const char *fallback) static void emit_one_type(FeEmitter *e, FeType *t); +static int type_needs_drop(FeType *t) +{ + unsigned i; + if (!t) return 0; + if (t->kind==FE_TYPE_OWNED) return 1; + if (t->kind==FE_TYPE_ARRAY) return type_needs_drop(t->elem); + if (t->kind==FE_TYPE_STRUCT) { + if (t->has_drop) return 1; + for (i=0;ifield_count;i++) if (type_needs_drop(t->fields[i].type)) return 1; + } + return 0; +} + static void emit_type_deps(FeEmitter *e, FeType *t) { unsigned i,j; @@ -35,6 +52,8 @@ static void emit_type_deps(FeEmitter *e, FeType *t) for (i=0;ivariant_count;i++) for (j=0;jvariants[i].field_count;j++) emit_one_type(e,t->variants[i].fields[j].type); + if (t->kind == FE_TYPE_ERROR_UNION && t->error_value) + emit_one_type(e,t->error_value); } static void emit_one_type(FeEmitter *e, FeType *t) @@ -43,7 +62,10 @@ static void emit_one_type(FeEmitter *e, FeType *t) if (t && strcmp(t->name,"io.Writer")==0) return; 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)) return; + t->kind != FE_TYPE_ARRAY && t->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; t->emit_state=1; emit_type_deps(e,t); if(t->kind==FE_TYPE_STRUCT) { @@ -55,6 +77,10 @@ static void emit_one_type(FeEmitter *e, FeType *t) } else if(t->kind==FE_TYPE_SLICE && t->cname) { fputs("typedef struct { ",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 *p, unsigned long n) { %s s; s.p=p; s.n=n; return s; }\n",t->cname,t->maker,fe_type_c_name(t->elem,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); + fputs(" v; } ;\n",e->out); } else if(t->kind==FE_TYPE_ENUM) { for(i=0;ivariant_count;i++) if(t->variants[i].field_count>1) { fprintf(e->out,"struct fe_payload_%s_%s {",t->name,t->variants[i].name); @@ -81,10 +107,95 @@ static void emit_type_defs(FeEmitter *e) for(t=e->check->types.types;t;t=t->next) emit_one_type(e,t); } +static FeNode *find_drop_method(FeEmitter *e, const char *name) +{ + FeNode *n; + FeNode *m; + for (n=e->check->ast->root ? e->check->ast->root->children : 0; n; n=n->next) + if (n->kind==FE_N_STRUCT && n->text && name && strcmp(n->text,name)==0) + for (m=n->children; m; m=m->next) + if (m->kind==FE_N_FN && m->text && strcmp(m->text,"drop")==0) + return m; + return 0; +} + +static void emit_drop_fields(FeEmitter *e, FeType *t) +{ + unsigned i; + FeType *ft; + for (i=t->field_count; i>0; --i) { + ft=t->fields[i-1].type; + 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); + fputs(") { ",e->out); + 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); + } 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) { + fprintf(e->out,"%s(&self->%s);\n",ft->drop_cname,t->fields[i-1].name); + } + } +} + +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 || t->kind==FE_TYPE_ARRAY) && + type_needs_drop(t) && t->drop_cname) + fprintf(e->out,"static void %s(%s *self);\n",t->drop_cname,t->cname); + for (t=e->check->types.types; t; t=t->next) { + if (t->kind!=FE_TYPE_STRUCT || !type_needs_drop(t) || !t->drop_cname) continue; + 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); + } + emit_drop_fields(e,t); + fputs("}\n",e->out); + } + for (t=e->check->types.types; t; t=t->next) + if (t->kind==FE_TYPE_ARRAY && type_needs_drop(t) && t->drop_cname) { + fprintf(e->out,"static void %s(%s *self) { unsigned long i; for (i=0; i<%lu; ++i) { ", + t->drop_cname,t->cname,t->length); + if (t->elem->kind==FE_TYPE_OWNED) { + fputs("if (self->a[i]) { ",e->out); + if (t->elem->elem && type_needs_drop(t->elem->elem) && t->elem->elem->drop_cname) + fprintf(e->out,"%s(self->a[i]); ",t->elem->elem->drop_cname); + fputs("free(self->a[i]); self->a[i]=0; }",e->out); + } else if (t->elem->drop_cname) + fprintf(e->out,"%s(&self->a[i]);",t->elem->drop_cname); + fputs(" } }\n",e->out); + } +} + static void emit_type_helpers(FeEmitter *e) { FeType *t; unsigned i,j; + for(t=e->check->types.types;t;t=t->next) { + if (t->kind==FE_TYPE_ERROR_UNION && t->error_value && + 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", + t->cname,t->alloc_cname,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->kind==FE_TYPE_STRUCT && t->maker) { fprintf(e->out,"static %s %s(",t->cname,t->maker); @@ -109,6 +220,7 @@ static void emit_type_helpers(FeEmitter *e) } } } + emit_drop_helpers(e); for(t=e->check->types.types;t;t=t->next) { if (t->kind==FE_TYPE_ARRAY && t->indexer) { fprintf(e->out,"static %s %s(%s x, unsigned long i) { ", @@ -501,6 +613,9 @@ static void emit_lvalue(FeEmitter *e, FeNode *n) if (n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_REF && 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_OWNED && + n->b && n->b->text && strcmp(n->b->text,"^")==0) { + fputs("(*",e->out); emit_expr(e,n->a); fputs(")",e->out); } else { emit_lvalue(e,n->a); fputc('.',e->out); fputs(n->b ? n->b->text : "member",e->out); } return; } @@ -512,6 +627,19 @@ static void emit_lvalue(FeEmitter *e, FeNode *n) emit_expr(e,n); } +static void emit_destroy_expr(FeEmitter *e, FeNode *n) +{ + fputs("(free(",e->out); emit_expr(e,n); 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); + fputs("=0",e->out); + } else { + fputs(", ",e->out); emit_lvalue(e,n); fputs("=0",e->out); + } + fputs(", 0)",e->out); +} + static FeNode *init_field(FeNode *n, const char *name) { FeNode *f; @@ -609,7 +737,15 @@ static void emit_expr(FeEmitter *e, FeNode *n) } case FE_N_UNARY: op = n->text ? n->text : ""; - if (strcmp(op, "try") == 0) { emit_expr(e,n->a); break; } + if (strcmp(op, "try") == 0) { + if (n->a && n->a->sem_type && + n->a->sem_type->kind==FE_TYPE_ERROR_UNION && + n->a->sem_type->error_value && + n->a->sem_type->error_value->kind!=FE_TYPE_VOID) { + fputc('(',e->out); emit_expr(e,n->a); fputs(").v",e->out); + } else emit_expr(e,n->a); + break; + } if (strcmp(op, "not") == 0) fputs("(!", e->out); else { fputc('(', e->out); @@ -641,6 +777,27 @@ static void emit_expr(FeEmitter *e, FeNode *n) FeVariantType *v; int special=0; if(n->text && (strcmp(n->text,"@print")==0 || strcmp(n->text,"@fprint")==0 || strcmp(n->text,"@sprint")==0)) { emit_m4_builtin(e,n); 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,"destroy")==0 && + n->children) { + emit_destroy_expr(e,n->children); + 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,"create")==0 && + n->children && n->children->kind==FE_N_IDENT) { + FeType *created=fe_type_intern(&e->check->types,n->children->text); + 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); + 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 && @@ -660,7 +817,11 @@ static void emit_expr(FeEmitter *e, FeNode *n) fputc('(', e->out); for (x = n->children; x; x = x->next) { if (x != n->children) fputs(", ", e->out); - emit_expr(e, x); + if ((x->flags & 0x100U) && x->kind==FE_N_IDENT && + x->sem_type && x->sem_type->kind==FE_TYPE_OWNED) { + fputs("(fe_live_",e->out); fputs(cname(x,"owned"),e->out); + fputs("=0, ",e->out); emit_expr(e,x); fputc(')',e->out); + } else emit_expr(e, x); } fputc(')', e->out); } @@ -674,6 +835,9 @@ static void emit_expr(FeEmitter *e, FeNode *n) else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_REF && 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_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_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); } break; @@ -701,6 +865,89 @@ static void emit_decl(FeEmitter *e, FeNode *n) } else emit_expr(e,n->b); } 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) { + pad(e); fputs("unsigned char fe_live_",e->out); + fputs(cname(n,"owned"),e->out); fputs("=0;\n",e->out); + } +} + +static void emit_owned_live(FeEmitter *e, FeNode *n, int value) +{ + if (n && n->sem_type && n->sem_type->kind==FE_TYPE_OWNED) { + pad(e); fputs("fe_live_",e->out); fputs(cname(n,"owned"),e->out); + fprintf(e->out,"=%d;\n",value); + } +} + +static void emit_value_drop(FeEmitter *e, FeNode *n) +{ + FeType *t=n ? n->sem_type : 0; + if (!n || !t || !type_needs_drop(t) || (n->flags & 0x100U) || + (n->flags & 0x200U)) return; + 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) { + fprintf(e->out,"%s(%s); ",t->elem->drop_cname,cname(n,"owned")); + } + fputs("free(",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")); + } +} + +static void emit_cleanup_block(FeEmitter *e, FeNode *n) +{ + FeNode *x; + unsigned count=0; + unsigned index; + unsigned seen=0xffffffffU; + unsigned depth; + /* A defer becomes active only after its declaration statement was + reached. In particular, a failing initializer must not run a later + defer merely because it shares this AST block. */ + for (depth=0; depthblock_depth; ++depth) + if (e->block_stack[depth]==n) { seen=e->block_seen[depth]; break; } + for (x=n ? n->children : 0, index=0; x; x=x->next, ++index) + if (indexkind==FE_N_DEFER || x->kind==FE_N_LET || x->kind==FE_N_VAR)) ++count; + while (count) { + index=0; + for (x=n->children; x; x=x->next) + if ((x->kind==FE_N_DEFER || x->kind==FE_N_LET || x->kind==FE_N_VAR) && + index++==count-1) { + if (x->kind==FE_N_DEFER) emit_stmt(e,x->a); else emit_value_drop(e,x); + break; + } + --count; + } +} + +static void emit_cleanup_to(FeEmitter *e, unsigned floor) +{ + unsigned i; + for (i=e->block_depth; i>floor; --i) emit_cleanup_block(e,e->block_stack[i-1]); +} + +static void emit_cleanup_all(FeEmitter *e) +{ + emit_cleanup_to(e,0); +} + +static void emit_error_return(FeEmitter *e, const char *error_expr) +{ + if (e->current_ret && e->current_ret->kind==FE_TYPE_ERROR_UNION && + e->current_ret->error_value && e->current_ret->error_value->kind!=FE_TYPE_VOID) { + fputs("return ",e->out); fputs(e->current_ret->maker,e->out); + fputs("(",e->out); fputs(error_expr,e->out); fputs(", (",e->out); + fputs(fe_type_c_name(e->current_ret->error_value,e->pointer_bits),e->out); + fputs(")0);\n",e->out); + } else { + fputs("return ",e->out); fputs(error_expr,e->out); fputs(";\n",e->out); + } } static void emit_block(FeEmitter *e, FeNode *n) @@ -718,12 +965,30 @@ static void emit_block(FeEmitter *e, FeNode *n) pad(e); fputs("{\n", e->out); ++e->indent; + if (e->block_depth<32U) { + e->block_stack[e->block_depth]=n; + e->block_seen[e->block_depth]=0; + ++e->block_depth; + } /* C89 requires declarations before statements in each actual block. */ 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); - for (x = n->children; x; x = x->next) emit_stmt(e, x); + 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); + } + { + unsigned seen=0; + for (x = n->children; x; x = x->next) { + ++seen; + if (e->block_depth) e->block_seen[e->block_depth-1]=seen; + emit_stmt(e, x); + } + } --e->indent; + emit_cleanup_block(e,n); + if (e->block_depth) --e->block_depth; if (e->fallthrough_block==n) { pad(e); fputs("return 0;\n",e->out); @@ -765,6 +1030,37 @@ static void emit_match(FeEmitter *e, FeNode *n, int value_context) } } +static int try_has_value(FeNode *n) +{ + return n && n->kind==FE_N_UNARY && n->text && strcmp(n->text,"try")==0 && + n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_ERROR_UNION && + n->a->sem_type->error_value && n->a->sem_type->error_value->kind!=FE_TYPE_VOID; +} + +static void emit_try_statement(FeEmitter *e, FeNode *try_node, FeNode *target) +{ + char temp[40]; + char error[48]; + FeType *result=try_node->a->sem_type; + sprintf(temp,"fe_try_%u",e->temp_serial++); + sprintf(error,"%s.e",temp); + pad(e); fputs("{ ",e->out); fputs(fe_type_c_name(result,e->pointer_bits),e->out); + fputc(' ',e->out); fputs(temp,e->out); fputs(" = ",e->out); + emit_expr(e,try_node->a); fputs("; if (",e->out); fputs(temp,e->out); + fputs(".e) {\n",e->out); ++e->indent; + emit_cleanup_all(e); + pad(e); emit_error_return(e,error); + --e->indent; pad(e); fputs("} ",e->out); + if (target) { + if (target->kind==FE_N_LET || target->kind==FE_N_VAR) + fputs(cname(target,"fe_local"),e->out); + else + emit_lvalue(e,target); + fputs(" = ",e->out); fputs(temp,e->out); fputs(".v;\n",e->out); + } + fputs("}\n",e->out); +} + static void emit_stmt(FeEmitter *e, FeNode *n) { if (!n) return; @@ -776,46 +1072,91 @@ static void emit_stmt(FeEmitter *e, FeNode *n) case FE_N_LET: case FE_N_VAR: if (n->b) { - pad(e); - fputs(cname(n, "fe_local"), e->out); - fputs(" = ", e->out); - emit_expr(e, n->b); - fputs(";\n", e->out); + if (try_has_value(n->b)) emit_try_statement(e,n->b,n); + else { + pad(e); + fputs(cname(n, "fe_local"), e->out); + fputs(" = ", e->out); + emit_expr(e, n->b); + fputs(";\n", e->out); + } + emit_owned_live(e,n,1); } break; case FE_N_ASSIGN: + if (n->a && n->a->kind==FE_N_IDENT && n->a->sem_type && + 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) && + 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); + fputs("=0; }\n",e->out); + } pad(e); emit_lvalue(e, n->a); fputc(' ', e->out); fputs(n->text ? n->text : "=", e->out); fputs(" ", e->out); - emit_expr(e, n->b); + if (n->b && (n->b->flags & 0x100U) && n->b->kind==FE_N_IDENT && + n->b->sem_type && n->b->sem_type->kind==FE_TYPE_OWNED) { + fputs("(fe_live_",e->out); fputs(cname(n->b,"owned"),e->out); + fputs("=0, ",e->out); emit_expr(e,n->b); fputc(')',e->out); + } else emit_expr(e, n->b); fputs(";\n", e->out); + if (n->a && n->a->kind==FE_N_IDENT) emit_owned_live(e,n->a,1); break; case FE_N_EXPR_STMT: - 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); - emit_expr(e,n->a->a); - fputs(") != 0) return fe_m4_error;\n",e->out); + if (try_has_value(n->a)) { + emit_try_statement(e,n->a,0); } else { - emit_expr(e, n->a); - fputs(";\n", e->out); + 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); + 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); + --e->indent; + pad(e); fputs("}\n",e->out); + } else { + emit_expr(e, n->a); + fputs(";\n", e->out); + } + } + break; + case FE_N_BREAK: + case FE_N_CONTINUE: + if (e->loop_depth) { + emit_cleanup_to(e,e->loop_floor[e->loop_depth-1]); + pad(e); fputs(n->kind==FE_N_BREAK ? "break;\n" : "continue;\n",e->out); } break; case FE_N_RETURN: - pad(e); if (n->a && n->a->kind == FE_N_MATCH) { emit_match(e,n->a,1); break; } - fputs("return", e->out); - if (n->a) { - fputc(' ', e->out); - emit_expr(e, n->a); + /* Evaluate before cleanup: `return p.^` must not dereference p after + its owned cleanup has run. The block-local temporary is declared + before all statements to retain C89 declaration ordering. */ + if (n->a && e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { + pad(e); fputs("fe_return_value = ",e->out); + if ((n->a->flags & 0x100U) && n->a->kind==FE_N_IDENT && + n->a->sem_type && n->a->sem_type->kind==FE_TYPE_OWNED) { + fputs("(fe_live_",e->out); fputs(cname(n->a,"owned"),e->out); + fputs("=0, ",e->out); emit_expr(e,n->a); fputc(')',e->out); + } else emit_expr(e,n->a); + fputs(";\n",e->out); } - fputs(";\n", e->out); + emit_cleanup_all(e); + pad(e); fputs("return",e->out); + if (n->a) fputs(" fe_return_value",e->out); + fputs(";\n",e->out); break; case FE_N_IF: pad(e); @@ -836,12 +1177,15 @@ static void emit_stmt(FeEmitter *e, FeNode *n) fputs("while (", e->out); emit_expr(e, n->a); fputs(") ", e->out); + if (e->loop_depth<16U) e->loop_floor[e->loop_depth++]=e->block_depth; if (n->b && n->b->kind == FE_N_BLOCK) emit_block(e, n->b); else emit_block(e, 0); + if (e->loop_depth) --e->loop_depth; fputc('\n', e->out); break; case FE_N_FOR: pad(e); fputs("{\n",e->out); ++e->indent; + if (e->loop_depth<16U) e->loop_floor[e->loop_depth++]=e->block_depth; if (n->c) { pad(e); fputs("unsigned long ",e->out); fputs(cname(n,"fe_index"),e->out); fputs(";\n",e->out); pad(e); fputs(cname(n,"fe_index"),e->out); fputs(" = ",e->out); emit_expr(e,n->a); fputs(";\n",e->out); @@ -869,7 +1213,9 @@ static void emit_stmt(FeEmitter *e, FeNode *n) fputc('&',e->out); if(mutable_iter) fputs("(*",e->out); fputs(temp,e->out); if(mutable_iter) fputs(")",e->out); if(bt && bt->kind==FE_TYPE_ARRAY) fputs(".a[fe_i]",e->out); else fputs(".p[fe_i]",e->out); fputs("; ",e->out); emit_block(e,n->b); fputs(" } }\n",e->out); } - --e->indent; pad(e); fputs("}\n",e->out); break; + --e->indent; + if (e->loop_depth) --e->loop_depth; + pad(e); fputs("}\n",e->out); break; case FE_N_MATCH: emit_match(e,n,0); break; default: @@ -899,12 +1245,15 @@ static void emit_fn(FeEmitter *e, FeNode *fn, int prototype) fputc(')', e->out); if (prototype) fputs(";\n", e->out); else { + FeType *old_ret=e->current_ret; + e->current_ret=fn->sem_type; fputs(" ", e->out); if (fn->sem_type && fn->sem_type->kind==FE_TYPE_ERROR_UNION && fn->sem_type->error_value && fn->sem_type->error_value->kind==FE_TYPE_VOID) e->fallthrough_block=fn->c; emit_block(e, fn->c); + e->current_ret=old_ret; fputc('\n', e->out); } } @@ -933,6 +1282,9 @@ void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check, e->no_checks = no_checks; e->temp_serial = 0; e->fallthrough_block = 0; + e->block_depth = 0; + e->loop_depth = 0; + e->current_ret = 0; } void fe_emit_c_program(FeEmitter *e) diff --git a/fec/src/emit_c.h b/fec/src/emit_c.h index bc927a5..93a9bca 100644 --- a/fec/src/emit_c.h +++ b/fec/src/emit_c.h @@ -11,6 +11,12 @@ typedef struct FeEmitter { int no_checks; unsigned temp_serial; FeNode *fallthrough_block; + FeNode *block_stack[32]; + unsigned block_seen[32]; + unsigned block_depth; + unsigned loop_floor[16]; + unsigned loop_depth; + FeType *current_ret; } FeEmitter; void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check, diff --git a/fec/src/types.c b/fec/src/types.c index 3d77341..f039b6d 100644 --- a/fec/src/types.c +++ b/fec/src/types.c @@ -19,10 +19,13 @@ static FeType *new_type(FeTypeCtx *ctx, const char *name, FeTypeKind kind) t->slicer = 0; t->full_slicer = 0; t->tail_slicer = 0; + t->drop_cname = 0; + t->alloc_cname = 0; t->bits = 0; t->is_unsigned = 0; t->packed = 0; t->is_error = 0; + t->has_drop = 0; t->length = 0; t->size = 0; t->align = 1; @@ -125,6 +128,7 @@ FeType *fe_type_array(FeTypeCtx *ctx, unsigned long length, FeType *elem) t->elem = elem; t->cname = generated_name(ctx, "struct fe_arr_", "type"); t->maker = generated_name(ctx, "fe_make_arr_", "type"); + t->drop_cname = generated_name(ctx, "fe_drop_arr_", "type"); t->indexer = generated_name(ctx, "fe_idx_arr_", "type"); t->slicer = generated_name(ctx, "fe_slice_arr_", "type"); t->full_slicer = generated_name(ctx, "fe_full_arr_", "type"); @@ -166,6 +170,19 @@ FeType *fe_type_ref(FeTypeCtx *ctx, FeType *elem, int mutable) return t; } +FeType *fe_type_owned(FeTypeCtx *ctx, FeType *elem) +{ + char key[128]; + FeType *t; + sprintf(key,"^%s",elem ? elem->name : "?"); + t=fe_type_intern(ctx,key); + if(t->kind==FE_TYPE_UNKNOWN) { + t->kind=FE_TYPE_OWNED; + t->elem=elem; + } + return t; +} + FeType *fe_type_error_union(FeTypeCtx *ctx, FeType *value) { char key[128]; @@ -175,6 +192,11 @@ FeType *fe_type_error_union(FeTypeCtx *ctx, FeType *value) if(t->kind==FE_TYPE_UNKNOWN) { t->kind=FE_TYPE_ERROR_UNION; t->error_value=value; + if (value && value->kind != FE_TYPE_VOID) { + t->cname=generated_name(ctx,"struct fe_result_","value"); + t->maker=generated_name(ctx,"fe_make_result_","value"); + t->alloc_cname=generated_name(ctx,"fe_alloc_result_","value"); + } } return t; } @@ -192,6 +214,9 @@ FeType *fe_type_declare_struct(FeTypeCtx *ctx, const FeNode *node, int packed) if (t->kind == FE_TYPE_STRUCT) return t; t->kind = FE_TYPE_STRUCT; t->packed = packed; + for (f = node->children; f; f = f->next) + if (f->kind==FE_N_FN && f->text && strcmp(f->text,"drop")==0) + t->has_drop=1; cname = (char *)fe_arena_alloc(ctx->arena, (unsigned long)strlen("struct fe_") + strlen(ctx->unit_name) + strlen(node->text) + 2UL); @@ -202,6 +227,7 @@ FeType *fe_type_declare_struct(FeTypeCtx *ctx, const FeNode *node, int packed) strcat(cname, node->text); t->cname = cname; t->maker = generated_name(ctx, "fe_make_", node->text); + t->drop_cname = generated_name(ctx, "fe_drop_", node->text); for (f = node->children; f; f = f->next) if (f->kind == FE_N_FIELD) ++count; t->field_count = count; @@ -337,7 +363,15 @@ static void layout_type(FeTypeCtx *ctx, FeType *t) if (t->kind == FE_TYPE_VOID || t->kind == FE_TYPE_UNKNOWN || t->kind == FE_TYPE_ERROR) { t->size = 0; t->align = 1; t->cycle_state = 2; return; } if (t->kind == FE_TYPE_ERROR_UNION) { - t->size = 2; t->align = ctx->pointer_bits == 16 ? 1U : 2U; + if (t->error_value && t->error_value->kind != FE_TYPE_VOID) { + layout_type(ctx,t->error_value); + t->align=ctx->pointer_bits==16 ? 1U : fe_type_align(t->error_value); + t->size=round_up(2UL,t->align)+fe_type_size(t->error_value); + t->size=round_up(t->size,t->align); + } else { + t->size=2; + t->align=ctx->pointer_bits==16 ? 1U : 2U; + } t->cycle_state = 2; return; } if (t->kind == FE_TYPE_BOOL || t->kind == FE_TYPE_CHAR) { @@ -354,6 +388,11 @@ static void layout_type(FeTypeCtx *ctx, FeType *t) t->align = ctx->pointer_bits == 16 ? 1U : 4U; t->cycle_state = 2; return; } + if (t->kind == FE_TYPE_OWNED) { + t->size = ctx->pointer_bits == 16 ? 2UL : 4UL; + t->align = ctx->pointer_bits == 16 ? 1U : 4U; + t->cycle_state = 2; return; + } if (t->kind == FE_TYPE_SLICE || t->kind == FE_TYPE_STR) { t->size = ctx->pointer_bits == 16 ? 4UL : 8UL; t->align = ctx->pointer_bits == 16 ? 1U : 4U; @@ -449,6 +488,8 @@ FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node) strcmp(node->text, "&mut") == 0)) return fe_type_ref(ctx, fe_type_from_ast(ctx,node->a), strcmp(node->text,"&mut") == 0); + if (node->text && strcmp(node->text,"^")==0) + return fe_type_owned(ctx,fe_type_from_ast(ctx,node->a)); if (node->text && strcmp(node->text, "[") == 0) { if (node->a) { if (node->a->kind == FE_N_LITERAL && node->a->text) @@ -495,7 +536,11 @@ const char *fe_type_c_name(const FeType *t, unsigned pointer_bits) if (!t) return "long"; if (t->cname) return t->cname; if (t->kind == FE_TYPE_VOID) return "void"; - if (t->kind == FE_TYPE_ERROR_UNION) return "unsigned short"; + if (t->kind == FE_TYPE_ERROR_UNION) { + if (t->error_value && t->error_value->kind != FE_TYPE_VOID && t->cname) + return t->cname; + return "unsigned short"; + } if (t->kind == FE_TYPE_BOOL || t->kind == FE_TYPE_CHAR) return "unsigned char"; if (t->kind == FE_TYPE_REF) { static char ref_name[128]; @@ -509,6 +554,12 @@ const char *fe_type_c_name(const FeType *t, unsigned pointer_bits) } return ref_name; } + if (t->kind == FE_TYPE_OWNED) { + static char owned_name[128]; + strcpy(owned_name,fe_type_c_name(t->elem,pointer_bits)); + strcat(owned_name," *"); + return owned_name; + } if (t->kind != FE_TYPE_INT) return "long"; if (strcmp(t->name, "usize") == 0) return pointer_bits == 16 ? "unsigned short" : "unsigned long"; if (strcmp(t->name, "isize") == 0) return pointer_bits == 16 ? "short" : "long"; diff --git a/fec/src/types.h b/fec/src/types.h index 16fa67e..4acab3c 100644 --- a/fec/src/types.h +++ b/fec/src/types.h @@ -6,7 +6,7 @@ typedef enum FeTypeKind { FE_TYPE_ERROR, FE_TYPE_ERROR_UNION, FE_TYPE_VOID, FE_TYPE_BOOL, FE_TYPE_CHAR, FE_TYPE_INT, FE_TYPE_STRUCT, FE_TYPE_ENUM, FE_TYPE_ARRAY, FE_TYPE_SLICE, FE_TYPE_STR, - FE_TYPE_REF, FE_TYPE_UNKNOWN + FE_TYPE_REF, FE_TYPE_OWNED, FE_TYPE_UNKNOWN } FeTypeKind; typedef struct FeFieldType FeFieldType; @@ -37,10 +37,13 @@ struct FeType { char *slicer; char *full_slicer; char *tail_slicer; + char *drop_cname; + char *alloc_cname; unsigned bits; int is_unsigned; int packed; int is_error; + int has_drop; unsigned long length; unsigned long size; unsigned align; @@ -71,6 +74,7 @@ FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node); FeType *fe_type_array(FeTypeCtx *ctx, unsigned long length, FeType *elem); FeType *fe_type_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); FeType *fe_type_declare_struct(FeTypeCtx *ctx, const FeNode *node, int packed); FeType *fe_type_declare_enum(FeTypeCtx *ctx, const FeNode *node); diff --git a/fec/test-dos.bat b/fec/test-dos.bat index acc08bb..5418842 100644 --- a/fec/test-dos.bat +++ b/fec/test-dos.bat @@ -227,6 +227,25 @@ fec.exe --target=bits32 --emit-c TESTS\M4\BAD-OPEN.FE -o TESTS\M4\BAD-OPEN.C > n if not errorlevel 1 goto test_fail fec.exe --target=bits32 --emit-c TESTS\M4\BAD-CLS.FE -o TESTS\M4\BAD-CLS.C > nul if not errorlevel 1 goto test_fail +fec.exe --target=bits32 --emit-c TESTS\M5\DEFER.FE -o TESTS\M5\DEFER.C > nul +if errorlevel 1 goto test_fail +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 +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 +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 +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 +if errorlevel 1 goto test_fail +TESTS\M5\RUNTIME.EXE +if errorlevel 1 goto test_fail echo OK>TEST.OK cd C:\FEC diff --git a/fec/tests/m5/bad-destroy.fe b/fec/tests/m5/bad-destroy.fe new file mode 100644 index 0000000..bc60537 --- /dev/null +++ b/fec/tests/m5/bad-destroy.fe @@ -0,0 +1,5 @@ +unit m5_bad_destroy; + +fn bad(x: i32) -> void { + mem.destroy(x); +} diff --git a/fec/tests/m5/bad-move.fe b/fec/tests/m5/bad-move.fe new file mode 100644 index 0000000..6614cff --- /dev/null +++ b/fec/tests/m5/bad-move.fe @@ -0,0 +1,8 @@ +unit m5_bad_move; + +fn take(p: ^i32) -> void { mem.destroy(p); } + +fn twice(p: ^i32) -> void { + take(p); + take(p); +} diff --git a/fec/tests/m5/defer.fe b/fec/tests/m5/defer.fe new file mode 100644 index 0000000..b21cbe3 --- /dev/null +++ b/fec/tests/m5/defer.fe @@ -0,0 +1,5 @@ +unit m5_defer; + +pub fn cleanup(p: ^i32) -> void { + defer { mem.destroy(p); } +} diff --git a/fec/tests/m5/owned.fe b/fec/tests/m5/owned.fe new file mode 100644 index 0000000..7f7a553 --- /dev/null +++ b/fec/tests/m5/owned.fe @@ -0,0 +1,9 @@ +unit m5_owned; + +fn main() -> void { + var p: ^i32 = try mem.create(i32); + p = try mem.create(i32); + p.^ = 7; + let value: i32 = p.^; + defer { mem.destroy(p); } +} diff --git a/fec/tests/m5/runtime.c b/fec/tests/m5/runtime.c new file mode 100644 index 0000000..2f3a110 --- /dev/null +++ b/fec/tests/m5/runtime.c @@ -0,0 +1,9 @@ +extern long fe_m5_runtime_run(long mode); + +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; + return 0; +} diff --git a/fec/tests/m5/runtime.fe b/fec/tests/m5/runtime.fe new file mode 100644 index 0000000..be03a9d --- /dev/null +++ b/fec/tests/m5/runtime.fe @@ -0,0 +1,19 @@ +unit m5_runtime; + +pub fn run(mode: i32) -> i32 { + var p: ^i32 = try mem.create(i32); + defer { mem.destroy(p); } + p.^ = 7; + if mode == 1 { + p = try mem.create(i32); + p.^ = 9; + return p.^; + } + while true { + break; + } + if mode == 2 { + return 0; + } + return p.^ - 7; +} diff --git a/fec/tests/run-tests.sh b/fec/tests/run-tests.sh index 5d211ac..9f159a7 100644 --- a/fec/tests/run-tests.sh +++ b/fec/tests/run-tests.sh @@ -91,3 +91,14 @@ for f in bad-many bad-open bad-cls; do fi done echo "M4 tests: formatting builtins passed" + +for f in defer owned; do + "$root"/fec --target=bits32 --emit-c "$root"/tests/m5/$f.fe -o "$m4tmp/m5-$f.c" +done +for f in bad-move bad-destroy; do + if "$root"/fec --target=bits32 --emit-c "$root"/tests/m5/$f.fe -o "$m4tmp/m5-$f.c" >/dev/null 2>/dev/null; then + echo "FAIL (accepted M5 ownership error): $f.fe" + exit 1 + fi +done +echo "M5 tests: owned move/defer checks passed" diff --git a/fec/vm-m1.bat b/fec/vm-m1.bat index b5755f1..e6e76a3 100644 --- a/fec/vm-m1.bat +++ b/fec/vm-m1.bat @@ -9,6 +9,7 @@ if not exist C:\FEC\TESTS\FAIL md C:\FEC\TESTS\FAIL if not exist C:\FEC\TESTS\M2 md C:\FEC\TESTS\M2 if not exist C:\FEC\TESTS\M3 md C:\FEC\TESTS\M3 if not exist C:\FEC\TESTS\M4 md C:\FEC\TESTS\M4 +if not exist C:\FEC\TESTS\M5 md C:\FEC\TESTS\M5 if exist C:\FEC\VM.FAIL del C:\FEC\VM.FAIL if exist C:\FEC\STAGE.FAIL del C:\FEC\STAGE.FAIL @@ -169,6 +170,18 @@ copy D:\FEC\TESTS\M4\BAD-OPEN.FE C:\FEC\TESTS\M4\BAD-OPEN.FE > nul if errorlevel 1 goto stage_fail copy D:\FEC\TESTS\M4\BAD-CLS.FE C:\FEC\TESTS\M4\BAD-CLS.FE > nul if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M5\DEFER.FE C:\FEC\TESTS\M5\DEFER.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M5\OWNED.FE C:\FEC\TESTS\M5\OWNED.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M5\BAD-MOVE.FE C:\FEC\TESTS\M5\BAD-MOVE.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M5\BAD-DES~1.FE C:\FEC\TESTS\M5\BAD-DES.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M5\RUNTIME.FE C:\FEC\TESTS\M5\RUNTIME.FE > nul +if errorlevel 1 goto stage_fail +copy D:\FEC\TESTS\M5\RUNTIME.C C:\FEC\TESTS\M5\RUNTIME.C > nul +if errorlevel 1 goto stage_fail call C:\FEC\TEST-DOS.BAT if exist C:\FEC\TEST.OK goto vm_success