authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-12 16:20:43-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-12 16:20:43-05:00
log804b82b6e82b938eaa736fc90442dd4fd552717e
tree2537f4f3c74d9c09771f1bad3d7cc8dcdede8000
parentf000f8a59a8bf1121ecbe9b60ae50cc0218d3ba3
parentef68420b785752d153e8bffd5e307a298b737c4d
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11130 from mitchellh/reify-enum

stage2: zirReify for enums

2 files changed, 179 insertions(+), 6 deletions(-)

src/Sema.zig+163-3
...@@ -12447,7 +12447,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12447,7 +12447,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12447 const payload_val = union_val.val.optionalValue() orelse12447 const payload_val = union_val.val.optionalValue() orelse
12448 return sema.addType(Type.initTag(.anyerror));12448 return sema.addType(Type.initTag(.anyerror));
12449 const slice_val = payload_val.castTag(.slice).?.data;12449 const slice_val = payload_val.castTag(.slice).?.data;
12450 const decl = slice_val.ptr.castTag(.decl_ref).?.data;12450 const decl = slice_val.ptr.pointerDecl().?;
12451 try sema.ensureDeclAnalyzed(decl);12451 try sema.ensureDeclAnalyzed(decl);
12452 const array_val = decl.val.castTag(.array).?.data;12452 const array_val = decl.val.castTag(.array).?.data;
1245312453
...@@ -12468,11 +12468,171 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12468,11 +12468,171 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12468 const ty = try Type.Tag.error_set_merged.create(sema.arena, names);12468 const ty = try Type.Tag.error_set_merged.create(sema.arena, names);
12469 return sema.addType(ty);12469 return sema.addType(ty);
12470 },12470 },
12471 .Enum => return sema.fail(block, src, "TODO: Sema.zirReify for Enum", .{}),12471 .Enum => {
12472 const struct_val = union_val.val.castTag(.@"struct").?.data;
12473 // TODO use reflection instead of magic numbers here
12474 // error_set: type,
12475 // layout: ContainerLayout,
12476 const layout_val = struct_val[0];
12477 // tag_type: type,
12478 const tag_type_val = struct_val[1];
12479 // fields: []const EnumField,
12480 const fields_val = struct_val[2];
12481 // decls: []const Declaration,
12482 const decls_val = struct_val[3];
12483 // is_exhaustive: bool,
12484 const is_exhaustive_val = struct_val[4];
12485
12486 // enum layout is always auto
12487 const layout = layout_val.toEnum(std.builtin.Type.ContainerLayout);
12488 if (layout != .Auto) {
12489 return sema.fail(block, src, "reified enums must have a layout .Auto", .{});
12490 }
12491
12492 // Decls
12493 const decls_slice_val = decls_val.castTag(.slice).?.data;
12494 const decls_decl = decls_slice_val.ptr.pointerDecl().?;
12495 try sema.ensureDeclAnalyzed(decls_decl);
12496 if (decls_decl.ty.arrayLen() > 0) {
12497 return sema.fail(block, src, "reified enums must have no decls", .{});
12498 }
12499
12500 const mod = sema.mod;
12501 const gpa = sema.gpa;
12502 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
12503 errdefer new_decl_arena.deinit();
12504 const new_decl_arena_allocator = new_decl_arena.allocator();
12505
12506 // Define our empty enum decl
12507 const enum_obj = try new_decl_arena_allocator.create(Module.EnumFull);
12508 const enum_ty_payload = try new_decl_arena_allocator.create(Type.Payload.EnumFull);
12509 enum_ty_payload.* = .{
12510 .base = .{
12511 .tag = if (!is_exhaustive_val.toBool())
12512 .enum_nonexhaustive
12513 else
12514 .enum_full,
12515 },
12516 .data = enum_obj,
12517 };
12518 const enum_ty = Type.initPayload(&enum_ty_payload.base);
12519 const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);
12520 const type_name = try sema.createTypeName(block, .anon);
12521 const new_decl = try mod.createAnonymousDeclNamed(block, .{
12522 .ty = Type.type,
12523 .val = enum_val,
12524 }, type_name);
12525 new_decl.owns_tv = true;
12526 errdefer mod.abortAnonDecl(new_decl);
12527
12528 enum_obj.* = .{
12529 .owner_decl = new_decl,
12530 .tag_ty = Type.initTag(.@"null"),
12531 .fields = .{},
12532 .values = .{},
12533 .node_offset = src.node_offset,
12534 .namespace = .{
12535 .parent = block.namespace,
12536 .ty = enum_ty,
12537 .file_scope = block.getFileScope(),
12538 },
12539 };
12540
12541 // Enum tag type
12542 var buffer: Value.ToTypeBuffer = undefined;
12543 enum_obj.tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator);
12544
12545 // Fields
12546 const slice_val = fields_val.castTag(.slice).?.data;
12547 const decl = slice_val.ptr.pointerDecl().?;
12548 try sema.ensureDeclAnalyzed(decl);
12549 const fields_len = try sema.usizeCast(block, src, decl.ty.arrayLen());
12550 if (fields_len > 0) {
12551 try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
12552 try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{
12553 .ty = enum_obj.tag_ty,
12554 });
12555
12556 const array_vals = decl.val.castTag(.array).?.data;
12557 for (array_vals) |elem_val| {
12558 const field_struct_val = elem_val.castTag(.@"struct").?.data;
12559 // TODO use reflection instead of magic numbers here
12560 // name: []const u8
12561 const name_val = field_struct_val[0];
12562 // value: comptime_int
12563 const value_val = field_struct_val[1];
12564
12565 const field_name = try name_val.toAllocatedBytes(
12566 Type.initTag(.const_slice_u8),
12567 new_decl_arena_allocator,
12568 );
12569
12570 const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name);
12571 if (gop.found_existing) {
12572 // TODO: better source location
12573 return sema.fail(block, src, "duplicate enum tag {s}", .{field_name});
12574 }
12575
12576 const copied_tag_val = try value_val.copy(new_decl_arena_allocator);
12577 enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{
12578 .ty = enum_obj.tag_ty,
12579 });
12580 }
12581 }
12582
12583 try new_decl.finalizeNewArena(&new_decl_arena);
12584 return sema.analyzeDeclVal(block, src, new_decl);
12585 },
12586 .Opaque => {
12587 const struct_val = union_val.val.castTag(.@"struct").?.data;
12588 // decls: []const Declaration,
12589 const decls_val = struct_val[0];
12590
12591 // Decls
12592 const decls_slice_val = decls_val.castTag(.slice).?.data;
12593 const decls_decl = decls_slice_val.ptr.pointerDecl().?;
12594 try sema.ensureDeclAnalyzed(decls_decl);
12595 if (decls_decl.ty.arrayLen() > 0) {
12596 return sema.fail(block, src, "reified opaque must have no decls", .{});
12597 }
12598
12599 const mod = sema.mod;
12600 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
12601 errdefer new_decl_arena.deinit();
12602 const new_decl_arena_allocator = new_decl_arena.allocator();
12603
12604 const opaque_obj = try new_decl_arena_allocator.create(Module.Opaque);
12605 const opaque_ty_payload = try new_decl_arena_allocator.create(Type.Payload.Opaque);
12606 opaque_ty_payload.* = .{
12607 .base = .{ .tag = .@"opaque" },
12608 .data = opaque_obj,
12609 };
12610 const opaque_ty = Type.initPayload(&opaque_ty_payload.base);
12611 const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty);
12612 const type_name = try sema.createTypeName(block, .anon);
12613 const new_decl = try mod.createAnonymousDeclNamed(block, .{
12614 .ty = Type.type,
12615 .val = opaque_val,
12616 }, type_name);
12617 new_decl.owns_tv = true;
12618 errdefer mod.abortAnonDecl(new_decl);
12619
12620 opaque_obj.* = .{
12621 .owner_decl = new_decl,
12622 .node_offset = src.node_offset,
12623 .namespace = .{
12624 .parent = block.namespace,
12625 .ty = opaque_ty,
12626 .file_scope = block.getFileScope(),
12627 },
12628 };
12629
12630 try new_decl.finalizeNewArena(&new_decl_arena);
12631 return sema.analyzeDeclVal(block, src, new_decl);
12632 },
12472 .Union => return sema.fail(block, src, "TODO: Sema.zirReify for Union", .{}),12633 .Union => return sema.fail(block, src, "TODO: Sema.zirReify for Union", .{}),
12473 .Fn => return sema.fail(block, src, "TODO: Sema.zirReify for Fn", .{}),12634 .Fn => return sema.fail(block, src, "TODO: Sema.zirReify for Fn", .{}),
12474 .BoundFn => @panic("TODO delete BoundFn from the language"),12635 .BoundFn => @panic("TODO delete BoundFn from the language"),
12475 .Opaque => return sema.fail(block, src, "TODO: Sema.zirReify for Opaque", .{}),
12476 .Frame => return sema.fail(block, src, "TODO: Sema.zirReify for Frame", .{}),12636 .Frame => return sema.fail(block, src, "TODO: Sema.zirReify for Frame", .{}),
12477 }12637 }
12478}12638}
test/behavior/type.zig+16-3
...@@ -199,7 +199,11 @@ test "Type.ErrorUnion" {...@@ -199,7 +199,11 @@ test "Type.ErrorUnion" {
199}199}
200200
201test "Type.Opaque" {201test "Type.Opaque" {
202 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO202 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
203 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
204 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
205 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
206 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
203207
204 const Opaque = @Type(.{208 const Opaque = @Type(.{
205 .Opaque = .{209 .Opaque = .{
...@@ -302,7 +306,11 @@ test "Type.Struct" {...@@ -302,7 +306,11 @@ test "Type.Struct" {
302}306}
303307
304test "Type.Enum" {308test "Type.Enum" {
305 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO309 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
310 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
311 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
312 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
313 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
306314
307 const Foo = @Type(.{315 const Foo = @Type(.{
308 .Enum = .{316 .Enum = .{
...@@ -321,7 +329,12 @@ test "Type.Enum" {...@@ -321,7 +329,12 @@ test "Type.Enum" {
321 try testing.expectEqual(@as(u8, 5), @enumToInt(Foo.b));329 try testing.expectEqual(@as(u8, 5), @enumToInt(Foo.b));
322 const Bar = @Type(.{330 const Bar = @Type(.{
323 .Enum = .{331 .Enum = .{
324 .layout = .Extern,332 // stage2 only has auto layouts
333 .layout = if (builtin.zig_backend == .stage1)
334 .Extern
335 else
336 .Auto,
337
325 .tag_type = u32,338 .tag_type = u32,
326 .fields = &.{339 .fields = &.{
327 .{ .name = "a", .value = 1 },340 .{ .name = "a", .value = 1 },