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:
+45
-39
@@ -1309,6 +1309,42 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, "\n");
|
||||
|
||||
// Tuple typedefs before enums/structs that embed them by value
|
||||
StringBuilder_Append(&cbe.sb, "/* Tuple types */\n");
|
||||
StringBuilder_Append(&cbe.sb, "typedef struct Tuple_int_int {\n int _0;\n int _1;\n} Tuple_int_int;\n");
|
||||
StringBuilder_Append(&cbe.sb, "typedef struct Tuple_int_int_int {\n int _0;\n int _1;\n int _2;\n} Tuple_int_int_int;\n");
|
||||
StringBuilder_Append(&cbe.sb, "typedef struct Tuple_Empty {\n char _pad;\n} Tuple_Empty;\n\n");
|
||||
|
||||
// Struct definitions before enums (enums may embed structs by value, e.g. Shape::Dot(Point))
|
||||
// Pass 1: emit structs with no value-typed struct fields (leaf structs)
|
||||
si = 0;
|
||||
while si < mod.structCount {
|
||||
if String_Eq(mod.structs[si].name, "") || CBE_StructHasGeneric(&mod.structs[si]) {
|
||||
si = si + 1;
|
||||
continue;
|
||||
}
|
||||
if CBE_StructHasValueStructField(&mod.structs[si]) {
|
||||
si = si + 1;
|
||||
continue;
|
||||
}
|
||||
CBE_EmitStructDef(cbe, &mod.structs[si]);
|
||||
si = si + 1;
|
||||
}
|
||||
// Pass 2: emit structs that contain value-typed struct fields
|
||||
si = 0;
|
||||
while si < mod.structCount {
|
||||
if String_Eq(mod.structs[si].name, "") || CBE_StructHasGeneric(&mod.structs[si]) {
|
||||
si = si + 1;
|
||||
continue;
|
||||
}
|
||||
if !CBE_StructHasValueStructField(&mod.structs[si]) {
|
||||
si = si + 1;
|
||||
continue;
|
||||
}
|
||||
CBE_EmitStructDef(cbe, &mod.structs[si]);
|
||||
si = si + 1;
|
||||
}
|
||||
|
||||
// Enum definitions
|
||||
var ei: int = 0;
|
||||
while ei < mod.enumCount {
|
||||
@@ -1364,22 +1400,28 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
||||
while vi < en.variantCount {
|
||||
let ev: *HirEnumVariant = &en.variants[vi];
|
||||
if ev.fieldCount > 0 {
|
||||
// Prefer stored C type name (handles Tuple_*, named types)
|
||||
var ft0: String = CBackend_TypeToC(ev.fieldType0);
|
||||
if !String_Eq(ev.fieldTypeName0, "") { ft0 = ev.fieldTypeName0; }
|
||||
var ft1: String = CBackend_TypeToC(ev.fieldType1);
|
||||
if !String_Eq(ev.fieldTypeName1, "") { ft1 = ev.fieldTypeName1; }
|
||||
if ev.fieldCount == 1 {
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(ev.fieldType0));
|
||||
StringBuilder_Append(&cbe.sb, ft0);
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
StringBuilder_Append(&cbe.sb, ev.name);
|
||||
StringBuilder_Append(&cbe.sb, "_0;\n");
|
||||
} else {
|
||||
// Nested anonymous struct (layout matches bootstrap data.Variant.Variant_i)
|
||||
StringBuilder_Append(&cbe.sb, " struct {\n");
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(ev.fieldType0));
|
||||
StringBuilder_Append(&cbe.sb, ft0);
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
StringBuilder_Append(&cbe.sb, ev.fieldName0);
|
||||
StringBuilder_Append(&cbe.sb, ";\n");
|
||||
if ev.fieldCount > 1 {
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(ev.fieldType1));
|
||||
StringBuilder_Append(&cbe.sb, ft1);
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
StringBuilder_Append(&cbe.sb, ev.fieldName1);
|
||||
StringBuilder_Append(&cbe.sb, ";\n");
|
||||
@@ -1423,45 +1465,9 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
||||
StringBuilder_Append(&cbe.sb, "\n");
|
||||
}
|
||||
|
||||
// Struct definitions (skip generics)
|
||||
// Pass 1: emit structs with no value-typed struct fields (leaf structs)
|
||||
si = 0;
|
||||
while si < mod.structCount {
|
||||
if String_Eq(mod.structs[si].name, "") || CBE_StructHasGeneric(&mod.structs[si]) {
|
||||
si = si + 1;
|
||||
continue;
|
||||
}
|
||||
if CBE_StructHasValueStructField(&mod.structs[si]) {
|
||||
si = si + 1;
|
||||
continue;
|
||||
}
|
||||
CBE_EmitStructDef(cbe, &mod.structs[si]);
|
||||
si = si + 1;
|
||||
}
|
||||
// Pass 2: emit structs that contain value-typed struct fields
|
||||
si = 0;
|
||||
while si < mod.structCount {
|
||||
if String_Eq(mod.structs[si].name, "") || CBE_StructHasGeneric(&mod.structs[si]) {
|
||||
si = si + 1;
|
||||
continue;
|
||||
}
|
||||
if !CBE_StructHasValueStructField(&mod.structs[si]) {
|
||||
si = si + 1;
|
||||
continue;
|
||||
}
|
||||
CBE_EmitStructDef(cbe, &mod.structs[si]);
|
||||
si = si + 1;
|
||||
}
|
||||
|
||||
// Fat function-pointer typedefs (BuxFn_*) — before forward decls
|
||||
CBE_EmitFatFuncTypedefs(cbe, mod);
|
||||
|
||||
// Common tuple struct types
|
||||
StringBuilder_Append(&cbe.sb, "/* Tuple types */\n");
|
||||
StringBuilder_Append(&cbe.sb, "typedef struct Tuple_int_int {\n int _0;\n int _1;\n} Tuple_int_int;\n");
|
||||
StringBuilder_Append(&cbe.sb, "typedef struct Tuple_int_int_int {\n int _0;\n int _1;\n int _2;\n} Tuple_int_int_int;\n");
|
||||
StringBuilder_Append(&cbe.sb, "typedef struct Tuple_Empty {\n char _pad;\n} Tuple_Empty;\n\n");
|
||||
|
||||
// Env structs for capturing closures (no static instance — heap per value)
|
||||
var ei2: int = 0;
|
||||
while ei2 < mod.funcCount {
|
||||
|
||||
@@ -126,8 +126,10 @@ struct HirEnumVariant {
|
||||
fieldCount: int;
|
||||
fieldType0: int;
|
||||
fieldName0: String;
|
||||
fieldTypeName0: String; // C type name (e.g. "int", "Tuple_int_int")
|
||||
fieldType1: int;
|
||||
fieldName1: String;
|
||||
fieldTypeName1: String;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
+7
-2
@@ -782,9 +782,12 @@ func Lcx_PatternBindings(ctx: *LowerCtx, subject: *HirNode, pat: *Pattern,
|
||||
dataLoad.child1 = dataPtr;
|
||||
dataLoad.typeName = String_Concat(enumName, "_Data");
|
||||
|
||||
// Multi-field: nested struct data.Variant; single-field: flat data.Variant_0
|
||||
// Multi-field: nested struct data.Variant (anonymous or Enum_Variant_Payload);
|
||||
// single-field: flat data.Variant_0
|
||||
var payloadBase: *HirNode = dataLoad;
|
||||
if fieldCount > 1 {
|
||||
let nestedName: String = String_Concat(String_Concat(enumName, "_"),
|
||||
String_Concat(variantName, "_Payload"));
|
||||
let vPtr: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
vPtr.kind = hFieldPtr;
|
||||
vPtr.line = line;
|
||||
@@ -796,7 +799,7 @@ func Lcx_PatternBindings(ctx: *LowerCtx, subject: *HirNode, pat: *Pattern,
|
||||
vLoad.line = line;
|
||||
vLoad.column = col;
|
||||
vLoad.child1 = vPtr;
|
||||
vLoad.typeName = variantName;
|
||||
vLoad.typeName = nestedName;
|
||||
payloadBase = vLoad;
|
||||
}
|
||||
|
||||
@@ -3977,10 +3980,12 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
|
||||
// Positional field names: Variant_0, Variant_1 (matches data.Variant_i / nested struct)
|
||||
hm.enums[ei].variants[vi].fieldName0 = String_Concat(v.name, "_0");
|
||||
hm.enums[ei].variants[vi].fieldType0 = Lcx_ResolveTypeKindFromName(v.fieldTypeName0);
|
||||
hm.enums[ei].variants[vi].fieldTypeName0 = v.fieldTypeName0;
|
||||
}
|
||||
if v.fieldCount > 1 {
|
||||
hm.enums[ei].variants[vi].fieldName1 = String_Concat(v.name, "_1");
|
||||
hm.enums[ei].variants[vi].fieldType1 = Lcx_ResolveTypeKindFromName(v.fieldTypeName1);
|
||||
hm.enums[ei].variants[vi].fieldTypeName1 = v.fieldTypeName1;
|
||||
}
|
||||
}
|
||||
vi = vi + 1;
|
||||
|
||||
+27
-5
@@ -148,6 +148,28 @@ func parserExpectIdentOrKeyword(p: *Parser, msg: String) -> LexToken {
|
||||
// Type parsing
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// C-friendly type name from a TypeExpr (named, tuple, pointer, …).
|
||||
func parserTypeExprCName(te: *TypeExpr) -> String {
|
||||
if te == null as *TypeExpr { return "int"; }
|
||||
if te.kind == tekTuple {
|
||||
if !String_Eq(te.typeName, "") { return te.typeName; }
|
||||
return "Tuple_Empty";
|
||||
}
|
||||
if te.kind == tekPointer || te.kind == tekRef || te.kind == tekMutRef {
|
||||
if te.pointerPointee != null as *TypeExpr {
|
||||
return String_Concat(parserTypeExprCName(te.pointerPointee), "*");
|
||||
}
|
||||
return "void*";
|
||||
}
|
||||
if !String_Eq(te.typeName, "") {
|
||||
if String_Eq(te.typeName, "String") || String_Eq(te.typeName, "str") {
|
||||
return "const char*";
|
||||
}
|
||||
return te.typeName;
|
||||
}
|
||||
return "int";
|
||||
}
|
||||
|
||||
func parserParseType(p: *Parser) -> *TypeExpr {
|
||||
let line: uint32 = parserCurToken(p).line;
|
||||
let col: uint32 = parserCurToken(p).column;
|
||||
@@ -1953,14 +1975,14 @@ func parserParseEnumDecl(p: *Parser, isPublic: bool) -> *Decl {
|
||||
v.fieldTypeName0 = "";
|
||||
v.fieldTypeName1 = "";
|
||||
|
||||
// Optional (Type, Type) data
|
||||
// Optional payload: (Type, Type) or (Tuple, ...) — full type expressions
|
||||
if parserMatch(p, tkLParen) {
|
||||
let t0: LexToken = parserExpect(p, tkIdent, "expected data type");
|
||||
v.fieldTypeName0 = t0.text;
|
||||
let te0: *TypeExpr = parserParseType(p);
|
||||
v.fieldTypeName0 = parserTypeExprCName(te0);
|
||||
v.fieldCount = 1;
|
||||
if parserMatch(p, tkComma) {
|
||||
let t1: LexToken = parserExpect(p, tkIdent, "expected data type");
|
||||
v.fieldTypeName1 = t1.text;
|
||||
let te1: *TypeExpr = parserParseType(p);
|
||||
v.fieldTypeName1 = parserTypeExprCName(te1);
|
||||
v.fieldCount = 2;
|
||||
}
|
||||
discard parserExpect(p, tkRParen, "expected ')'");
|
||||
|
||||
Reference in New Issue
Block a user