diff --git a/fec/src/emit_c.c b/fec/src/emit_c.c index b168c39..1fc185b 100644 --- a/fec/src/emit_c.c +++ b/fec/src/emit_c.c @@ -111,74 +111,7 @@ static FeNode *find_drop_method(FeEmitter *e, const char *name) return 0; } -static void emit_drop_fields(FeEmitter *e, FeType *t) -{ - unsigned i; - FeType *ft; - for (i=t->field_count; i>0; --i) { - ft=t->fields[i-1].type; - if (!type_needs_drop(ft)) continue; - if (ft->kind==FE_TYPE_OWNED) { - fputs("if (self->",e->out); fputs(t->fields[i-1].name,e->out); - if(ft->elem && ft->elem->kind==FE_TYPE_SLICE) fputs(".p",e->out); - fputs(") { ",e->out); - if(ft->elem && ft->elem->kind==FE_TYPE_SLICE) { - fputs("free(self->",e->out); fputs(t->fields[i-1].name,e->out); - fputs(".p); self->",e->out); fputs(t->fields[i-1].name,e->out); - fputs(".p=0; ",e->out); - } else if (ft->elem && type_needs_drop(ft->elem) && ft->elem->drop_cname) { - fprintf(e->out,"%s(self->%s); ",ft->elem->drop_cname,t->fields[i-1].name); - } - if(!(ft->elem && ft->elem->kind==FE_TYPE_SLICE)) { - fputs("free(self->",e->out); fputs(t->fields[i-1].name,e->out); - fputs("); self->",e->out); fputs(t->fields[i-1].name,e->out); - fputs("=0; ",e->out); - } - fputs("}\n",e->out); - } else if (ft->kind==FE_TYPE_STRUCT && ft->drop_cname) { - fprintf(e->out,"%s(&self->%s);\n",ft->drop_cname,t->fields[i-1].name); - } else if (ft->kind==FE_TYPE_ARRAY && ft->drop_cname) { - fprintf(e->out,"%s(&self->%s);\n",ft->drop_cname,t->fields[i-1].name); - } - } -} -static void emit_drop_helpers(FeEmitter *e) -{ - FeType *t; - FeNode *method; - for (t=e->check->types.types; t; t=t->next) - if(t->kind==FE_TYPE_STRUCT && (method=find_drop_method(e,t->name))!=0) - fprintf(e->out,"void %s(%s *self);\n", - cname(method,"fe_drop_method"),t->cname); - for (t=e->check->types.types; t; t=t->next) - if ((t->kind==FE_TYPE_STRUCT || t->kind==FE_TYPE_ARRAY) && - type_needs_drop(t) && t->drop_cname) - fprintf(e->out,"static void %s(%s *self);\n",t->drop_cname,t->cname); - for (t=e->check->types.types; t; t=t->next) { - if (t->kind!=FE_TYPE_STRUCT || !type_needs_drop(t) || !t->drop_cname) continue; - fprintf(e->out,"static void %s(%s *self) {\n",t->drop_cname,t->cname); - method=find_drop_method(e,t->name); - if (method) { - fprintf(e->out,"%s(self);\n",cname(method,"fe_drop_method")); - } - emit_drop_fields(e,t); - fputs("}\n",e->out); - } - for (t=e->check->types.types; t; t=t->next) - if (t->kind==FE_TYPE_ARRAY && type_needs_drop(t) && t->drop_cname) { - fprintf(e->out,"static void %s(%s *self) { unsigned long i; for (i=0; i<%lu; ++i) { ", - t->drop_cname,t->cname,t->length); - if (t->elem->kind==FE_TYPE_OWNED) { - fputs("if (self->a[i]) { ",e->out); - if (t->elem->elem && type_needs_drop(t->elem->elem) && t->elem->elem->drop_cname) - fprintf(e->out,"%s(self->a[i]); ",t->elem->elem->drop_cname); - fputs("free(self->a[i]); self->a[i]=0; }",e->out); - } else if (t->elem->drop_cname) - fprintf(e->out,"%s(&self->a[i]);",t->elem->drop_cname); - fputs(" } }\n",e->out); - } -} /* SPEC 12.3 lists `trim` among the built-in alias methods on `str`, called as `line.trim()`. The checker accepts it; this emits the lowering. Only the @@ -196,96 +129,6 @@ static int node_uses_trim(FeNode *n) return 0; } -static void emit_type_helpers(FeEmitter *e) -{ - FeType *t; - unsigned i,j; - for(t=e->check->types.types;t;t=t->next) { - if (t->kind==FE_TYPE_ERROR_UNION && t->error_value && - t->error_value->kind!=FE_TYPE_VOID) { - fprintf(e->out,"static %s %s(unsigned short e, %s v) { %s r; r.e=e; r.v=v; return r; }\n", - t->cname,t->maker,fe_type_c_name(t->error_value,e->pointer_bits),t->cname); - if (t->error_value->kind==FE_TYPE_OWNED && - t->error_value->elem && - t->error_value->elem->kind==FE_TYPE_SLICE) { - FeType *item=t->error_value->elem->elem; - fprintf(e->out,"static %s %s(unsigned long n) { %s r; r.v.p=(%s*)malloc(sizeof(%s)*n); r.v.n=n; r.e=(r.v.p || !n) ? 0 : 1; return r; }\n", - t->cname,t->alloc_cname,t->cname, - fe_type_c_name(item,e->pointer_bits), - fe_type_c_name(item,e->pointer_bits)); - } else if (t->error_value->kind==FE_TYPE_OWNED) { - fprintf(e->out,"static %s %s(%s v) { %s r; r.v=(%s)malloc(sizeof(%s)); if(r.v) *r.v=v; r.e=r.v ? 0 : 1; return r; }\n", - t->cname,t->alloc_cname, - fe_type_c_name(t->error_value->elem,e->pointer_bits),t->cname, - fe_type_c_name(t->error_value,e->pointer_bits), - fe_type_c_name(t->error_value->elem,e->pointer_bits)); - } - } - } - for(t=e->check->types.types;t;t=t->next) { - if(t->replace_cname) { - const char *ct=fe_type_c_name(t,e->pointer_bits); - fprintf(e->out,"static %s %s(%s *dst, %s value) { %s old=*dst; *dst=value; return old; }\n", - ct,t->replace_cname,ct,ct,ct); - } - if(t->kind==FE_TYPE_STRUCT && t->maker) { - fprintf(e->out,"static %s %s(",t->cname,t->maker); - for(i=0;ifield_count;i++) { if(i) fputs(", ",e->out); fputs(fe_type_c_name(t->fields[i].type,e->pointer_bits),e->out); fprintf(e->out," p%u",i); } - fputs(") { ",e->out); fprintf(e->out,"%s v;",t->cname); - 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(",t->cname,t->maker); - for(i=0;ilength;i++) { if(i) fputs(", ",e->out); fputs(fe_type_c_name(t->elem,e->pointer_bits),e->out); fprintf(e->out," p%u",i); } - fputs(") { ",e->out); fprintf(e->out,"%s v;",t->cname); - 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) { - for(i=0;ivariant_count;i++) { - FeVariantType *v=&t->variants[i]; - fprintf(e->out,"static %s %s(",t->cname,v->maker); - for(j=0;jfield_count;j++) { if(j) fputs(", ",e->out); fputs(fe_type_c_name(v->fields[j].type,e->pointer_bits),e->out); fprintf(e->out," p%u",j); } - fputs(") { ",e->out); fprintf(e->out,"%s x; x.tag=%u;",t->cname,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); - } - } - } - emit_drop_helpers(e); - for(t=e->check->types.types;t;t=t->next) { - if (t->kind==FE_TYPE_ARRAY && t->indexer) { - fprintf(e->out,"static %s %s(%s x, unsigned long i) { ", - fe_type_c_name(t->elem,e->pointer_bits),t->indexer,t->cname); - if(!e->no_checks) fprintf(e->out,"if (i >= %lu) fe_trap_bounds(); ",t->length); - fprintf(e->out,"return x.a[i]; }\n"); - fprintf(e->out,"static %s %s(%s *x, unsigned long a, unsigned long b) { ", - fe_type_c_name(fe_type_slice(&e->check->types,t->elem),e->pointer_bits),t->slicer,t->cname); - if(!e->no_checks) fputs("if (a > b || b > ",e->out), fprintf(e->out,"%lu",t->length), fputs(") fe_trap_bounds(); ",e->out); - fprintf(e->out,"return %s(x->a+a,b-a); }\n",fe_type_slice(&e->check->types,t->elem)->maker); - fprintf(e->out,"static %s %s(%s *x) { return %s(x,0,%lu); }\n",fe_type_c_name(fe_type_slice(&e->check->types,t->elem),e->pointer_bits),t->full_slicer,t->cname,t->slicer,t->length); - fprintf(e->out,"static %s %s(%s *x, unsigned long a) { return %s(x,a,%lu); }\n",fe_type_c_name(fe_type_slice(&e->check->types,t->elem),e->pointer_bits),t->tail_slicer,t->cname,t->slicer,t->length); - } else if (t->kind==FE_TYPE_SLICE && t->indexer) { - fprintf(e->out,"static %s %s(%s x, unsigned long i) { ", - fe_type_c_name(t->elem,e->pointer_bits),t->indexer,t->cname); - if(!e->no_checks) fputs("if (i >= x.n) fe_trap_bounds(); ",e->out); - fputs("return x.p[i]; }\n",e->out); - fprintf(e->out,"static %s %s(%s x, unsigned long a, unsigned long b) { ", - fe_type_c_name(t,e->pointer_bits),t->slicer,t->cname); - 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",t->cname,t->full_slicer,t->cname,t->slicer); - fprintf(e->out,"static %s %s(%s x, unsigned long a) { return %s(x,a,x.n); }\n",t->cname,t->tail_slicer,t->cname,t->slicer); - if (node_uses_trim(e->check->ast->root) && !t->ref_mut) { - fprintf(e->out, - "static %s fe_trim_%s(%s s) { unsigned long a=0; unsigned long b=s.n;" - " while (aa && (s.p[b-1]==' '||s.p[b-1]=='\\t'||s.p[b-1]=='\\r'||s.p[b-1]=='\\n')) --b;" - " return %s(s.p+a,b-a); }\n", - t->cname,t->cname,t->cname,t->maker); - } - } - } -} static void emit_m4_runtime(FeEmitter *e) { diff --git a/tools/tests/test_host_syntax.py b/tools/tests/test_host_syntax.py index 56408e5..8fb4972 100644 --- a/tools/tests/test_host_syntax.py +++ b/tools/tests/test_host_syntax.py @@ -7,13 +7,17 @@ declaration or a signature that disagrees with its definition used to surface only after a DOSBox-X boot and a full compiler build; here it surfaces in about a second, with the line number. -It uses the same Open Watcom the project targets, just the Windows-hosted build, -and the same strictness as ``fec/build-dos.bat`` (``-za -wx -wcd=202``). The -target differs -- wcc386 is 32-bit where the DOS build is 16-bit large model -- -so this catches syntax, types and declarations, not code generation or memory -model problems. +It runs the pinned toolchain's Windows-hosted 16-bit driver with the exact +command ``fec/build-dos.bat`` uses, so the diagnostics match what the DOS build +sees. A 32-bit compile is not equivalent: it misses warnings that only the +16-bit large model reports, which is how a dead function survived the M7 +unification with a clean 32-bit check. -Skipped when Watcom is not installed, so the suite still runs anywhere. +What it still cannot see is the DOS environment itself -- memory limits, the +command line length, the filesystem. The DOS build and the milestone suite +remain the gate. + +Skipped when the toolchain has not been downloaded, so the suite runs anywhere. """ from __future__ import annotations @@ -34,11 +38,13 @@ SOURCES = ("arena", "diag", "lexer", "ast", "parser", "types", "m7", "own", def _watcom() -> Path | None: + """Prefer the toolchain the runner pins over anything installed system-wide.""" root = os.environ.get("WATCOM") - candidates = [Path(root)] if root else [] - candidates.append(Path("C:/WATCOM19")) + candidates = [ROOT / ".dosboxx" / "watcom"] + if root: + candidates.append(Path(root)) for base in candidates: - if (base / "binnt" / "wcc386.exe").is_file(): + if (base / "binnt" / "wcl.exe").is_file(): return base return None @@ -47,7 +53,7 @@ def _watcom() -> Path | None: def watcom() -> Path: base = _watcom() if base is None: - pytest.skip("Open Watcom is not installed on the host; set WATCOM to enable") + pytest.skip("run `ferro-dos setup` to download the pinned toolchain") return base @@ -64,14 +70,18 @@ def test_source_compiles_clean(name: str, watcom: Path, objdir: Path) -> None: env = dict(os.environ) env["WATCOM"] = os.fspath(watcom) env["INCLUDE"] = os.fspath(watcom / "h") + env["PATH"] = os.pathsep.join( + [os.fspath(watcom / "binnt"), env.get("PATH", "")]) + # The same command build-dos.bat runs, minus the object name. completed = subprocess.run( - [os.fspath(watcom / "binnt" / "wcc386.exe"), "-q", "-za", "-wx", - "-wcd=202", "-zq", f"-i={SRC}", os.fspath(source)], + [os.fspath(watcom / "binnt" / "wcl.exe"), "-q", "-za", "-wx", + "-bt=dos", "-ml", "-k32768", "-c", f"-i={SRC}", os.fspath(source)], cwd=objdir, capture_output=True, text=True, env=env, timeout=120, ) output = (completed.stdout + completed.stderr).strip() - # -wx keeps warnings meaningful, so treat any diagnostic as a failure: the - # DOS build runs the same flags and stops on them. + # -wx keeps warnings meaningful, so treat any diagnostic as a failure. The + # DOS build prints them to a screen nobody reads, which is how they + # accumulate unnoticed. assert completed.returncode == 0 and not output, ( f"{name}.c does not compile clean\n{output}" )