ww: finish initialization validation parity
This commit is contained in:
@@ -3193,6 +3193,10 @@ static int
|
||||
init_tagged_raw_static(Type *u, Node *n)
|
||||
{
|
||||
Node *r = init_strip_cast(n);
|
||||
/* Cstage keeps true/false as untyped-bool, distinct from the concrete
|
||||
* bool variant selected by runtime boxing. Keep boolean payloads on that
|
||||
* runtime path in both stages so package artifacts remain identical. */
|
||||
if (r != NULL && (r->kind == N_TRUE || r->kind == N_FALSE)) return 0;
|
||||
Type *ru = type_chase_named(r ? r->type : NULL);
|
||||
u64 ignored;
|
||||
return u != NULL && u->kind == TY_TAGGED && !u->nullable
|
||||
@@ -3336,8 +3340,13 @@ init_expr_static(Type *t, Node *n)
|
||||
if (u != NULL && u->kind == TY_TAGGED && !u->nullable) {
|
||||
Type *ru = type_chase_named(r->type);
|
||||
if (!variant_present(u->params, r->type)) return 0;
|
||||
if (ru != NULL && (ru->kind == TY_STR || ru->kind == TY_SLICE))
|
||||
return r->kind == N_STRLIT;
|
||||
/* String carriers need a relocation-bearing tagged row. Cstage's
|
||||
* untyped string is not the concrete str variant here; keep every
|
||||
* such mutable value on the common runtime path rather than let one
|
||||
* stage classify the same spelling as static data. */
|
||||
if (ru != NULL && (ru->kind == TY_STR
|
||||
|| ru->kind == TY_UNTYPED_STR || ru->kind == TY_SLICE))
|
||||
return 0;
|
||||
return init_tagged_raw_static(u, r);
|
||||
}
|
||||
if (init_fnptr_static(r)) return 1;
|
||||
@@ -3345,6 +3354,67 @@ init_expr_static(Type *t, Node *n)
|
||||
return fold_int_literal(r, &ignored);
|
||||
}
|
||||
|
||||
/* A slice literal has no declared element count for WW's trailing `...`
|
||||
* repeat to fill. Keep this a checker-owned semantic rejection when a
|
||||
* mutable package let moves from static data to runtime initialization;
|
||||
* otherwise the backing counter drops the marker and silently publishes the
|
||||
* explicit prefix. Array repeats remain valid because their target length is
|
||||
* known. Recurse through the aggregate shapes package-init lowering owns so
|
||||
* a nested slice cannot bypass the same rule. */
|
||||
static int
|
||||
init_validate_slice_repeats(Checker *c, Type *want, Node *expr)
|
||||
{
|
||||
Node *r = init_strip_cast(expr);
|
||||
Type *u = type_chase_named(want);
|
||||
if (r == NULL || u == NULL) return 0;
|
||||
if (u->kind == TY_SLICE && r->kind == N_ARRLIT) {
|
||||
for (Node *e = r->list; e; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str != NULL
|
||||
&& strcmp(e->str, "...") == 0) {
|
||||
err(c, e->pos, "'...' repeat has no target length "
|
||||
"in a slice literal");
|
||||
return -1;
|
||||
}
|
||||
if (init_validate_slice_repeats(c, u->sub, e) < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (u->kind == TY_ARRAY && r->kind == N_ARRLIT) {
|
||||
for (Node *e = r->list; e; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str != NULL
|
||||
&& strcmp(e->str, "...") == 0)
|
||||
continue;
|
||||
if (init_validate_slice_repeats(c, u->sub, e) < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (u->kind == TY_STRUCT && r->kind == N_STRUCTLIT) {
|
||||
for (Node *e = r->list; e; e = e->next) {
|
||||
Tfield *field = NULL;
|
||||
for (Tfield *f = u->fields; f; f = f->next)
|
||||
if (e->str != NULL && f->name != NULL
|
||||
&& strcmp(e->str, f->name) == 0) {
|
||||
field = f;
|
||||
break;
|
||||
}
|
||||
if (field != NULL
|
||||
&& init_validate_slice_repeats(c, field->type,
|
||||
e->lhs) < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (u->kind == TY_TUPLE && r->kind == N_TUPLE) {
|
||||
Tparam *p = u->params;
|
||||
for (Node *e = r->list; e && p; e = e->next, p = p->next)
|
||||
if (init_validate_slice_repeats(c, p->type, e) < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct initwalkitem {
|
||||
Node *node;
|
||||
struct initwalkitem *next;
|
||||
@@ -3665,6 +3735,8 @@ init_lower_package(Checker *c, Node *file)
|
||||
return -1;
|
||||
}
|
||||
nvar++;
|
||||
if (init_validate_slice_repeats(c, d->type, d->rhs) < 0)
|
||||
continue;
|
||||
if (!init_expr_static(d->type, d->rhs)) {
|
||||
d->runtimeinit = 1;
|
||||
nruntime++;
|
||||
@@ -4601,8 +4673,19 @@ check_file(Checker *c, Node *file)
|
||||
* returns 0 silently and is left untouched).
|
||||
* Stamp LAST — the assignability check above
|
||||
* consumes the pre-stamp cexpr type. */
|
||||
/* An explicit scalar-to-tagged cast is also the carrier
|
||||
* selection. Folding it to an integer literal while keeping
|
||||
* the tagged result type makes cgen consume scalar registers
|
||||
* as an already-wide tagged ABI value. Mutable package lets
|
||||
* preserve the cast so the concrete carrier is boxed correctly;
|
||||
* const remains on its established static-only path. */
|
||||
Type *foldt = type_chase_named(d->type);
|
||||
int preserve_tagged_cast = d->op != TK_CONST && d->rhs
|
||||
&& d->rhs->kind == N_CAST && foldt != NULL
|
||||
&& foldt->kind == TY_TAGGED && !foldt->nullable;
|
||||
u64 dv;
|
||||
if (d->rhs && !fold_int_literal(d->rhs, &dv)
|
||||
if (d->rhs && !preserve_tagged_cast
|
||||
&& !fold_int_literal(d->rhs, &dv)
|
||||
&& eval_def_const(c, d->rhs, &dv, 0))
|
||||
stamp_intlit(c, d->rhs, dv);
|
||||
} else if (d->type && d->type->kind == TY_ARRAY
|
||||
|
||||
Reference in New Issue
Block a user