mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
stage2: reify structs and tuples (#11144)
Implements `@Type` for structs, anon structs, and tuples. This is another place that would probably benefit from a `.reified_struct` type tag but will defer for later in the interest of getting tests passing first.
This commit is contained in:
parent
5919b10048
commit
1ebe3bd01d
200
src/Sema.zig
200
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 };
|
||||
|
||||
@ -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" {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user