From 015cc41e505ec640c84b91b6fca4fa353593df66 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 11 Mar 2022 17:44:52 -0800 Subject: [PATCH 1/5] stage2: zirReify for enums --- src/Sema.zig | 116 ++++++++++++++++++++++++++++++++++++++++- test/behavior/type.zig | 13 ++++- 2 files changed, 126 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 44b2f33d56..61b602437d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12470,7 +12470,121 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const ty = try Type.Tag.error_set_merged.create(sema.arena, names); return sema.addType(ty); }, - .Enum => return sema.fail(block, src, "TODO: Sema.zirReify for Enum", .{}), + .Enum => { + const struct_val = union_val.val.castTag(.@"struct").?.data; + // TODO use reflection instead of magic numbers here + // error_set: type, + // 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]; + // is_exhaustive: bool, + const is_exhaustive_val = struct_val[4]; + + // enum layout is always auto + const layout = layout_val.toEnum(std.builtin.Type.ContainerLayout); + if (layout != .Auto) { + return sema.fail(block, src, "reified enums must have a layout .Auto", .{}); + } + + // Decls + const decls_slice_val = decls_val.castTag(.slice).?.data; + const decls_decl = decls_slice_val.ptr.castTag(.decl_ref).?.data; + try sema.ensureDeclAnalyzed(decls_decl); + if (decls_decl.ty.arrayLen() > 0) { + return sema.fail(block, src, "reified enums must have no decls", .{}); + } + + const mod = sema.mod; + const gpa = sema.gpa; + var new_decl_arena = std.heap.ArenaAllocator.init(gpa); + errdefer new_decl_arena.deinit(); + const new_decl_arena_allocator = new_decl_arena.allocator(); + + // Define our empty enum decl + const enum_obj = try new_decl_arena_allocator.create(Module.EnumFull); + const enum_ty_payload = try new_decl_arena_allocator.create(Type.Payload.EnumFull); + enum_ty_payload.* = .{ + .base = .{ + .tag = if (!is_exhaustive_val.toBool()) + .enum_nonexhaustive + else + .enum_full, + }, + .data = enum_obj, + }; + const enum_ty = Type.initPayload(&enum_ty_payload.base); + const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); + const type_name = try sema.createTypeName(block, .anon); + const new_decl = try mod.createAnonymousDeclNamed(block, .{ + .ty = Type.type, + .val = enum_val, + }, type_name); + new_decl.owns_tv = true; + errdefer mod.abortAnonDecl(new_decl); + + enum_obj.* = .{ + .owner_decl = new_decl, + .tag_ty = Type.initTag(.@"null"), + .fields = .{}, + .values = .{}, + .node_offset = src.node_offset, + .namespace = .{ + .parent = block.namespace, + .ty = enum_ty, + .file_scope = block.getFileScope(), + }, + }; + + // Enum tag type + var buffer: Value.ToTypeBuffer = undefined; + 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.castTag(.decl_ref).?.data; + try sema.ensureDeclAnalyzed(decl); + const fields_len = decl.ty.arrayLen(); + 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(.array).?.data; + for (array_vals) |elem_val| { + const field_struct_val = elem_val.castTag(.@"struct").?.data; + // TODO use reflection instead of magic numbers here + // name: []const u8 + const name_val = field_struct_val[0]; + // value: comptime_int + const value_val = field_struct_val[1]; + + const field_name = try name_val.toAllocatedBytes( + Type.initTag(.const_slice_u8), + new_decl_arena_allocator, + ); + + const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name); + if (gop.found_existing) { + // TODO: better source location + return sema.fail(block, src, "duplicate enum tag {s}", .{field_name}); + } + + const copied_tag_val = try value_val.copy(new_decl_arena_allocator); + enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ + .ty = enum_obj.tag_ty, + }); + } + } + + 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", .{}), .Fn => return sema.fail(block, src, "TODO: Sema.zirReify for Fn", .{}), .BoundFn => @panic("TODO delete BoundFn from the language"), diff --git a/test/behavior/type.zig b/test/behavior/type.zig index 06b5959f77..7119b4f9f8 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -302,7 +302,11 @@ test "Type.Struct" { } test "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 Foo = @Type(.{ .Enum = .{ @@ -321,7 +325,12 @@ test "Type.Enum" { try testing.expectEqual(@as(u8, 5), @enumToInt(Foo.b)); const Bar = @Type(.{ .Enum = .{ - .layout = .Extern, + // stage2 only has auto layouts + .layout = if (builtin.zig_backend == .stage1) + .Extern + else + .Auto, + .tag_type = u32, .fields = &.{ .{ .name = "a", .value = 1 }, From 08159be375bf6771f3e2cd73656477504e329f8b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 11 Mar 2022 21:21:29 -0800 Subject: [PATCH 2/5] stage2: enum field length must be a usize --- src/Sema.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 61b602437d..b304eab99d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12548,7 +12548,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const slice_val = fields_val.castTag(.slice).?.data; const decl = slice_val.ptr.castTag(.decl_ref).?.data; try sema.ensureDeclAnalyzed(decl); - const fields_len = decl.ty.arrayLen(); + const fields_len = @intCast(usize, decl.ty.arrayLen()); 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, .{ From 744b4ad5787edd84aee7b80248350d3313ed8a15 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 11 Mar 2022 21:36:06 -0800 Subject: [PATCH 3/5] stage2: reify should use pointerDecl to get array value --- src/Sema.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index b304eab99d..5d729cba30 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12449,7 +12449,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const payload_val = union_val.val.optionalValue() orelse return sema.addType(Type.initTag(.anyerror)); const slice_val = payload_val.castTag(.slice).?.data; - const decl = slice_val.ptr.castTag(.decl_ref).?.data; + const decl = slice_val.ptr.pointerDecl().?; try sema.ensureDeclAnalyzed(decl); const array_val = decl.val.castTag(.array).?.data; @@ -12493,7 +12493,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I // Decls const decls_slice_val = decls_val.castTag(.slice).?.data; - const decls_decl = decls_slice_val.ptr.castTag(.decl_ref).?.data; + const decls_decl = decls_slice_val.ptr.pointerDecl().?; try sema.ensureDeclAnalyzed(decls_decl); if (decls_decl.ty.arrayLen() > 0) { return sema.fail(block, src, "reified enums must have no decls", .{}); @@ -12546,7 +12546,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I // Fields const slice_val = fields_val.castTag(.slice).?.data; - const decl = slice_val.ptr.castTag(.decl_ref).?.data; + const decl = slice_val.ptr.pointerDecl().?; try sema.ensureDeclAnalyzed(decl); const fields_len = @intCast(usize, decl.ty.arrayLen()); if (fields_len > 0) { From 237d08389a8f565aad806735e958d9191518770d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 11 Mar 2022 21:37:41 -0800 Subject: [PATCH 4/5] stage2: use usizecast for safety --- src/Sema.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 5d729cba30..99ec1191d9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12548,7 +12548,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const slice_val = fields_val.castTag(.slice).?.data; const decl = slice_val.ptr.pointerDecl().?; try sema.ensureDeclAnalyzed(decl); - const fields_len = @intCast(usize, decl.ty.arrayLen()); + const fields_len = try sema.usizeCast(block, src, decl.ty.arrayLen()); 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, .{ From ef68420b785752d153e8bffd5e307a298b737c4d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 12 Mar 2022 11:02:24 -0800 Subject: [PATCH 5/5] stage2: reify opaque {} --- src/Sema.zig | 48 +++++++++++++++++++++++++++++++++++++++++- test/behavior/type.zig | 6 +++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 99ec1191d9..bc80fae8f1 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12585,10 +12585,56 @@ 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); }, + .Opaque => { + const struct_val = union_val.val.castTag(.@"struct").?.data; + // decls: []const Declaration, + const decls_val = struct_val[0]; + + // Decls + const decls_slice_val = decls_val.castTag(.slice).?.data; + const decls_decl = decls_slice_val.ptr.pointerDecl().?; + try sema.ensureDeclAnalyzed(decls_decl); + if (decls_decl.ty.arrayLen() > 0) { + return sema.fail(block, src, "reified opaque must have no decls", .{}); + } + + const mod = sema.mod; + 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 opaque_obj = try new_decl_arena_allocator.create(Module.Opaque); + const opaque_ty_payload = try new_decl_arena_allocator.create(Type.Payload.Opaque); + opaque_ty_payload.* = .{ + .base = .{ .tag = .@"opaque" }, + .data = opaque_obj, + }; + const opaque_ty = Type.initPayload(&opaque_ty_payload.base); + const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty); + const type_name = try sema.createTypeName(block, .anon); + const new_decl = try mod.createAnonymousDeclNamed(block, .{ + .ty = Type.type, + .val = opaque_val, + }, type_name); + new_decl.owns_tv = true; + errdefer mod.abortAnonDecl(new_decl); + + opaque_obj.* = .{ + .owner_decl = new_decl, + .node_offset = src.node_offset, + .namespace = .{ + .parent = block.namespace, + .ty = opaque_ty, + .file_scope = block.getFileScope(), + }, + }; + + 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", .{}), .Fn => return sema.fail(block, src, "TODO: Sema.zirReify for Fn", .{}), .BoundFn => @panic("TODO delete BoundFn from the language"), - .Opaque => return sema.fail(block, src, "TODO: Sema.zirReify for Opaque", .{}), .Frame => return sema.fail(block, src, "TODO: Sema.zirReify for Frame", .{}), } } diff --git a/test/behavior/type.zig b/test/behavior/type.zig index 7119b4f9f8..ff86aa28e8 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -199,7 +199,11 @@ test "Type.ErrorUnion" { } test "Type.Opaque" { - 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 Opaque = @Type(.{ .Opaque = .{