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

@@ -12713,7 +12713,30 @@ fn indexresult(c: *checker, e: *node) *node = {
if (u.kind == nkind.N_TSLICE) { return u.lhs; };
if (u.kind == nkind.N_TARRAY) { return u.lhs; };
if (u.kind == nkind.N_TNAME) {
if (streq(u.str, "str")) { return mktname(c, "u8"); };
// 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).
if (streq(u.str, "str")) {
// #14: reject INDEX of a bare def-global scalar str — an
// inline compile-time constant with no storage to index
// (cgen refs an unbacked symbol -> link-fail ww / segfault
// cs). let/param/local + string-literal operands stay
// valid; only the SK_DEF scalar-str operand is
// unindexable. cstage twin: check.c N_INDEX TY_STR arm.
if (e.lhs != nil && e.lhs.kind == nkind.N_IDENT) {
let s: *sym = scopelookupprefer(c.cur, c.curmod, e.lhs.str);
if (s != nil && s.skind == skind.SK_DEF) {
cerr("error: cannot index a def-constant str '");
cerr(e.lhs.str);
cerr("'; bind it to a `let` (def strings are inline constants, not storage-backed)\n");
c.errs += 1;
return nil;
};
};
return mktname(c, "u8");
};
};
if (u.kind == nkind.N_TPTR) {
let inner: *node = u.lhs;