feat: parenthesized type grouping — (T) groups, (T,) is a 1-tuple (issue 0177)

In type position, parentheses now mirror value position: (T) (a single
unnamed element, no trailing comma) is a GROUPING that resolves to the
inner type; (T,) is a 1-tuple; (A, B) a 2-tuple; named (x: T) and spread
(..Ts) stay tuples; (...) -> R stays a function type. This lets a
closure/optional/function type be parenthesized for readability without
silently becoming a 1-tuple:
  [1](Closure(i64,i64) -> i64)   // array of closures (issue 0177) -> 7
  ?(?i64)                        // genuine nested optional (issue 0165 intent)

Parser: src/parser.zig returns the inner node for a single unnamed
non-spread no-trailing-comma parenthesized type. formatTypeName (both
generic.zig diagnostics + types.zig reflection) now render a 1-tuple as
(T,) so the spelling is unambiguous and diagnostics are self-consistent.
The 0165 coerce/stmt note reworded accordingly.

specs.md §Type Syntax updated; basic/0036 wrap return -> (i64,); obsolete
diagnostic 1195 removed (?(?i64) now compiles); regression
examples/types/0201-types-parenthesized-type-grouping.sx added; 0414 .ir
golden regenerated for the (T,) rendering. Resolves 0177; updates
0165/0170. Verified by 3 adversarial reviews; suite 792/0.
This commit is contained in:
agra
2026-06-23 10:43:47 +03:00
parent c41f51aed3
commit 555ccdc024
18 changed files with 120 additions and 40 deletions

View File

@@ -1,18 +0,0 @@
// `?(?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,5 +0,0 @@
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;
| ^^^^^^^^^^^^^^^^^^^^^^^^