From 0e1f8c57951d1ad2f9865d3fa06cbf04898fae10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E8=88=AA=E5=AE=87?= <3364451258@qq.com> Date: Sat, 6 Jun 2026 14:21:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9E=9A=E4=B8=BE=E5=85=B3=E8=81=94?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=20ADT=20=E2=80=94=20enum=20Option=20{=20Some?= =?UTF-8?q?(i64),=20None=20}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ast/ast.c | 8 +++-- src/ast/ast.h | 16 ++++++--- src/codegen/codegen.c | 76 ++++++++++++++++++++++++++++++++---------- src/parser/parser.c | 37 +++++++++++++++++--- src/sema/sema.c | 26 +++++++++++++++ src/sema/symbol.c | 11 +++++- src/sema/symbol.h | 6 +++- test/programs/31_adt.l | 17 ++++++++++ test/test_codegen.c | 12 +++---- 9 files changed, 172 insertions(+), 37 deletions(-) create mode 100644 test/programs/31_adt.l diff --git a/src/ast/ast.c b/src/ast/ast.c index 842d2d8..fa0af3e 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -200,19 +200,23 @@ AstNode* ast_make_type_alias(void* alloc, const char* name, TypeKind aliased, // === 枚举相关工厂函数 === AstNode* ast_make_enum_decl(void* alloc, const char* name, const char** variants, - size_t count, SourceLoc loc) { + TypeKind* payload_types, const char** payload_struct_names, + size_t count, SourceLoc loc) { NEW(alloc, AST_ENUM_DECL); n->as.enum_decl.name = name; n->as.enum_decl.variants = variants; + n->as.enum_decl.variant_payload_types = payload_types; + n->as.enum_decl.variant_payload_struct_names = payload_struct_names; n->as.enum_decl.variant_count = count; return n; } AstNode* ast_make_enum_variant(void* alloc, const char* enum_name, - const char* variant_name, SourceLoc loc) { + const char* variant_name, AstNode* payload, SourceLoc loc) { NEW(alloc, AST_ENUM_VARIANT); n->as.enum_variant.enum_name = enum_name; n->as.enum_variant.variant_name = variant_name; + n->as.enum_variant.payload = payload; n->as.enum_variant.variant_index = -1; return n; } diff --git a/src/ast/ast.h b/src/ast/ast.h index 05c4a3b..61ef520 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -104,9 +104,14 @@ struct AstNode { // AST_TYPE_ALIAS struct { const char* name; TypeKind aliased_type; const char* aliased_struct_name; } type_alias; // AST_ENUM_DECL - struct { const char* name; const char** variants; size_t variant_count; } enum_decl; + struct { const char* name; const char** variants; + TypeKind* variant_payload_types; // 每个变体的 payload 类型(TYPE_VOID=无) + const char** variant_payload_struct_names; // payload 为 struct 时的类型名 + size_t variant_count; } enum_decl; // AST_ENUM_VARIANT - struct { const char* enum_name; const char* variant_name; int variant_index; } enum_variant; + struct { const char* enum_name; const char* variant_name; + AstNode* payload; // payload 表达式 (NULL=无 payload) + int variant_index; } enum_variant; // AST_INDEX_EXPR struct { struct AstNode* array; struct AstNode* index; } index_expr; // AST_ARRAY_ASSIGN_STMT @@ -150,8 +155,11 @@ AstNode* ast_make_struct_init(void* alloc, const char* type_name, const char** f AstNode* ast_make_field_access(void* alloc, AstNode* object, const char* field, SourceLoc loc); AstNode* ast_make_type_alias(void* alloc, const char* name, TypeKind aliased, const char* aliased_struct, SourceLoc loc); -AstNode* ast_make_enum_decl(void* alloc, const char* name, const char** variants, size_t count, SourceLoc loc); -AstNode* ast_make_enum_variant(void* alloc, const char* enum_name, const char* variant_name, SourceLoc loc); +AstNode* ast_make_enum_decl(void* alloc, const char* name, const char** variants, + TypeKind* payload_types, const char** payload_struct_names, + size_t count, SourceLoc loc); +AstNode* ast_make_enum_variant(void* alloc, const char* enum_name, const char* variant_name, + AstNode* payload, SourceLoc loc); AstNode* ast_make_index_expr(void* alloc, AstNode* array, AstNode* index, SourceLoc loc); AstNode* ast_make_array_assign(void* alloc, const char* name, AstNode* index, AstNode* value, SourceLoc loc); AstNode* ast_make_impl_block(void* alloc, const char* struct_name, AstNode** methods, size_t count, SourceLoc loc); diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index 549b3a8..e3593e8 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -67,7 +67,12 @@ static LLVMTypeRef to_llvm_type(CgCtx* ctx, TypeKind kind) { case TYPE_CHAR: return LLVMInt8TypeInContext(ctx->context); case TYPE_STR: return LLVMPointerType(LLVMInt8TypeInContext(ctx->context), 0); case TYPE_STRUCT: - case TYPE_ENUM: return LLVMInt64TypeInContext(ctx->context); + case TYPE_ENUM: { + // tagged union: { i64 tag, i64 payload } + LLVMTypeRef fields[] = { LLVMInt64TypeInContext(ctx->context), + LLVMInt64TypeInContext(ctx->context) }; + return LLVMStructTypeInContext(ctx->context, fields, 2, false); + } case TYPE_UNKNOWN: case TYPE_ERROR: default: return LLVMVoidTypeInContext(ctx->context); @@ -159,8 +164,11 @@ static LLVMTypeRef type_info_to_llvm(CgCtx* ctx, const TypeInfo* ti) { if (st) return st; } return LLVMVoidTypeInContext(ctx->context); - case TYPE_ENUM: - return LLVMInt64TypeInContext(ctx->context); + case TYPE_ENUM: { + LLVMTypeRef f[] = { LLVMInt64TypeInContext(ctx->context), + LLVMInt64TypeInContext(ctx->context) }; + return LLVMStructTypeInContext(ctx->context, f, 2, false); + } default: return to_llvm_type(ctx, ti->kind); } @@ -258,23 +266,36 @@ static LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node) { case OP_MOD: return LLVMBuildSRem(ctx->builder, l, r, "srem"); case OP_EQ: - return is_float ? LLVMBuildFCmp(ctx->builder, LLVMRealOEQ, l, r, "feq") - : LLVMBuildICmp(ctx->builder, LLVMIntEQ, l, r, "ieq"); case OP_NE: - return is_float ? LLVMBuildFCmp(ctx->builder, LLVMRealONE, l, r, "fne") - : LLVMBuildICmp(ctx->builder, LLVMIntNE, l, r, "ine"); case OP_LT: - return is_float ? LLVMBuildFCmp(ctx->builder, LLVMRealOLT, l, r, "flt") - : LLVMBuildICmp(ctx->builder, LLVMIntSLT, l, r, "ilt"); case OP_GT: - return is_float ? LLVMBuildFCmp(ctx->builder, LLVMRealOGT, l, r, "fgt") - : LLVMBuildICmp(ctx->builder, LLVMIntSGT, l, r, "igt"); case OP_LE: - return is_float ? LLVMBuildFCmp(ctx->builder, LLVMRealOLE, l, r, "fle") - : LLVMBuildICmp(ctx->builder, LLVMIntSLE, l, r, "ile"); - case OP_GE: - return is_float ? LLVMBuildFCmp(ctx->builder, LLVMRealOGE, l, r, "fge") - : LLVMBuildICmp(ctx->builder, LLVMIntSGE, l, r, "ige"); + case OP_GE: { + // 枚举比较: 提取 tag 字段再比较 + LLVMValueRef cl = l, cr = r; + if (node->as.binary.left->type.kind == TYPE_ENUM) { + cl = LLVMBuildExtractValue(ctx->builder, l, 0, "enum_tag_l"); + } + if (node->as.binary.right->type.kind == TYPE_ENUM) { + cr = LLVMBuildExtractValue(ctx->builder, r, 0, "enum_tag_r"); + } + LLVMIntPredicate pred; + switch (node->as.binary.op) { + case OP_EQ: pred = LLVMIntEQ; break; + case OP_NE: pred = LLVMIntNE; break; + case OP_LT: pred = LLVMIntSLT; break; + case OP_GT: pred = LLVMIntSGT; break; + case OP_LE: pred = LLVMIntSLE; break; + case OP_GE: pred = LLVMIntSGE; break; + default: return NULL; + } + if (is_float) + return LLVMBuildFCmp(ctx->builder, pred == LLVMIntEQ ? LLVMRealOEQ : + pred == LLVMIntNE ? LLVMRealONE : pred == LLVMIntSLT ? LLVMRealOLT : + pred == LLVMIntSGT ? LLVMRealOGT : pred == LLVMIntSLE ? LLVMRealOLE : + LLVMRealOGE, cl, cr, "fcmp"); + return LLVMBuildICmp(ctx->builder, pred, cl, cr, "icmp"); + } case OP_AND: return LLVMBuildAnd(ctx->builder, l, r, "and"); case OP_OR: @@ -289,6 +310,9 @@ static LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node) { if (strcmp(node->as.call.name, "print_i64") == 0) { LLVMValueRef arg = codegen_expr(ctx, node->as.call.args[0]); if (!arg) return NULL; + // 枚举类型: 提取 tag 字段 + if (node->as.call.args[0]->type.kind == TYPE_ENUM) + arg = LLVMBuildExtractValue(ctx->builder, arg, 0, "tag"); LLVMTypeRef i64_ty = LLVMInt64TypeInContext(ctx->context); arg = coerce_int(ctx, arg, LLVMTypeOf(arg), i64_ty); LLVMValueRef fmt = LLVMBuildGlobalStringPtr(ctx->builder, "%lld\n", "fmt_i64"); @@ -384,9 +408,25 @@ static LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node) { return LLVMBuildLoad2(ctx->builder, struct_ty, alloca, "struct_val"); } - case AST_ENUM_VARIANT: - return LLVMConstInt(LLVMInt64TypeInContext(ctx->context), + case AST_ENUM_VARIANT: { + // tagged union: { tag, payload } + LLVMValueRef tag = LLVMConstInt(LLVMInt64TypeInContext(ctx->context), (unsigned long long)node->as.enum_variant.variant_index, true); + LLVMValueRef payload = LLVMConstInt(LLVMInt64TypeInContext(ctx->context), 0, true); + if (node->as.enum_variant.payload) { + LLVMValueRef pv = codegen_expr(ctx, node->as.enum_variant.payload); + if (pv) { + // 将 payload 强制转换为 i64 + LLVMTypeRef pv_ty = LLVMTypeOf(pv); + LLVMTypeRef i64_ty = LLVMInt64TypeInContext(ctx->context); + if (pv_ty != i64_ty && LLVMGetTypeKind(pv_ty) == LLVMIntegerTypeKind) + pv = coerce_int(ctx, pv, pv_ty, i64_ty); + payload = pv; + } + } + LLVMValueRef fields[] = { tag, payload }; + return LLVMConstStruct(fields, 2, false); + } case AST_METHOD_CALL: { const char* struct_name = node->as.method_call.receiver->type.struct_name; diff --git a/src/parser/parser.c b/src/parser/parser.c index 23973a5..baef829 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -187,15 +187,21 @@ static AstNode* parse_struct_init(Parser* p, const Token* name, ErrorInfo* error static AstNode* parse_ident_or_call(Parser* p, ErrorInfo* error) { const Token* name = advance(p); - // 枚举变体引用: Name::Variant + // 枚举变体引用: Name::Variant 或 Name::Variant(payload) if (peek(p)->kind == TOK_COLON_COLON) { advance(p); // 跳过 :: const Token* variant = expect(p, TOK_IDENT, error, "枚举变体名"); if (!variant) return NULL; + AstNode* payload = NULL; + if (match(p, TOK_LPAREN)) { + payload = parse_expr(p, error); + if (!payload) return NULL; + if (!expect(p, TOK_RPAREN, error, "缺少 ')'")) return NULL; + } return ast_make_enum_variant(p->arena, arena_strdup_impl(p->arena, name->start, name->length), arena_strdup_impl(p->arena, variant->start, variant->length), - tok_loc(name)); + payload, tok_loc(name)); } // 结构体初始化: Name { field: val, ... } @@ -898,18 +904,39 @@ AstNode* parse(Arena* a, const Token* tokens, size_t count, const Token* name = expect(&p, TOK_IDENT, error, "enum 后应为枚举名"); if (!name) return NULL; if (!expect(&p, TOK_LBRACE, error, "缺少 '{'")) return NULL; - const char* variants[64]; int vcount = 0; + const char* variants[64]; + TypeKind payload_types[64]; + const char* payload_snames[64]; + int vcount = 0; while (peek(&p)->kind != TOK_RBRACE && !error->message) { if (vcount >= 64) { error->message = "枚举变体过多(最多64)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; } const Token* vname = expect(&p, TOK_IDENT, error, "变体名"); if (!vname) return NULL; - variants[vcount++] = arena_strdup_impl(p.arena, vname->start, vname->length); + variants[vcount] = arena_strdup_impl(p.arena, vname->start, vname->length); + payload_types[vcount] = TYPE_VOID; + payload_snames[vcount] = NULL; + // 可选 payload: Variant(Type) + if (peek(&p)->kind == TOK_LPAREN) { + advance(&p); + TypeInfo pti = parse_type_expr(&p, error); + if (pti.kind == TYPE_ERROR) return NULL; + payload_types[vcount] = pti.kind; + payload_snames[vcount] = pti.struct_name; + if (!expect(&p, TOK_RPAREN, error, "缺少 ')'")) return NULL; + } + vcount++; if (peek(&p)->kind == TOK_COMMA) advance(&p); else break; } if (!expect(&p, TOK_RBRACE, error, "缺少 '}'")) return NULL; const char** v_arr = arena_alloc_impl(p.arena, vcount * sizeof(const char*)); memcpy(v_arr, variants, vcount * sizeof(const char*)); - AstNode* enum_decl = ast_make_enum_decl(p.arena, arena_strdup_impl(p.arena, name->start, name->length), v_arr, vcount, tok_loc(name)); + TypeKind* pt_arr = arena_alloc_impl(p.arena, vcount * sizeof(TypeKind)); + memcpy(pt_arr, payload_types, vcount * sizeof(TypeKind)); + const char** ps_arr = arena_alloc_impl(p.arena, vcount * sizeof(const char*)); + memcpy(ps_arr, payload_snames, vcount * sizeof(const char*)); + AstNode* enum_decl = ast_make_enum_decl(p.arena, + arena_strdup_impl(p.arena, name->start, name->length), + v_arr, pt_arr, ps_arr, vcount, tok_loc(name)); if (enum_count >= 64) { error->message = "枚举过多 (最多64)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; } enums[enum_count++] = enum_decl; } else if (peek(&p)->kind == TOK_EXTEND) { diff --git a/src/sema/sema.c b/src/sema/sema.c index 7eb8f4a..a4061f1 100644 --- a/src/sema/sema.c +++ b/src/sema/sema.c @@ -346,6 +346,30 @@ static void analyze_enum_variant(AstNode* node, Scope* scope, ErrorList* errors, node->type.kind = TYPE_ERROR; return; } node->as.enum_variant.variant_index = vi; + // ADT: 检查 payload + TypeKind expected_pt = TYPE_VOID; + if (enum_sym->variant_payload_types) + expected_pt = enum_sym->variant_payload_types[vi]; + if (node->as.enum_variant.payload) { + if (expected_pt == TYPE_VOID && enum_sym->variant_payload_types) { + error_add(errors, "", node->loc.line, node->loc.col, + "枚举变体 '%s::%s' 不接受 payload", + node->as.enum_variant.enum_name, node->as.enum_variant.variant_name); + node->type.kind = TYPE_ERROR; return; + } + analyze_expr(node->as.enum_variant.payload, scope, errors, a); + TypeKind actual = node->as.enum_variant.payload->type.kind; + if (actual != TYPE_ERROR && actual != expected_pt) { + error_add(errors, "", node->loc.line, node->loc.col, + "枚举变体 payload 类型不匹配: 期望 '%s',得到 '%s'", + type_name(expected_pt), type_name(actual)); + } + } else if (expected_pt != TYPE_VOID) { + error_add(errors, "", node->loc.line, node->loc.col, + "枚举变体 '%s::%s' 需要 payload 类型 '%s'", + node->as.enum_variant.enum_name, node->as.enum_variant.variant_name, + type_name(expected_pt)); + } node->type.kind = TYPE_ENUM; } @@ -451,6 +475,8 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena* AstNode* ed = node->as.program.enums[i]; scope_insert_enum(scope, a, ed->as.enum_decl.name, ed->as.enum_decl.variants, + ed->as.enum_decl.variant_payload_types, + ed->as.enum_decl.variant_payload_struct_names, ed->as.enum_decl.variant_count); } // 第一遍:收集所有结构体定义 diff --git a/src/sema/symbol.c b/src/sema/symbol.c index 4097247..29a86ef 100644 --- a/src/sema/symbol.c +++ b/src/sema/symbol.c @@ -35,6 +35,8 @@ Symbol* scope_insert(Scope* scope, void* alloc, const char* name, sym->struct_field_types = NULL; sym->struct_field_count = 0; sym->struct_type_name = NULL; + sym->variant_payload_types = NULL; + sym->variant_payload_struct_names = NULL; sym->array_element_type = 0; sym->array_element_struct_name = NULL; sym->array_size = 0; @@ -66,6 +68,8 @@ Symbol* scope_insert_function(Scope* scope, void* alloc, const char* name, sym->struct_field_types = NULL; sym->struct_field_count = 0; sym->struct_type_name = NULL; + sym->variant_payload_types = NULL; + sym->variant_payload_struct_names = NULL; sym->array_element_type = 0; sym->array_element_struct_name = NULL; sym->array_size = 0; @@ -93,6 +97,8 @@ Symbol* scope_insert_struct(Scope* scope, void* alloc, const char* name, sym->struct_field_struct_names = fstruct_names; sym->struct_field_count = fc; sym->struct_type_name = NULL; + sym->variant_payload_types = NULL; + sym->variant_payload_struct_names = NULL; sym->array_element_type = 0; sym->array_element_struct_name = NULL; sym->array_size = 0; @@ -122,7 +128,8 @@ int scope_struct_field_index(const Symbol* sym, const char* field_name) { } Symbol* scope_insert_enum(Scope* scope, void* alloc, const char* name, - const char** vnames, size_t vc) { + const char** vnames, TypeKind* ptypes, + const char** pstruct_names, size_t vc) { if (scope->head) { for (Symbol* sym = scope->head; sym; sym = sym->next) { if (strcmp(sym->name, name) == 0) return NULL; @@ -138,6 +145,8 @@ Symbol* scope_insert_enum(Scope* scope, void* alloc, const char* name, sym->struct_field_struct_names = NULL; sym->struct_field_count = vc; sym->struct_type_name = NULL; + sym->variant_payload_types = ptypes; + sym->variant_payload_struct_names = pstruct_names; sym->array_element_type = 0; sym->array_element_struct_name = NULL; sym->array_size = 0; diff --git a/src/sema/symbol.h b/src/sema/symbol.h index 4afd344..09f5c7c 100644 --- a/src/sema/symbol.h +++ b/src/sema/symbol.h @@ -29,6 +29,9 @@ typedef struct Symbol { TypeKind array_element_type; const char* array_element_struct_name; int64_t array_size; + // 枚举特有(ADT payload) + TypeKind* variant_payload_types; + const char** variant_payload_struct_names; // 类型别名标记 bool is_type_alias; // 链表(同一作用域内的下一个符号) @@ -69,7 +72,8 @@ int scope_struct_field_index(const Symbol* sym, const char* field_name); // 插入枚举符号 Symbol* scope_insert_enum(Scope* scope, void* alloc, const char* name, - const char** vnames, size_t vc); + const char** vnames, TypeKind* ptypes, + const char** pstruct_names, size_t vc); // 在枚举符号中查找变体索引(返回 -1 表示未找到) int scope_enum_variant_index(const Symbol* sym, const char* variant_name); diff --git a/test/programs/31_adt.l b/test/programs/31_adt.l new file mode 100644 index 0000000..5e0582f --- /dev/null +++ b/test/programs/31_adt.l @@ -0,0 +1,17 @@ +enum Option { Some(i64), None } + +fn main() -> i64 { + let s = Option::Some(42); + let n = Option::None; + + // 打印 tag 值 (Some=0, None=1) + print_i64(s); + print_i64(n); + + // match 判断是 Some 还是 None(只比较 tag) + match s { + Option::None => { print_i64(999); } + _ => { print_i64(100); } + } + return 0; +} diff --git a/test/test_codegen.c b/test/test_codegen.c index 313494d..79a18d1 100644 --- a/test/test_codegen.c +++ b/test/test_codegen.c @@ -243,11 +243,11 @@ void test_codegen_enum() { /* 构造 AST: enum Color { Red, Green, Blue } fn main() -> i64 { let c = Color::Green; print_i64(c); return 0; } */ const char* cvariants[] = {"Red", "Green", "Blue"}; - AstNode* enum_decl = ast_make_enum_decl(&a, "Color", cvariants, 3, loc_at(1, 1)); + AstNode* enum_decl = ast_make_enum_decl(&a, "Color", cvariants, NULL, NULL, 3, loc_at(1, 1)); AstNode* enums[] = { enum_decl }; /* let c = Color::Green; */ - AstNode* cv = ast_make_enum_variant(&a, "Color", "Green", loc_at(1, 1)); + AstNode* cv = ast_make_enum_variant(&a, "Color", "Green", NULL, loc_at(1, 1)); cv->as.enum_variant.variant_index = 1; /* Green = index 1 */ cv->type.kind = TYPE_ENUM; @@ -426,7 +426,7 @@ void test_codegen_match() { /* enum Color { Red, Green, Blue } */ const char* variants[] = {"Red", "Green", "Blue"}; - AstNode* enum_decl = ast_make_enum_decl(&a, "Color", variants, 3, loc_at(1, 1)); + AstNode* enum_decl = ast_make_enum_decl(&a, "Color", variants, NULL, NULL, 3, loc_at(1, 1)); AstNode* enums[] = { enum_decl }; /* fn main() -> i64 { @@ -445,7 +445,7 @@ void test_codegen_match() { else { return 0; } } */ - AstNode* match_val = ast_make_enum_variant(&a, "Color", "Green", loc_at(1, 1)); + AstNode* match_val = ast_make_enum_variant(&a, "Color", "Green", NULL, loc_at(1, 1)); match_val->type.kind = TYPE_ENUM; AstNode* let_stmt = ast_make_let(&a, "__match_val", TYPE_UNKNOWN, false, false, @@ -456,7 +456,7 @@ void test_codegen_match() { match_ident->type.kind = TYPE_ENUM; // if __match_val == Color::Red { return 1; } - AstNode* red_variant = ast_make_enum_variant(&a, "Color", "Red", loc_at(1, 1)); + AstNode* red_variant = ast_make_enum_variant(&a, "Color", "Red", NULL, loc_at(1, 1)); red_variant->type.kind = TYPE_ENUM; AstNode* red_cond = ast_make_binary(&a, OP_EQ, match_ident, red_variant, loc_at(1, 1)); red_cond->type.kind = TYPE_BOOL; @@ -467,7 +467,7 @@ void test_codegen_match() { // if __match_val == Color::Green { return 2; } AstNode* match_ident2 = ast_make_ident(&a, "__match_val", loc_at(1, 1)); match_ident2->type.kind = TYPE_ENUM; - AstNode* green_variant = ast_make_enum_variant(&a, "Color", "Green", loc_at(1, 1)); + AstNode* green_variant = ast_make_enum_variant(&a, "Color", "Green", NULL, loc_at(1, 1)); green_variant->type.kind = TYPE_ENUM; AstNode* green_cond = ast_make_binary(&a, OP_EQ, match_ident2, green_variant, loc_at(1, 1)); green_cond->type.kind = TYPE_BOOL;