From 3b8c8ac67468d64ca1e2dfc64c93b46d5ed49cd7 Mon Sep 17 00:00:00 2001 From: Sebastian Jeong Date: Mon, 17 Aug 2026 01:49:02 +0900 Subject: [PATCH] refactor: unify M7 lowering and C emission emitcm7.c was the same wrapper trick as the checker: nineteen #define renames, a textual include of emit_c.c, and a per-unit feature scan choosing between two emitters. Five of the M7 halves fell through to their M6 counterpart, so those become the single entry point with the old body renamed to *_core; the other thirteen never delegated at all and simply replace the M6 version. The scan is gone from both places it was used -- the program entry and the expression emitter, whose switch already ended in a default that delegates. fe_emit_c_program_core went with the dispatch that was its only caller. emitcm7.c is deleted and the build compiles emit_c.c directly. Nothing in the compiler now selects an implementation by looking for `?` or `!` in a unit. This is the pair to the checker commit; together they end the two-engine split that produced four of the five defects fixed while getting M7 to pass. --- fec/Makefile | 2 +- fec/build-dos.bat | 4 +- fec/src/emit_c.c | 1632 ++++++++++++++++++++++++++++++++++++--------- fec/src/emitcm7.c | 1420 --------------------------------------- 4 files changed, 1325 insertions(+), 1733 deletions(-) delete mode 100644 fec/src/emitcm7.c diff --git a/fec/Makefile b/fec/Makefile index 92e678b..a6eb4c5 100644 --- a/fec/Makefile +++ b/fec/Makefile @@ -1,7 +1,7 @@ CC ?= cc CFLAGS ?= -O2 -Wall -Wextra -std=c89 CPPFLAGS ?= -Isrc -SRC = src/arena.c src/diag.c src/lexer.c src/ast.c src/parser.c src/types.c src/m7.c src/own.c src/check.c src/lower.c src/emitcm7.c src/driver.c +SRC = src/arena.c src/diag.c src/lexer.c src/ast.c src/parser.c src/types.c src/m7.c src/own.c src/check.c src/lower.c src/emit_c.c src/driver.c OBJ = $(SRC:.c=.o) .PHONY: all clean dos-build diff --git a/fec/build-dos.bat b/fec/build-dos.bat index 627336d..733cbea 100644 --- a/fec/build-dos.bat +++ b/fec/build-dos.bat @@ -31,8 +31,8 @@ wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=check.obj src\check.c if errorlevel 1 goto build_fail wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=lower.obj src\lower.c if errorlevel 1 goto build_fail -rem Use an unambiguous short object name for the M7 emitter source. -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=emitc.obj src\emitcm7.c +rem Use an unambiguous short object name for the emitter source. +wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=emitc.obj src\emit_c.c if errorlevel 1 goto build_fail wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=driver.obj src\driver.c if errorlevel 1 goto build_fail diff --git a/fec/src/emit_c.c b/fec/src/emit_c.c index 6d44e33..53c37d7 100644 --- a/fec/src/emit_c.c +++ b/fec/src/emit_c.c @@ -2,8 +2,8 @@ #include #include -static void emit_expr(FeEmitter *e, FeNode *n); -static void emit_stmt(FeEmitter *e, FeNode *n); +static void emit_expr_core(FeEmitter *e, FeNode *n); +static void emit_stmt_core(FeEmitter *e, FeNode *n); static void emit_block(FeEmitter *e, FeNode *n); static void pad(FeEmitter *e) @@ -28,18 +28,6 @@ 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) { @@ -107,16 +95,6 @@ static void emit_one_type(FeEmitter *e, FeType *t) t->emit_state=2; } -static void emit_type_defs(FeEmitter *e) -{ - FeType *t; - /* Every fixed array has a slice conversion helper, even if this unit - only indexes the array. Intern those result types before emission so - their typedefs are present before helper definitions. */ - for(t=e->check->types.types;t;t=t->next) - if(t->kind==FE_TYPE_ARRAY) fe_type_slice(&e->check->types,t->elem); - 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) { @@ -348,6 +326,15 @@ static int node_uses_m4(FeNode *n) static void emit_expr(FeEmitter *e, FeNode *n); static void emit_stmt(FeEmitter *e, FeNode *n); +static void emit_value_drop(FeEmitter *e, FeNode *n); +static void emit_cleanup_block(FeEmitter *e, FeNode *n); +static void emit_cleanup_to(FeEmitter *e, unsigned depth); +static void emit_param_cleanup(FeEmitter *e); +static void emit_error_return(FeEmitter *e, FeNode *n); +static void emit_fn(FeEmitter *e, FeNode *fn, int prototype); +static void emit_main_wrapper(FeEmitter *e, FeNode *fn); +static void emit_type_defs(FeEmitter *e); +static void emit_match(FeEmitter *e, FeNode *n, int value_context); static int stmt_definitely_returns(FeNode *n); @@ -645,32 +632,6 @@ static void emit_m4_builtin(FeEmitter *e, FeNode *n) (void)count; } -static void emit_lvalue(FeEmitter *e, FeNode *n) -{ - FeType *bt; - if (!n) { fputs("fe_bad_lvalue",e->out); return; } - if (n->kind==FE_N_IDENT) { fputs(cname(n,"fe_local"),e->out); return; } - if (n->kind==FE_N_MEMBER) { - 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_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; - } - if (n->kind==FE_N_INDEX) { - bt=n->a ? n->a->sem_type : 0; - emit_lvalue(e,n->a); fputs(bt && bt->kind==FE_TYPE_ARRAY ? ".a[" : ".p[",e->out); - emit_expr(e,n->b); fputc(']',e->out); return; - } - emit_expr(e,n); -} static void emit_destroy_expr(FeEmitter *e, FeNode *n) { @@ -757,7 +718,7 @@ static void emit_slice(FeEmitter *e, FeNode *n) emit_slice_call(e,n); } -static void emit_expr(FeEmitter *e, FeNode *n) +static void emit_expr_core(FeEmitter *e, FeNode *n) { FeNode *x; const char *op; @@ -996,181 +957,16 @@ static void emit_expr(FeEmitter *e, FeNode *n) } } -static void emit_decl(FeEmitter *e, FeNode *n) -{ - pad(e); - fputs(ctype(e, n), e->out); - fputc(' ', e->out); - fputs(cname(n, "fe_local"), e->out); - if (n->kind==FE_N_CONST && n->b) { - fputs(" = ",e->out); - if (n->b->kind==FE_N_LITERAL && n->b->text && n->b->text[0]=='"') { - fputs("{ (const unsigned char*)",e->out); - emit_c_literal(e->out,n->b->text,1); - fputs(", sizeof(",e->out); - emit_c_literal(e->out,n->b->text,1); - fputs(")-1 }",e->out); - } else emit_expr(e,n->b); - } - fputs(";\n", e->out); - if ((n->kind==FE_N_LET || n->kind==FE_N_VAR) && n->sem_type && - 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); - } -} -static void emit_owned_live(FeEmitter *e, FeNode *n, int value) -{ - 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); - } -} -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 && 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("fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fputs("=0; }\n",e->out); - } else if (t->drop_cname) { - 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); - } -} -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_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) -{ - 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) -{ - FeNode *x; - if (!n) { - pad(e); - fputs("{\n", e->out); - ++e->indent; - --e->indent; - pad(e); - fputc('}', e->out); - return; - } - 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); - 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 && 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); - } - } - 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->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); - fputs("return 0;\n",e->out); - e->fallthrough_block=0; - } - pad(e); - fputc('}', e->out); -} -static void emit_match(FeEmitter *e, FeNode *n, int value_context) +static void emit_match_core(FeEmitter *e, FeNode *n, int value_context) { FeNode *arm; FeType *t=n->a ? n->a->sem_type : 0; @@ -1233,7 +1029,7 @@ static void emit_try_statement(FeEmitter *e, FeNode *try_node, FeNode *target) fputs("}\n",e->out); } -static void emit_stmt(FeEmitter *e, FeNode *n) +static void emit_stmt_core(FeEmitter *e, FeNode *n) { if (!n) return; switch (n->kind) { @@ -1409,46 +1205,8 @@ static void emit_stmt(FeEmitter *e, FeNode *n) } } -static void emit_fn(FeEmitter *e, FeNode *fn, int prototype) -{ - FeNode *p; - const char *ret; - ret = fn->sem_type ? fe_type_c_name(fn->sem_type, e->pointer_bits) : - (fn->b ? ctype(e, fn->b) : "void"); - fputs(ret, e->out); - fputc(' ', e->out); - fputs(cname(fn, "fe_fn"), e->out); - fputc('(', e->out); - p = fn->a ? fn->a->children : 0; - if (!p) fputs("void", e->out); - while (p) { - if (p != fn->a->children) fputs(", ", 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; - } - fputc(')', e->out); - 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 && - fn->sem_type->error_value->kind==FE_TYPE_VOID) - e->fallthrough_block=fn->c; - emit_block(e, fn->c); - e->current_ret=old_ret; - e->current_fn=old_fn; - fputc('\n', e->out); - } -} -static void emit_main_wrapper(FeEmitter *e, FeNode *fn) +static void emit_main_wrapper_core(FeEmitter *e, FeNode *fn) { fputs("int main(void) {\n ", e->out); if (fn->sem_type && fn->sem_type->kind == FE_TYPE_VOID) { @@ -1478,75 +1236,1329 @@ void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check, e->current_fn = 0; } + +#include "m7.h" +#include "lower.h" + +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 emit_lvalue(FeEmitter *e, FeNode *n); + +static int type_needs_drop(FeType *t) +{ + return fe_lower_type_needs_drop(t); +} + +static const char *m7_c_type(FeEmitter *e, FeType *t) +{ + if (!t) return "long"; + if ((t->kind==FE_TYPE_ENUM && t->is_error) || + strcmp(t->name,"core.Error")==0) + return "unsigned short"; + return fe_type_c_name(t,e->pointer_bits); +} + +static char *m7_temp_name(FeEmitter *e) +{ + char number[24]; + char *p; + unsigned long len; + sprintf(number,"%u",e->temp_serial++); + len=(unsigned long)strlen("fe_m7_tmp_")+ + (unsigned long)strlen(number)+1UL; + p=(char *)fe_arena_alloc(&e->check->ast->arena,len); + if (!p) return 0; + strcpy(p,"fe_m7_tmp_"); + strcat(p,number); + return p; +} + +static int m7_needs_temp(FeNode *n) +{ + if (!n) return 0; + if (fe_m7_is_try(n)) return 1; + if (n->kind==FE_N_BINARY && fe_m7_lazy_kind(n)!=FE_M7_LAZY_NONE) + return 1; + if (n->kind==FE_N_IF && n->text && strcmp(n->text,"if let")==0) + return 1; + if (n->kind==FE_N_MATCH && n->a && n->a->sem_type && + n->a->sem_type->kind==FE_TYPE_OPTIONAL) + return 1; + return 0; +} + +static FeType *m7_temp_type(FeNode *n) +{ + if (!n) return 0; + if (fe_m7_is_try(n)) return n->a ? n->a->sem_type : 0; + if (n->kind==FE_N_BINARY) return n->a ? n->a->sem_type : 0; + if ((n->kind==FE_N_IF || n->kind==FE_N_MATCH) && n->a) + return n->a->sem_type; + return 0; +} + +static void m7_prepare_temps(FeEmitter *e, FeNode *n) +{ + FeNode *x; + if (!n) return; + if (m7_needs_temp(n) && !n->aux_cname) + n->aux_cname=m7_temp_name(e); + m7_prepare_temps(e,n->a); + m7_prepare_temps(e,n->b); + m7_prepare_temps(e,n->c); + for (x=n->children;x;x=x->next) m7_prepare_temps(e,x); +} + +static void m7_emit_temp_decls(FeEmitter *e, FeNode *n) +{ + FeNode *x; + FeType *t; + if (!n) return; + if (m7_needs_temp(n) && n->aux_cname) { + t=m7_temp_type(n); + if (t) { + pad(e); fputs(m7_c_type(e,t),e->out); fputc(' ',e->out); + fputs(n->aux_cname,e->out); fputs(";\n",e->out); + } + } + m7_emit_temp_decls(e,n->a); + m7_emit_temp_decls(e,n->b); + m7_emit_temp_decls(e,n->c); + for (x=n->children;x;x=x->next) m7_emit_temp_decls(e,x); +} + +static void m7_emit_type(FeEmitter *e, FeType *t) +{ + unsigned i; + unsigned j; + if (!t || t->emit_state) return; + if (t->kind==FE_TYPE_OPTIONAL) { + t->emit_state=1; + m7_emit_type(e,t->elem); + if (!fe_m7_optional_uses_niche(t->elem) && t->cname) { + fputs(t->cname,e->out); fputs(" { unsigned char has; ",e->out); + fputs(m7_c_type(e,t->elem),e->out); + fputs(" v; };\n",e->out); + } + t->emit_state=2; + return; + } + if (t->kind==FE_TYPE_ARRAY) m7_emit_type(e,t->elem); + if (t->kind==FE_TYPE_SLICE) m7_emit_type(e,t->elem); + if (t->kind==FE_TYPE_OWNED) m7_emit_type(e,t->elem); + if (t->kind==FE_TYPE_STRUCT) + for (i=0;ifield_count;++i) m7_emit_type(e,t->fields[i].type); + if (t->kind==FE_TYPE_ENUM) + for (i=0;ivariant_count;++i) + for (j=0;jvariants[i].field_count;++j) + m7_emit_type(e,t->variants[i].fields[j].type); + if (t->kind==FE_TYPE_ERROR_UNION) { + m7_emit_type(e,t->elem); + m7_emit_type(e,t->error_value); + } + emit_one_type(e,t); +} + +static void emit_type_defs(FeEmitter *e) +{ + FeType *t; + for (t=e->check->types.types;t;t=t->next) + if (t->kind==FE_TYPE_ARRAY) + fe_type_slice(&e->check->types,t->elem); + for (t=e->check->types.types;t;t=t->next) m7_emit_type(e,t); +} + +static void m7_emit_drop_access(FeEmitter *e, FeType *t, + const char *access) +{ + if (!t || !access || !type_needs_drop(t)) return; + if (t->kind==FE_TYPE_OWNED) { + if (t->elem && t->elem->kind==FE_TYPE_SLICE) { + fprintf(e->out,"if ((%s).p) { free((%s).p); (%s).p=0; } ", + access,access,access); + } else { + fprintf(e->out,"if (%s) { ",access); + if (t->elem && type_needs_drop(t->elem) && t->elem->drop_cname) + fprintf(e->out,"%s(%s); ",t->elem->drop_cname,access); + fprintf(e->out,"free(%s); %s=0; } ",access,access); + } + return; + } + if (t->drop_cname) + fprintf(e->out,"%s(&(%s)); ",t->drop_cname,access); +} + +static void m7_emit_drop_helpers(FeEmitter *e) +{ + FeType *t; + FeNode *method; + unsigned i; + unsigned j; + char access[256]; + for (t=e->check->types.types;t;t=t->next) + if (type_needs_drop(t) && t->drop_cname && + (t->kind==FE_TYPE_STRUCT || t->kind==FE_TYPE_ARRAY || + t->kind==FE_TYPE_OPTIONAL || t->kind==FE_TYPE_ERROR_UNION)) + fprintf(e->out,"static void %s(%s *self);\n", + t->drop_cname,m7_c_type(e,t)); + for (t=e->check->types.types;t;t=t->next) { + if (!type_needs_drop(t) || !t->drop_cname) continue; + if (t->kind==FE_TYPE_STRUCT) { + fprintf(e->out,"static void %s(%s *self) { ", + t->drop_cname,m7_c_type(e,t)); + method=find_drop_method(e,t->name); + if (method) fprintf(e->out,"%s(self); ",cname(method,"fe_drop_method")); + for (i=t->field_count;i>0;--i) { + sprintf(access,"self->%s",t->fields[i-1U].name); + m7_emit_drop_access(e,t->fields[i-1U].type,access); + } + fputs("}\n",e->out); + } else if (t->kind==FE_TYPE_ARRAY) { + fprintf(e->out,"static void %s(%s *self) { unsigned long i; for (i=0; i<%lu; ++i) { ", + t->drop_cname,m7_c_type(e,t),t->length); + strcpy(access,"self->a[i]"); + m7_emit_drop_access(e,t->elem,access); + fputs("} }\n",e->out); + } else if (t->kind==FE_TYPE_OPTIONAL) { + fprintf(e->out,"static void %s(%s *self) { ", + t->drop_cname,m7_c_type(e,t)); + if (fe_m7_optional_uses_niche(t->elem)) { + fputs("if (*self) { ",e->out); + m7_emit_drop_access(e,t->elem,"*self"); + fputs("} ",e->out); + } else { + fputs("if (self->has) { ",e->out); + m7_emit_drop_access(e,t->elem,"self->v"); + fputs("self->has=0; } ",e->out); + } + fputs("}\n",e->out); + } else if (t->kind==FE_TYPE_ERROR_UNION && t->error_value && + t->error_value->kind!=FE_TYPE_VOID) { + fprintf(e->out,"static void %s(%s *self) { if (!self->e) { ", + t->drop_cname,m7_c_type(e,t)); + m7_emit_drop_access(e,t->error_value,"self->v"); + fputs("self->e=1; } }\n",e->out); + } + } + /* Error enums are scalar codes, so their enum payload helper functions + from M3 are deliberately not emitted in the M7 path. */ + (void)j; +} + +static void m7_emit_type_helpers(FeEmitter *e) +{ + FeType *t; + FeType *st; + unsigned i; + unsigned j; + const char *ct; + FeVariantType *v; + for (t=e->check->types.types;t;t=t->next) { + if (t->kind==FE_TYPE_OPTIONAL) { + if (!fe_m7_optional_uses_niche(t->elem)) { + fprintf(e->out,"static %s %s(%s v) { %s r; r.has=1; r.v=v; return r; }\n", + m7_c_type(e,t),t->maker,m7_c_type(e,t->elem),m7_c_type(e,t)); + fprintf(e->out,"static %s %s(void) { %s r; memset(&r,0,sizeof(r)); return r; }\n", + m7_c_type(e,t),t->none_cname,m7_c_type(e,t)); + fprintf(e->out,"static %s %s(%s x) { ", + m7_c_type(e,t->elem),t->unwrap_cname,m7_c_type(e,t)); + if (!e->no_checks) fputs("if (!x.has) fe_trap_bounds(); ",e->out); + fputs("return x.v; }\n",e->out); + } else { + fprintf(e->out,"static %s %s(%s x) { ", + m7_c_type(e,t->elem),t->unwrap_cname,m7_c_type(e,t)); + if (!e->no_checks) fputs("if (!x) fe_trap_bounds(); ",e->out); + fputs("return x; }\n",e->out); + } + } + 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", + m7_c_type(e,t),t->maker,m7_c_type(e,t->error_value),m7_c_type(e,t)); + if (t->none_cname) + fprintf(e->out,"static %s %s(unsigned short e) { %s r; memset(&r,0,sizeof(r)); r.e=e; return r; }\n", + m7_c_type(e,t),t->none_cname,m7_c_type(e,t)); + 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", + m7_c_type(e,t),t->alloc_cname,m7_c_type(e,t), + m7_c_type(e,item),m7_c_type(e,item)); + } 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", + m7_c_type(e,t),t->alloc_cname, + m7_c_type(e,t->error_value->elem),m7_c_type(e,t), + m7_c_type(e,t->error_value), + m7_c_type(e,t->error_value->elem)); + } + } + } + for (t=e->check->types.types;t;t=t->next) { + if (t->replace_cname) { + ct=m7_c_type(e,t); + 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(",m7_c_type(e,t),t->maker); + for (i=0;ifield_count;i++) { + if (i) fputs(", ",e->out); + fputs(m7_c_type(e,t->fields[i].type),e->out); + fprintf(e->out," p%u",i); + } + fprintf(e->out,") { %s v;",m7_c_type(e,t)); + for (i=0;ifield_count;i++) + fprintf(e->out," v.%s=p%u;",t->fields[i].name,i); + fputs(" return v; }\n",e->out); + } else if (t->kind==FE_TYPE_ARRAY && t->maker) { + fprintf(e->out,"static %s %s(",m7_c_type(e,t),t->maker); + for (i=0;ilength;i++) { + if (i) fputs(", ",e->out); + fputs(m7_c_type(e,t->elem),e->out); + fprintf(e->out," p%u",i); + } + fprintf(e->out,") { %s v;",m7_c_type(e,t)); + for (i=0;ilength;i++) fprintf(e->out," v.a[%u]=p%u;",i,i); + fputs(" return v; }\n",e->out); + } else if (t->kind==FE_TYPE_ENUM && !t->is_error) { + for (i=0;ivariant_count;i++) { + v=&t->variants[i]; + fprintf(e->out,"static %s %s(",m7_c_type(e,t),v->maker); + for (j=0;jfield_count;j++) { + if (j) fputs(", ",e->out); + fputs(m7_c_type(e,v->fields[j].type),e->out); + fprintf(e->out," p%u",j); + } + fprintf(e->out,") { %s x; x.tag=%u;",m7_c_type(e,t),v->tag); + for (j=0;jfield_count;j++) { + if (v->field_count==1) + fprintf(e->out," x.payload.%s=p%u;",v->name,j); + else + fprintf(e->out," x.payload.%s.%s=p%u;",v->name, + v->fields[j].name,j); + } + fputs(" return x; }\n",e->out); + } + } + } + m7_emit_drop_helpers(e); + /* Reuse the mature M3 index/slice helper generator. It does not depend + on M7 drop policy and all wrapper dependencies are already emitted. */ + 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) { ", + m7_c_type(e,t->elem),t->indexer,m7_c_type(e,t)); + if (!e->no_checks) + fprintf(e->out,"if (i >= %lu) fe_trap_bounds(); ",t->length); + fputs("return x.a[i]; }\n",e->out); + if (t->slicer) { + st=fe_type_slice(&e->check->types,t->elem); + fprintf(e->out,"static %s %s(%s *x, unsigned long a, unsigned long b) { ", + m7_c_type(e,st),t->slicer,m7_c_type(e,t)); + if (!e->no_checks) + fprintf(e->out,"if (a > b || b > %lu) fe_trap_bounds(); ",t->length); + fprintf(e->out,"return %s(x->a+a,b-a); }\n",st->maker); + fprintf(e->out,"static %s %s(%s *x) { return %s(x,0,%lu); }\n", + m7_c_type(e,st),t->full_slicer,m7_c_type(e,t),t->slicer,t->length); + fprintf(e->out,"static %s %s(%s *x, unsigned long a) { return %s(x,a,%lu); }\n", + m7_c_type(e,st),t->tail_slicer,m7_c_type(e,t),t->slicer,t->length); + } + } else if (t->kind==FE_TYPE_SLICE && t->indexer) { + fprintf(e->out,"static %s %s(%s x, unsigned long i) { ", + m7_c_type(e,t->elem),t->indexer,m7_c_type(e,t)); + if (!e->no_checks) fputs("if (i >= x.n) fe_trap_bounds(); ",e->out); + fputs("return x.p[i]; }\n",e->out); + if (t->slicer) { + fprintf(e->out,"static %s %s(%s x, unsigned long a, unsigned long b) { ", + m7_c_type(e,t),t->slicer,m7_c_type(e,t)); + if (!e->no_checks) + fputs("if (a > b || b > x.n) fe_trap_bounds(); ",e->out); + fprintf(e->out,"return %s(x.p+a,b-a); }\n",t->maker); + fprintf(e->out,"static %s %s(%s x) { return %s(x,0,x.n); }\n", + m7_c_type(e,t),t->full_slicer,m7_c_type(e,t),t->slicer); + fprintf(e->out,"static %s %s(%s x, unsigned long a) { return %s(x,a,x.n); }\n", + m7_c_type(e,t),t->tail_slicer,m7_c_type(e,t),t->slicer); + } + } + } +} + +static void m7_emit_present(FeEmitter *e, FeType *opt, const char *name) +{ + if (fe_m7_optional_uses_niche(opt->elem)) { + fputs("(",e->out); fputs(name,e->out); fputs(" != 0)",e->out); + } else { + fputs(name,e->out); fputs(".has",e->out); + } +} + +static void m7_emit_payload_var(FeEmitter *e, FeType *opt, const char *name) +{ + (void)e; + fputs(name,e->out); + if (!fe_m7_optional_uses_niche(opt->elem)) fputs(".v",e->out); +} + +static void m7_emit_error_member(FeEmitter *e, FeNode *n) +{ + FeVariantType *v; + FeType *t; + t=n && n->a ? n->a->sem_type : 0; + v=t && t->kind==FE_TYPE_ENUM ? + fe_type_variant(t,n->b ? n->b->text : "") : 0; + if (v) fprintf(e->out,"%u",v->tag); + else fputs("0",e->out); +} + +static void m7_emit_raw_expr(FeEmitter *e, FeNode *n); + +static void m7_emit_contextual(FeEmitter *e, FeNode *n) +{ + FeType *ctx; + FeType *actual; + FeType *error_type; + ctx=n ? n->sem_context : 0; + actual=n ? n->sem_type : 0; + if (!ctx) { m7_emit_raw_expr(e,n); return; } + if (ctx->kind==FE_TYPE_OPTIONAL) { + if (fe_m7_is_null(n)) { + if (fe_m7_optional_uses_niche(ctx->elem)) fputs("0",e->out); + else { fputs(ctx->none_cname,e->out); fputs("()",e->out); } + return; + } + if (fe_m7_optional_uses_niche(ctx->elem)) { + m7_emit_raw_expr(e,n); + } else { + fputs(ctx->maker,e->out); fputc('(',e->out); + m7_emit_raw_expr(e,n); fputc(')',e->out); + } + return; + } + if (ctx->kind==FE_TYPE_ERROR_UNION) { + error_type=ctx->elem; + if (!error_type) error_type=fe_type_intern(&e->check->types,"core.Error"); + if (actual && fe_type_equal(actual,ctx->error_value)) { + if (ctx->error_value->kind==FE_TYPE_VOID) fputs("0",e->out); + else { + fputs(ctx->maker,e->out); fputs("(0, ",e->out); + m7_emit_raw_expr(e,n); fputc(')',e->out); + } + return; + } + if (actual && fe_type_equal(actual,error_type)) { + if (ctx->error_value->kind==FE_TYPE_VOID) + m7_emit_raw_expr(e,n); + else { + fputs(ctx->none_cname,e->out); fputc('(',e->out); + m7_emit_raw_expr(e,n); fputc(')',e->out); + } + return; + } + } + m7_emit_raw_expr(e,n); +} + +static void emit_expr(FeEmitter *e, FeNode *n) +{ + if (!n) { fputs("0",e->out); return; } + if (n->sem_context) m7_emit_contextual(e,n); + else m7_emit_raw_expr(e,n); +} + +static void emit_lvalue(FeEmitter *e, FeNode *n) +{ + FeType *bt; + if (!n) { fputs("fe_bad_lvalue",e->out); return; } + if (n->kind==FE_N_IDENT) { + fputs(cname(n,"fe_local"),e->out); + return; + } + /* A declaration names its own storage. The initializer for `let`/`var` is + emitted as a separate assignment statement, so the declaration node is + handed here as the target; without this it falls through to the raw + expression path, which emits a declaration as "0" and produces `0 = ...`. */ + if (n->kind==FE_N_LET || n->kind==FE_N_VAR || n->kind==FE_N_CONST) { + fputs(cname(n,"fe_local"),e->out); + return; + } + if (n->kind==FE_N_MEMBER) { + bt=n->a ? n->a->sem_type : 0; + if (n->text && strcmp(n->text,".?")==0) { + emit_expr(e,n); + return; + } + if ((bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) && + n->b && n->b->text && strcmp(n->b->text,"^")==0) { + fputs("(*",e->out); emit_expr(e,n->a); fputc(')',e->out); + } else if (bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) { + emit_expr(e,n->a); fputs("->",e->out); + fputs(n->b && n->b->text ? n->b->text : "member",e->out); + } else { + emit_lvalue(e,n->a); fputc('.',e->out); + fputs(n->b && n->b->text ? n->b->text : "member",e->out); + } + return; + } + if (n->kind==FE_N_INDEX) { + bt=n->a ? n->a->sem_type : 0; + emit_lvalue(e,n->a); + fputs(bt && bt->kind==FE_TYPE_ARRAY ? ".a[" : ".p[",e->out); + emit_expr(e,n->b); fputc(']',e->out); + return; + } + m7_emit_raw_expr(e,n); +} + +static void m7_emit_call(FeEmitter *e, FeNode *n) +{ + FeNode *x; + FeNode *call_param; + FeVariantType *v; + int special; + call_param=0; + special=0; + 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) { + FeNode *arg=n->children; + fputs("(free(",e->out); emit_expr(e,arg); + if (arg->sem_type && arg->sem_type->kind==FE_TYPE_OWNED && + arg->sem_type->elem && arg->sem_type->elem->kind==FE_TYPE_SLICE) + fputs(".p",e->out); + fputc(')',e->out); + if (arg->kind==FE_N_IDENT) { + fputs(", ",e->out); emit_lvalue(e,arg); + if (arg->sem_type && arg->sem_type->elem && + arg->sem_type->elem->kind==FE_TYPE_SLICE) fputs(".p=0",e->out); + else fputs("=0",e->out); + fputs(", fe_live_",e->out); fputs(cname(arg,"owned"),e->out); + fputs("=0",e->out); + } + fputs(", 0)",e->out); + return; + } + 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); + return; + } + 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) { + 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); + fputs(result->alloc_cname ? result->alloc_cname : "fe_bad_alloc",e->out); + fputc('(',e->out); emit_expr(e,n->children); fputc(')',e->out); + return; + } + 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; + fputs(result && result->alloc_cname ? result->alloc_cname : + "fe_bad_slice_alloc",e->out); + fputc('(',e->out); emit_expr(e,n->children->next); fputc(')',e->out); + return; + } + if (n->text && (strcmp(n->text,"@print")==0 || + strcmp(n->text,"@fprint")==0 || strcmp(n->text,"@sprint")==0)) { + emit_m4_builtin(e,n); + return; + } + 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); + return; + } + 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 && + !n->a->a->sem_type->is_error) { + v=fe_type_variant(n->a->a->sem_type,n->a->b ? n->a->b->text : ""); + fputs(v ? v->maker : "fe_bad_variant",e->out); + } 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); + return; + } else if (n->a) emit_expr(e,n->a); + else fputs(n->text ? n->text : "fe_builtin",e->out); + if (!special) { + if (n->sem_decl && n->sem_decl->kind==FE_N_FN && n->sem_decl->a) + call_param=n->sem_decl->a->children; + fputc('(',e->out); + for (x=n->children;x;x=x->next) { + FeType *want=call_param && call_param->a ? + fe_type_from_ast(&e->check->types,call_param->a) : 0; + if (x!=n->children) fputs(", ",e->out); + if (want && want->kind==FE_TYPE_SLICE && !want->ref_mut && + x->sem_type && x->sem_type->kind==FE_TYPE_SLICE && + x->sem_type->ref_mut) { + fputs(want->maker,e->out); fputc('(',e->out); + emit_expr(e,x); fputs(".p, ",e->out); + emit_expr(e,x); fputs(".n)",e->out); + } else emit_expr(e,x); + if (call_param) call_param=call_param->next; + } + fputc(')',e->out); + } +} + +/* `dst = ;` as a statement, avoiding a comma expression on the right. + + A consumed identifier lowers to `(fe_live_x=0, x)`. When dst is a struct, + Watcom crashes on a struct assignment whose right side is a comma expression + -- hard enough to take DOSBox-X down with it -- so clear the move flag as its + own statement and assign the plain name. */ +static void m7_emit_assign_stmt(FeEmitter *e, const char *dst, FeNode *src) +{ + if (src && src->kind==FE_N_IDENT && (src->flags & FE_OWN_NODE_CONSUMED) && + src->sem_type && type_needs_drop(src->sem_type)) { + pad(e); fputs("fe_live_",e->out); fputs(cname(src,"owned"),e->out); + fputs("=0;\n",e->out); + pad(e); fputs(dst,e->out); fputs(" = ",e->out); + fputs(cname(src,"fe_missing"),e->out); fputs(";\n",e->out); + return; + } + pad(e); fputs(dst,e->out); fputs(" = ",e->out); + emit_expr(e,src); fputs(";\n",e->out); +} + +static void m7_emit_raw_expr(FeEmitter *e, FeNode *n) +{ + FeNode *x; + FeType *bt; + FeVariantType *v; + const char *op; + FeM7LazyKind lazy; + if (!n) { fputs("0",e->out); return; } + /* No feature scan: the switch below handles the node kinds this emitter + changes and its default hands everything else to emit_expr_core, so the + same path serves a unit whether or not it mentions optionals. */ + switch (n->kind) { + case FE_N_IDENT: + if ((n->flags & FE_OWN_NODE_CONSUMED) && 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 (fe_m7_is_null(n)) fputs("0",e->out); + else emit_expr_core(e,n); + break; + case FE_N_UNARY: + op=n->text ? n->text : ""; + 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) { + fputs("(",e->out); fputs(n->aux_cname,e->out); + fputs(" = ",e->out); emit_expr(e,n->a); fputs(", ",e->out); + fputs(n->aux_cname,e->out); fputs(".v)",e->out); + } else emit_expr(e,n->a); + } else if (strcmp(op,"&")==0 || strcmp(op,"&mut")==0) { + fputs("(&",e->out); emit_lvalue(e,n->a); fputc(')',e->out); + } else if (strcmp(op,"not")==0) { + fputs("(!",e->out); emit_expr(e,n->a); fputc(')',e->out); + } else { + fputc('(',e->out); fputs(op,e->out); emit_expr(e,n->a); + fputc(')',e->out); + } + break; + case FE_N_BINARY: + lazy=fe_m7_lazy_kind(n); + if (lazy==FE_M7_LAZY_ORELSE) { + FeType *opt=n->a ? n->a->sem_type : 0; + fputs("((",e->out); fputs(n->aux_cname,e->out); fputs(" = ",e->out); + emit_expr(e,n->a); fputs("), ",e->out); + m7_emit_present(e,opt,n->aux_cname); fputs(" ? ",e->out); + if (fe_m7_optional_uses_niche(opt->elem)) fputs(n->aux_cname,e->out); + else { fputs(n->aux_cname,e->out); fputs(".v",e->out); } + fputs(" : ",e->out); emit_expr(e,n->b); fputc(')',e->out); + } else if (lazy==FE_M7_LAZY_CATCH && !n->c) { + FeType *res=n->a ? n->a->sem_type : 0; + fputs("((",e->out); fputs(n->aux_cname,e->out); fputs(" = ",e->out); + emit_expr(e,n->a); fputs("), ",e->out); + if (res && res->error_value && res->error_value->kind==FE_TYPE_VOID) { + fputs(n->aux_cname,e->out); fputs(" ? ",e->out); + emit_expr(e,n->b); fputs(" : 0)",e->out); + } else { + fputs(n->aux_cname,e->out); fputs(".e ? ",e->out); + emit_expr(e,n->b); fputs(" : ",e->out); + fputs(n->aux_cname,e->out); fputs(".v)",e->out); + } + } else if (lazy==FE_M7_LAZY_CATCH && n->c) { + fputs("0",e->out); + } else if ((n->text && (strcmp(n->text,"==")==0 || + strcmp(n->text,"!=")==0)) && + (fe_m7_is_null(n->a) || fe_m7_is_null(n->b))) { + FeNode *value=fe_m7_is_null(n->a) ? n->b : n->a; + FeType *opt=value ? value->sem_type : 0; + if (opt && opt->kind==FE_TYPE_OPTIONAL && + !fe_m7_optional_uses_niche(opt->elem)) { + fputs("(!",e->out); emit_expr(e,value); fputs(".has)",e->out); + if (strcmp(n->text,"!=")==0) { + fputs(" == 0",e->out); + } + } else { + fputc('(',e->out); emit_expr(e,value); + fputs(strcmp(n->text,"==")==0 ? " == 0)" : " != 0)",e->out); + } + } else { + op=n->text ? n->text : "+"; + fputc('(',e->out); emit_expr(e,n->a); + if (strcmp(op,"and")==0) fputs(" && ",e->out); + else if (strcmp(op,"or")==0) fputs(" || ",e->out); + else fputs(op,e->out); + emit_expr(e,n->b); fputc(')',e->out); + } + break; + case FE_N_MEMBER: + bt=n->a ? n->a->sem_type : 0; + if (n->text && strcmp(n->text,".?")==0 && bt && + bt->kind==FE_TYPE_OPTIONAL) { + fputs(bt->unwrap_cname,e->out); fputc('(',e->out); + emit_expr(e,n->a); fputc(')',e->out); + } else if (bt && bt->kind==FE_TYPE_ENUM && bt->is_error) { + m7_emit_error_member(e,n); + } else if ((bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) && + n->b && n->b->text && strcmp(n->b->text,"^")==0) { + fputs("(*",e->out); emit_expr(e,n->a); fputc(')',e->out); + } else if (bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) { + emit_expr(e,n->a); fputs("->",e->out); + fputs(n->b && n->b->text ? n->b->text : "member",e->out); + } else if (bt && bt->kind==FE_TYPE_ENUM && !bt->is_error) { + v=fe_type_variant(bt,n->b ? n->b->text : ""); + if (v) { fputs(v->maker,e->out); fputs("()",e->out); } + else fputs("0",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; + case FE_N_CALL: + m7_emit_call(e,n); + break; + case FE_N_TYPE: + if (n->text && strcmp(n->text,"as")==0) { + fputs("((",e->out); fputs(m7_c_type(e,n->sem_type),e->out); + fputc(')',e->out); emit_expr(e,n->a); fputc(')',e->out); + } else emit_expr(e,n->a); + break; + case FE_N_INDEX: + bt=n->a ? n->a->sem_type : 0; + if (n->c || !n->b) { + emit_expr_core(e,n); + } else if (bt && bt->indexer) { + fputs(bt->indexer,e->out); fputc('(',e->out); + emit_expr(e,n->a); fputs(", ",e->out); emit_expr(e,n->b); + fputc(')',e->out); + } else fputs("0",e->out); + break; + case FE_N_STRUCT_INIT: + case FE_N_ARRAY_INIT: + emit_expr_core(e,n); + break; + default: + emit_expr_core(e,n); + break; + } + (void)x; +} + +/* Emit the initializer for a `const` declaration. + + A string literal normally lowers to a maker call, but C89 requires the + initializer of an aggregate -- at file scope and for automatics alike -- to be + a constant expression, and the build runs with -za. Emit the slice braced + instead. Returns non-zero when it handled the initializer. */ +static int m7_emit_const_init(FeEmitter *e, FeNode *n) +{ + if (n->kind!=FE_N_CONST || !n->b || n->b->kind!=FE_N_LITERAL || + !n->b->text || n->b->text[0]!='"') return 0; + fputs("{ (const unsigned char*)",e->out); + emit_c_literal(e->out,n->b->text,1); + fputs(", sizeof(",e->out); + emit_c_literal(e->out,n->b->text,1); + fputs(")-1 }",e->out); + return 1; +} + +static void emit_decl(FeEmitter *e, FeNode *n) +{ + pad(e); fputs(m7_c_type(e,n->sem_type),e->out); fputc(' ',e->out); + fputs(cname(n,"fe_local"),e->out); + if (n->kind==FE_N_CONST && n->b) { + fputs(" = ",e->out); + if (!m7_emit_const_init(e,n)) emit_expr(e,n->b); + } + fputs(";\n",e->out); + if ((n->kind==FE_N_LET || n->kind==FE_N_VAR) && n->sem_type && + 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); + } +} + +static void emit_owned_live(FeEmitter *e, FeNode *n, int value) +{ + 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); + } +} + +static void emit_value_drop(FeEmitter *e, FeNode *n) +{ + FeType *t; + t=n ? n->sem_type : 0; + if (!n || !t || !type_needs_drop(t) || + (n->flags & FE_OWN_NODE_CONSUMED) || + (n->flags & FE_OWN_NODE_DEFER_CAPTURE)) return; + pad(e); fputs("if (fe_live_",e->out); fputs(cname(n,"owned"),e->out); + fputs(") { ",e->out); + if (t->kind==FE_TYPE_OWNED) { + 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 if (t->drop_cname) { + fprintf(e->out,"%s(&%s); ",t->drop_cname,cname(n,"local")); + } + fputs("fe_live_",e->out); fputs(cname(n,"owned"),e->out); + fputs("=0; }\n",e->out); +} + +static void emit_cleanup_block(FeEmitter *e, FeNode *n) +{ + FeNode *x; + unsigned count; + unsigned index; + unsigned seen; + unsigned depth; + count=0; + seen=0xffffffffU; + 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-1U) { + 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-1U]); +} + +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) +{ + FeType *ret=e->current_ret; + if (ret && ret->kind==FE_TYPE_ERROR_UNION && ret->error_value && + ret->error_value->kind!=FE_TYPE_VOID) { + fputs("return ",e->out); fputs(ret->none_cname,e->out); + fputc('(',e->out); fputs(error_expr,e->out); fputs(");\n",e->out); + } else { + fputs("return ",e->out); fputs(error_expr,e->out); fputs(";\n",e->out); + } +} + +static void m7_emit_try_error_check(FeEmitter *e, FeNode *n) +{ + FeType *result=n->a ? n->a->sem_type : 0; + pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); + emit_expr(e,n->a); fputs(";\n",e->out); + pad(e); fputs("if (",e->out); fputs(n->aux_cname,e->out); + if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) + fputs(".e",e->out); + fputs(") {\n",e->out); ++e->indent; + emit_cleanup_all(e); + pad(e); + if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) { + char error[192]; + sprintf(error,"%s.e",n->aux_cname); + emit_error_return(e,error); + } else emit_error_return(e,n->aux_cname); + --e->indent; pad(e); fputs("}\n",e->out); +} + +static void m7_emit_catch_block(FeEmitter *e, FeNode *n, + FeNode *target) +{ + FeType *result=n->a ? n->a->sem_type : 0; + FeNode *binding=n->b; + pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); + emit_expr(e,n->a); fputs(";\n",e->out); + pad(e); fputs("if (",e->out); fputs(n->aux_cname,e->out); + if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) + fputs(".e",e->out); + fputs(") {\n",e->out); ++e->indent; + if (binding && binding->cname) { + pad(e); fputs("unsigned short ",e->out); fputs(binding->cname,e->out); + fputs(" = ",e->out); fputs(n->aux_cname,e->out); + if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) + fputs(".e",e->out); + fputs(";\n",e->out); + } + emit_stmt(e,n->c); + --e->indent; pad(e); fputs("}",e->out); + if (target && result && result->error_value && + result->error_value->kind!=FE_TYPE_VOID) { + fputs(" else {\n",e->out); ++e->indent; + pad(e); emit_lvalue(e,target); fputs(" = ",e->out); + fputs(n->aux_cname,e->out); fputs(".v;\n",e->out); + emit_owned_live(e,target,1); + --e->indent; pad(e); fputs("}",e->out); + } + fputc('\n',e->out); +} + +static void m7_emit_optional_match(FeEmitter *e, FeNode *n) +{ + FeType *opt=n->a ? n->a->sem_type : 0; + FeNode *arm; + FeNode *binding; + int first; + pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); + emit_expr(e,n->a); fputs(";\n",e->out); + first=1; + for (arm=n->children;arm;arm=arm->next) { + if (arm->text && strcmp(arm->text,"Some")==0) { + pad(e); if (!first) fputs("else ",e->out); + fputs("if (",e->out); m7_emit_present(e,opt,n->aux_cname); + fputs(") {\n",e->out); ++e->indent; + binding=arm->children; + if (binding && binding->cname) { + pad(e); fputs(m7_c_type(e,binding->sem_type),e->out); + fputc(' ',e->out); fputs(binding->cname,e->out); fputs(" = ",e->out); + m7_emit_payload_var(e,opt,n->aux_cname); fputs(";\n",e->out); + } + if (arm->a) emit_stmt(e,arm->a); + --e->indent; pad(e); fputs("}\n",e->out); + first=0; + } else if (arm->text && strcmp(arm->text,"None")==0) { + pad(e); if (!first) fputs("else ",e->out); + fputs("if (!",e->out); m7_emit_present(e,opt,n->aux_cname); + fputs(") {\n",e->out); ++e->indent; + if (arm->a) emit_stmt(e,arm->a); + --e->indent; pad(e); fputs("}\n",e->out); + first=0; + } else if (arm->text && strcmp(arm->text,"_")==0) { + pad(e); if (!first) fputs("else ",e->out); + fputs("{\n",e->out); ++e->indent; + if (arm->a) emit_stmt(e,arm->a); + --e->indent; pad(e); fputs("}\n",e->out); + first=0; + } + } +} + +static void m7_emit_if_let(FeEmitter *e, FeNode *n) +{ + FeType *opt=n->a ? n->a->sem_type : 0; + FeNode *binding=n->children; + int some=n->aux_text && strcmp(n->aux_text,"Some")==0; + pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); + emit_expr(e,n->a); fputs(";\n",e->out); + pad(e); fputs("if (",e->out); + if (!some) fputc('!',e->out); + m7_emit_present(e,opt,n->aux_cname); fputs(") {\n",e->out); + ++e->indent; + if (some && binding && binding->cname) { + pad(e); fputs(m7_c_type(e,binding->sem_type),e->out); fputc(' ',e->out); + fputs(binding->cname,e->out); fputs(" = ",e->out); + m7_emit_payload_var(e,opt,n->aux_cname); fputs(";\n",e->out); + } + if (n->b) emit_stmt(e,n->b); + --e->indent; pad(e); fputs("}",e->out); + if (n->c) { + fputs(" else ",e->out); + emit_stmt(e,n->c); + } + fputc('\n',e->out); +} + +static void emit_block(FeEmitter *e, FeNode *n) +{ + FeNode *x; + unsigned seen; + if (!n) { + pad(e); fputs("{}",e->out); return; + } + 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; + } + 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) { + if (e->current_fn->a) { + FeNode *p; + for (p=e->current_fn->a->children;p;p=p->next) + if (p->sem_type && type_needs_drop(p->sem_type)) { + pad(e); fputs("unsigned char fe_live_",e->out); + fputs(cname(p,"owned"),e->out); fputs("=1;\n",e->out); + } + } + m7_emit_temp_decls(e,n); + } + if (e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { + pad(e); fputs(m7_c_type(e,e->current_ret),e->out); + fputs(" fe_return_value;\n",e->out); + } + seen=0; + for (x=n->children;x;x=x->next) { + ++seen; + if (e->block_depth) e->block_seen[e->block_depth-1U]=seen; + emit_stmt(e,x); + } + --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); fputs("return 0;\n",e->out); + e->fallthrough_block=0; + } + pad(e); fputc('}',e->out); +} + +static void emit_stmt(FeEmitter *e, FeNode *n) +{ + FeType *result; + if (!n) return; + switch (n->kind) { + case FE_N_BLOCK: + emit_block(e,n); fputc('\n',e->out); break; + case FE_N_LET: + case FE_N_VAR: + if (n->b) { + if (fe_m7_is_try(n->b)) { + m7_emit_try_error_check(e,n->b); + pad(e); emit_lvalue(e,n); fputs(" = ",e->out); + fputs(n->b->aux_cname,e->out); + result=n->b->a ? n->b->a->sem_type : 0; + if (result && result->error_value && + result->error_value->kind!=FE_TYPE_VOID) fputs(".v",e->out); + fputs(";\n",e->out); emit_owned_live(e,n,1); + } else if (n->b->kind==FE_N_BINARY && n->b->c && + fe_m7_lazy_kind(n->b)==FE_M7_LAZY_CATCH) { + m7_emit_catch_block(e,n->b,n); + } else { + pad(e); emit_lvalue(e,n); 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) emit_value_drop(e,n->a); + pad(e); emit_lvalue(e,n->a); fputc(' ',e->out); + fputs(n->text ? n->text : "=",e->out); fputc(' ',e->out); + 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: + if (fe_m7_is_try(n->a)) { + m7_emit_try_error_check(e,n->a); + } else if (n->a && n->a->kind==FE_N_BINARY && n->a->c && + fe_m7_lazy_kind(n->a)==FE_M7_LAZY_CATCH) { + m7_emit_catch_block(e,n->a,0); + } else { + pad(e); emit_expr(e,n->a); fputs(";\n",e->out); + } + break; + case FE_N_DEFER: + break; + case FE_N_RETURN: + if (n->a && fe_m7_is_try(n->a)) { + FeNode *tr=n->a; + FeType *res=tr->a ? tr->a->sem_type : 0; + m7_emit_try_error_check(e,tr); + if (e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { + pad(e); fputs("fe_return_value = ",e->out); + if (e->current_ret->kind==FE_TYPE_ERROR_UNION && + e->current_ret->error_value && + e->current_ret->error_value->kind!=FE_TYPE_VOID) { + fputs(e->current_ret->maker,e->out); fputs("(0, ",e->out); + fputs(tr->aux_cname,e->out); + if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) + fputs(".v",e->out); + fputc(')',e->out); + } else { + fputs(tr->aux_cname,e->out); + if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) + fputs(".v",e->out); + } + fputs(";\n",e->out); + } + emit_cleanup_all(e); + pad(e); fputs("return fe_return_value;\n",e->out); + } else if (n->a && n->a->kind==FE_N_BINARY && n->a->c && + fe_m7_lazy_kind(n->a)==FE_M7_LAZY_CATCH) { + /* A value catch-block is lowered as a temporary local success + assignment; the handler is required by the checker to exit. */ + FeNode *cx=n->a; + FeType *res=cx->a ? cx->a->sem_type : 0; + pad(e); fputs(cx->aux_cname,e->out); fputs(" = ",e->out); + emit_expr(e,cx->a); fputs(";\n",e->out); + pad(e); fputs("if (",e->out); fputs(cx->aux_cname,e->out); + if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) + fputs(".e",e->out); + fputs(") {\n",e->out); ++e->indent; + if (cx->b && cx->b->cname) { + pad(e); fputs("unsigned short ",e->out); fputs(cx->b->cname,e->out); + fputs(" = ",e->out); fputs(cx->aux_cname,e->out); + if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) + fputs(".e",e->out); + fputs(";\n",e->out); + } + emit_stmt(e,cx->c); + --e->indent; pad(e); fputs("}\n",e->out); + pad(e); fputs("fe_return_value = ",e->out); + fputs(cx->aux_cname,e->out); + if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) + fputs(".v",e->out); + fputs(";\n",e->out); + emit_cleanup_all(e); + pad(e); fputs("return fe_return_value;\n",e->out); + } else if (n->a && n->a->kind==FE_N_BINARY && !n->a->c && + fe_m7_lazy_kind(n->a)==FE_M7_LAZY_CATCH && + e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { + /* Short catch in return position. As an expression this lowers to + `((tmp = X), tmp.e ? fallback : tmp.v)`, and when X carries a move + it becomes a struct assignment whose right side is itself a comma + expression -- which crashes wcc386 hard enough to take DOSBox-X + down with it. The same lowering as statements is also plainer. */ + FeNode *cx=n->a; + FeType *res=cx->a ? cx->a->sem_type : 0; + int has_value=res && res->error_value && + res->error_value->kind!=FE_TYPE_VOID; + m7_emit_assign_stmt(e,cx->aux_cname,cx->a); + pad(e); fputs("if (",e->out); fputs(cx->aux_cname,e->out); + if (has_value) fputs(".e",e->out); + fputs(") {\n",e->out); ++e->indent; + pad(e); fputs("fe_return_value = ",e->out); + emit_expr(e,cx->b); fputs(";\n",e->out); + --e->indent; pad(e); fputs("} else {\n",e->out); ++e->indent; + pad(e); fputs("fe_return_value = ",e->out); + fputs(cx->aux_cname,e->out); + if (has_value) fputs(".v",e->out); + fputs(";\n",e->out); + --e->indent; pad(e); fputs("}\n",e->out); + emit_cleanup_all(e); + pad(e); fputs("return fe_return_value;\n",e->out); + } else { + if (n->a && e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { + pad(e); fputs("fe_return_value = ",e->out); + emit_expr(e,n->a); 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: + if (n->text && strcmp(n->text,"if let")==0) { + m7_emit_if_let(e,n); + } else { + pad(e); fputs("if (",e->out); emit_expr(e,n->a); fputs(") ",e->out); + emit_block(e,n->b); + if (n->c) { + fputs(" else ",e->out); + if (n->c->kind==FE_N_IF) emit_stmt(e,n->c); + else emit_block(e,n->c); + } + fputc('\n',e->out); + } + break; + case FE_N_MATCH: + if (n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_OPTIONAL) + m7_emit_optional_match(e,n); + else emit_match_core(e,n,0); + break; + case FE_N_BREAK: + case FE_N_CONTINUE: + if (e->loop_depth) { + emit_cleanup_to(e,e->loop_floor[e->loop_depth-1U]); + pad(e); fputs(n->kind==FE_N_BREAK ? "break;\n" : "continue;\n",e->out); + } + break; + case FE_N_WHILE: + pad(e); 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; + emit_block(e,n->b); + if (e->loop_depth) --e->loop_depth; + fputc('\n',e->out); + break; + case FE_N_FOR: + emit_stmt_core(e,n); + break; + default: + emit_stmt_core(e,n); + break; + } +} + +static void emit_fn(FeEmitter *e, FeNode *fn, int prototype) +{ + FeNode *p; + FeType *old_ret; + FeNode *old_fn; + fputs(m7_c_type(e,fn->sem_type ? fn->sem_type : + (fn->b ? fe_type_from_ast(&e->check->types,fn->b) : + fe_type_intern(&e->check->types,"void"))),e->out); + fputc(' ',e->out); fputs(cname(fn,"fe_fn"),e->out); fputc('(',e->out); + p=fn->a ? fn->a->children : 0; + if (!p) fputs("void",e->out); + while (p) { + if (p!=fn->a->children) fputs(", ",e->out); + fputs(m7_c_type(e,p->sem_type ? p->sem_type : + fe_type_from_ast(&e->check->types,p->a)),e->out); + fputc(' ',e->out); fputs(cname(p,"fe_arg"),e->out); + p=p->next; + } + fputc(')',e->out); + if (prototype) { fputs(";\n",e->out); return; } + old_ret=e->current_ret; + old_fn=e->current_fn; + e->current_ret=fn->sem_type; + e->current_fn=fn; + m7_prepare_temps(e,fn->c); + fputc(' ',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; + e->current_fn=old_fn; + fputc('\n',e->out); +} + +static void emit_main_wrapper(FeEmitter *e, FeNode *fn) +{ + FeType *ret=fn->sem_type; + if (ret && ret->kind==FE_TYPE_ERROR_UNION && ret->error_value && + ret->error_value->kind!=FE_TYPE_VOID) { + fputs("int main(void) { ",e->out); fputs(m7_c_type(e,ret),e->out); + fputs(" r = ",e->out); fputs(cname(fn,"fe_main"),e->out); + fputs("(); return r.e ? 1 : 0; }\n",e->out); + } else emit_main_wrapper_core(e,fn); +} + void fe_emit_c_program(FeEmitter *e) { FeNode *n; - FeNode *main_fn = 0; + FeNode *main_fn; FeType *type; int need_m4; + main_fn=0; need_m4=node_uses_m4(e->check->ast->root); - for (type=e->check->types.types; type; type=type->next) + for (type=e->check->types.types;type;type=type->next) if (strcmp(type->name,"io.Writer")==0) need_m4=1; - /* stdio is only reached by the M4 writer runtime (fwrite/stdout/stderr). - Parsing it costs far more than the generated body -- a 26-line unit pulls - in about 1900 lines of headers -- so leave it out when nothing uses it. */ - fputs("/* generated by fec M4 */\n#include \n#include \n#include \n", e->out); - if (need_m4) fputs("#include \n", e->out); - fputs("typedef char fe_assert_u8[(sizeof(unsigned char)==1) ? 1 : -1];\ntypedef char fe_assert_u16[(sizeof(unsigned short)==2) ? 1 : -1];\ntypedef char fe_assert_u32[(sizeof(unsigned long)==4) ? 1 : -1];\n", e->out); + /* See emit_c.c: stdio only comes in with the M4 writer runtime. */ + fputs("/* generated by fec M7 */\n#include \n#include \n#include \n",e->out); + if (need_m4) fputs("#include \n",e->out); + fputs("typedef char fe_assert_u8[(sizeof(unsigned char)==1) ? 1 : -1];\ntypedef char fe_assert_u16[(sizeof(unsigned short)==2) ? 1 : -1];\ntypedef char fe_assert_u32[(sizeof(unsigned long)==4) ? 1 : -1];\n",e->out); if (e->pointer_bits==16) 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(); }\nstatic unsigned short fe_error_temp;\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); - for (n = e->check->ast->root ? e->check->ast->root->children : 0; - n; n = n->next) { - if (n->kind == FE_N_GLOBAL || n->kind == FE_N_CONST) { - fputs(ctype(e, n), e->out); - fputc(' ', e->out); - fputs(cname(n, "fe_global"), e->out); + m7_emit_type_helpers(e); + for (n=e->check->ast->root ? e->check->ast->root->children : 0;n;n=n->next) { + if (n->kind==FE_N_GLOBAL || n->kind==FE_N_CONST) { + fputs(m7_c_type(e,n->sem_type),e->out); fputc(' ',e->out); + fputs(cname(n,"fe_global"),e->out); if (n->b) { - fputs(" = ", e->out); - if (n->kind==FE_N_CONST && n->b->kind==FE_N_LITERAL && - n->b->text && n->b->text[0]=='"') { - fputs("{ (const unsigned char*)",e->out); - emit_c_literal(e->out,n->b->text,1); - fputs(", sizeof(",e->out); - emit_c_literal(e->out,n->b->text,1); - fputs(")-1 }",e->out); - } else emit_expr(e, n->b); + fputs(" = ",e->out); + if (!m7_emit_const_init(e,n)) emit_expr(e,n->b); } - fputs(";\n", e->out); + fputs(";\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, 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_FN) { + 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); - } + 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/emitcm7.c b/fec/src/emitcm7.c deleted file mode 100644 index c91b96c..0000000 --- a/fec/src/emitcm7.c +++ /dev/null @@ -1,1420 +0,0 @@ -/* M7 C backend integration. Keep the verified M1-M6 emitter available as - an exact fast path, while M7 sources reuse its mature helpers from this - translation unit and override only expression/control-flow semantics that - changed in v0.1.8. */ -#define type_needs_drop type_needs_drop_m6 -#define emit_expr emit_expr_m6 -#define emit_stmt emit_stmt_m6 -#define emit_block emit_block_m6 -#define emit_lvalue emit_lvalue_m6 -#define emit_decl emit_decl_m6 -#define emit_owned_live emit_owned_live_m6 -#define emit_value_drop emit_value_drop_m6 -#define emit_cleanup_block emit_cleanup_block_m6 -#define emit_cleanup_to emit_cleanup_to_m6 -#define emit_param_cleanup emit_param_cleanup_m6 -#define emit_cleanup_all emit_cleanup_all_m6 -#define emit_error_return emit_error_return_m6 -#define emit_match emit_match_m6 -#define emit_fn emit_fn_m6 -#define emit_main_wrapper emit_main_wrapper_m6 -#define emit_type_defs emit_type_defs_m6 -#define fe_emit_c_init fe_emit_c_init_m6 -#define fe_emit_c_program fe_emit_c_program_m6 -#include "emit_c.c" -#undef type_needs_drop -#undef emit_expr -#undef emit_stmt -#undef emit_block -#undef emit_lvalue -#undef emit_decl -#undef emit_owned_live -#undef emit_value_drop -#undef emit_cleanup_block -#undef emit_cleanup_to -#undef emit_param_cleanup -#undef emit_cleanup_all -#undef emit_error_return -#undef emit_match -#undef emit_fn -#undef emit_main_wrapper -#undef emit_type_defs -#undef fe_emit_c_init -#undef fe_emit_c_program - -#include "m7.h" -#include "lower.h" - -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 emit_lvalue(FeEmitter *e, FeNode *n); - -static int type_needs_drop(FeType *t) -{ - return fe_lower_type_needs_drop(t); -} - -static const char *m7_c_type(FeEmitter *e, FeType *t) -{ - if (!t) return "long"; - if ((t->kind==FE_TYPE_ENUM && t->is_error) || - strcmp(t->name,"core.Error")==0) - return "unsigned short"; - return fe_type_c_name(t,e->pointer_bits); -} - -static int m7_node_feature(FeNode *n) -{ - FeNode *x; - if (!n) return 0; - if (n->sem_context) return 1; - if (n->sem_type && (n->sem_type->kind==FE_TYPE_OPTIONAL || - n->sem_type->kind==FE_TYPE_ERROR_UNION || - (n->sem_type->kind==FE_TYPE_ENUM && n->sem_type->is_error))) - return 1; - if (fe_m7_is_null(n) || fe_m7_is_try(n)) return 1; - if (n->kind==FE_N_MEMBER && n->text && strcmp(n->text,".?")==0) - return 1; - if (n->kind==FE_N_BINARY && fe_m7_lazy_kind(n)!=FE_M7_LAZY_NONE) - return 1; - if (n->kind==FE_N_IF && n->text && strcmp(n->text,"if let")==0) - return 1; - if (n->kind==FE_N_ARM && n->text && - (strcmp(n->text,"Some")==0 || strcmp(n->text,"None")==0)) - return 1; - if (m7_node_feature(n->a) || m7_node_feature(n->b) || - m7_node_feature(n->c)) return 1; - for (x=n->children;x;x=x->next) - if (m7_node_feature(x)) return 1; - return 0; -} - -static int m7_program_feature(FeEmitter *e) -{ - FeNode *n; - FeType *t; - if (!e || !e->check) return 0; - for (t=e->check->types.types;t;t=t->next) - if (t->kind==FE_TYPE_OPTIONAL || t->kind==FE_TYPE_ERROR_UNION || - (t->kind==FE_TYPE_ENUM && t->is_error)) return 1; - for (n=e->check->ast->root ? e->check->ast->root->children : 0; - n;n=n->next) - if (m7_node_feature(n) || n->kind==FE_N_ERROR_DECL) return 1; - return 0; -} - -static char *m7_temp_name(FeEmitter *e) -{ - char number[24]; - char *p; - unsigned long len; - sprintf(number,"%u",e->temp_serial++); - len=(unsigned long)strlen("fe_m7_tmp_")+ - (unsigned long)strlen(number)+1UL; - p=(char *)fe_arena_alloc(&e->check->ast->arena,len); - if (!p) return 0; - strcpy(p,"fe_m7_tmp_"); - strcat(p,number); - return p; -} - -static int m7_needs_temp(FeNode *n) -{ - if (!n) return 0; - if (fe_m7_is_try(n)) return 1; - if (n->kind==FE_N_BINARY && fe_m7_lazy_kind(n)!=FE_M7_LAZY_NONE) - return 1; - if (n->kind==FE_N_IF && n->text && strcmp(n->text,"if let")==0) - return 1; - if (n->kind==FE_N_MATCH && n->a && n->a->sem_type && - n->a->sem_type->kind==FE_TYPE_OPTIONAL) - return 1; - return 0; -} - -static FeType *m7_temp_type(FeNode *n) -{ - if (!n) return 0; - if (fe_m7_is_try(n)) return n->a ? n->a->sem_type : 0; - if (n->kind==FE_N_BINARY) return n->a ? n->a->sem_type : 0; - if ((n->kind==FE_N_IF || n->kind==FE_N_MATCH) && n->a) - return n->a->sem_type; - return 0; -} - -static void m7_prepare_temps(FeEmitter *e, FeNode *n) -{ - FeNode *x; - if (!n) return; - if (m7_needs_temp(n) && !n->aux_cname) - n->aux_cname=m7_temp_name(e); - m7_prepare_temps(e,n->a); - m7_prepare_temps(e,n->b); - m7_prepare_temps(e,n->c); - for (x=n->children;x;x=x->next) m7_prepare_temps(e,x); -} - -static void m7_emit_temp_decls(FeEmitter *e, FeNode *n) -{ - FeNode *x; - FeType *t; - if (!n) return; - if (m7_needs_temp(n) && n->aux_cname) { - t=m7_temp_type(n); - if (t) { - pad(e); fputs(m7_c_type(e,t),e->out); fputc(' ',e->out); - fputs(n->aux_cname,e->out); fputs(";\n",e->out); - } - } - m7_emit_temp_decls(e,n->a); - m7_emit_temp_decls(e,n->b); - m7_emit_temp_decls(e,n->c); - for (x=n->children;x;x=x->next) m7_emit_temp_decls(e,x); -} - -static void m7_emit_type(FeEmitter *e, FeType *t) -{ - unsigned i; - unsigned j; - if (!t || t->emit_state) return; - if (t->kind==FE_TYPE_OPTIONAL) { - t->emit_state=1; - m7_emit_type(e,t->elem); - if (!fe_m7_optional_uses_niche(t->elem) && t->cname) { - fputs(t->cname,e->out); fputs(" { unsigned char has; ",e->out); - fputs(m7_c_type(e,t->elem),e->out); - fputs(" v; };\n",e->out); - } - t->emit_state=2; - return; - } - if (t->kind==FE_TYPE_ARRAY) m7_emit_type(e,t->elem); - if (t->kind==FE_TYPE_SLICE) m7_emit_type(e,t->elem); - if (t->kind==FE_TYPE_OWNED) m7_emit_type(e,t->elem); - if (t->kind==FE_TYPE_STRUCT) - for (i=0;ifield_count;++i) m7_emit_type(e,t->fields[i].type); - if (t->kind==FE_TYPE_ENUM) - for (i=0;ivariant_count;++i) - for (j=0;jvariants[i].field_count;++j) - m7_emit_type(e,t->variants[i].fields[j].type); - if (t->kind==FE_TYPE_ERROR_UNION) { - m7_emit_type(e,t->elem); - m7_emit_type(e,t->error_value); - } - emit_one_type(e,t); -} - -static void emit_type_defs(FeEmitter *e) -{ - FeType *t; - for (t=e->check->types.types;t;t=t->next) - if (t->kind==FE_TYPE_ARRAY) - fe_type_slice(&e->check->types,t->elem); - for (t=e->check->types.types;t;t=t->next) m7_emit_type(e,t); -} - -static void m7_emit_drop_access(FeEmitter *e, FeType *t, - const char *access) -{ - if (!t || !access || !type_needs_drop(t)) return; - if (t->kind==FE_TYPE_OWNED) { - if (t->elem && t->elem->kind==FE_TYPE_SLICE) { - fprintf(e->out,"if ((%s).p) { free((%s).p); (%s).p=0; } ", - access,access,access); - } else { - fprintf(e->out,"if (%s) { ",access); - if (t->elem && type_needs_drop(t->elem) && t->elem->drop_cname) - fprintf(e->out,"%s(%s); ",t->elem->drop_cname,access); - fprintf(e->out,"free(%s); %s=0; } ",access,access); - } - return; - } - if (t->drop_cname) - fprintf(e->out,"%s(&(%s)); ",t->drop_cname,access); -} - -static void m7_emit_drop_helpers(FeEmitter *e) -{ - FeType *t; - FeNode *method; - unsigned i; - unsigned j; - char access[256]; - for (t=e->check->types.types;t;t=t->next) - if (type_needs_drop(t) && t->drop_cname && - (t->kind==FE_TYPE_STRUCT || t->kind==FE_TYPE_ARRAY || - t->kind==FE_TYPE_OPTIONAL || t->kind==FE_TYPE_ERROR_UNION)) - fprintf(e->out,"static void %s(%s *self);\n", - t->drop_cname,m7_c_type(e,t)); - for (t=e->check->types.types;t;t=t->next) { - if (!type_needs_drop(t) || !t->drop_cname) continue; - if (t->kind==FE_TYPE_STRUCT) { - fprintf(e->out,"static void %s(%s *self) { ", - t->drop_cname,m7_c_type(e,t)); - method=find_drop_method(e,t->name); - if (method) fprintf(e->out,"%s(self); ",cname(method,"fe_drop_method")); - for (i=t->field_count;i>0;--i) { - sprintf(access,"self->%s",t->fields[i-1U].name); - m7_emit_drop_access(e,t->fields[i-1U].type,access); - } - fputs("}\n",e->out); - } else if (t->kind==FE_TYPE_ARRAY) { - fprintf(e->out,"static void %s(%s *self) { unsigned long i; for (i=0; i<%lu; ++i) { ", - t->drop_cname,m7_c_type(e,t),t->length); - strcpy(access,"self->a[i]"); - m7_emit_drop_access(e,t->elem,access); - fputs("} }\n",e->out); - } else if (t->kind==FE_TYPE_OPTIONAL) { - fprintf(e->out,"static void %s(%s *self) { ", - t->drop_cname,m7_c_type(e,t)); - if (fe_m7_optional_uses_niche(t->elem)) { - fputs("if (*self) { ",e->out); - m7_emit_drop_access(e,t->elem,"*self"); - fputs("} ",e->out); - } else { - fputs("if (self->has) { ",e->out); - m7_emit_drop_access(e,t->elem,"self->v"); - fputs("self->has=0; } ",e->out); - } - fputs("}\n",e->out); - } else if (t->kind==FE_TYPE_ERROR_UNION && t->error_value && - t->error_value->kind!=FE_TYPE_VOID) { - fprintf(e->out,"static void %s(%s *self) { if (!self->e) { ", - t->drop_cname,m7_c_type(e,t)); - m7_emit_drop_access(e,t->error_value,"self->v"); - fputs("self->e=1; } }\n",e->out); - } - } - /* Error enums are scalar codes, so their enum payload helper functions - from M3 are deliberately not emitted in the M7 path. */ - (void)j; -} - -static void m7_emit_type_helpers(FeEmitter *e) -{ - FeType *t; - FeType *st; - unsigned i; - unsigned j; - const char *ct; - FeVariantType *v; - for (t=e->check->types.types;t;t=t->next) { - if (t->kind==FE_TYPE_OPTIONAL) { - if (!fe_m7_optional_uses_niche(t->elem)) { - fprintf(e->out,"static %s %s(%s v) { %s r; r.has=1; r.v=v; return r; }\n", - m7_c_type(e,t),t->maker,m7_c_type(e,t->elem),m7_c_type(e,t)); - fprintf(e->out,"static %s %s(void) { %s r; memset(&r,0,sizeof(r)); return r; }\n", - m7_c_type(e,t),t->none_cname,m7_c_type(e,t)); - fprintf(e->out,"static %s %s(%s x) { ", - m7_c_type(e,t->elem),t->unwrap_cname,m7_c_type(e,t)); - if (!e->no_checks) fputs("if (!x.has) fe_trap_bounds(); ",e->out); - fputs("return x.v; }\n",e->out); - } else { - fprintf(e->out,"static %s %s(%s x) { ", - m7_c_type(e,t->elem),t->unwrap_cname,m7_c_type(e,t)); - if (!e->no_checks) fputs("if (!x) fe_trap_bounds(); ",e->out); - fputs("return x; }\n",e->out); - } - } - 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", - m7_c_type(e,t),t->maker,m7_c_type(e,t->error_value),m7_c_type(e,t)); - if (t->none_cname) - fprintf(e->out,"static %s %s(unsigned short e) { %s r; memset(&r,0,sizeof(r)); r.e=e; return r; }\n", - m7_c_type(e,t),t->none_cname,m7_c_type(e,t)); - 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", - m7_c_type(e,t),t->alloc_cname,m7_c_type(e,t), - m7_c_type(e,item),m7_c_type(e,item)); - } 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", - m7_c_type(e,t),t->alloc_cname, - m7_c_type(e,t->error_value->elem),m7_c_type(e,t), - m7_c_type(e,t->error_value), - m7_c_type(e,t->error_value->elem)); - } - } - } - for (t=e->check->types.types;t;t=t->next) { - if (t->replace_cname) { - ct=m7_c_type(e,t); - 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(",m7_c_type(e,t),t->maker); - for (i=0;ifield_count;i++) { - if (i) fputs(", ",e->out); - fputs(m7_c_type(e,t->fields[i].type),e->out); - fprintf(e->out," p%u",i); - } - fprintf(e->out,") { %s v;",m7_c_type(e,t)); - for (i=0;ifield_count;i++) - fprintf(e->out," v.%s=p%u;",t->fields[i].name,i); - fputs(" return v; }\n",e->out); - } else if (t->kind==FE_TYPE_ARRAY && t->maker) { - fprintf(e->out,"static %s %s(",m7_c_type(e,t),t->maker); - for (i=0;ilength;i++) { - if (i) fputs(", ",e->out); - fputs(m7_c_type(e,t->elem),e->out); - fprintf(e->out," p%u",i); - } - fprintf(e->out,") { %s v;",m7_c_type(e,t)); - for (i=0;ilength;i++) fprintf(e->out," v.a[%u]=p%u;",i,i); - fputs(" return v; }\n",e->out); - } else if (t->kind==FE_TYPE_ENUM && !t->is_error) { - for (i=0;ivariant_count;i++) { - v=&t->variants[i]; - fprintf(e->out,"static %s %s(",m7_c_type(e,t),v->maker); - for (j=0;jfield_count;j++) { - if (j) fputs(", ",e->out); - fputs(m7_c_type(e,v->fields[j].type),e->out); - fprintf(e->out," p%u",j); - } - fprintf(e->out,") { %s x; x.tag=%u;",m7_c_type(e,t),v->tag); - for (j=0;jfield_count;j++) { - if (v->field_count==1) - fprintf(e->out," x.payload.%s=p%u;",v->name,j); - else - fprintf(e->out," x.payload.%s.%s=p%u;",v->name, - v->fields[j].name,j); - } - fputs(" return x; }\n",e->out); - } - } - } - m7_emit_drop_helpers(e); - /* Reuse the mature M3 index/slice helper generator. It does not depend - on M7 drop policy and all wrapper dependencies are already emitted. */ - 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) { ", - m7_c_type(e,t->elem),t->indexer,m7_c_type(e,t)); - if (!e->no_checks) - fprintf(e->out,"if (i >= %lu) fe_trap_bounds(); ",t->length); - fputs("return x.a[i]; }\n",e->out); - if (t->slicer) { - st=fe_type_slice(&e->check->types,t->elem); - fprintf(e->out,"static %s %s(%s *x, unsigned long a, unsigned long b) { ", - m7_c_type(e,st),t->slicer,m7_c_type(e,t)); - if (!e->no_checks) - fprintf(e->out,"if (a > b || b > %lu) fe_trap_bounds(); ",t->length); - fprintf(e->out,"return %s(x->a+a,b-a); }\n",st->maker); - fprintf(e->out,"static %s %s(%s *x) { return %s(x,0,%lu); }\n", - m7_c_type(e,st),t->full_slicer,m7_c_type(e,t),t->slicer,t->length); - fprintf(e->out,"static %s %s(%s *x, unsigned long a) { return %s(x,a,%lu); }\n", - m7_c_type(e,st),t->tail_slicer,m7_c_type(e,t),t->slicer,t->length); - } - } else if (t->kind==FE_TYPE_SLICE && t->indexer) { - fprintf(e->out,"static %s %s(%s x, unsigned long i) { ", - m7_c_type(e,t->elem),t->indexer,m7_c_type(e,t)); - if (!e->no_checks) fputs("if (i >= x.n) fe_trap_bounds(); ",e->out); - fputs("return x.p[i]; }\n",e->out); - if (t->slicer) { - fprintf(e->out,"static %s %s(%s x, unsigned long a, unsigned long b) { ", - m7_c_type(e,t),t->slicer,m7_c_type(e,t)); - if (!e->no_checks) - fputs("if (a > b || b > x.n) fe_trap_bounds(); ",e->out); - fprintf(e->out,"return %s(x.p+a,b-a); }\n",t->maker); - fprintf(e->out,"static %s %s(%s x) { return %s(x,0,x.n); }\n", - m7_c_type(e,t),t->full_slicer,m7_c_type(e,t),t->slicer); - fprintf(e->out,"static %s %s(%s x, unsigned long a) { return %s(x,a,x.n); }\n", - m7_c_type(e,t),t->tail_slicer,m7_c_type(e,t),t->slicer); - } - } - } -} - -static void m7_emit_present(FeEmitter *e, FeType *opt, const char *name) -{ - if (fe_m7_optional_uses_niche(opt->elem)) { - fputs("(",e->out); fputs(name,e->out); fputs(" != 0)",e->out); - } else { - fputs(name,e->out); fputs(".has",e->out); - } -} - -static void m7_emit_payload_var(FeEmitter *e, FeType *opt, const char *name) -{ - (void)e; - fputs(name,e->out); - if (!fe_m7_optional_uses_niche(opt->elem)) fputs(".v",e->out); -} - -static void m7_emit_error_member(FeEmitter *e, FeNode *n) -{ - FeVariantType *v; - FeType *t; - t=n && n->a ? n->a->sem_type : 0; - v=t && t->kind==FE_TYPE_ENUM ? - fe_type_variant(t,n->b ? n->b->text : "") : 0; - if (v) fprintf(e->out,"%u",v->tag); - else fputs("0",e->out); -} - -static void m7_emit_raw_expr(FeEmitter *e, FeNode *n); - -static void m7_emit_contextual(FeEmitter *e, FeNode *n) -{ - FeType *ctx; - FeType *actual; - FeType *error_type; - ctx=n ? n->sem_context : 0; - actual=n ? n->sem_type : 0; - if (!ctx) { m7_emit_raw_expr(e,n); return; } - if (ctx->kind==FE_TYPE_OPTIONAL) { - if (fe_m7_is_null(n)) { - if (fe_m7_optional_uses_niche(ctx->elem)) fputs("0",e->out); - else { fputs(ctx->none_cname,e->out); fputs("()",e->out); } - return; - } - if (fe_m7_optional_uses_niche(ctx->elem)) { - m7_emit_raw_expr(e,n); - } else { - fputs(ctx->maker,e->out); fputc('(',e->out); - m7_emit_raw_expr(e,n); fputc(')',e->out); - } - return; - } - if (ctx->kind==FE_TYPE_ERROR_UNION) { - error_type=ctx->elem; - if (!error_type) error_type=fe_type_intern(&e->check->types,"core.Error"); - if (actual && fe_type_equal(actual,ctx->error_value)) { - if (ctx->error_value->kind==FE_TYPE_VOID) fputs("0",e->out); - else { - fputs(ctx->maker,e->out); fputs("(0, ",e->out); - m7_emit_raw_expr(e,n); fputc(')',e->out); - } - return; - } - if (actual && fe_type_equal(actual,error_type)) { - if (ctx->error_value->kind==FE_TYPE_VOID) - m7_emit_raw_expr(e,n); - else { - fputs(ctx->none_cname,e->out); fputc('(',e->out); - m7_emit_raw_expr(e,n); fputc(')',e->out); - } - return; - } - } - m7_emit_raw_expr(e,n); -} - -static void emit_expr(FeEmitter *e, FeNode *n) -{ - if (!n) { fputs("0",e->out); return; } - if (n->sem_context) m7_emit_contextual(e,n); - else m7_emit_raw_expr(e,n); -} - -static void emit_lvalue(FeEmitter *e, FeNode *n) -{ - FeType *bt; - if (!n) { fputs("fe_bad_lvalue",e->out); return; } - if (n->kind==FE_N_IDENT) { - fputs(cname(n,"fe_local"),e->out); - return; - } - /* A declaration names its own storage. The initializer for `let`/`var` is - emitted as a separate assignment statement, so the declaration node is - handed here as the target; without this it falls through to the raw - expression path, which emits a declaration as "0" and produces `0 = ...`. */ - if (n->kind==FE_N_LET || n->kind==FE_N_VAR || n->kind==FE_N_CONST) { - fputs(cname(n,"fe_local"),e->out); - return; - } - if (n->kind==FE_N_MEMBER) { - bt=n->a ? n->a->sem_type : 0; - if (n->text && strcmp(n->text,".?")==0) { - emit_expr(e,n); - return; - } - if ((bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) && - n->b && n->b->text && strcmp(n->b->text,"^")==0) { - fputs("(*",e->out); emit_expr(e,n->a); fputc(')',e->out); - } else if (bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) { - emit_expr(e,n->a); fputs("->",e->out); - fputs(n->b && n->b->text ? n->b->text : "member",e->out); - } else { - emit_lvalue(e,n->a); fputc('.',e->out); - fputs(n->b && n->b->text ? n->b->text : "member",e->out); - } - return; - } - if (n->kind==FE_N_INDEX) { - bt=n->a ? n->a->sem_type : 0; - emit_lvalue(e,n->a); - fputs(bt && bt->kind==FE_TYPE_ARRAY ? ".a[" : ".p[",e->out); - emit_expr(e,n->b); fputc(']',e->out); - return; - } - m7_emit_raw_expr(e,n); -} - -static void m7_emit_call(FeEmitter *e, FeNode *n) -{ - FeNode *x; - FeNode *call_param; - FeVariantType *v; - int special; - call_param=0; - special=0; - 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) { - FeNode *arg=n->children; - fputs("(free(",e->out); emit_expr(e,arg); - if (arg->sem_type && arg->sem_type->kind==FE_TYPE_OWNED && - arg->sem_type->elem && arg->sem_type->elem->kind==FE_TYPE_SLICE) - fputs(".p",e->out); - fputc(')',e->out); - if (arg->kind==FE_N_IDENT) { - fputs(", ",e->out); emit_lvalue(e,arg); - if (arg->sem_type && arg->sem_type->elem && - arg->sem_type->elem->kind==FE_TYPE_SLICE) fputs(".p=0",e->out); - else fputs("=0",e->out); - fputs(", fe_live_",e->out); fputs(cname(arg,"owned"),e->out); - fputs("=0",e->out); - } - fputs(", 0)",e->out); - return; - } - 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); - return; - } - 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) { - 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); - fputs(result->alloc_cname ? result->alloc_cname : "fe_bad_alloc",e->out); - fputc('(',e->out); emit_expr(e,n->children); fputc(')',e->out); - return; - } - 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; - fputs(result && result->alloc_cname ? result->alloc_cname : - "fe_bad_slice_alloc",e->out); - fputc('(',e->out); emit_expr(e,n->children->next); fputc(')',e->out); - return; - } - if (n->text && (strcmp(n->text,"@print")==0 || - strcmp(n->text,"@fprint")==0 || strcmp(n->text,"@sprint")==0)) { - emit_m4_builtin(e,n); - return; - } - 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); - return; - } - 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 && - !n->a->a->sem_type->is_error) { - v=fe_type_variant(n->a->a->sem_type,n->a->b ? n->a->b->text : ""); - fputs(v ? v->maker : "fe_bad_variant",e->out); - } 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); - return; - } else if (n->a) emit_expr(e,n->a); - else fputs(n->text ? n->text : "fe_builtin",e->out); - if (!special) { - if (n->sem_decl && n->sem_decl->kind==FE_N_FN && n->sem_decl->a) - call_param=n->sem_decl->a->children; - fputc('(',e->out); - for (x=n->children;x;x=x->next) { - FeType *want=call_param && call_param->a ? - fe_type_from_ast(&e->check->types,call_param->a) : 0; - if (x!=n->children) fputs(", ",e->out); - if (want && want->kind==FE_TYPE_SLICE && !want->ref_mut && - x->sem_type && x->sem_type->kind==FE_TYPE_SLICE && - x->sem_type->ref_mut) { - fputs(want->maker,e->out); fputc('(',e->out); - emit_expr(e,x); fputs(".p, ",e->out); - emit_expr(e,x); fputs(".n)",e->out); - } else emit_expr(e,x); - if (call_param) call_param=call_param->next; - } - fputc(')',e->out); - } -} - -/* `dst = ;` as a statement, avoiding a comma expression on the right. - - A consumed identifier lowers to `(fe_live_x=0, x)`. When dst is a struct, - Watcom crashes on a struct assignment whose right side is a comma expression - -- hard enough to take DOSBox-X down with it -- so clear the move flag as its - own statement and assign the plain name. */ -static void m7_emit_assign_stmt(FeEmitter *e, const char *dst, FeNode *src) -{ - if (src && src->kind==FE_N_IDENT && (src->flags & FE_OWN_NODE_CONSUMED) && - src->sem_type && type_needs_drop(src->sem_type)) { - pad(e); fputs("fe_live_",e->out); fputs(cname(src,"owned"),e->out); - fputs("=0;\n",e->out); - pad(e); fputs(dst,e->out); fputs(" = ",e->out); - fputs(cname(src,"fe_missing"),e->out); fputs(";\n",e->out); - return; - } - pad(e); fputs(dst,e->out); fputs(" = ",e->out); - emit_expr(e,src); fputs(";\n",e->out); -} - -static void m7_emit_raw_expr(FeEmitter *e, FeNode *n) -{ - FeNode *x; - FeType *bt; - FeVariantType *v; - const char *op; - FeM7LazyKind lazy; - if (!n) { fputs("0",e->out); return; } - if (!m7_node_feature(n)) { - emit_expr_m6(e,n); - return; - } - switch (n->kind) { - case FE_N_IDENT: - if ((n->flags & FE_OWN_NODE_CONSUMED) && 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 (fe_m7_is_null(n)) fputs("0",e->out); - else emit_expr_m6(e,n); - break; - case FE_N_UNARY: - op=n->text ? n->text : ""; - 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) { - fputs("(",e->out); fputs(n->aux_cname,e->out); - fputs(" = ",e->out); emit_expr(e,n->a); fputs(", ",e->out); - fputs(n->aux_cname,e->out); fputs(".v)",e->out); - } else emit_expr(e,n->a); - } else if (strcmp(op,"&")==0 || strcmp(op,"&mut")==0) { - fputs("(&",e->out); emit_lvalue(e,n->a); fputc(')',e->out); - } else if (strcmp(op,"not")==0) { - fputs("(!",e->out); emit_expr(e,n->a); fputc(')',e->out); - } else { - fputc('(',e->out); fputs(op,e->out); emit_expr(e,n->a); - fputc(')',e->out); - } - break; - case FE_N_BINARY: - lazy=fe_m7_lazy_kind(n); - if (lazy==FE_M7_LAZY_ORELSE) { - FeType *opt=n->a ? n->a->sem_type : 0; - fputs("((",e->out); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs("), ",e->out); - m7_emit_present(e,opt,n->aux_cname); fputs(" ? ",e->out); - if (fe_m7_optional_uses_niche(opt->elem)) fputs(n->aux_cname,e->out); - else { fputs(n->aux_cname,e->out); fputs(".v",e->out); } - fputs(" : ",e->out); emit_expr(e,n->b); fputc(')',e->out); - } else if (lazy==FE_M7_LAZY_CATCH && !n->c) { - FeType *res=n->a ? n->a->sem_type : 0; - fputs("((",e->out); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs("), ",e->out); - if (res && res->error_value && res->error_value->kind==FE_TYPE_VOID) { - fputs(n->aux_cname,e->out); fputs(" ? ",e->out); - emit_expr(e,n->b); fputs(" : 0)",e->out); - } else { - fputs(n->aux_cname,e->out); fputs(".e ? ",e->out); - emit_expr(e,n->b); fputs(" : ",e->out); - fputs(n->aux_cname,e->out); fputs(".v)",e->out); - } - } else if (lazy==FE_M7_LAZY_CATCH && n->c) { - fputs("0",e->out); - } else if ((n->text && (strcmp(n->text,"==")==0 || - strcmp(n->text,"!=")==0)) && - (fe_m7_is_null(n->a) || fe_m7_is_null(n->b))) { - FeNode *value=fe_m7_is_null(n->a) ? n->b : n->a; - FeType *opt=value ? value->sem_type : 0; - if (opt && opt->kind==FE_TYPE_OPTIONAL && - !fe_m7_optional_uses_niche(opt->elem)) { - fputs("(!",e->out); emit_expr(e,value); fputs(".has)",e->out); - if (strcmp(n->text,"!=")==0) { - fputs(" == 0",e->out); - } - } else { - fputc('(',e->out); emit_expr(e,value); - fputs(strcmp(n->text,"==")==0 ? " == 0)" : " != 0)",e->out); - } - } else { - op=n->text ? n->text : "+"; - fputc('(',e->out); emit_expr(e,n->a); - if (strcmp(op,"and")==0) fputs(" && ",e->out); - else if (strcmp(op,"or")==0) fputs(" || ",e->out); - else fputs(op,e->out); - emit_expr(e,n->b); fputc(')',e->out); - } - break; - case FE_N_MEMBER: - bt=n->a ? n->a->sem_type : 0; - if (n->text && strcmp(n->text,".?")==0 && bt && - bt->kind==FE_TYPE_OPTIONAL) { - fputs(bt->unwrap_cname,e->out); fputc('(',e->out); - emit_expr(e,n->a); fputc(')',e->out); - } else if (bt && bt->kind==FE_TYPE_ENUM && bt->is_error) { - m7_emit_error_member(e,n); - } else if ((bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) && - n->b && n->b->text && strcmp(n->b->text,"^")==0) { - fputs("(*",e->out); emit_expr(e,n->a); fputc(')',e->out); - } else if (bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) { - emit_expr(e,n->a); fputs("->",e->out); - fputs(n->b && n->b->text ? n->b->text : "member",e->out); - } else if (bt && bt->kind==FE_TYPE_ENUM && !bt->is_error) { - v=fe_type_variant(bt,n->b ? n->b->text : ""); - if (v) { fputs(v->maker,e->out); fputs("()",e->out); } - else fputs("0",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; - case FE_N_CALL: - m7_emit_call(e,n); - break; - case FE_N_TYPE: - if (n->text && strcmp(n->text,"as")==0) { - fputs("((",e->out); fputs(m7_c_type(e,n->sem_type),e->out); - fputc(')',e->out); emit_expr(e,n->a); fputc(')',e->out); - } else emit_expr(e,n->a); - break; - case FE_N_INDEX: - bt=n->a ? n->a->sem_type : 0; - if (n->c || !n->b) { - emit_expr_m6(e,n); - } else if (bt && bt->indexer) { - fputs(bt->indexer,e->out); fputc('(',e->out); - emit_expr(e,n->a); fputs(", ",e->out); emit_expr(e,n->b); - fputc(')',e->out); - } else fputs("0",e->out); - break; - case FE_N_STRUCT_INIT: - case FE_N_ARRAY_INIT: - emit_expr_m6(e,n); - break; - default: - emit_expr_m6(e,n); - break; - } - (void)x; -} - -/* Emit the initializer for a `const` declaration. - - A string literal normally lowers to a maker call, but C89 requires the - initializer of an aggregate -- at file scope and for automatics alike -- to be - a constant expression, and the build runs with -za. Emit the slice braced - instead. Returns non-zero when it handled the initializer. */ -static int m7_emit_const_init(FeEmitter *e, FeNode *n) -{ - if (n->kind!=FE_N_CONST || !n->b || n->b->kind!=FE_N_LITERAL || - !n->b->text || n->b->text[0]!='"') return 0; - fputs("{ (const unsigned char*)",e->out); - emit_c_literal(e->out,n->b->text,1); - fputs(", sizeof(",e->out); - emit_c_literal(e->out,n->b->text,1); - fputs(")-1 }",e->out); - return 1; -} - -static void emit_decl(FeEmitter *e, FeNode *n) -{ - pad(e); fputs(m7_c_type(e,n->sem_type),e->out); fputc(' ',e->out); - fputs(cname(n,"fe_local"),e->out); - if (n->kind==FE_N_CONST && n->b) { - fputs(" = ",e->out); - if (!m7_emit_const_init(e,n)) emit_expr(e,n->b); - } - fputs(";\n",e->out); - if ((n->kind==FE_N_LET || n->kind==FE_N_VAR) && n->sem_type && - 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); - } -} - -static void emit_owned_live(FeEmitter *e, FeNode *n, int value) -{ - 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); - } -} - -static void emit_value_drop(FeEmitter *e, FeNode *n) -{ - FeType *t; - t=n ? n->sem_type : 0; - if (!n || !t || !type_needs_drop(t) || - (n->flags & FE_OWN_NODE_CONSUMED) || - (n->flags & FE_OWN_NODE_DEFER_CAPTURE)) return; - pad(e); fputs("if (fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fputs(") { ",e->out); - if (t->kind==FE_TYPE_OWNED) { - 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 if (t->drop_cname) { - fprintf(e->out,"%s(&%s); ",t->drop_cname,cname(n,"local")); - } - fputs("fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fputs("=0; }\n",e->out); -} - -static void emit_cleanup_block(FeEmitter *e, FeNode *n) -{ - FeNode *x; - unsigned count; - unsigned index; - unsigned seen; - unsigned depth; - count=0; - seen=0xffffffffU; - 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-1U) { - 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-1U]); -} - -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) -{ - FeType *ret=e->current_ret; - if (ret && ret->kind==FE_TYPE_ERROR_UNION && ret->error_value && - ret->error_value->kind!=FE_TYPE_VOID) { - fputs("return ",e->out); fputs(ret->none_cname,e->out); - fputc('(',e->out); fputs(error_expr,e->out); fputs(");\n",e->out); - } else { - fputs("return ",e->out); fputs(error_expr,e->out); fputs(";\n",e->out); - } -} - -static void m7_emit_try_error_check(FeEmitter *e, FeNode *n) -{ - FeType *result=n->a ? n->a->sem_type : 0; - pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - pad(e); fputs("if (",e->out); fputs(n->aux_cname,e->out); - if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(") {\n",e->out); ++e->indent; - emit_cleanup_all(e); - pad(e); - if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) { - char error[192]; - sprintf(error,"%s.e",n->aux_cname); - emit_error_return(e,error); - } else emit_error_return(e,n->aux_cname); - --e->indent; pad(e); fputs("}\n",e->out); -} - -static void m7_emit_catch_block(FeEmitter *e, FeNode *n, - FeNode *target) -{ - FeType *result=n->a ? n->a->sem_type : 0; - FeNode *binding=n->b; - pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - pad(e); fputs("if (",e->out); fputs(n->aux_cname,e->out); - if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(") {\n",e->out); ++e->indent; - if (binding && binding->cname) { - pad(e); fputs("unsigned short ",e->out); fputs(binding->cname,e->out); - fputs(" = ",e->out); fputs(n->aux_cname,e->out); - if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(";\n",e->out); - } - emit_stmt(e,n->c); - --e->indent; pad(e); fputs("}",e->out); - if (target && result && result->error_value && - result->error_value->kind!=FE_TYPE_VOID) { - fputs(" else {\n",e->out); ++e->indent; - pad(e); emit_lvalue(e,target); fputs(" = ",e->out); - fputs(n->aux_cname,e->out); fputs(".v;\n",e->out); - emit_owned_live(e,target,1); - --e->indent; pad(e); fputs("}",e->out); - } - fputc('\n',e->out); -} - -static void m7_emit_optional_match(FeEmitter *e, FeNode *n) -{ - FeType *opt=n->a ? n->a->sem_type : 0; - FeNode *arm; - FeNode *binding; - int first; - pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - first=1; - for (arm=n->children;arm;arm=arm->next) { - if (arm->text && strcmp(arm->text,"Some")==0) { - pad(e); if (!first) fputs("else ",e->out); - fputs("if (",e->out); m7_emit_present(e,opt,n->aux_cname); - fputs(") {\n",e->out); ++e->indent; - binding=arm->children; - if (binding && binding->cname) { - pad(e); fputs(m7_c_type(e,binding->sem_type),e->out); - fputc(' ',e->out); fputs(binding->cname,e->out); fputs(" = ",e->out); - m7_emit_payload_var(e,opt,n->aux_cname); fputs(";\n",e->out); - } - if (arm->a) emit_stmt(e,arm->a); - --e->indent; pad(e); fputs("}\n",e->out); - first=0; - } else if (arm->text && strcmp(arm->text,"None")==0) { - pad(e); if (!first) fputs("else ",e->out); - fputs("if (!",e->out); m7_emit_present(e,opt,n->aux_cname); - fputs(") {\n",e->out); ++e->indent; - if (arm->a) emit_stmt(e,arm->a); - --e->indent; pad(e); fputs("}\n",e->out); - first=0; - } else if (arm->text && strcmp(arm->text,"_")==0) { - pad(e); if (!first) fputs("else ",e->out); - fputs("{\n",e->out); ++e->indent; - if (arm->a) emit_stmt(e,arm->a); - --e->indent; pad(e); fputs("}\n",e->out); - first=0; - } - } -} - -static void m7_emit_if_let(FeEmitter *e, FeNode *n) -{ - FeType *opt=n->a ? n->a->sem_type : 0; - FeNode *binding=n->children; - int some=n->aux_text && strcmp(n->aux_text,"Some")==0; - pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - pad(e); fputs("if (",e->out); - if (!some) fputc('!',e->out); - m7_emit_present(e,opt,n->aux_cname); fputs(") {\n",e->out); - ++e->indent; - if (some && binding && binding->cname) { - pad(e); fputs(m7_c_type(e,binding->sem_type),e->out); fputc(' ',e->out); - fputs(binding->cname,e->out); fputs(" = ",e->out); - m7_emit_payload_var(e,opt,n->aux_cname); fputs(";\n",e->out); - } - if (n->b) emit_stmt(e,n->b); - --e->indent; pad(e); fputs("}",e->out); - if (n->c) { - fputs(" else ",e->out); - emit_stmt(e,n->c); - } - fputc('\n',e->out); -} - -static void emit_block(FeEmitter *e, FeNode *n) -{ - FeNode *x; - unsigned seen; - if (!n) { - pad(e); fputs("{}",e->out); return; - } - 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; - } - 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) { - if (e->current_fn->a) { - FeNode *p; - for (p=e->current_fn->a->children;p;p=p->next) - if (p->sem_type && type_needs_drop(p->sem_type)) { - pad(e); fputs("unsigned char fe_live_",e->out); - fputs(cname(p,"owned"),e->out); fputs("=1;\n",e->out); - } - } - m7_emit_temp_decls(e,n); - } - if (e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - pad(e); fputs(m7_c_type(e,e->current_ret),e->out); - fputs(" fe_return_value;\n",e->out); - } - seen=0; - for (x=n->children;x;x=x->next) { - ++seen; - if (e->block_depth) e->block_seen[e->block_depth-1U]=seen; - emit_stmt(e,x); - } - --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); fputs("return 0;\n",e->out); - e->fallthrough_block=0; - } - pad(e); fputc('}',e->out); -} - -static void emit_stmt(FeEmitter *e, FeNode *n) -{ - FeType *result; - if (!n) return; - switch (n->kind) { - case FE_N_BLOCK: - emit_block(e,n); fputc('\n',e->out); break; - case FE_N_LET: - case FE_N_VAR: - if (n->b) { - if (fe_m7_is_try(n->b)) { - m7_emit_try_error_check(e,n->b); - pad(e); emit_lvalue(e,n); fputs(" = ",e->out); - fputs(n->b->aux_cname,e->out); - result=n->b->a ? n->b->a->sem_type : 0; - if (result && result->error_value && - result->error_value->kind!=FE_TYPE_VOID) fputs(".v",e->out); - fputs(";\n",e->out); emit_owned_live(e,n,1); - } else if (n->b->kind==FE_N_BINARY && n->b->c && - fe_m7_lazy_kind(n->b)==FE_M7_LAZY_CATCH) { - m7_emit_catch_block(e,n->b,n); - } else { - pad(e); emit_lvalue(e,n); 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) emit_value_drop(e,n->a); - pad(e); emit_lvalue(e,n->a); fputc(' ',e->out); - fputs(n->text ? n->text : "=",e->out); fputc(' ',e->out); - 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: - if (fe_m7_is_try(n->a)) { - m7_emit_try_error_check(e,n->a); - } else if (n->a && n->a->kind==FE_N_BINARY && n->a->c && - fe_m7_lazy_kind(n->a)==FE_M7_LAZY_CATCH) { - m7_emit_catch_block(e,n->a,0); - } else { - pad(e); emit_expr(e,n->a); fputs(";\n",e->out); - } - break; - case FE_N_DEFER: - break; - case FE_N_RETURN: - if (n->a && fe_m7_is_try(n->a)) { - FeNode *tr=n->a; - FeType *res=tr->a ? tr->a->sem_type : 0; - m7_emit_try_error_check(e,tr); - if (e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - pad(e); fputs("fe_return_value = ",e->out); - if (e->current_ret->kind==FE_TYPE_ERROR_UNION && - e->current_ret->error_value && - e->current_ret->error_value->kind!=FE_TYPE_VOID) { - fputs(e->current_ret->maker,e->out); fputs("(0, ",e->out); - fputs(tr->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".v",e->out); - fputc(')',e->out); - } else { - fputs(tr->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".v",e->out); - } - fputs(";\n",e->out); - } - emit_cleanup_all(e); - pad(e); fputs("return fe_return_value;\n",e->out); - } else if (n->a && n->a->kind==FE_N_BINARY && n->a->c && - fe_m7_lazy_kind(n->a)==FE_M7_LAZY_CATCH) { - /* A value catch-block is lowered as a temporary local success - assignment; the handler is required by the checker to exit. */ - FeNode *cx=n->a; - FeType *res=cx->a ? cx->a->sem_type : 0; - pad(e); fputs(cx->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,cx->a); fputs(";\n",e->out); - pad(e); fputs("if (",e->out); fputs(cx->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(") {\n",e->out); ++e->indent; - if (cx->b && cx->b->cname) { - pad(e); fputs("unsigned short ",e->out); fputs(cx->b->cname,e->out); - fputs(" = ",e->out); fputs(cx->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(";\n",e->out); - } - emit_stmt(e,cx->c); - --e->indent; pad(e); fputs("}\n",e->out); - pad(e); fputs("fe_return_value = ",e->out); - fputs(cx->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".v",e->out); - fputs(";\n",e->out); - emit_cleanup_all(e); - pad(e); fputs("return fe_return_value;\n",e->out); - } else if (n->a && n->a->kind==FE_N_BINARY && !n->a->c && - fe_m7_lazy_kind(n->a)==FE_M7_LAZY_CATCH && - e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - /* Short catch in return position. As an expression this lowers to - `((tmp = X), tmp.e ? fallback : tmp.v)`, and when X carries a move - it becomes a struct assignment whose right side is itself a comma - expression -- which crashes wcc386 hard enough to take DOSBox-X - down with it. The same lowering as statements is also plainer. */ - FeNode *cx=n->a; - FeType *res=cx->a ? cx->a->sem_type : 0; - int has_value=res && res->error_value && - res->error_value->kind!=FE_TYPE_VOID; - m7_emit_assign_stmt(e,cx->aux_cname,cx->a); - pad(e); fputs("if (",e->out); fputs(cx->aux_cname,e->out); - if (has_value) fputs(".e",e->out); - fputs(") {\n",e->out); ++e->indent; - pad(e); fputs("fe_return_value = ",e->out); - emit_expr(e,cx->b); fputs(";\n",e->out); - --e->indent; pad(e); fputs("} else {\n",e->out); ++e->indent; - pad(e); fputs("fe_return_value = ",e->out); - fputs(cx->aux_cname,e->out); - if (has_value) fputs(".v",e->out); - fputs(";\n",e->out); - --e->indent; pad(e); fputs("}\n",e->out); - emit_cleanup_all(e); - pad(e); fputs("return fe_return_value;\n",e->out); - } else { - if (n->a && e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - pad(e); fputs("fe_return_value = ",e->out); - emit_expr(e,n->a); 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: - if (n->text && strcmp(n->text,"if let")==0) { - m7_emit_if_let(e,n); - } else { - pad(e); fputs("if (",e->out); emit_expr(e,n->a); fputs(") ",e->out); - emit_block(e,n->b); - if (n->c) { - fputs(" else ",e->out); - if (n->c->kind==FE_N_IF) emit_stmt(e,n->c); - else emit_block(e,n->c); - } - fputc('\n',e->out); - } - break; - case FE_N_MATCH: - if (n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_OPTIONAL) - m7_emit_optional_match(e,n); - else emit_match_m6(e,n,0); - break; - case FE_N_BREAK: - case FE_N_CONTINUE: - if (e->loop_depth) { - emit_cleanup_to(e,e->loop_floor[e->loop_depth-1U]); - pad(e); fputs(n->kind==FE_N_BREAK ? "break;\n" : "continue;\n",e->out); - } - break; - case FE_N_WHILE: - pad(e); 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; - emit_block(e,n->b); - if (e->loop_depth) --e->loop_depth; - fputc('\n',e->out); - break; - case FE_N_FOR: - emit_stmt_m6(e,n); - break; - default: - emit_stmt_m6(e,n); - break; - } -} - -static void emit_fn(FeEmitter *e, FeNode *fn, int prototype) -{ - FeNode *p; - FeType *old_ret; - FeNode *old_fn; - fputs(m7_c_type(e,fn->sem_type ? fn->sem_type : - (fn->b ? fe_type_from_ast(&e->check->types,fn->b) : - fe_type_intern(&e->check->types,"void"))),e->out); - fputc(' ',e->out); fputs(cname(fn,"fe_fn"),e->out); fputc('(',e->out); - p=fn->a ? fn->a->children : 0; - if (!p) fputs("void",e->out); - while (p) { - if (p!=fn->a->children) fputs(", ",e->out); - fputs(m7_c_type(e,p->sem_type ? p->sem_type : - fe_type_from_ast(&e->check->types,p->a)),e->out); - fputc(' ',e->out); fputs(cname(p,"fe_arg"),e->out); - p=p->next; - } - fputc(')',e->out); - if (prototype) { fputs(";\n",e->out); return; } - old_ret=e->current_ret; - old_fn=e->current_fn; - e->current_ret=fn->sem_type; - e->current_fn=fn; - m7_prepare_temps(e,fn->c); - fputc(' ',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; - e->current_fn=old_fn; - fputc('\n',e->out); -} - -static void emit_main_wrapper(FeEmitter *e, FeNode *fn) -{ - FeType *ret=fn->sem_type; - if (ret && ret->kind==FE_TYPE_ERROR_UNION && ret->error_value && - ret->error_value->kind!=FE_TYPE_VOID) { - fputs("int main(void) { ",e->out); fputs(m7_c_type(e,ret),e->out); - fputs(" r = ",e->out); fputs(cname(fn,"fe_main"),e->out); - fputs("(); return r.e ? 1 : 0; }\n",e->out); - } else emit_main_wrapper_m6(e,fn); -} - -void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check, - unsigned pointer_bits, int no_checks) -{ - fe_emit_c_init_m6(e,out,check,pointer_bits,no_checks); -} - -void fe_emit_c_program(FeEmitter *e) -{ - FeNode *n; - FeNode *main_fn; - FeType *type; - int need_m4; - if (!m7_program_feature(e)) { - fe_emit_c_program_m6(e); - return; - } - main_fn=0; - need_m4=node_uses_m4(e->check->ast->root); - for (type=e->check->types.types;type;type=type->next) - if (strcmp(type->name,"io.Writer")==0) need_m4=1; - /* See emit_c.c: stdio only comes in with the M4 writer runtime. */ - fputs("/* generated by fec M7 */\n#include \n#include \n#include \n",e->out); - if (need_m4) fputs("#include \n",e->out); - fputs("typedef char fe_assert_u8[(sizeof(unsigned char)==1) ? 1 : -1];\ntypedef char fe_assert_u16[(sizeof(unsigned short)==2) ? 1 : -1];\ntypedef char fe_assert_u32[(sizeof(unsigned long)==4) ? 1 : -1];\n",e->out); - if (e->pointer_bits==16) - 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(); }\nstatic unsigned short fe_error_temp;\n\n",e->out); - emit_type_defs(e); - if (need_m4) emit_m4_runtime(e); - m7_emit_type_helpers(e); - for (n=e->check->ast->root ? e->check->ast->root->children : 0;n;n=n->next) { - if (n->kind==FE_N_GLOBAL || n->kind==FE_N_CONST) { - fputs(m7_c_type(e,n->sem_type),e->out); fputc(' ',e->out); - fputs(cname(n,"fe_global"),e->out); - if (n->b) { - fputs(" = ",e->out); - if (!m7_emit_const_init(e,n)) emit_expr(e,n->b); - } - fputs(";\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,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); } -}