wcc/check: #14 reject def-global scalar str index (silent segfault) (both stages)

def S:str = "hi"; S[0] silently segfaulted: a def is a compile-time
constant, never materialized as DATA (unlike let), so indexing it emitted
an unbacked main.S(SB) reference -> cstage ran into frame garbage,
wwstage link-failed. str[i] itself is valid ww (a deliberate Go-like
str[i]->u8 byte-index that lib/strings compare/dup depend on), so the fix
is narrow: the N_INDEX TY_STR arm now rejects an index whose operand is a
bare SK_DEF scalar-str symbol, both stages -- 'cannot index a def-constant
str; bind it to a let'. INDEX-ONLY: len(S) and &S are already loud, and a
def's .len/.ptr field reads (the load-bearing w6l INTERP) are N_DOT, a
different arm, and stay valid.

A rule-9 WHY-comment records str[i]->u8 as a sanctioned divergence from
Hare's strings.toutf8. The full make-it-work fold (len(S)->2, S[0]->byte)
is deferred (#16). byte-id 990-997 8/8. test/wcc/821 table-driven.
This commit is contained in:
2026-06-08 20:17:05 +09:00
parent 29a2ab2a72
commit 03fc7c7abe
6 changed files with 440 additions and 4 deletions

View File

@@ -1437,8 +1437,33 @@ cexpr(Checker *c, Node *n)
type_name(c->a, u->sub));
return n->type = u->sub;
}
if (u && u->kind == TY_STR)
if (u && u->kind == TY_STR) {
/* rule-9 divergence-doc (drew .ai/drew-14-ruling.md):
* `str[i] -> u8` is a deliberate Go-like direct
* byte-index, a SANCTIONED ww divergence from Hare's
* `strings::toutf8(s)[i]` (the Hare reference checker
* rejects str-index, harec check.c:362). lib/strings
* is load-bearing on it (compare/dup/join). */
/* #14: a `def` scalar str is an inline compile-time
* CONSTANT (def-as-constant; not storage-backed like a
* `let`), so it has no address to index — cgen would
* load a frame-garbage base and segfault. Only the bare
* def-global operand is unindexable; let/param/local +
* string-literal operands stay valid. The faithful
* def-as-constant splice-index is deferred (no
* consumer). */
if (n->lhs->kind == N_IDENT) {
Sym *s = scope_lookup_prefer(c->cur,
c->cur_mod, n->lhs->str);
if (s && s->kind == SK_DEF)
return n->type = err(c, n->pos,
"cannot index a def-constant str "
"'%s'; bind it to a `let` (def "
"strings are inline constants, not "
"storage-backed)", n->lhs->str);
}
return n->type = ty_u8;
}
/* `*[N]T` auto-decays to `[N]T` indexing — drill into the
* inner T so callers see the element type, matching C's
* pointer-to-array semantics. `*[]T` does NOT auto-decay: