wcc: exprfloatkind structural/stamp floatness-disagreement bridge (#121)
Add classifytinfo(n.type_) stampkind alongside structural structkind; abort on floatness-disagreement only ((structkind!=0)!=(stampkind!=0)); return structkind (byte-id-neutral). TRANSIENT — deleted in commit 3 (the flip + permanent residual sibling-evidence guard). The corpus-wide validation that licenses commit 3's flip.
This commit is contained in:
@@ -12777,19 +12777,82 @@ export fn isf32type(c: *cgen, t: *node) bool = {
|
|||||||
return typeisf32(t.type_: *tinfo);
|
return typeisf32(t.type_: *tinfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// classifytinfo — map a checker stamp (node.type_ as *tinfo) to the
|
||||||
|
// exprfloatkind value-class: f32 → 1, f64 → 2, integer/unknown/nil → 0.
|
||||||
|
// The TY_NAMED peel + TY_F32/F64 split live in typeisf32 / typeisfloat
|
||||||
|
// (typ.ww:437,350; cf. the split at check.ww:1323) — reuse them rather
|
||||||
|
// than re-implementing the peel (rule 13: route through the type table).
|
||||||
|
// #121 (Package B) collapse target: exprtype stamps each node's RESULT
|
||||||
|
// type on n.type_, so reading it here mirrors every structural arm at
|
||||||
|
// its actual node. Commit 2 uses it only to CROSS-CHECK the structural
|
||||||
|
// classifier (the consistency bridge below); commit 3 makes it the body.
|
||||||
|
fn classifytinfo(t: *tinfo) i32 = {
|
||||||
|
if (typeisf32(t)) { return 1; };
|
||||||
|
if (typeisfloat(t)) { return 2; };
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
// exprfloatkind — classify an expression's value-class so callers can
|
// exprfloatkind — classify an expression's value-class so callers can
|
||||||
// pick float vs integer codegen without a full type system. Returns:
|
// pick float vs integer codegen without a full type system. Returns
|
||||||
// 0 — integer-like (or unknown — same fallback the existing cgen
|
// 0 integer-like/unknown, 1 f32, 2 f64.
|
||||||
// takes today)
|
//
|
||||||
// 1 — f32
|
// #121 (Package B) consistency BRIDGE — byte-id-NEUTRAL, TRANSIENT.
|
||||||
// 2 — f64
|
// The wwstage classifies float-ness STRUCTURALLY (exprfloatkindstruct), a
|
||||||
|
// gate-blind divergence from cstage's checker-stamp read (node_isfloat).
|
||||||
|
// Before flipping to the stamp (commit 3), this proves the stamp AGREES
|
||||||
|
// with the structural classifier on every float node the corpus exercises.
|
||||||
|
// A wrong float classification miscompiles silently and the bootstrap
|
||||||
|
// barely exercises floats, so the disagreement is surfaced LOUD, not
|
||||||
|
// absorbed. Whole bridge (struct body + this wrapper) is DELETED in
|
||||||
|
// commit 3; the residual sibling-evidence guard lands at that point as
|
||||||
|
// the permanent net.
|
||||||
|
//
|
||||||
|
// RELAXED condition — floatness-disagreement ONLY, NOT precision (Drew /
|
||||||
|
// Rob 2026-05-26):
|
||||||
|
// abort iff (structkind != 0) != (stampkind != 0).
|
||||||
|
// CAT A (float↔int): caught loud — a stamp-miss on a float node, or a
|
||||||
|
// stamp-extra on an int the structural arm correctly classifies as not-
|
||||||
|
// float; both are real bugs to surface. CAT B (f32-vs-f64 coarseness,
|
||||||
|
// e.g. structural N_FLOATLIT returns 2 unconditionally while stamp says
|
||||||
|
// 1 on a tsuffix-stamped 1.5f32): TOLERATED — the stamp is the
|
||||||
|
// authoritative source (cstage's single source), commit 3's flip
|
||||||
|
// converges toward it; the structural arm being f32-blind is the very
|
||||||
|
// reason for the collapse, not a bug to abort on. Non-load-bearing for
|
||||||
|
// asm in let-store contexts the corpus exercises (964 byte-id holds at
|
||||||
|
// HEAD with the f32 literals classified coarse f64).
|
||||||
|
//
|
||||||
|
// Returns structkind UNCHANGED → behaviour identical to pre-bridge.
|
||||||
|
export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
||||||
|
let structkind: i32 = exprfloatkindstruct(c, n);
|
||||||
|
let stampkind: i32 = classifytinfo(n.type_: *tinfo);
|
||||||
|
if ((structkind != 0) != (stampkind != 0)) {
|
||||||
|
let msg: str = "exprfloatkind: float node mis/un-stamped (cs!=ww) at ";
|
||||||
|
os.write(2, msg.ptr, msg.len: u64);
|
||||||
|
if (n.file.len > 0) {
|
||||||
|
os.write(2, n.file.ptr, n.file.len: u64);
|
||||||
|
os.write(2, ":".ptr, 1u64);
|
||||||
|
let ls: str = strconv.i32tos(n.line, strconv.base.DEC);
|
||||||
|
os.write(2, ls.ptr, ls.len: u64);
|
||||||
|
os.write(2, " ".ptr, 1u64);
|
||||||
|
};
|
||||||
|
let kn: str = nkname(n.kind);
|
||||||
|
os.write(2, kn.ptr, kn.len: u64);
|
||||||
|
os.write(2, "\n".ptr, 1u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
return structkind;
|
||||||
|
};
|
||||||
|
|
||||||
|
// exprfloatkindstruct — the STRUCTURAL classifier (#121 commit-2 form;
|
||||||
|
// commit 3 deletes it once the bridge proves the stamp suffices).
|
||||||
// Recognises: float literals, idents bound to float lets/locals,
|
// Recognises: float literals, idents bound to float lets/locals,
|
||||||
// chained casts whose target is float, and (recursively) the inner
|
// chained casts whose target is float, and (recursively) the inner
|
||||||
// expr of a non-narrowing wrapping construct. Anything we can't
|
// expr of a non-narrowing wrapping construct. Anything we can't
|
||||||
// pin down conservatively reports integer — the worst case is that
|
// pin down conservatively reports integer — the worst case is that
|
||||||
// CVT* is skipped for an exotic case the user can still spell with
|
// CVT* is skipped for an exotic case the user can still spell with
|
||||||
// an explicit local.
|
// an explicit local. Recurses through exprfloatkind (the bridge), so
|
||||||
export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
// the consistency check fires on every sub-node the recursion visits.
|
||||||
|
fn exprfloatkindstruct(c: *cgen, n: *node) i32 = {
|
||||||
if (n == nil) { return 0; };
|
if (n == nil) { return 0; };
|
||||||
let k: nkind = n.kind;
|
let k: nkind = n.kind;
|
||||||
if (k == nkind.N_FLOATLIT) { return 2; };
|
if (k == nkind.N_FLOATLIT) { return 2; };
|
||||||
|
|||||||
@@ -1860,19 +1860,82 @@ export fn isf32type(c: *cgen, t: *node) bool = {
|
|||||||
return typeisf32(t.type_: *tinfo);
|
return typeisf32(t.type_: *tinfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// classifytinfo — map a checker stamp (node.type_ as *tinfo) to the
|
||||||
|
// exprfloatkind value-class: f32 → 1, f64 → 2, integer/unknown/nil → 0.
|
||||||
|
// The TY_NAMED peel + TY_F32/F64 split live in typeisf32 / typeisfloat
|
||||||
|
// (typ.ww:437,350; cf. the split at check.ww:1323) — reuse them rather
|
||||||
|
// than re-implementing the peel (rule 13: route through the type table).
|
||||||
|
// #121 (Package B) collapse target: exprtype stamps each node's RESULT
|
||||||
|
// type on n.type_, so reading it here mirrors every structural arm at
|
||||||
|
// its actual node. Commit 2 uses it only to CROSS-CHECK the structural
|
||||||
|
// classifier (the consistency bridge below); commit 3 makes it the body.
|
||||||
|
fn classifytinfo(t: *tinfo) i32 = {
|
||||||
|
if (typeisf32(t)) { return 1; };
|
||||||
|
if (typeisfloat(t)) { return 2; };
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
// exprfloatkind — classify an expression's value-class so callers can
|
// exprfloatkind — classify an expression's value-class so callers can
|
||||||
// pick float vs integer codegen without a full type system. Returns:
|
// pick float vs integer codegen without a full type system. Returns
|
||||||
// 0 — integer-like (or unknown — same fallback the existing cgen
|
// 0 integer-like/unknown, 1 f32, 2 f64.
|
||||||
// takes today)
|
//
|
||||||
// 1 — f32
|
// #121 (Package B) consistency BRIDGE — byte-id-NEUTRAL, TRANSIENT.
|
||||||
// 2 — f64
|
// The wwstage classifies float-ness STRUCTURALLY (exprfloatkindstruct), a
|
||||||
|
// gate-blind divergence from cstage's checker-stamp read (node_isfloat).
|
||||||
|
// Before flipping to the stamp (commit 3), this proves the stamp AGREES
|
||||||
|
// with the structural classifier on every float node the corpus exercises.
|
||||||
|
// A wrong float classification miscompiles silently and the bootstrap
|
||||||
|
// barely exercises floats, so the disagreement is surfaced LOUD, not
|
||||||
|
// absorbed. Whole bridge (struct body + this wrapper) is DELETED in
|
||||||
|
// commit 3; the residual sibling-evidence guard lands at that point as
|
||||||
|
// the permanent net.
|
||||||
|
//
|
||||||
|
// RELAXED condition — floatness-disagreement ONLY, NOT precision (Drew /
|
||||||
|
// Rob 2026-05-26):
|
||||||
|
// abort iff (structkind != 0) != (stampkind != 0).
|
||||||
|
// CAT A (float↔int): caught loud — a stamp-miss on a float node, or a
|
||||||
|
// stamp-extra on an int the structural arm correctly classifies as not-
|
||||||
|
// float; both are real bugs to surface. CAT B (f32-vs-f64 coarseness,
|
||||||
|
// e.g. structural N_FLOATLIT returns 2 unconditionally while stamp says
|
||||||
|
// 1 on a tsuffix-stamped 1.5f32): TOLERATED — the stamp is the
|
||||||
|
// authoritative source (cstage's single source), commit 3's flip
|
||||||
|
// converges toward it; the structural arm being f32-blind is the very
|
||||||
|
// reason for the collapse, not a bug to abort on. Non-load-bearing for
|
||||||
|
// asm in let-store contexts the corpus exercises (964 byte-id holds at
|
||||||
|
// HEAD with the f32 literals classified coarse f64).
|
||||||
|
//
|
||||||
|
// Returns structkind UNCHANGED → behaviour identical to pre-bridge.
|
||||||
|
export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
||||||
|
let structkind: i32 = exprfloatkindstruct(c, n);
|
||||||
|
let stampkind: i32 = classifytinfo(n.type_: *tinfo);
|
||||||
|
if ((structkind != 0) != (stampkind != 0)) {
|
||||||
|
let msg: str = "exprfloatkind: float node mis/un-stamped (cs!=ww) at ";
|
||||||
|
os.write(2, msg.ptr, msg.len: u64);
|
||||||
|
if (n.file.len > 0) {
|
||||||
|
os.write(2, n.file.ptr, n.file.len: u64);
|
||||||
|
os.write(2, ":".ptr, 1u64);
|
||||||
|
let ls: str = strconv.i32tos(n.line, strconv.base.DEC);
|
||||||
|
os.write(2, ls.ptr, ls.len: u64);
|
||||||
|
os.write(2, " ".ptr, 1u64);
|
||||||
|
};
|
||||||
|
let kn: str = nkname(n.kind);
|
||||||
|
os.write(2, kn.ptr, kn.len: u64);
|
||||||
|
os.write(2, "\n".ptr, 1u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
return structkind;
|
||||||
|
};
|
||||||
|
|
||||||
|
// exprfloatkindstruct — the STRUCTURAL classifier (#121 commit-2 form;
|
||||||
|
// commit 3 deletes it once the bridge proves the stamp suffices).
|
||||||
// Recognises: float literals, idents bound to float lets/locals,
|
// Recognises: float literals, idents bound to float lets/locals,
|
||||||
// chained casts whose target is float, and (recursively) the inner
|
// chained casts whose target is float, and (recursively) the inner
|
||||||
// expr of a non-narrowing wrapping construct. Anything we can't
|
// expr of a non-narrowing wrapping construct. Anything we can't
|
||||||
// pin down conservatively reports integer — the worst case is that
|
// pin down conservatively reports integer — the worst case is that
|
||||||
// CVT* is skipped for an exotic case the user can still spell with
|
// CVT* is skipped for an exotic case the user can still spell with
|
||||||
// an explicit local.
|
// an explicit local. Recurses through exprfloatkind (the bridge), so
|
||||||
export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
// the consistency check fires on every sub-node the recursion visits.
|
||||||
|
fn exprfloatkindstruct(c: *cgen, n: *node) i32 = {
|
||||||
if (n == nil) { return 0; };
|
if (n == nil) { return 0; };
|
||||||
let k: nkind = n.kind;
|
let k: nkind = n.kind;
|
||||||
if (k == nkind.N_FLOATLIT) { return 2; };
|
if (k == nkind.N_FLOATLIT) { return 2; };
|
||||||
|
|||||||
@@ -12777,19 +12777,82 @@ export fn isf32type(c: *cgen, t: *node) bool = {
|
|||||||
return typeisf32(t.type_: *tinfo);
|
return typeisf32(t.type_: *tinfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// classifytinfo — map a checker stamp (node.type_ as *tinfo) to the
|
||||||
|
// exprfloatkind value-class: f32 → 1, f64 → 2, integer/unknown/nil → 0.
|
||||||
|
// The TY_NAMED peel + TY_F32/F64 split live in typeisf32 / typeisfloat
|
||||||
|
// (typ.ww:437,350; cf. the split at check.ww:1323) — reuse them rather
|
||||||
|
// than re-implementing the peel (rule 13: route through the type table).
|
||||||
|
// #121 (Package B) collapse target: exprtype stamps each node's RESULT
|
||||||
|
// type on n.type_, so reading it here mirrors every structural arm at
|
||||||
|
// its actual node. Commit 2 uses it only to CROSS-CHECK the structural
|
||||||
|
// classifier (the consistency bridge below); commit 3 makes it the body.
|
||||||
|
fn classifytinfo(t: *tinfo) i32 = {
|
||||||
|
if (typeisf32(t)) { return 1; };
|
||||||
|
if (typeisfloat(t)) { return 2; };
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
// exprfloatkind — classify an expression's value-class so callers can
|
// exprfloatkind — classify an expression's value-class so callers can
|
||||||
// pick float vs integer codegen without a full type system. Returns:
|
// pick float vs integer codegen without a full type system. Returns
|
||||||
// 0 — integer-like (or unknown — same fallback the existing cgen
|
// 0 integer-like/unknown, 1 f32, 2 f64.
|
||||||
// takes today)
|
//
|
||||||
// 1 — f32
|
// #121 (Package B) consistency BRIDGE — byte-id-NEUTRAL, TRANSIENT.
|
||||||
// 2 — f64
|
// The wwstage classifies float-ness STRUCTURALLY (exprfloatkindstruct), a
|
||||||
|
// gate-blind divergence from cstage's checker-stamp read (node_isfloat).
|
||||||
|
// Before flipping to the stamp (commit 3), this proves the stamp AGREES
|
||||||
|
// with the structural classifier on every float node the corpus exercises.
|
||||||
|
// A wrong float classification miscompiles silently and the bootstrap
|
||||||
|
// barely exercises floats, so the disagreement is surfaced LOUD, not
|
||||||
|
// absorbed. Whole bridge (struct body + this wrapper) is DELETED in
|
||||||
|
// commit 3; the residual sibling-evidence guard lands at that point as
|
||||||
|
// the permanent net.
|
||||||
|
//
|
||||||
|
// RELAXED condition — floatness-disagreement ONLY, NOT precision (Drew /
|
||||||
|
// Rob 2026-05-26):
|
||||||
|
// abort iff (structkind != 0) != (stampkind != 0).
|
||||||
|
// CAT A (float↔int): caught loud — a stamp-miss on a float node, or a
|
||||||
|
// stamp-extra on an int the structural arm correctly classifies as not-
|
||||||
|
// float; both are real bugs to surface. CAT B (f32-vs-f64 coarseness,
|
||||||
|
// e.g. structural N_FLOATLIT returns 2 unconditionally while stamp says
|
||||||
|
// 1 on a tsuffix-stamped 1.5f32): TOLERATED — the stamp is the
|
||||||
|
// authoritative source (cstage's single source), commit 3's flip
|
||||||
|
// converges toward it; the structural arm being f32-blind is the very
|
||||||
|
// reason for the collapse, not a bug to abort on. Non-load-bearing for
|
||||||
|
// asm in let-store contexts the corpus exercises (964 byte-id holds at
|
||||||
|
// HEAD with the f32 literals classified coarse f64).
|
||||||
|
//
|
||||||
|
// Returns structkind UNCHANGED → behaviour identical to pre-bridge.
|
||||||
|
export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
||||||
|
let structkind: i32 = exprfloatkindstruct(c, n);
|
||||||
|
let stampkind: i32 = classifytinfo(n.type_: *tinfo);
|
||||||
|
if ((structkind != 0) != (stampkind != 0)) {
|
||||||
|
let msg: str = "exprfloatkind: float node mis/un-stamped (cs!=ww) at ";
|
||||||
|
os.write(2, msg.ptr, msg.len: u64);
|
||||||
|
if (n.file.len > 0) {
|
||||||
|
os.write(2, n.file.ptr, n.file.len: u64);
|
||||||
|
os.write(2, ":".ptr, 1u64);
|
||||||
|
let ls: str = strconv.i32tos(n.line, strconv.base.DEC);
|
||||||
|
os.write(2, ls.ptr, ls.len: u64);
|
||||||
|
os.write(2, " ".ptr, 1u64);
|
||||||
|
};
|
||||||
|
let kn: str = nkname(n.kind);
|
||||||
|
os.write(2, kn.ptr, kn.len: u64);
|
||||||
|
os.write(2, "\n".ptr, 1u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
return structkind;
|
||||||
|
};
|
||||||
|
|
||||||
|
// exprfloatkindstruct — the STRUCTURAL classifier (#121 commit-2 form;
|
||||||
|
// commit 3 deletes it once the bridge proves the stamp suffices).
|
||||||
// Recognises: float literals, idents bound to float lets/locals,
|
// Recognises: float literals, idents bound to float lets/locals,
|
||||||
// chained casts whose target is float, and (recursively) the inner
|
// chained casts whose target is float, and (recursively) the inner
|
||||||
// expr of a non-narrowing wrapping construct. Anything we can't
|
// expr of a non-narrowing wrapping construct. Anything we can't
|
||||||
// pin down conservatively reports integer — the worst case is that
|
// pin down conservatively reports integer — the worst case is that
|
||||||
// CVT* is skipped for an exotic case the user can still spell with
|
// CVT* is skipped for an exotic case the user can still spell with
|
||||||
// an explicit local.
|
// an explicit local. Recurses through exprfloatkind (the bridge), so
|
||||||
export fn exprfloatkind(c: *cgen, n: *node) i32 = {
|
// the consistency check fires on every sub-node the recursion visits.
|
||||||
|
fn exprfloatkindstruct(c: *cgen, n: *node) i32 = {
|
||||||
if (n == nil) { return 0; };
|
if (n == nil) { return 0; };
|
||||||
let k: nkind = n.kind;
|
let k: nkind = n.kind;
|
||||||
if (k == nkind.N_FLOATLIT) { return 2; };
|
if (k == nkind.N_FLOATLIT) { return 2; };
|
||||||
|
|||||||
Reference in New Issue
Block a user