feat: nested multi-field enum patterns (bootstrap + selfhost)

Multi-field variants use Enum_Variant_Payload nested types (avoids tag name clash).
Sema resolves data.Variant.Variant_i; enum field types fully resolve tuples.
Selfhost parses full type exprs in enum payloads; emit structs/tuples before enums.
Example: examples/nested_patterns.bux (Pair::Two, Box::Val((a,c)), Shape::Dot).
This commit is contained in:
2026-07-18 01:33:36 +03:00
parent f9185c96b2
commit 66f11d1869
12 changed files with 383 additions and 209 deletions
+30 -3
View File
@@ -1330,12 +1330,18 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
if enumSym != nil and enumSym.decl != nil and enumSym.decl.kind == dkEnum:
# Look for the field in enum variants
for variant in enumSym.decl.declEnumVariants:
# Check positional fields: Ok_0, Ok_1, etc.
# Multi-field / named-field variant: data.Variant → Enum_Variant_Payload
# (suffix avoids clashing with tag constant Enum_Variant)
if variant.fields.len > 1 and variant.name == expr.exprFieldName:
return makeNamed(enumName & "_" & variant.name & "_Payload")
if variant.namedFields.len > 0 and variant.name == expr.exprFieldName:
return makeNamed(enumName & "_" & variant.name & "_Payload")
# Single positional fields: Ok_0, Ok_1, etc. (flat on the union)
for i, f in variant.fields:
let fieldName = variant.name & "_" & $i
if fieldName == expr.exprFieldName:
return sema.resolveType(f)
# Check named fields
# Named fields nested under data.Variant.name
for nf in variant.namedFields:
if nf.name == expr.exprFieldName:
return sema.resolveType(nf.ftype)
@@ -1378,7 +1384,28 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
else:
sema.emitError(expr.loc, &"cannot access field on type {obj.toString}")
else:
sema.emitError(expr.loc, &"cannot access field on type {obj.toString}")
# Synthetic nested multi-field type Enum_Variant_Payload — generated for
# multi-field / named-field algebraic variants (not a user-declared type).
var foundNested = false
for (_, gsym) in sema.globalScope.table.pairs:
if gsym.decl == nil or gsym.decl.kind != dkEnum: continue
let ename = gsym.decl.declEnumName
for variant in gsym.decl.declEnumVariants:
let nestedName = ename & "_" & variant.name & "_Payload"
if nestedName != objType.name: continue
foundNested = true
for i, f in variant.fields:
let fieldName = variant.name & "_" & $i
if fieldName == expr.exprFieldName:
return sema.resolveType(f)
for nf in variant.namedFields:
if nf.name == expr.exprFieldName:
return sema.resolveType(nf.ftype)
sema.emitError(expr.loc, &"nested variant type '{objType.name}' has no field '{expr.exprFieldName}'")
return makeUnknown()
if not foundNested:
sema.emitError(expr.loc, &"undeclared type '{objType.name}'")
return makeUnknown()
elif objType.kind == tkDynRef:
# Trait object: methods come from the interface
let ifaceName = objType.name