In type position (T) is a 1-tuple (specs.md:843), so ?(?i64) is optional(tuple(?i64)); assigning a bare ?i64 had coerceToType classify .none and pass the value through, then optionalWrap built a corrupt insertvalue that aborted the LLVM verifier. After coercing toward an optional's child, verify the coerced type equals the child type (stmt.zig decl-init + coerce.zig .optional_wrap); on mismatch emit a located diagnostic (tuple-specific note only when the child is a tuple). formatTypeName now renders tuples as (x: i64, y: i64). Regressions: optionals/0911 (nested optional via alias, round-trip), diagnostics/1195 (the mismatch diagnostic). Updated diagnostics/1101 + protocols/0414 goldens for the improved tuple type-name rendering. Verified by 3 adversarial reviews. Filed adjacent bug 0171 (?any child not canonicalized).
2.2 KiB
0171 — ?any optional child is a non-canonical any TypeId (box-into-any rule misses, value silently discarded)
Symptom
An optional whose child is any (?any) is broken. Baseline (before the issue
0165 fix) silently DISCARDED the boxed value: x : ?any = 42; v := x! yields an
empty box any{}, not 42 — the payload is lowered as a zero-size {}. After
the 0165 fix the same code now produces a clean type-mismatch diagnostic
(cannot assign a value of type 'i64' to optional '?any': its payload type is 'any'), which is strictly better than silent corruption but still means ?any
does not work.
Root cause (from adversarial review of issue 0165)
The box-into-any coercion rule (src/ir/conversions.zig ~line 57) keys on the
BUILTIN .any enum TypeId. But an optional's child any is a SEPARATELY
interned TypeId (observed @enumFromInt(246), type-name "any") that is NOT
identity-equal to the builtin .any. So classify(i64, child_any) falls through
to .none, returns the value unchanged (i64), and the wrap is invalid. The
any type is not being canonicalized to the builtin TypeId when it appears as an
optional child.
Reproduction
#import "modules/std.sx";
main :: () {
x : ?any = 42;
v := x!;
print("{}\n", v); // expected: a boxed 42; baseline yields empty any{}
}
Investigation prompt
Canonicalize any as an optional child (and likely any other compound position)
to the builtin .any TypeId at type-resolution/interning time, so the
box-into-any rule in src/ir/conversions.zig classifies correctly and ?any
round-trips. Find where the optional child type is resolved/interned
(src/ir/types.zig optionalOf / the type resolver) and ensure an any child
maps to the canonical builtin TypeId rather than a fresh interned copy.
Alternatively, make the box-into-any classifier compare by type-KIND
(info == .any) rather than TypeId identity — but canonicalization is the more
robust fix (it also fixes ==, size_of, and any other identity check on the
any child). Verify the repro round-trips a boxed value; add an
examples/types/01xx-optional-any.sx regression. Low priority — ?any is used
nowhere in library/ or examples/.