From b0c55ff777b42887dfb84e79b07c94a0f51344e1 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 10 May 2022 10:39:31 +0200 Subject: [PATCH] Support enum associated values. --- src/compiler/compiler_internal.h | 2 + src/compiler/enums.h | 1 + src/compiler/llvm_codegen.c | 55 +++++- src/compiler/llvm_codegen_expr.c | 11 ++ src/compiler/parse_global.c | 1 + src/compiler/sema_decls.c | 168 ++++++++++++++---- src/compiler/sema_expr.c | 15 ++ src/compiler/sema_internal.h | 2 +- src/compiler/symtab.c | 1 + .../enumerations/enum_associated_value.c3t | 51 ++++++ test/test_suite/types/enum_ok.c3 | 2 +- 11 files changed, 273 insertions(+), 36 deletions(-) create mode 100644 test/test_suite/enumerations/enum_associated_value.c3t diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 50a64a498..4a01afb0b 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -301,6 +301,7 @@ typedef struct Path *path; const char *name; SourceSpan span; + AttributeType attr_kind : 8; union { Expr *expr; @@ -359,6 +360,7 @@ typedef struct VarDecl_ }; union { + int32_t index; struct { struct SemaContext_ *context; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 402849fb9..89ac65c5f 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -665,6 +665,7 @@ typedef enum ATTRIBUTE_AUTOIMPORT, ATTRIBUTE_OPERATOR, ATTRIBUTE_PURE, + ATTRIBUTE_REFLECT, ATTRIBUTE_NONE, NUMBER_OF_ATTRIBUTES = ATTRIBUTE_NONE, } AttributeType; diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 7b14e803a..000795bf5 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -730,7 +730,8 @@ void llvm_emit_introspection_type_from_decl(GenContext *c, Decl *decl) } if (decl_is_enum_kind(decl)) { - unsigned elements = vec_size(decl->enums.values); + Decl **enum_vals = decl->enums.values; + unsigned elements = vec_size(enum_vals); LLVMTypeRef element_type = llvm_get_type(c, type_voidptr); LLVMTypeRef elements_type = LLVMArrayType(element_type, elements); scratch_buffer_clear(); @@ -744,8 +745,54 @@ void llvm_emit_introspection_type_from_decl(GenContext *c, Decl *decl) for (unsigned i = 0; i < elements; i++) { AlignSize store_align; - decl->enums.values[i]->backend_ref = llvm_emit_array_gep_raw(c, enum_elements, elements_type, i, alignment, &store_align); + enum_vals[i]->backend_ref = llvm_emit_array_gep_raw(c, enum_elements, elements_type, i, alignment, &store_align); } + Decl **associated_values = decl->enums.parameters; + unsigned associated_value_count = vec_size(associated_values); + if (associated_value_count && elements) + { + + LLVMValueRef *values = malloc_arena(elements * sizeof(LLVMValueRef)); + LLVMTypeRef val_type; + VECEACH(associated_values, ai) + { + val_type = NULL; + bool mixed = false; + for (unsigned i = 0; i < elements; i++) + { + BEValue value; + llvm_emit_expr(c, &value, enum_vals[i]->enum_constant.args[ai]); + assert(!llvm_value_is_addr(&value)); + LLVMValueRef llvm_value = llvm_value_is_bool(&value) ? LLVMConstZExt(value.value, c->byte_type) : value.value; + values[i] = llvm_value; + if (!val_type) + { + val_type = LLVMTypeOf(llvm_value); + continue; + } + if (val_type != LLVMTypeOf(llvm_value)) mixed = true; + } + Decl *associated_value = associated_values[ai]; + LLVMValueRef associated_value_arr = mixed ? LLVMConstStruct(values, elements, true) : LLVMConstArray(val_type, values, elements); + scratch_buffer_clear(); + scratch_buffer_append(decl->extname); + scratch_buffer_append("$$"); + scratch_buffer_append(associated_value->name); + LLVMValueRef global_ref = LLVMAddGlobal(c->module, LLVMTypeOf(associated_value_arr), scratch_buffer_to_string()); + LLVMSetInitializer(global_ref, associated_value_arr); + LLVMSetGlobalConstant(global_ref, true); + if (mixed) + { + LLVMTypeRef cast_type = llvm_get_ptr_type(c, type_get_array(associated_value->type, elements)); + associated_value->backend_ref = LLVMConstBitCast(global_ref, cast_type); + } + else + { + associated_value->backend_ref = global_ref; + } + } + } + } scratch_buffer_clear(); scratch_buffer_append("introspect."); @@ -1004,6 +1051,10 @@ void *llvm_gen(Module *module) { llvm_emit_type_decls(gen_context, unit->types[i]); } + VECEACH(unit->enums, i) + { + llvm_emit_type_decls(gen_context, unit->enums[i]); + } VECEACH(unit->functions, i) { llvm_emit_function_decl(gen_context, unit->functions[i]); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 0e15e72ed..0d2d9a373 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -1081,6 +1081,17 @@ static inline void gencontext_emit_access_addr(GenContext *context, BEValue *be_ llvm_emit_expr(context, be_value, parent); Decl *member = expr->access_expr.ref; + Type *flat_type = type_flatten_distinct_failable(parent->type); + if (flat_type->type_kind == TYPE_ENUM) + { + llvm_value_rvalue(context, be_value); + LLVMTypeRef value_type = llvm_get_type(context, type_get_array(member->type, vec_size(flat_type->decl->enums.values))); + AlignSize align = LLVMGetAlignment(member->backend_ref); + AlignSize alignment; + LLVMValueRef ptr = llvm_emit_array_gep_raw_index(context, member->backend_ref, value_type, be_value->value, align, &alignment); + llvm_value_set_address(be_value, ptr, member->type, alignment); + return; + } gencontext_emit_member_addr(context, be_value, type_lowering(parent->type)->decl, member); } diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 6f19d9bea..a6d03e357 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1857,6 +1857,7 @@ static inline bool parse_enum_spec(ParseContext *c, TypeInfo **type_ref, Decl*** SEMA_ERROR_LAST("Vararg parameters are not allowed as enum parameters."); return false; } + last_parameter->var.index = vec_size(*parameters_ref) - 1; if (!try_consume(c, TOKEN_COMMA)) { EXPECT_OR_RET(TOKEN_RPAREN, false); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 3839dd9ce..64d217245 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -370,12 +370,11 @@ static bool sema_analyse_struct_union(SemaContext *context, Decl *decl) { Attr *attr = decl->attributes[i]; - AttributeType attribute = sema_analyse_attribute(context, attr, domain); - if (attribute == ATTRIBUTE_NONE) return decl_poison(decl); + if (!sema_analyse_attribute(context, attr, domain)) return decl_poison(decl); bool had = false; #define SET_ATTR(_X) had = decl->func_decl._X; decl->func_decl._X = true; break - switch (attribute) + switch (attr->attr_kind) { case ATTRIBUTE_EXTNAME: had = decl->has_extname; @@ -537,12 +536,11 @@ static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl) { Attr *attr = decl->attributes[i]; - AttributeType attribute = sema_analyse_attribute(context, attr, ATTR_BITSTRUCT); - if (attribute == ATTRIBUTE_NONE) return decl_poison(decl); + if (!sema_analyse_attribute(context, attr, ATTR_BITSTRUCT)) return decl_poison(decl); bool had = false; #define SET_ATTR(_X) had = decl->bitstruct._X; decl->bitstruct._X = true; break - switch (attribute) + switch (attr->attr_kind) { case ATTRIBUTE_OVERLAP: SET_ATTR(overlap); @@ -762,6 +760,49 @@ static inline bool sema_analyse_distinct(SemaContext *context, Decl *decl) return true; } +static inline bool sema_analyse_enum_param(SemaContext *context, Decl *param, bool *has_default) +{ + *has_default = false; + assert(param->decl_kind == DECL_VAR); + // We need to check that the parameters are not typeless nor are of any macro parameter kind. + if (param->var.kind != VARDECL_PARAM && !param->var.type_info) + { + SEMA_ERROR(param, "An associated value must be a normal typed parameter."); + return false; + } + + if (vec_size(param->attributes)) + { + SEMA_ERROR(param->attributes[0], "There are no valid attributes for associated values."); + return false; + } + if (!sema_resolve_type_info(context, param->var.type_info)) return false; + if (param->var.vararg) + { + param->var.type_info->type = type_get_subarray(param->var.type_info->type); + } + param->type = param->var.type_info->type; + if (param->var.init_expr) + { + Expr *expr = param->var.init_expr; + + if (!sema_analyse_expr_rhs(context, param->type, expr, true)) return false; + if (IS_FAILABLE(expr)) + { + SEMA_ERROR(expr, "Default arguments may not be failable."); + return false; + } + if (!expr_is_constant_eval(expr, CONSTANT_EVAL_ANY)) + { + SEMA_ERROR(expr, "Only constant expressions may be used as default values."); + return false; + } + *has_default = true; + } + param->alignment = type_abi_alignment(param->type); + return true; +} + static inline bool sema_analyse_enum(SemaContext *context, Decl *decl) { // Resolve the type of the enum. @@ -778,6 +819,40 @@ static inline bool sema_analyse_enum(SemaContext *context, Decl *decl) } DEBUG_LOG("* Enum type resolved to %s.", type->name); + + Decl **associated_values = decl->enums.parameters; + unsigned associated_value_count = vec_size(associated_values); + unsigned mandatory_count = 0; + bool default_values_used = false; + for (unsigned i = 0; i < associated_value_count; i++) + { + Decl *value = associated_values[i]; + switch (value->resolve_status) + { + case RESOLVE_DONE: + continue; + case RESOLVE_RUNNING: + SEMA_ERROR(value, "Recursive definition found."); + return false; + case RESOLVE_NOT_DONE: + value->resolve_status = RESOLVE_RUNNING; + break; + } + bool has_default = false; + if (!sema_analyse_enum_param(context, value, &has_default)) return false; + if (!has_default) + { + mandatory_count++; + if (default_values_used && !value->var.vararg) + { + SEMA_ERROR(value, "Non-default parameters cannot appear after default parameters."); + return false; + } + } + default_values_used |= has_default; + value->resolve_status = RESOLVE_DONE; + } + bool success = true; unsigned enums = vec_size(decl->enums.values); Int128 value = { 0, 0 }; @@ -811,6 +886,36 @@ static inline bool sema_analyse_enum(SemaContext *context, Decl *decl) // Update the value value.low++; + + Expr **args = enum_value->enum_constant.args; + unsigned arg_count = vec_size(args); + if (arg_count > associated_value_count) + { + if (!associated_value_count) + { + SEMA_ERROR(args[0], "No associated values are defined for this enum."); + return false; + } + SEMA_ERROR(args[associated_value_count], "Only %d associated value(s) may be defined for this enum."); + return false; + } + if (arg_count < mandatory_count) + { + SEMA_ERROR(enum_value, "Expected associated value(s) defined for this enum."); + return false; + } + for (unsigned j = 0; j < arg_count; j++) + { + Expr *arg = args[j]; + + if (!sema_analyse_expr_rhs(context, associated_values[j]->type, arg, false)) return false; + if (!expr_is_constant_eval(arg, CONSTANT_EVAL_ANY)) + { + SEMA_ERROR(arg, "Expected a constant expression as parameter."); + return false; + } + } + REMINDER("named parameters and defaults do not work"); enum_value->resolve_status = RESOLVE_DONE; } return success; @@ -1111,13 +1216,13 @@ static const char *attribute_domain_to_string(AttributeDomain domain) } UNREACHABLE } -AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, AttributeDomain domain) +bool sema_analyse_attribute(SemaContext *context, Attr *attr, AttributeDomain domain) { AttributeType type = attribute_by_name(attr); if (type == ATTRIBUTE_NONE) { sema_error_at(attr->span, "There is no attribute with the name '%s', did you mistype?", attr->name); - return ATTRIBUTE_NONE; + return false; } static AttributeDomain attribute_domain[NUMBER_OF_ATTRIBUTES] = { [ATTRIBUTE_WEAK] = ATTR_FUNC | ATTR_CONST | ATTR_GLOBAL, @@ -1141,14 +1246,16 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute [ATTRIBUTE_OVERLAP] = ATTR_BITSTRUCT, [ATTRIBUTE_AUTOIMPORT] = ATTR_MACRO | ATTR_FUNC, [ATTRIBUTE_OPERATOR] = ATTR_MACRO | ATTR_FUNC, + [ATTRIBUTE_REFLECT] = ATTR_ENUM, [ATTRIBUTE_PURE] = ATTR_CALL, }; if ((attribute_domain[type] & domain) != domain) { sema_error_at(attr->span, "'%s' is not a valid %s attribute.", attr->name, attribute_domain_to_string(domain)); - return ATTRIBUTE_NONE; + return false; } + attr->attr_kind = type; switch (type) { case ATTRIBUTE_CDECL: @@ -1156,7 +1263,7 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute case ATTRIBUTE_STDCALL: case ATTRIBUTE_VECCALL: case ATTRIBUTE_REGCALL: - return type; + return true; case ATTRIBUTE_OPERATOR: { Expr *expr = attr->expr; @@ -1183,44 +1290,44 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute { goto FAILED_OP_TYPE; } - return ATTRIBUTE_OPERATOR; + return true; FAILED_OP_TYPE: SEMA_ERROR(attr, "'operator' requires an operator type argument: '%s', '%s' or '%s'.", kw_elementat, kw_elementref, kw_len); - return ATTRIBUTE_NONE; + return false; } case ATTRIBUTE_ALIGN: if (!attr->expr) { sema_error_at(attr->span, "'align' requires an power-of-2 argument, e.g. align(8)."); - return ATTRIBUTE_NONE; + return false; } if (!sema_analyse_expr(context, attr->expr)) return false; if (attr->expr->expr_kind != EXPR_CONST || !type_is_integer(attr->expr->type->canonical)) { SEMA_ERROR(attr->expr, "Expected a constant integer value as argument."); - return ATTRIBUTE_NONE; + return false; } { if (int_ucomp(attr->expr->const_expr.ixx, MAX_ALIGNMENT, BINARYOP_GT)) { SEMA_ERROR(attr->expr, "Alignment must be less or equal to %ull.", MAX_ALIGNMENT); - return ATTRIBUTE_NONE; + return false; } if (int_ucomp(attr->expr->const_expr.ixx, 0, BINARYOP_LE)) { SEMA_ERROR(attr->expr, "Alignment must be greater than zero."); - return ATTRIBUTE_NONE; + return false; } uint64_t align = int_to_u64(attr->expr->const_expr.ixx); if (!is_power_of_two(align)) { SEMA_ERROR(attr->expr, "Alignment must be a power of two."); - return ATTRIBUTE_NONE; + return false; } attr->alignment = (AlignSize)align; } - return type; + return true; case ATTRIBUTE_SECTION: case ATTRIBUTE_EXTNAME: if (context->unit->module->is_generic) @@ -1231,22 +1338,22 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute if (!attr->expr) { sema_error_at(attr->span, "'%s' requires a string argument, e.g. %s(\"foo\").", attr->name, attr->name); - return ATTRIBUTE_NONE; + return false; } if (!sema_analyse_expr(context, attr->expr)) return false; if (attr->expr->expr_kind != EXPR_CONST || attr->expr->const_expr.const_kind != CONST_STRING) { SEMA_ERROR(attr->expr, "Expected a constant string value as argument."); - return ATTRIBUTE_NONE; + return false; } - return type; + return true; default: if (attr->expr) { SEMA_ERROR(attr->expr, "'%s' should not have any arguments.", attr->name); - return ATTRIBUTE_NONE; + return false; } - return type; + return true; } } @@ -1465,13 +1572,12 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) { Attr *attr = decl->attributes[i]; - AttributeType attribute = sema_analyse_attribute(context, attr, ATTR_FUNC); - if (attribute == ATTRIBUTE_NONE) return decl_poison(decl); + if (!sema_analyse_attribute(context, attr, ATTR_FUNC)) return decl_poison(decl); bool had = false; #define SET_ATTR(_X) had = decl->func_decl._X; decl->func_decl._X = true; break - switch (attribute) + switch (attr->attr_kind) { case ATTRIBUTE_OPERATOR: had = decl->operator > 0; @@ -1628,11 +1734,10 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl) { Attr *attr = decl->attributes[i]; - AttributeType attribute = sema_analyse_attribute(context, attr, ATTR_MACRO); - if (attribute == ATTRIBUTE_NONE) return decl_poison(decl); + if (!sema_analyse_attribute(context, attr, ATTR_MACRO)) return decl_poison(decl); bool had = false; - switch (attribute) + switch (attr->attr_kind) { case ATTRIBUTE_OPERATOR: had = decl->operator > 0; @@ -1784,12 +1889,11 @@ static bool sema_analyse_attributes_for_var(SemaContext *context, Decl *decl) { Attr *attr = decl->attributes[i]; - AttributeType attribute = sema_analyse_attribute(context, attr, domain); - if (attribute == ATTRIBUTE_NONE) return decl_poison(decl); + if (!sema_analyse_attribute(context, attr, domain)) decl_poison(decl); bool had = false; #define SET_ATTR(_X) had = decl->func_decl._X; decl->func_decl._X = true; break - switch (attribute) + switch (attr->attr_kind) { case ATTRIBUTE_EXTNAME: had = decl->has_extname; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 3d05207f9..355144089 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -2879,6 +2879,14 @@ static void add_members_to_context(SemaContext *context, Decl *decl) if (!type_is_user_defined(type)) break; decl = type->decl; } + if (decl_is_enum_kind(decl)) + { + Decl **members = decl->enums.parameters; + VECEACH(members, i) + { + sema_add_member(context, members[i]); + } + } if (decl_is_struct_type(decl) || decl->decl_kind == DECL_BITSTRUCT) { Decl **members = decl->strukt.members; @@ -3259,6 +3267,13 @@ CHECK_DEEPER: member = sema_resolve_symbol_in_current_dynamic_scope(context, kw); SCOPE_END; + if (member && decl_is_enum_kind(decl) && parent->expr_kind == EXPR_CONST) + { + assert(parent->const_expr.const_kind == CONST_ENUM); + Expr *copy_init = expr_macro_copy(parent->const_expr.enum_val->enum_constant.args[member->var.index]); + expr_replace(expr, copy_init); + return true; + } if (!member) { Decl *ambiguous = NULL; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 7c044d6c8..6a753770c 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -56,7 +56,7 @@ bool splitpathref(const char *string, ArraySize len, Path **path_ref, const char #define IS_CONST(_x) ((_x)->expr_kind == EXPR_CONST) -AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, AttributeDomain domain); +bool sema_analyse_attribute(SemaContext *context, Attr *attr, AttributeDomain domain); bool expr_is_ltype(Expr *expr); bool sema_expr_check_assign(SemaContext *c, Expr *expr); diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 0d6a8a3ca..0c17a69b0 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -216,6 +216,7 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_OVERLAP] = KW_DEF("@overlap"); attribute_list[ATTRIBUTE_OPERATOR] = KW_DEF("@operator"); attribute_list[ATTRIBUTE_PURE] = kw_at_pure; + attribute_list[ATTRIBUTE_REFLECT] = KW_DEF("@reflect"); attribute_list[ATTRIBUTE_AUTOIMPORT] = KW_DEF("@autoimport"); for (unsigned i = 0; i < NUMBER_OF_ATTRIBUTES; i++) diff --git a/test/test_suite/enumerations/enum_associated_value.c3t b/test/test_suite/enumerations/enum_associated_value.c3t new file mode 100644 index 000000000..4a2b83191 --- /dev/null +++ b/test/test_suite/enumerations/enum_associated_value.c3t @@ -0,0 +1,51 @@ +// #target: macos-x64 +module test; + +enum Foo : uint (int val, char* testme) +{ + A(123, "Number A"), + B(333, "Number B"), +} + +fn void main() +{ + int x = Foo.A.val; + Foo f = Foo.B; + Foo g = Foo.A; + libc::printf("%d (%s) %d (%s)\n", f.val, f.testme, g.val, g.testme); +} + +/* #expect: test.ll + +@"test.Foo$elements" = linkonce constant [2 x i8*] zeroinitializer +@"test.Foo$$val" = constant [2 x i32] [i32 123, i32 333] +@.str = private unnamed_addr constant [9 x i8] c"Number A\00", align 1 +@.str.1 = private unnamed_addr constant [9 x i8] c"Number B\00", align 1 +@"test.Foo$$testme" = constant [2 x i8*] [i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.1, i32 0, i32 0)] +@introspect.Foo = linkonce constant i8 1 +@.str.2 = private unnamed_addr constant [17 x i8] c"%d (%s) %d (%s)\0A\00", align 1 + +; Function Attrs: nounwind +define void @test.main() #0 { +entry: + %x = alloca i32, align 4 + %f = alloca i32, align 4 + %g = alloca i32, align 4 + store i32 123, i32* %x, align 4 + store i32 1, i32* %f, align 4 + store i32 0, i32* %g, align 4 + %0 = load i32, i32* %f, align 4 + %1 = getelementptr inbounds [2 x i32], [2 x i32]* @"test.Foo$$val", i32 0, i32 %0 + %2 = load i32, i32* %1, align 4 + %3 = load i32, i32* %f, align 4 + %4 = getelementptr inbounds [2 x i8*], [2 x i8*]* @"test.Foo$$testme", i32 0, i32 %3 + %5 = load i8*, i8** %4, align 8 + %6 = load i32, i32* %g, align 4 + %7 = getelementptr inbounds [2 x i32], [2 x i32]* @"test.Foo$$val", i32 0, i32 %6 + %8 = load i32, i32* %7, align 4 + %9 = load i32, i32* %g, align 4 + %10 = getelementptr inbounds [2 x i8*], [2 x i8*]* @"test.Foo$$testme", i32 0, i32 %9 + %11 = load i8*, i8** %10, align 8 + %12 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @.str.2, i32 0, i32 0), i32 %2, i8* %5, i32 %8, i8* %11) + ret void +} diff --git a/test/test_suite/types/enum_ok.c3 b/test/test_suite/types/enum_ok.c3 index ed54853b2..1374947c8 100644 --- a/test/test_suite/types/enum_ok.c3 +++ b/test/test_suite/types/enum_ok.c3 @@ -21,7 +21,7 @@ enum EnumTestDefault enum EnumWithErrorData2 : int (int bar, ) { - TEST + TEST // #error: Expected associated value } enum EnumTestErrorType4