Commit 1ebe3bd01d
src/Sema.zig
@@ -12459,7 +12459,6 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
const ty = try Type.array(sema.arena, len, sentinel, child_ty);
return sema.addType(ty);
},
- .Struct => return sema.fail(block, src, "TODO: Sema.zirReify for Struct", .{}),
.Optional => {
const struct_val = union_val.val.castTag(.@"struct").?.data;
// TODO use reflection instead of magic numbers here
@@ -12515,10 +12514,31 @@ 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);
},
+ .Struct => {
+ // TODO use reflection instead of magic numbers here
+ const struct_val = union_val.val.castTag(.@"struct").?.data;
+ // layout: containerlayout,
+ const layout_val = struct_val[0];
+ // fields: []const enumfield,
+ const fields_val = struct_val[1];
+ // decls: []const declaration,
+ const decls_val = struct_val[2];
+ // is_tuple: bool,
+ const is_tuple_val = struct_val[3];
+
+ // Decls
+ if (decls_val.sliceLen() > 0) {
+ return sema.fail(block, src, "reified structs must have no decls", .{});
+ }
+
+ return if (is_tuple_val.toBool())
+ try sema.reifyTuple(block, src, fields_val)
+ else
+ try sema.reifyStruct(block, inst, src, layout_val, fields_val);
+ },
.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,
@@ -12537,10 +12557,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.pointerDecl().?;
- try sema.ensureDeclAnalyzed(decls_decl);
- if (decls_decl.ty.arrayLen() > 0) {
+ if (decls_val.sliceLen() > 0) {
return sema.fail(block, src, "reified enums must have no decls", .{});
}
@@ -12636,10 +12653,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
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) {
+ if (decls_val.sliceLen() > 0) {
return sema.fail(block, src, "reified opaque must have no decls", .{});
}
@@ -12684,6 +12698,172 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
}
}
+fn reifyTuple(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ fields_val: Value,
+) CompileError!Air.Inst.Ref {
+ const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen());
+ if (fields_len == 0) return sema.addType(Type.initTag(.empty_struct_literal));
+
+ const types = try sema.arena.alloc(Type, fields_len);
+ const values = try sema.arena.alloc(Value, fields_len);
+
+ var used_fields: std.AutoArrayHashMapUnmanaged(u32, void) = .{};
+ defer used_fields.deinit(sema.gpa);
+ try used_fields.ensureTotalCapacity(sema.gpa, 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(.@"struct").?.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];
+ //default_value: ?*const anyopaque,
+ const default_value_val = field_struct_val[2];
+
+ const field_name = try name_val.toAllocatedBytes(
+ Type.initTag(.const_slice_u8),
+ sema.arena,
+ );
+
+ const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| {
+ return sema.fail(
+ block,
+ src,
+ "tuple cannot have non-numeric field '{s}': {}",
+ .{ field_name, err },
+ );
+ };
+
+ if (field_index >= fields_len) {
+ return sema.fail(
+ block,
+ src,
+ "tuple field {} exceeds tuple field count",
+ .{field_index},
+ );
+ }
+
+ const gop = used_fields.getOrPutAssumeCapacity(field_index);
+ if (gop.found_existing) {
+ // TODO: better source location
+ return sema.fail(block, src, "duplicate tuple field {}", .{field_index});
+ }
+
+ const default_val = if (default_value_val.optionalValue()) |opt_val| blk: {
+ const payload_val = if (opt_val.pointerDecl()) |opt_decl|
+ opt_decl.val
+ else
+ opt_val;
+ break :blk try payload_val.copy(sema.arena);
+ } else Value.initTag(.unreachable_value);
+
+ var buffer: Value.ToTypeBuffer = undefined;
+ types[field_index] = try field_type_val.toType(&buffer).copy(sema.arena);
+ values[field_index] = default_val;
+ }
+
+ const ty = try Type.Tag.tuple.create(sema.arena, .{
+ .types = types,
+ .values = values,
+ });
+ return sema.addType(ty);
+}
+
+fn reifyStruct(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+ src: LazySrcLoc,
+ layout_val: Value,
+ fields_val: Value,
+) CompileError!Air.Inst.Ref {
+ 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 struct_obj = try new_decl_arena_allocator.create(Module.Struct);
+ const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj);
+ const new_struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty);
+ const type_name = try sema.createTypeName(block, .anon);
+ const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
+ .ty = Type.type,
+ .val = new_struct_val,
+ }, type_name);
+ new_decl.owns_tv = true;
+ errdefer sema.mod.abortAnonDecl(new_decl);
+ struct_obj.* = .{
+ .owner_decl = new_decl,
+ .fields = .{},
+ .node_offset = src.node_offset,
+ .zir_index = inst,
+ .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout),
+ .status = .have_field_types,
+ .known_non_opv = undefined,
+ .namespace = .{
+ .parent = block.namespace,
+ .ty = struct_ty,
+ .file_scope = block.getFileScope(),
+ },
+ };
+
+ // Fields
+ const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen());
+ try struct_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(.@"struct").?.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];
+ //default_value: ?*const anyopaque,
+ const default_value_val = field_struct_val[2];
+ // is_comptime: bool,
+ const is_comptime_val = field_struct_val[3];
+ // alignment: comptime_int,
+ const alignment_val = field_struct_val[4];
+
+ const field_name = try name_val.toAllocatedBytes(
+ Type.initTag(.const_slice_u8),
+ new_decl_arena_allocator,
+ );
+
+ const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
+ if (gop.found_existing) {
+ // TODO: better source location
+ return sema.fail(block, src, "duplicate struct field {s}", .{field_name});
+ }
+
+ const default_val = if (default_value_val.optionalValue()) |opt_val| blk: {
+ const payload_val = if (opt_val.pointerDecl()) |opt_decl|
+ opt_decl.val
+ else
+ opt_val;
+ break :blk try payload_val.copy(new_decl_arena_allocator);
+ } else Value.initTag(.unreachable_value);
+
+ 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),
+ .default_val = default_val,
+ .is_comptime = is_comptime_val.toBool(),
+ .offset = undefined,
+ };
+ }
+
+ try new_decl.finalizeNewArena(&new_decl_arena);
+ return sema.analyzeDeclVal(block, src, new_decl);
+}
+
fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
test/behavior/type.zig
@@ -260,7 +260,11 @@ test "Type.ErrorSet" {
}
test "Type.Struct" {
- 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 A = @Type(@typeInfo(struct { x: u8, y: u32 }));
const infoA = @typeInfo(A).Struct;
@@ -303,6 +307,46 @@ test "Type.Struct" {
try testing.expectEqual(@as(u32, 5), @ptrCast(*const u32, infoC.fields[1].default_value.?).*);
try testing.expectEqual(@as(usize, 0), infoC.decls.len);
try testing.expectEqual(@as(bool, false), infoC.is_tuple);
+
+ // anon structs
+ const D = @Type(@typeInfo(@TypeOf(.{ .x = 3, .y = 5 })));
+ const infoD = @typeInfo(D).Struct;
+ try testing.expectEqual(Type.ContainerLayout.Auto, infoD.layout);
+ try testing.expectEqualSlices(u8, "x", infoD.fields[0].name);
+ try testing.expectEqual(comptime_int, infoD.fields[0].field_type);
+ try testing.expectEqual(@as(comptime_int, 3), @ptrCast(*const comptime_int, infoD.fields[0].default_value.?).*);
+ try testing.expectEqualSlices(u8, "y", infoD.fields[1].name);
+ try testing.expectEqual(comptime_int, infoD.fields[1].field_type);
+ try testing.expectEqual(@as(comptime_int, 5), @ptrCast(*const comptime_int, infoD.fields[1].default_value.?).*);
+ try testing.expectEqual(@as(usize, 0), infoD.decls.len);
+ try testing.expectEqual(@as(bool, false), infoD.is_tuple);
+
+ // tuples
+ const E = @Type(@typeInfo(@TypeOf(.{ 1, 2 })));
+ const infoE = @typeInfo(E).Struct;
+ try testing.expectEqual(Type.ContainerLayout.Auto, infoE.layout);
+ try testing.expectEqualSlices(u8, "0", infoE.fields[0].name);
+ try testing.expectEqual(comptime_int, infoE.fields[0].field_type);
+ try testing.expectEqual(@as(comptime_int, 1), @ptrCast(*const comptime_int, infoE.fields[0].default_value.?).*);
+ try testing.expectEqualSlices(u8, "1", infoE.fields[1].name);
+ try testing.expectEqual(comptime_int, infoE.fields[1].field_type);
+ try testing.expectEqual(@as(comptime_int, 2), @ptrCast(*const comptime_int, infoE.fields[1].default_value.?).*);
+ try testing.expectEqual(@as(usize, 0), infoE.decls.len);
+ try testing.expectEqual(@as(bool, true), infoE.is_tuple);
+
+ // empty struct
+ const F = @Type(@typeInfo(struct {}));
+ const infoF = @typeInfo(F).Struct;
+ try testing.expectEqual(Type.ContainerLayout.Auto, infoF.layout);
+ try testing.expect(infoF.fields.len == 0);
+ try testing.expectEqual(@as(bool, false), infoF.is_tuple);
+
+ // empty tuple
+ const G = @Type(@typeInfo(@TypeOf(.{})));
+ const infoG = @typeInfo(G).Struct;
+ try testing.expectEqual(Type.ContainerLayout.Auto, infoG.layout);
+ try testing.expect(infoG.fields.len == 0);
+ try testing.expectEqual(@as(bool, true), infoG.is_tuple);
}
test "Type.Enum" {