fix: diagnose ?(?T) tuple-payload mismatch instead of malformed IR (issue 0165)

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).
This commit is contained in:
agra
2026-06-22 21:54:12 +03:00
parent 3e8d003e3d
commit 0bc8005b99
16 changed files with 198 additions and 4 deletions

View File

@@ -1,5 +1,5 @@
// Out-of-range tuple index produces a clear
// `error: field 'N' not found on type 'tuple'` diagnostic and exit 1.
// `error: field 'N' not found on type '(i64, i64)'` diagnostic and exit 1.
main :: () -> i32 {
t := (10, 20);

View File

@@ -0,0 +1,18 @@
// `?(?i64)` is `optional` wrapping the SINGLE-FIELD TUPLE `(?i64)` — in type
// position `(T)` is a 1-tuple, not a grouping (specs.md §"Tuple Types"). So
// assigning a bare `?i64` value to a `?(?i64)` slot is a type mismatch: the
// optional's payload is the tuple `(?i64)`, not `?i64`.
//
// Regression (issue 0165): this used to silently lower to a malformed
// `insertvalue { {{i64,i1}}, i1 }` that aborted the LLVM verifier. It now
// produces a clean diagnostic naming the payload type and pointing at the
// parens-are-a-tuple gotcha. (To write a genuine nested optional, alias the
// inner one: `Opt :: ?i64; x : ?Opt = ...` — see
// examples/optionals/0911-nested-optional-via-alias.sx.)
#import "modules/std.sx";
main :: () {
inner : ?i64 = 5;
outer : ?(?i64) = inner;
print("unreachable\n");
}

View File

@@ -1,4 +1,4 @@
error: field '42' not found on type 'tuple'
error: field '42' not found on type '(i64, i64)'
--> examples/diagnostics/1101-diagnostics-err-tuple-oob.sx:6:15
|
6 | return xx t.42;

View File

@@ -0,0 +1,5 @@
error: cannot assign a value of type '?i64' to optional '?(?i64)': its payload type is '(?i64)' (note: in type position '(T)' is a single-field tuple, not a grouping — write the inner optional without parentheses)
--> examples/diagnostics/1195-diagnostics-err-parenthesized-optional-tuple.sx:16:3
|
16 | outer : ?(?i64) = inner;
| ^^^^^^^^^^^^^^^^^^^^^^^^