Sema: implement pointer-to-tuple coercion to slice and struct

This commit is contained in:
Andrew Kelley 2022-03-09 17:33:01 -07:00
parent 3b6e8fa59e
commit f32a77b30d
5 changed files with 86 additions and 23 deletions

View File

@ -16052,13 +16052,38 @@ fn coerce(
return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
}
// cast from pointer to anonymous struct to pointer to union
if (dest_info.pointee_type.zigTypeTag() == .Union and
inst_ty.zigTypeTag() == .Pointer and
inst_ty.childType().tag() == .anon_struct and
!dest_info.mutable)
{
return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
switch (dest_info.size) {
.C, .Many => {},
.One => switch (dest_info.pointee_type.zigTypeTag()) {
.Union => {
// cast from pointer to anonymous struct to pointer to union
if (inst_ty.isSinglePointer() and
inst_ty.childType().isAnonStruct() and
!dest_info.mutable)
{
return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
}
},
.Struct => {
// cast from pointer to anonymous struct to pointer to struct
if (inst_ty.isSinglePointer() and
inst_ty.childType().isAnonStruct() and
!dest_info.mutable)
{
return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
}
},
else => {},
},
.Slice => {
// pointer to tuple to slice
if (inst_ty.isSinglePointer() and
inst_ty.childType().isTuple() and
!dest_info.mutable and dest_info.size == .Slice)
{
return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src);
}
},
}
// This will give an extra hint on top of what the bottom of this func would provide.
@ -17365,6 +17390,20 @@ fn coerceAnonStructToUnionPtrs(
return sema.analyzeRef(block, union_ty_src, union_inst);
}
fn coerceAnonStructToStructPtrs(
sema: *Sema,
block: *Block,
ptr_struct_ty: Type,
struct_ty_src: LazySrcLoc,
ptr_anon_struct: Air.Inst.Ref,
anon_struct_src: LazySrcLoc,
) !Air.Inst.Ref {
const struct_ty = ptr_struct_ty.childType();
const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src);
const struct_inst = try sema.coerceTupleToStruct(block, struct_ty, struct_ty_src, anon_struct, anon_struct_src);
return sema.analyzeRef(block, struct_ty_src, struct_inst);
}
/// If the lengths match, coerces element-wise.
fn coerceArrayLike(
sema: *Sema,
@ -17494,6 +17533,27 @@ fn coerceTupleToArray(
);
}
/// If the lengths match, coerces element-wise.
fn coerceTupleToSlicePtrs(
sema: *Sema,
block: *Block,
slice_ty: Type,
slice_ty_src: LazySrcLoc,
ptr_tuple: Air.Inst.Ref,
tuple_src: LazySrcLoc,
) !Air.Inst.Ref {
const tuple_ty = sema.typeOf(ptr_tuple).childType();
const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
const slice_info = slice_ty.ptrInfo().data;
const array_ty = try Type.array(sema.arena, tuple_ty.structFieldCount(), slice_info.sentinel, slice_info.pointee_type);
const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src);
if (slice_info.@"align" != 0) {
return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{});
}
const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst);
return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src);
}
/// Handles both tuples and anon struct literals. Coerces field-wise. Reports
/// errors for both extra fields and missing fields.
fn coerceTupleToStruct(
@ -17504,11 +17564,13 @@ fn coerceTupleToStruct(
inst: Air.Inst.Ref,
inst_src: LazySrcLoc,
) !Air.Inst.Ref {
if (dest_ty.isTupleOrAnonStruct()) {
const struct_ty = try sema.resolveTypeFields(block, dest_ty_src, dest_ty);
if (struct_ty.isTupleOrAnonStruct()) {
return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to tuples", .{});
}
const fields = dest_ty.structFields();
const fields = struct_ty.structFields();
const field_vals = try sema.arena.alloc(Value, fields.count());
const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
mem.set(Air.Inst.Ref, field_refs, .none);
@ -17523,7 +17585,7 @@ fn coerceTupleToStruct(
payload.data.names[i]
else
try std.fmt.allocPrint(sema.arena, "{d}", .{i});
const field_index = try sema.structFieldIndex(block, dest_ty, field_name, field_src);
const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src);
const field = fields.values()[field_index];
if (field.is_comptime) {
return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to structs when one of the destination struct fields is comptime", .{});
@ -17567,17 +17629,17 @@ fn coerceTupleToStruct(
}
if (root_msg) |msg| {
try sema.addDeclaredHereNote(msg, dest_ty);
try sema.addDeclaredHereNote(msg, struct_ty);
return sema.failWithOwnedErrorMsg(block, msg);
}
if (runtime_src) |rs| {
try sema.requireRuntimeBlock(block, rs);
return block.addAggregateInit(dest_ty, field_refs);
return block.addAggregateInit(struct_ty, field_refs);
}
return sema.addConstant(
dest_ty,
struct_ty,
try Value.Tag.@"struct".create(sema.arena, field_vals),
);
}

View File

@ -3502,6 +3502,7 @@ pub const Type = extern union {
.tuple => ty.castTag(.tuple).?.data.types.len,
.anon_struct => ty.castTag(.anon_struct).?.data.types.len,
.@"struct" => ty.castTag(.@"struct").?.data.fields.count(),
.empty_struct, .empty_struct_literal => 0,
else => unreachable,
};
@ -5070,7 +5071,7 @@ pub const Type = extern union {
pub fn isAnonStruct(ty: Type) bool {
return switch (ty.tag()) {
.anon_struct => true,
.anon_struct, .empty_struct_literal => true,
else => false,
};
}

View File

@ -6,17 +6,15 @@ const native_arch = builtin.target.cpu.arch;
var foo: u8 align(4) = 100;
test "global variable alignment" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
comptime try expect(@TypeOf(&foo) == *align(4) u8);
{
const slice = @as(*[1]u8, &foo)[0..];
const slice = @as(*align(4) [1]u8, &foo)[0..];
comptime try expect(@TypeOf(slice) == *align(4) [1]u8);
}
{
var runtime_zero: usize = 0;
const slice = @as(*[1]u8, &foo)[runtime_zero..];
const slice = @as(*align(4) [1]u8, &foo)[runtime_zero..];
comptime try expect(@TypeOf(slice) == []align(4) u8);
}
}
@ -86,8 +84,6 @@ test "size of extern struct with 128-bit field" {
}
test "@ptrCast preserves alignment of bigger source" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
var x: u32 align(16) = 1234;
const ptr = @ptrCast(*u8, &x);
try expect(@TypeOf(ptr) == *align(16) u8);

View File

@ -1072,7 +1072,11 @@ test "type coercion of anon struct literal to struct" {
}
test "type coercion of pointer to anon struct literal to pointer to 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_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) 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 S = struct {
const S2 = struct {

View File

@ -159,8 +159,8 @@ test "array-like initializer for tuple types" {
const S = struct {
fn doTheTest() !void {
var obj: T = .{ -1234, 128 };
try testing.expectEqual(@as(i32, -1234), obj[0]);
try testing.expectEqual(@as(u8, 128), obj[1]);
try expect(@as(i32, -1234) == obj[0]);
try expect(@as(u8, 128) == obj[1]);
}
};