From 5a5956bd204017321a86dd058898c0dc57ff2c03 Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Fri, 21 Aug 2020 14:31:24 -0600 Subject: [PATCH] Implement @Type for Enum --- src/analyze.cpp | 231 ++++++++++++++++++---------------- src/ir.cpp | 69 +++++++++- test/compile_errors.zig | 25 ++-- test/stage1/behavior/type.zig | 34 +++++ 4 files changed, 243 insertions(+), 116 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 7b71d7166b..acdbf3e933 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2586,7 +2586,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { return ErrorNone; AstNode *decl_node = enum_type->data.enumeration.decl_node; - assert(decl_node->type == NodeTypeContainerDecl); if (enum_type->data.enumeration.resolve_loop_flag) { if (enum_type->data.enumeration.resolve_status != ResolveStatusInvalid) { @@ -2600,15 +2599,20 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { enum_type->data.enumeration.resolve_loop_flag = true; - assert(!enum_type->data.enumeration.fields); - uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length; - if (field_count == 0) { - add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields")); + uint32_t field_count; + if (decl_node->type == NodeTypeContainerDecl) { + assert(!enum_type->data.enumeration.fields); + field_count = (uint32_t)decl_node->data.container_decl.fields.length; + if (field_count == 0) { + add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields")); - enum_type->data.enumeration.src_field_count = field_count; - enum_type->data.enumeration.fields = nullptr; - enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; - return ErrorSemanticAnalyzeFail; + enum_type->data.enumeration.src_field_count = field_count; + enum_type->data.enumeration.fields = nullptr; + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + return ErrorSemanticAnalyzeFail; + } + } else { + field_count = enum_type->data.enumeration.src_field_count; } Scope *scope = &enum_type->data.enumeration.decls_scope->base; @@ -2624,8 +2628,16 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { enum_type->abi_size = tag_int_type->abi_size; enum_type->abi_align = tag_int_type->abi_align; - if (decl_node->data.container_decl.init_arg_expr != nullptr) { - ZigType *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); + ZigType *wanted_tag_int_type = nullptr; + if (decl_node->type == NodeTypeContainerDecl) { + if (decl_node->data.container_decl.init_arg_expr != nullptr) { + wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); + } + } else { + wanted_tag_int_type = enum_type->data.enumeration.tag_int_type; + } + + if (wanted_tag_int_type != nullptr) { if (type_is_invalid(wanted_tag_int_type)) { enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; } else if (wanted_tag_int_type->id != ZigTypeIdInt && @@ -2654,7 +2666,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { } } - enum_type->data.enumeration.non_exhaustive = false; enum_type->data.enumeration.tag_int_type = tag_int_type; enum_type->size_in_bits = tag_int_type->size_in_bits; enum_type->abi_size = tag_int_type->abi_size; @@ -2663,121 +2674,131 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { BigInt bi_one; bigint_init_unsigned(&bi_one, 1); - AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1); - if (buf_eql_str(last_field_node->data.struct_field.name, "_")) { - field_count -= 1; - if (field_count > 1 && log2_u64(field_count) == enum_type->size_in_bits) { - add_node_error(g, last_field_node, buf_sprintf("non-exhaustive enum specifies every value")); - enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + if (decl_node->type == NodeTypeContainerDecl) { + AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1); + if (buf_eql_str(last_field_node->data.struct_field.name, "_")) { + if (last_field_node->data.struct_field.value != nullptr) { + add_node_error(g, last_field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum")); + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + } + if (decl_node->data.container_decl.init_arg_expr == nullptr) { + add_node_error(g, decl_node, buf_sprintf("non-exhaustive enum must specify size")); + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + } + enum_type->data.enumeration.non_exhaustive = true; + } else { + enum_type->data.enumeration.non_exhaustive = false; } - if (decl_node->data.container_decl.init_arg_expr == nullptr) { - add_node_error(g, last_field_node, buf_sprintf("non-exhaustive enum must specify size")); - enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; - } - if (last_field_node->data.struct_field.value != nullptr) { - add_node_error(g, last_field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum")); - enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; - } - enum_type->data.enumeration.non_exhaustive = true; } - enum_type->data.enumeration.src_field_count = field_count; - enum_type->data.enumeration.fields = heap::c_allocator.allocate(field_count); - enum_type->data.enumeration.fields_by_name.init(field_count); - - HashMap occupied_tag_values = {}; - occupied_tag_values.init(field_count); - - TypeEnumField *last_enum_field = nullptr; - - for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { - AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); - TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; - type_enum_field->name = field_node->data.struct_field.name; - type_enum_field->decl_index = field_i; - type_enum_field->decl_node = field_node; - - if (field_node->data.struct_field.type != nullptr) { - ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type, - buf_sprintf("structs and unions, not enums, support field types")); - add_error_note(g, msg, decl_node, - buf_sprintf("consider 'union(enum)' here")); - } else if (field_node->data.struct_field.align_expr != nullptr) { - ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.align_expr, - buf_sprintf("structs and unions, not enums, support field alignment")); - add_error_note(g, msg, decl_node, - buf_sprintf("consider 'union(enum)' here")); - } - - if (buf_eql_str(type_enum_field->name, "_")) { - add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last")); + if (enum_type->data.enumeration.non_exhaustive) { + field_count -= 1; + if (field_count > 1 && log2_u64(field_count) == enum_type->size_in_bits) { + add_node_error(g, decl_node, buf_sprintf("non-exhaustive enum specifies every value")); enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; } + } - auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field); - if (field_entry != nullptr) { - ErrorMsg *msg = add_node_error(g, field_node, - buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name))); - add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here")); - enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; - continue; - } + if (decl_node->type == NodeTypeContainerDecl) { + enum_type->data.enumeration.src_field_count = field_count; + enum_type->data.enumeration.fields = heap::c_allocator.allocate(field_count); + enum_type->data.enumeration.fields_by_name.init(field_count); - AstNode *tag_value = field_node->data.struct_field.value; + HashMap occupied_tag_values = {}; + occupied_tag_values.init(field_count); - if (tag_value != nullptr) { - // A user-specified value is available - ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, - nullptr, UndefBad); - if (type_is_invalid(result->type)) { + TypeEnumField *last_enum_field = nullptr; + + for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { + AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); + TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; + type_enum_field->name = field_node->data.struct_field.name; + type_enum_field->decl_index = field_i; + type_enum_field->decl_node = field_node; + + if (field_node->data.struct_field.type != nullptr) { + ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type, + buf_sprintf("structs and unions, not enums, support field types")); + add_error_note(g, msg, decl_node, + buf_sprintf("consider 'union(enum)' here")); + } else if (field_node->data.struct_field.align_expr != nullptr) { + ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.align_expr, + buf_sprintf("structs and unions, not enums, support field alignment")); + add_error_note(g, msg, decl_node, + buf_sprintf("consider 'union(enum)' here")); + } + + if (buf_eql_str(type_enum_field->name, "_")) { + add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last")); + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + } + + auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field); + if (field_entry != nullptr) { + ErrorMsg *msg = add_node_error(g, field_node, + buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name))); + add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here")); enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; continue; } - assert(result->special != ConstValSpecialRuntime); - assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt); + AstNode *tag_value = field_node->data.struct_field.value; - bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint); - } else { - // No value was explicitly specified: allocate the last value + 1 - // or, if this is the first element, zero - if (last_enum_field != nullptr) { - bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one); + if (tag_value != nullptr) { + // A user-specified value is available + ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, + nullptr, UndefBad); + if (type_is_invalid(result->type)) { + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + continue; + } + + assert(result->special != ConstValSpecialRuntime); + assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt); + + bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint); } else { - bigint_init_unsigned(&type_enum_field->value, 0); + // No value was explicitly specified: allocate the last value + 1 + // or, if this is the first element, zero + if (last_enum_field != nullptr) { + bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one); + } else { + bigint_init_unsigned(&type_enum_field->value, 0); + } + + // Make sure we can represent this number with tag_int_type + if (!bigint_fits_in_bits(&type_enum_field->value, + tag_int_type->size_in_bits, + tag_int_type->data.integral.is_signed)) { + enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; + + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &type_enum_field->value, 10); + add_node_error(g, field_node, + buf_sprintf("enumeration value %s too large for type '%s'", + buf_ptr(val_buf), buf_ptr(&tag_int_type->name))); + + break; + } } - // Make sure we can represent this number with tag_int_type - if (!bigint_fits_in_bits(&type_enum_field->value, - tag_int_type->size_in_bits, - tag_int_type->data.integral.is_signed)) { + // Make sure the value is unique + auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node); + if (entry != nullptr && enum_type->data.enumeration.layout != ContainerLayoutExtern) { enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &type_enum_field->value, 10); - add_node_error(g, field_node, - buf_sprintf("enumeration value %s too large for type '%s'", - buf_ptr(val_buf), buf_ptr(&tag_int_type->name))); - break; + ErrorMsg *msg = add_node_error(g, field_node, + buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); + add_error_note(g, msg, entry->value, + buf_sprintf("other occurrence here")); } + + last_enum_field = type_enum_field; } - - // Make sure the value is unique - auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node); - if (entry != nullptr && enum_type->data.enumeration.layout != ContainerLayoutExtern) { - enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; - - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &type_enum_field->value, 10); - - ErrorMsg *msg = add_node_error(g, field_node, - buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); - add_error_note(g, msg, entry->value, - buf_sprintf("other occurrence here")); - } - - last_enum_field = type_enum_field; + occupied_tag_values.deinit(); } if (enum_type->data.enumeration.resolve_status == ResolveStatusInvalid) @@ -2786,8 +2807,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { enum_type->data.enumeration.resolve_loop_flag = false; enum_type->data.enumeration.resolve_status = ResolveStatusSizeKnown; - occupied_tag_values.deinit(); - return ErrorNone; } diff --git a/src/ir.cpp b/src/ir.cpp index 976c50b8ff..731f31fbb4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -26276,7 +26276,74 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI return entry; } - case ZigTypeIdEnum: + case ZigTypeIdEnum: { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "Enum", nullptr)); + + ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0); + assert(layout_value->special == ConstValSpecialStatic); + assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr)); + ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); + + ZigType *tag_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "tag_type", 1); + + ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 2); + assert(fields_value->special == ConstValSpecialStatic); + assert(is_slice(fields_value->type)); + ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index]; + ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index]; + size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint); + + ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 3); + assert(decls_value->special == ConstValSpecialStatic); + assert(is_slice(decls_value->type)); + ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index]; + size_t decls_len = bigint_as_usize(&decls_len_value->data.x_bigint); + if (decls_len != 0) { + ir_add_error(ira, source_instr, buf_create_from_str("TypeInfo.Enum.decls must be empty for @Type")); + return ira->codegen->invalid_inst_gen->value->type; + } + + Error err; + bool is_exhaustive; + if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_exhaustive", 4, &is_exhaustive))) + return ira->codegen->invalid_inst_gen->value->type; + + ZigType *entry = new_type_table_entry(ZigTypeIdEnum); + buf_init_from_buf(&entry->name, + get_anon_type_name(ira->codegen, ira->old_irb.exec, "enum", source_instr->scope, source_instr->source_node, &entry->name)); + entry->data.enumeration.decl_node = source_instr->source_node; + entry->data.enumeration.tag_int_type = tag_type; + entry->data.enumeration.decls_scope = create_decls_scope(ira->codegen, nullptr, nullptr, entry, entry, &entry->name); + entry->data.enumeration.fields = heap::c_allocator.allocate(fields_len); + entry->data.enumeration.fields_by_name.init(fields_len); + entry->data.enumeration.src_field_count = fields_len; + entry->data.enumeration.layout = layout; + entry->data.enumeration.non_exhaustive = !is_exhaustive; + + assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray); + assert(fields_ptr->data.x_ptr.data.base_array.elem_index == 0); + ZigValue *fields_arr = fields_ptr->data.x_ptr.data.base_array.array_val; + assert(fields_arr->special == ConstValSpecialStatic); + assert(fields_arr->data.x_array.special == ConstArraySpecialNone); + for (size_t i = 0; i < fields_len; i++) { + ZigValue *field_value = &fields_arr->data.x_array.data.s_none.elements[i]; + assert(field_value->type == ir_type_info_get_type(ira, "EnumField", nullptr)); + TypeEnumField *field = &entry->data.enumeration.fields[i]; + field->name = buf_alloc(); + if ((err = get_const_field_buf(ira, source_instr->source_node, field_value, "name", 0, field->name))) + return ira->codegen->invalid_inst_gen->value->type; + field->decl_index = i; + field->decl_node = source_instr->source_node; + if (entry->data.enumeration.fields_by_name.put_unique(field->name, field) != nullptr) { + ir_add_error(ira, source_instr, buf_sprintf("duplicate enum field '%s'", buf_ptr(field->name))); + return ira->codegen->invalid_inst_gen->value->type; + } + field->value = *get_const_field_lit_int(ira, source_instr->source_node, field_value, "value", 1); + } + + return entry; + } case ZigTypeIdUnion: ir_add_error(ira, source_instr, buf_sprintf( "TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId))); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index ca75b2fa9c..633df50368 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,22 @@ const tests = @import("tests.zig"); const std = @import("std"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add("struct with declarations unavailable for @Type", + \\export fn entry() void { + \\ _ = @Type(@typeInfo(struct { const foo = 1; })); + \\} + , &[_][]const u8{ + "tmp.zig:2:15: error: TypeInfo.Struct.decls must be empty for @Type", + }); + + cases.add("enum with declarations unavailable for @Type", + \\export fn entry() void { + \\ _ = @Type(@typeInfo(enum { foo, const bar = 1; })); + \\} + , &[_][]const u8{ + "tmp.zig:2:15: error: TypeInfo.Enum.decls must be empty for @Type", + }); + cases.addTest("reject extern variables with initializers", \\extern var foo: int = 2; , &[_][]const u8{ @@ -1400,15 +1416,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , &[_][]const u8{ "tmp.zig:3:36: error: expected type 'std.builtin.TypeInfo', found 'std.builtin.Int'", }); - - cases.add("struct with declarations unavailable for @Type", - \\export fn entry() void { - \\ _ = @Type(@typeInfo(struct { const foo = 1; })); - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: TypeInfo.Struct.decls must be empty for @Type", - }); - cases.add("wrong type for argument tuple to @asyncCall", \\export fn entry1() void { \\ var frame: @Frame(foo) = undefined; diff --git a/test/stage1/behavior/type.zig b/test/stage1/behavior/type.zig index 8ebf670279..949576fd04 100644 --- a/test/stage1/behavior/type.zig +++ b/test/stage1/behavior/type.zig @@ -280,3 +280,37 @@ test "Type.Struct" { testing.expectEqual(@as(usize, 0), infoC.decls.len); testing.expectEqual(@as(bool, false), infoC.is_tuple); } + +test "Type.Enum" { + const Foo = @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = u8, + .fields = &[_]TypeInfo.EnumField{ + .{ .name = "a", .value = 1 }, + .{ .name = "b", .value = 5 }, + }, + .decls = &[_]TypeInfo.Declaration{}, + .is_exhaustive = true, + }, + }); + testing.expectEqual(true, @typeInfo(Foo).Enum.is_exhaustive); + testing.expectEqual(@as(u8, 1), @enumToInt(Foo.a)); + testing.expectEqual(@as(u8, 5), @enumToInt(Foo.b)); + const Bar = @Type(.{ + .Enum = .{ + .layout = .Extern, + .tag_type = u32, + .fields = &[_]TypeInfo.EnumField{ + .{ .name = "a", .value = 1 }, + .{ .name = "b", .value = 5 }, + }, + .decls = &[_]TypeInfo.Declaration{}, + .is_exhaustive = false, + }, + }); + testing.expectEqual(false, @typeInfo(Bar).Enum.is_exhaustive); + testing.expectEqual(@as(u32, 1), @enumToInt(Bar.a)); + testing.expectEqual(@as(u32, 5), @enumToInt(Bar.b)); + testing.expectEqual(@as(u32, 6), @enumToInt(@intToEnum(Bar, 6))); +}