dev: compile the generated C, and fix what that caught

Thirty-one cases only ever emitted C and never built it -- m5 owned/defer, and
every ok* acceptance fixture in m6 and m7. They asserted that fec produced a
file, not that the file was a program, so a malformed emission sat unnoticed.
Compile each one with wcl386 -c. Fixtures without a main cannot be run, so this
is the floor for them; it is a conformance check on the backend's output, not an
assertion about C, so a future backend swaps the command rather than the intent.

It found two defects in the M6 emitter immediately, both predating M7:

&s[0] took the address of an rvalue. Borrowing went through emit_expr, which
lowers an index to the bounds-checking accessor, so `&s[0]` became
`&fe_idx_slice_type_2(s, 0)`. emit_lvalue spells the same element as `s.p[0]`.
The M7 emitter already did this correctly; only the M6 path was wrong.

line.trim() was never lowered. SPEC lists trim among the built-in alias methods
on str and the checker accepts it, but no emitter case existed, so it emitted
`fe_l_line_0.trim()` -- a member access on a slice struct. Emit the helper, and
only for programs that actually trim.

Also set core=dynamic and cycles=max. core=auto uses the interpreter in real
mode, which is where the 16-bit compiler build spends its time; nothing here is
timing sensitive. m6 drops from 31s to 17s. Raise the DOSBox timeout to match a
whole-suite run, which now pays a process spawn per compile check.

Verified: --only m6, 57 passed.
This commit is contained in:
2026-08-17 01:14:47 +09:00
parent 23079ba9fb
commit aaf31302d5
3 changed files with 90 additions and 11 deletions
+39 -1
View File
@@ -199,6 +199,22 @@ static void emit_drop_helpers(FeEmitter *e)
}
}
/* 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
helper for slice types actually reached by a trim call is emitted, so a
program that never trims does not carry it. */
static int node_uses_trim(FeNode *n)
{
FeNode *x;
if (!n) return 0;
if (n->kind==FE_N_CALL && n->a && n->a->kind==FE_N_MEMBER &&
n->a->b && n->a->b->text && strcmp(n->a->b->text,"trim")==0) return 1;
if (node_uses_trim(n->a) || node_uses_trim(n->b) || node_uses_trim(n->c))
return 1;
for (x=n->children; x; x=x->next) if (node_uses_trim(x)) return 1;
return 0;
}
static void emit_type_helpers(FeEmitter *e)
{
FeType *t;
@@ -278,6 +294,14 @@ static void emit_type_helpers(FeEmitter *e)
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 (a<b && (s.p[a]==' '||s.p[a]=='\\t'||s.p[a]=='\\r'||s.p[a]=='\\n')) ++a;"
" while (b>a && (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);
}
}
}
}
@@ -812,7 +836,12 @@ static void emit_expr(FeEmitter *e, FeNode *n)
fputc('(', e->out);
fputs(strcmp(op,"&mut")==0 ? "&" : op, e->out);
}
emit_expr(e, n->a);
/* Borrowing needs a place, not a value. emit_expr lowers an index to
the bounds-checking accessor, and the address of that call is not an
lvalue -- `&s[0]` became `&fe_idx_slice_type_2(s, 0)`, which C
rejects. emit_lvalue spells the same element as `s.p[0]`. */
if (strcmp(op,"&")==0 || strcmp(op,"&mut")==0) emit_lvalue(e, n->a);
else emit_expr(e, n->a);
fputc(')', e->out);
break;
case FE_N_BINARY:
@@ -871,6 +900,15 @@ static void emit_expr(FeEmitter *e, FeNode *n)
fputc('(',e->out); emit_expr(e,n->children->next); fputc(')',e->out);
special=1;
}
else if(n->a && n->a->kind==FE_N_MEMBER && n->a->b && n->a->b->text &&
strcmp(n->a->b->text,"trim")==0 && !n->children &&
n->a->a && n->a->a->sem_type &&
n->a->a->sem_type->kind==FE_TYPE_SLICE &&
n->a->a->sem_type->cname) {
fputs("fe_trim_",e->out); fputs(n->a->a->sem_type->cname,e->out);
fputc('(',e->out); emit_expr(e,n->a->a); fputc(')',e->out);
special=1;
}
else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a &&
n->a->a->kind==FE_N_IDENT && n->a->a->text &&
strcmp(n->a->a->text,"mem")==0 && n->a->b &&