Merge pull request #10992 from mitchellh/peer-slices

stage2: peer resolve *[N]T to []T and E![]T and [*]T, handle in-memory coercion
This commit is contained in:
Andrew Kelley 2022-02-26 18:49:44 -05:00 committed by GitHub
commit e81b21a0ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 228 additions and 50 deletions

View File

@ -17100,6 +17100,18 @@ fn resolvePeerTypes(
const chosen_ty = sema.typeOf(chosen);
if (candidate_ty.eql(chosen_ty))
continue;
// If the candidate can coerce into our chosen type, we're done.
// If the chosen type can coerce into the candidate, use that.
if ((try sema.coerceInMemoryAllowed(block, chosen_ty, candidate_ty, false, target, src, src)) == .ok) {
continue;
}
if ((try sema.coerceInMemoryAllowed(block, candidate_ty, chosen_ty, false, target, src, src)) == .ok) {
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
}
const candidate_ty_tag = candidate_ty.zigTypeTag();
const chosen_ty_tag = chosen_ty.zigTypeTag();
@ -17180,6 +17192,30 @@ fn resolvePeerTypes(
},
else => {},
},
.ErrorUnion => {
const payload_ty = candidate_ty.errorUnionPayload();
if (chosen_ty_tag == .Pointer and
chosen_ty.ptrSize() == .One and
chosen_ty.childType().zigTypeTag() == .Array and
payload_ty.isSlice())
{
const chosen_child_ty = chosen_ty.childType();
const chosen_elem_ty = chosen_child_ty.elemType2();
const candidate_elem_ty = payload_ty.elemType2();
if ((try sema.coerceInMemoryAllowed(block, candidate_elem_ty, chosen_elem_ty, false, target, src, src)) == .ok) {
chosen = candidate;
chosen_i = candidate_i + 1;
convert_to_slice = false; // it already is a slice
// If the prev pointer is const then we need to const
if (chosen_child_ty.isConstPtr())
make_the_slice_const = true;
continue;
}
}
},
.Pointer => {
if (candidate_ty.ptrSize() == .C) {
if (chosen_ty_tag == .Int or chosen_ty_tag == .ComptimeInt) {
@ -17192,6 +17228,80 @@ fn resolvePeerTypes(
}
}
// *[N]T to [*]T
if (candidate_ty.ptrSize() == .Many and
chosen_ty_tag == .Pointer and
chosen_ty.ptrSize() == .One and
chosen_ty.childType().zigTypeTag() == .Array)
{
chosen = candidate;
chosen_i = candidate_i + 1;
convert_to_slice = false;
if (chosen_ty.childType().isConstPtr() and !candidate_ty.childType().isConstPtr())
make_the_slice_const = true;
continue;
}
// *[N]T to [*]T (prev is many pointer)
if (candidate_ty.ptrSize() == .One and
candidate_ty.childType().zigTypeTag() == .Array and
chosen_ty_tag == .Pointer and
chosen_ty.ptrSize() == .Many)
{
if (candidate_ty.childType().isConstPtr() and !chosen_ty.childType().isConstPtr())
make_the_slice_const = true;
continue;
}
// *[N]T to []T (prev is slice)
// *[N]T to E![]T
if ((chosen_ty.isSlice() or (chosen_ty_tag == .ErrorUnion and chosen_ty.errorUnionPayload().isSlice())) and
candidate_ty.ptrSize() == .One and
candidate_ty.childType().zigTypeTag() == .Array)
{
const chosen_elem_ty = switch (chosen_ty_tag) {
.ErrorUnion => chosen_ty.errorUnionPayload().elemType2(),
else => chosen_ty.elemType2(),
};
const candidate_elem_ty = candidate_ty.childType().elemType2();
if ((try sema.coerceInMemoryAllowed(block, candidate_elem_ty, chosen_elem_ty, false, target, src, src)) == .ok) {
convert_to_slice = false; // it already is a slice
// If the pointer is const then we need to const
if (candidate_ty.childType().isConstPtr())
make_the_slice_const = true;
continue;
}
}
// *[N]T to []T (current is slice)
if (chosen_ty_tag == .Pointer and
chosen_ty.ptrSize() == .One and
chosen_ty.childType().zigTypeTag() == .Array and
candidate_ty.isSlice())
{
const chosen_child_ty = chosen_ty.childType();
const chosen_elem_ty = chosen_child_ty.elemType2();
const candidate_elem_ty = candidate_ty.elemType2();
if ((try sema.coerceInMemoryAllowed(block, candidate_elem_ty, chosen_elem_ty, false, target, src, src)) == .ok) {
chosen = candidate;
chosen_i = candidate_i + 1;
convert_to_slice = false; // it already is a slice
// If the prev pointer is const then we need to const
if (chosen_child_ty.isConstPtr())
make_the_slice_const = true;
continue;
}
}
// *[N]T and *[M]T
// verify both are pointers to known lengths
if (chosen_ty_tag == .Pointer and
@ -17329,6 +17439,13 @@ fn resolvePeerTypes(
return Type.ptr(sema.arena, info.data);
}
if (make_the_slice_const) {
// turn []T => []const T
var info = chosen_ty.ptrInfo();
info.data.mutable = false;
return Type.ptr(sema.arena, info.data);
}
return chosen_ty;
}

View File

@ -358,7 +358,10 @@ fn testCastIntToErr(err: anyerror) !void {
}
test "peer resolve array and const slice" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
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_x86_64) return error.SkipZigTest; // TODO
try testPeerResolveArrayConstSlice(true);
comptime try testPeerResolveArrayConstSlice(true);
@ -415,7 +418,10 @@ fn gimmeErrOrSlice() anyerror![]u8 {
}
test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
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_x86_64) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() anyerror!void {
@ -494,7 +500,10 @@ fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 {
}
test "single-item pointer of array to slice to unknown length pointer" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
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_x86_64) return error.SkipZigTest; // TODO
try testCastPtrOfArrayToSliceAndPtr();
comptime try testCastPtrOfArrayToSliceAndPtr();
@ -609,17 +618,25 @@ test "peer type resolution: unreachable, error set, unreachable" {
}
test "peer cast *[0]T to E![]const T" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
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_x86_64) return error.SkipZigTest; // TODO
var buffer: [5]u8 = "abcde".*;
var buf: anyerror![]const u8 = buffer[0..];
var b = false;
var y = if (b) &[0]u8{} else buf;
var z = if (!b) buf else &[0]u8{};
try expect(mem.eql(u8, "abcde", y catch unreachable));
try expect(mem.eql(u8, "abcde", z catch unreachable));
}
test "peer cast *[0]T to []const T" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
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_x86_64) return error.SkipZigTest; // TODO
var buffer: [5]u8 = "abcde".*;
var buf: []const u8 = buffer[0..];
@ -628,6 +645,18 @@ test "peer cast *[0]T to []const T" {
try expect(mem.eql(u8, "abcde", y));
}
test "peer cast *[N]T to [*]T" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
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_x86_64) return error.SkipZigTest; // TODO
var array = [4:99]i32{ 1, 2, 3, 4 };
var dest: [*]i32 = undefined;
try expect(@TypeOf(&array, dest) == [*]i32);
try expect(@TypeOf(dest, &array) == [*]i32);
}
test "peer resolution of string literals" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
@ -648,55 +677,89 @@ test "peer resolution of string literals" {
comptime try S.doTheTest(.b);
}
test "type coercion related to sentinel-termination" {
test "peer cast [:x]T to []T" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
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_x86_64) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var array = [4:0]i32{ 1, 2, 3, 4 };
var slice: [:0]i32 = &array;
var dest: []i32 = slice;
try expect(mem.eql(i32, dest, &[_]i32{ 1, 2, 3, 4 }));
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "peer cast [N:x]T to [N]T" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
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_x86_64) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var array = [4:0]i32{ 1, 2, 3, 4 };
var dest: [4]i32 = array;
try expect(mem.eql(i32, &dest, &[_]i32{ 1, 2, 3, 4 }));
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "peer cast *[N:x]T to *[N]T" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
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_x86_64) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var array = [4:0]i32{ 1, 2, 3, 4 };
var dest: *[4]i32 = &array;
try expect(mem.eql(i32, dest, &[_]i32{ 1, 2, 3, 4 }));
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "peer cast [*:x]T to [*]T" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
// [:x]T to []T
{
var array = [4:0]i32{ 1, 2, 3, 4 };
var slice: [:0]i32 = &array;
var dest: []i32 = slice;
try expect(mem.eql(i32, dest, &[_]i32{ 1, 2, 3, 4 }));
}
var array = [4:99]i32{ 1, 2, 3, 4 };
var dest: [*]i32 = &array;
try expect(dest[0] == 1);
try expect(dest[1] == 2);
try expect(dest[2] == 3);
try expect(dest[3] == 4);
try expect(dest[4] == 99);
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
// [*:x]T to [*]T
{
var array = [4:99]i32{ 1, 2, 3, 4 };
var dest: [*]i32 = &array;
try expect(dest[0] == 1);
try expect(dest[1] == 2);
try expect(dest[2] == 3);
try expect(dest[3] == 4);
try expect(dest[4] == 99);
}
test "peer cast [:x]T to [*:x]T" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
// [N:x]T to [N]T
{
var array = [4:0]i32{ 1, 2, 3, 4 };
var dest: [4]i32 = array;
try expect(mem.eql(i32, &dest, &[_]i32{ 1, 2, 3, 4 }));
}
// *[N:x]T to *[N]T
{
var array = [4:0]i32{ 1, 2, 3, 4 };
var dest: *[4]i32 = &array;
try expect(mem.eql(i32, dest, &[_]i32{ 1, 2, 3, 4 }));
}
// [:x]T to [*:x]T
{
var array = [4:0]i32{ 1, 2, 3, 4 };
var slice: [:0]i32 = &array;
var dest: [*:0]i32 = slice;
try expect(dest[0] == 1);
try expect(dest[1] == 2);
try expect(dest[2] == 3);
try expect(dest[3] == 4);
try expect(dest[4] == 0);
}
const S = struct {
fn doTheTest() !void {
var array = [4:0]i32{ 1, 2, 3, 4 };
var slice: [:0]i32 = &array;
var dest: [*:0]i32 = slice;
try expect(dest[0] == 1);
try expect(dest[1] == 2);
try expect(dest[2] == 3);
try expect(dest[3] == 4);
try expect(dest[4] == 0);
}
};
try S.doTheTest();
@ -746,8 +809,6 @@ test "peer type resolution implicit cast to variable type" {
}
test "variable initialization uses result locations properly with regards to the type" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
var b = true;
const x: i32 = if (b) 1 else 2;
try expect(x == 1);