feat: multiple return values — bare-paren signatures, named returns, must-set, defaults
A function may return multiple values via a bare-paren return signature: `-> (A, B)` / `-> (x: A, y: B)` / `-> (A, B, !)` (error always the last slot), and `-> ()` is `void`. This is DISTINCT from a `Tuple(…)` value — return-position only (a dedicated `ReturnTypeExpr` AST node resolving to a reused `.tuple` TypeId); a parameter / field / variable annotation `x: (A, B)` is rejected. A single-value `-> (T, !)` stays a plain failable (= `-> T !`). Returns use the bare comma form `return a, b` / `return x = a, y = b` (no `.( … )` literal). Consume by destructuring (`a, b := f()`) or single-bind + field access (`c := f(); c.sum`); a failable bound value holds only the value slots (the error stays on the `!` channel). Named return slots are in-scope assignable locals; with no explicit `return` the implicit return is synthesized from them. Path-sensitive definite-assignment enforces the must-set rule, and a slot may carry a default that exempts it. Validation rejects arity mismatches, out-of-slot-order named elements, a slot/parameter name collision, a comma list from a single-value function, and a multi-return signature used as a value type. Examples 0202-0213; readme + specs updated. issues/0197 files a pre-existing annotated-assignment type-check gap (`x: i32 = "hi"` segfaults) surfaced by the adversarial review.
This commit is contained in:
20
src/ast.zig
20
src/ast.zig
@@ -86,6 +86,7 @@ pub const Node = struct {
|
||||
function_type_expr: FunctionTypeExpr,
|
||||
closure_type_expr: ClosureTypeExpr,
|
||||
tuple_type_expr: TupleTypeExpr,
|
||||
return_type_expr: ReturnTypeExpr,
|
||||
tuple_literal: TupleLiteral,
|
||||
ufcs_alias: UfcsAlias,
|
||||
c_import_decl: CImportDecl,
|
||||
@@ -867,6 +868,25 @@ pub const TupleTypeExpr = struct {
|
||||
field_names: ?[]const []const u8, // null for positional
|
||||
};
|
||||
|
||||
/// A bare-paren MULTI-RETURN signature `(A, B)` / `(x: A, y: B)` / `(A, B, !)`
|
||||
/// (≥2 value slots, error always the LAST slot). A function with this return
|
||||
/// returns MULTIPLE VALUES — a DISTINCT thing from one `Tuple(…)` value: it
|
||||
/// reuses the tuple ABI under the hood (resolves to a `.tuple` TypeId), but is
|
||||
/// valid ONLY as a function/closure return type (the general type resolver
|
||||
/// rejects it anywhere else), and its result is consumed only by destructuring
|
||||
/// (`a, b := f()`), never bound to a single value. Same shape as a tuple type so
|
||||
/// the resolver can reuse the field-resolution path. The single-value `(T, !)`
|
||||
/// (one value + error) is NOT this — it is a plain failable, `-> T !`.
|
||||
pub const ReturnTypeExpr = struct {
|
||||
field_types: []const *Node,
|
||||
field_names: ?[]const []const u8, // null for positional
|
||||
/// Per-slot default value expressions (`(sum: i32 = 0, good: bool)`), 1:1
|
||||
/// with `field_types`; an entry is null when that slot has no default. null
|
||||
/// (the whole field) when NO slot has a default. A defaulted named slot is
|
||||
/// exempt from the must-set rule — the default seeds the slot local.
|
||||
field_defaults: ?[]const ?*Node = null,
|
||||
};
|
||||
|
||||
pub const TupleLiteral = struct {
|
||||
elements: []const TupleElement,
|
||||
// Explicit tuple type for the `Tuple(...).( ... )` typed-construction form
|
||||
|
||||
Reference in New Issue
Block a user