diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-02-26 18:49:44 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-02-26 18:49:44 -0500 |
| commit | e81b21a0ea955422835fb42a14bfa2db6bd74146 (patch) | |
| tree | 658b8a3c08eefb15b210e040ccc28345e5f39a32 | |
| parent | bf3c88b68d9f8042b79b7d69d401429811d9de7c (diff) | |
| parent | 156316bc7c3797362b5d16da320884a7aadabd6f (diff) | |
| download | zig-e81b21a0ea955422835fb42a14bfa2db6bd74146.tar.gz zig-e81b21a0ea955422835fb42a14bfa2db6bd74146.zip | |
Merge pull request #10992 from mitchellh/peer-slices
stage2: peer resolve *[N]T to []T and E![]T and [*]T, handle in-memory coercion
| -rw-r--r-- | src/Sema.zig | 117 | ||||
| -rw-r--r-- | test/behavior/cast.zig | 159 |
2 files changed, 227 insertions, 49 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index e5c38c8450..7326fd9bc6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -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; } diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 50d99897c8..d7071f64df 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -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" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO +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 { - // [: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: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(); +} - // [*: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 [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 - // [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 })); - } + 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(); +} - // *[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 })); - } +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 - // [: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 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 { + 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(); +} + +test "peer cast [:x]T to [*:x]T" { + if (builtin.zig_backend != .stage1) 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: [*: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); |
