From edd07aa80815dc634d437588f5c9aad7dc9039dc Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 14 Mar 2022 14:34:28 -0700 Subject: [PATCH 1/2] stage2: reify unions --- src/Sema.zig | 100 ++++++++++++++++++++++++++++++++++++++++- test/behavior/type.zig | 18 ++++++-- 2 files changed, 113 insertions(+), 5 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index eeee020a31..5a56f819da 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12691,7 +12691,103 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I try new_decl.finalizeNewArena(&new_decl_arena); return sema.analyzeDeclVal(block, src, new_decl); }, - .Union => return sema.fail(block, src, "TODO: Sema.zirReify for Union", .{}), + .Union => { + // TODO use reflection instead of magic numbers here + const struct_val = union_val.val.castTag(.aggregate).?.data; + // layout: containerlayout, + const layout_val = struct_val[0]; + // tag_type: ?type, + const tag_type_val = struct_val[1]; + // fields: []const enumfield, + const fields_val = struct_val[2]; + // decls: []const declaration, + const decls_val = struct_val[3]; + + // Decls + if (decls_val.sliceLen() > 0) { + return sema.fail(block, src, "reified unions must have no decls", .{}); + } + + var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); + errdefer new_decl_arena.deinit(); + const new_decl_arena_allocator = new_decl_arena.allocator(); + + const union_obj = try new_decl_arena_allocator.create(Module.Union); + const type_tag: Type.Tag = if (!tag_type_val.isNull()) .union_tagged else .@"union"; + const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union); + union_payload.* = .{ + .base = .{ .tag = type_tag }, + .data = union_obj, + }; + const union_ty = Type.initPayload(&union_payload.base); + const new_union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty); + const type_name = try sema.createTypeName(block, .anon); + const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ + .ty = Type.type, + .val = new_union_val, + }, type_name); + new_decl.owns_tv = true; + errdefer sema.mod.abortAnonDecl(new_decl); + union_obj.* = .{ + .owner_decl = new_decl, + .tag_ty = Type.initTag(.@"null"), + .fields = .{}, + .node_offset = src.node_offset, + .zir_index = inst, + .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout), + .status = .have_field_types, + .namespace = .{ + .parent = block.namespace, + .ty = union_ty, + .file_scope = block.getFileScope(), + }, + }; + + // Tag type + const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen()); + union_obj.tag_ty = if (tag_type_val.optionalValue()) |payload_val| blk: { + var buffer: Value.ToTypeBuffer = undefined; + break :blk try payload_val.toType(&buffer).copy(new_decl_arena_allocator); + } else try sema.generateUnionTagTypeSimple(block, fields_len); + + // Fields + if (fields_len > 0) { + try union_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); + + var i: usize = 0; + while (i < fields_len) : (i += 1) { + const elem_val = try fields_val.elemValue(sema.arena, i); + const field_struct_val = elem_val.castTag(.aggregate).?.data; + // TODO use reflection instead of magic numbers here + // name: []const u8 + const name_val = field_struct_val[0]; + // field_type: type, + const field_type_val = field_struct_val[1]; + // alignment: comptime_int, + const alignment_val = field_struct_val[2]; + + const field_name = try name_val.toAllocatedBytes( + Type.initTag(.const_slice_u8), + new_decl_arena_allocator, + ); + + const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); + if (gop.found_existing) { + // TODO: better source location + return sema.fail(block, src, "duplicate union field {s}", .{field_name}); + } + + var buffer: Value.ToTypeBuffer = undefined; + gop.value_ptr.* = .{ + .ty = try field_type_val.toType(&buffer).copy(new_decl_arena_allocator), + .abi_align = try alignment_val.copy(new_decl_arena_allocator), + }; + } + } + + try new_decl.finalizeNewArena(&new_decl_arena); + return sema.analyzeDeclVal(block, src, new_decl); + }, .Fn => return sema.fail(block, src, "TODO: Sema.zirReify for Fn", .{}), .BoundFn => @panic("TODO delete BoundFn from the language"), .Frame => @panic("TODO implement https://github.com/ziglang/zig/issues/10710"), @@ -20417,7 +20513,7 @@ fn generateUnionTagTypeNumbered( return enum_ty; } -fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: u32) !Type { +fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: usize) !Type { const mod = sema.mod; var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); diff --git a/test/behavior/type.zig b/test/behavior/type.zig index 15b4638488..0335bd3589 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -395,7 +395,11 @@ test "Type.Enum" { } test "Type.Union" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const Untagged = @Type(.{ .Union = .{ @@ -458,7 +462,11 @@ test "Type.Union" { } test "Type.Union from Type.Enum" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const Tag = @Type(.{ .Enum = .{ @@ -486,7 +494,11 @@ test "Type.Union from Type.Enum" { } test "Type.Union from regular enum" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const E = enum { working_as_expected }; const T = @Type(.{ From 53de31d62c91b96cbf082169d76d02f6900f6273 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 14 Mar 2022 14:38:05 -0700 Subject: [PATCH 2/2] stage2: enum reification should use sliceLen and elemValue --- src/Sema.zig | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 5a56f819da..b339e4374d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12607,18 +12607,16 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I enum_obj.tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator); // Fields - const slice_val = fields_val.castTag(.slice).?.data; - const decl = slice_val.ptr.pointerDecl().?; - try sema.ensureDeclAnalyzed(decl); - const fields_len = try sema.usizeCast(block, src, decl.ty.arrayLen()); + const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen()); if (fields_len > 0) { try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ .ty = enum_obj.tag_ty, }); - const array_vals = decl.val.castTag(.aggregate).?.data; - for (array_vals) |elem_val| { + var i: usize = 0; + while (i < fields_len) : (i += 1) { + const elem_val = try fields_val.elemValue(sema.arena, i); const field_struct_val = elem_val.castTag(.aggregate).?.data; // TODO use reflection instead of magic numbers here // name: []const u8