diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 2ffbdaa5ad..44b86af985 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -334,6 +334,7 @@ pub const TypeInfo = union(enum) { /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. pub const Enum = struct { + /// TODO enums should no longer have this field in type info. layout: ContainerLayout, tag_type: type, fields: []const EnumField, diff --git a/src/Sema.zig b/src/Sema.zig index fd6cfe552b..257b50322b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5951,7 +5951,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } const all_tags_handled = for (seen_fields) |seen_src| { if (seen_src == null) break false; - } else true; + } else !operand_ty.isNonexhaustiveEnum(); switch (special_prong) { .none => { @@ -9035,9 +9035,119 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }), ); }, - else => |t| return sema.fail(block, src, "TODO: implement zirTypeInfo for {s}", .{ - @tagName(t), - }), + .Enum => { + // TODO: look into memoizing this result. + var int_tag_type_buffer: Type.Payload.Bits = undefined; + const int_tag_ty = try ty.intTagType(&int_tag_type_buffer).copy(sema.arena); + + const is_exhaustive = if (ty.isNonexhaustiveEnum()) Value.@"false" else Value.@"true"; + + var fields_anon_decl = try block.startAnonDecl(); + defer fields_anon_decl.deinit(); + + const enum_field_ty = t: { + const enum_field_ty_decl = (try sema.namespaceLookup( + block, + src, + type_info_ty.getNamespace().?, + "EnumField", + )).?; + try sema.mod.declareDeclDependency(sema.owner_decl, enum_field_ty_decl); + try sema.ensureDeclAnalyzed(enum_field_ty_decl); + var buffer: Value.ToTypeBuffer = undefined; + break :t try enum_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena()); + }; + + const enum_fields = ty.enumFields(); + const enum_field_vals = try fields_anon_decl.arena().alloc(Value, enum_fields.count()); + + for (enum_field_vals) |*field_val, i| { + var tag_val_payload: Value.Payload.U32 = .{ + .base = .{ .tag = .enum_field_index }, + .data = @intCast(u32, i), + }; + const tag_val = Value.initPayload(&tag_val_payload.base); + + var buffer: Value.Payload.U64 = undefined; + const int_val = try tag_val.enumToInt(ty, &buffer).copy(fields_anon_decl.arena()); + + const name = enum_fields.keys()[i]; + const name_val = v: { + var anon_decl = try block.startAnonDecl(); + defer anon_decl.deinit(); + const bytes = try anon_decl.arena().dupeZ(u8, name); + const new_decl = try anon_decl.finish( + try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), + try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + ); + break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl); + }; + + const enum_field_fields = try fields_anon_decl.arena().create([2]Value); + enum_field_fields.* = .{ + // name: []const u8, + name_val, + // value: comptime_int, + int_val, + }; + field_val.* = try Value.Tag.@"struct".create(fields_anon_decl.arena(), enum_field_fields); + } + + const fields_val = v: { + const new_decl = try fields_anon_decl.finish( + try Type.Tag.array.create(fields_anon_decl.arena(), .{ + .len = enum_field_vals.len, + .elem_type = enum_field_ty, + }), + try Value.Tag.array.create( + fields_anon_decl.arena(), + try fields_anon_decl.arena().dupe(Value, enum_field_vals), + ), + ); + break :v try Value.Tag.decl_ref.create(sema.arena, new_decl); + }; + + if (ty.getNamespace()) |namespace| { + if (namespace.decls.count() != 0) { + return sema.fail(block, src, "TODO: implement zirTypeInfo for Enum which has declarations", .{}); + } + } + const decls_val = Value.initTag(.empty_array); + + const field_values = try sema.arena.create([5]Value); + field_values.* = .{ + // layout: ContainerLayout, + try Value.Tag.enum_field_index.create( + sema.arena, + @enumToInt(std.builtin.TypeInfo.ContainerLayout.Auto), + ), + + // tag_type: type, + try Value.Tag.ty.create(sema.arena, int_tag_ty), + // fields: []const EnumField, + fields_val, + // decls: []const Declaration, + decls_val, + // is_exhaustive: bool, + is_exhaustive, + }; + + return sema.addConstant( + type_info_ty, + try Value.Tag.@"union".create(sema.arena, .{ + .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Enum)), + .val = try Value.Tag.@"struct".create(sema.arena, field_values), + }), + ); + }, + .Struct => return sema.fail(block, src, "TODO: implement zirTypeInfo for Struct", .{}), + .ErrorSet => return sema.fail(block, src, "TODO: implement zirTypeInfo for ErrorSet", .{}), + .Union => return sema.fail(block, src, "TODO: implement zirTypeInfo for Union", .{}), + .BoundFn => @panic("TODO remove this type from the language and compiler"), + .Opaque => return sema.fail(block, src, "TODO: implement zirTypeInfo for Opaque", .{}), + .Frame => return sema.fail(block, src, "TODO: implement zirTypeInfo for Frame", .{}), + .AnyFrame => return sema.fail(block, src, "TODO: implement zirTypeInfo for AnyFrame", .{}), + .Vector => return sema.fail(block, src, "TODO: implement zirTypeInfo for Vector", .{}), } } @@ -15153,13 +15263,13 @@ fn getBuiltin( ); const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst.?, src); const builtin_ty = try sema.analyzeAsType(block, src, builtin_inst); - const opt_ty_inst = try sema.namespaceLookupRef( + const opt_ty_decl = try sema.namespaceLookup( block, src, builtin_ty.getNamespace().?, name, ); - return sema.analyzeLoad(block, src, opt_ty_inst.?, src); + return sema.analyzeDeclVal(block, src, opt_ty_decl.?); } fn getBuiltinType( diff --git a/src/value.zig b/src/value.zig index c105e3e742..be26e38d52 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1461,14 +1461,25 @@ pub const Value = extern union { return false; } - if (ty.zigTypeTag() == .Type) { - var buf_a: ToTypeBuffer = undefined; - var buf_b: ToTypeBuffer = undefined; - const a_type = a.toType(&buf_a); - const b_type = b.toType(&buf_b); - return a_type.eql(b_type); + switch (ty.zigTypeTag()) { + .Type => { + var buf_a: ToTypeBuffer = undefined; + var buf_b: ToTypeBuffer = undefined; + const a_type = a.toType(&buf_a); + const b_type = b.toType(&buf_b); + return a_type.eql(b_type); + }, + .Enum => { + var buf_a: Payload.U64 = undefined; + var buf_b: Payload.U64 = undefined; + const a_val = a.enumToInt(ty, &buf_a); + const b_val = b.enumToInt(ty, &buf_b); + var buf_ty: Type.Payload.Bits = undefined; + const int_ty = ty.intTagType(&buf_ty); + return eql(a_val, b_val, int_ty); + }, + else => return order(a, b).compare(.eq), } - return order(a, b).compare(.eq); } pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash) void { @@ -3037,6 +3048,8 @@ pub const Value = extern union { pub const undef = initTag(.undef); pub const @"void" = initTag(.void_value); pub const @"null" = initTag(.null_value); + pub const @"false" = initTag(.bool_false); + pub const @"true" = initTag(.bool_true); }; var negative_one_payload: Value.Payload.I64 = .{ diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 5a3777a1d7..3429f62229 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -605,3 +605,44 @@ test "enum with specified tag values" { try testEnumWithSpecifiedTagValues(MultipleChoice.C); comptime try testEnumWithSpecifiedTagValues(MultipleChoice.C); } + +test "non-exhaustive enum" { + const S = struct { + const E = enum(u8) { a, b, _ }; + + fn doTheTest(y: u8) !void { + var e: E = .b; + try expect(switch (e) { + .a => false, + .b => true, + _ => false, + }); + e = @intToEnum(E, 12); + try expect(switch (e) { + .a => false, + .b => false, + _ => true, + }); + + try expect(switch (e) { + .a => false, + .b => false, + else => true, + }); + e = .b; + try expect(switch (e) { + .a => false, + else => true, + }); + + try expect(@typeInfo(E).Enum.fields.len == 2); + e = @intToEnum(E, 12); + try expect(@enumToInt(e) == 12); + e = @intToEnum(E, y); + try expect(@enumToInt(e) == 52); + try expect(@typeInfo(E).Enum.is_exhaustive == false); + } + }; + try S.doTheTest(52); + comptime try S.doTheTest(52); +} diff --git a/test/behavior/enum_stage1.zig b/test/behavior/enum_stage1.zig index 954cd81a4b..9d1770f446 100644 --- a/test/behavior/enum_stage1.zig +++ b/test/behavior/enum_stage1.zig @@ -2,50 +2,6 @@ const expect = @import("std").testing.expect; const mem = @import("std").mem; const Tag = @import("std").meta.Tag; -test "non-exhaustive enum" { - const S = struct { - const E = enum(u8) { - a, - b, - _, - }; - fn doTheTest(y: u8) !void { - var e: E = .b; - try expect(switch (e) { - .a => false, - .b => true, - _ => false, - }); - e = @intToEnum(E, 12); - try expect(switch (e) { - .a => false, - .b => false, - _ => true, - }); - - try expect(switch (e) { - .a => false, - .b => false, - else => true, - }); - e = .b; - try expect(switch (e) { - .a => false, - else => true, - }); - - try expect(@typeInfo(E).Enum.fields.len == 2); - e = @intToEnum(E, 12); - try expect(@enumToInt(e) == 12); - e = @intToEnum(E, y); - try expect(@enumToInt(e) == 52); - try expect(@typeInfo(E).Enum.is_exhaustive == false); - } - }; - try S.doTheTest(52); - comptime try S.doTheTest(52); -} - test "empty non-exhaustive enum" { const S = struct { const E = enum(u8) {