Merge pull request #15726 from mlugg/feat/peer-type-resolution-but-better

Sema: rewrite peer type resolution
This commit is contained in:
Andrew Kelley 2023-06-14 07:18:28 -07:00 committed by GitHub
commit 496320d935
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 2191 additions and 687 deletions

View File

@ -4568,6 +4568,7 @@ pub fn sliceLen(ip: *const InternPool, i: Index) Index {
/// * int <=> int
/// * int <=> enum
/// * enum_literal => enum
/// * float <=> float
/// * ptr <=> ptr
/// * opt ptr <=> ptr
/// * opt ptr <=> opt ptr
@ -4579,6 +4580,7 @@ pub fn sliceLen(ip: *const InternPool, i: Index) Index {
/// * error set => error union
/// * payload => error union
/// * fn <=> fn
/// * aggregate <=> aggregate (where children can also be coerced)
pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Allocator.Error!Index {
const old_ty = ip.typeOf(val);
if (old_ty == new_ty) return val;
@ -4623,6 +4625,23 @@ pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Al
else => if (ip.isIntegerType(new_ty))
return getCoercedInts(ip, gpa, int, new_ty),
},
.float => |float| switch (ip.indexToKey(new_ty)) {
.simple_type => |simple| switch (simple) {
.f16,
.f32,
.f64,
.f80,
.f128,
.c_longdouble,
.comptime_float,
=> return ip.get(gpa, .{ .float = .{
.ty = new_ty,
.storage = float.storage,
} }),
else => {},
},
else => {},
},
.enum_tag => |enum_tag| if (ip.isIntegerType(new_ty))
return getCoercedInts(ip, gpa, ip.indexToKey(enum_tag.int).int, new_ty),
.enum_literal => |enum_literal| switch (ip.indexToKey(new_ty)) {
@ -4688,6 +4707,80 @@ pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Al
.ty = new_ty,
.val = error_union.val,
} }),
.aggregate => |aggregate| {
const new_len = @intCast(usize, ip.aggregateTypeLen(new_ty));
direct: {
const old_ty_child = switch (ip.indexToKey(old_ty)) {
inline .array_type, .vector_type => |seq_type| seq_type.child,
.anon_struct_type, .struct_type => break :direct,
else => unreachable,
};
const new_ty_child = switch (ip.indexToKey(new_ty)) {
inline .array_type, .vector_type => |seq_type| seq_type.child,
.anon_struct_type, .struct_type => break :direct,
else => unreachable,
};
if (old_ty_child != new_ty_child) break :direct;
// TODO: write something like getCoercedInts to avoid needing to dupe here
switch (aggregate.storage) {
.bytes => |bytes| {
const bytes_copy = try gpa.dupe(u8, bytes[0..new_len]);
defer gpa.free(bytes_copy);
return ip.get(gpa, .{ .aggregate = .{
.ty = new_ty,
.storage = .{ .bytes = bytes_copy },
} });
},
.elems => |elems| {
const elems_copy = try gpa.dupe(InternPool.Index, elems[0..new_len]);
defer gpa.free(elems_copy);
return ip.get(gpa, .{ .aggregate = .{
.ty = new_ty,
.storage = .{ .elems = elems_copy },
} });
},
.repeated_elem => |elem| {
return ip.get(gpa, .{ .aggregate = .{
.ty = new_ty,
.storage = .{ .repeated_elem = elem },
} });
},
}
}
// Direct approach failed - we must recursively coerce elems
const agg_elems = try gpa.alloc(InternPool.Index, new_len);
defer gpa.free(agg_elems);
// First, fill the vector with the uncoerced elements. We do this to avoid key
// lifetime issues, since it'll allow us to avoid referencing `aggregate` after we
// begin interning elems.
switch (aggregate.storage) {
.bytes => {
// We have to intern each value here, so unfortunately we can't easily avoid
// the repeated indexToKey calls.
for (agg_elems, 0..) |*elem, i| {
const x = ip.indexToKey(val).aggregate.storage.bytes[i];
elem.* = try ip.get(gpa, .{ .int = .{
.ty = .u8_type,
.storage = .{ .u64 = x },
} });
}
},
.elems => |elems| @memcpy(agg_elems, elems[0..new_len]),
.repeated_elem => |elem| @memset(agg_elems, elem),
}
// Now, coerce each element to its new type.
for (agg_elems, 0..) |*elem, i| {
const new_elem_ty = switch (ip.indexToKey(new_ty)) {
inline .array_type, .vector_type => |seq_type| seq_type.child,
.anon_struct_type => |anon_struct_type| anon_struct_type.types[i],
.struct_type => |struct_type| ip.structPtr(struct_type.index.unwrap().?)
.fields.values()[i].ty.toIntern(),
else => unreachable,
};
elem.* = try ip.getCoerced(gpa, elem.*, new_elem_ty);
}
return ip.get(gpa, .{ .aggregate = .{ .ty = new_ty, .storage = .{ .elems = agg_elems } } });
},
else => {},
},
}

View File

@ -6978,7 +6978,7 @@ pub fn intBitsForValue(mod: *Module, val: Value, sign: bool) u16 {
assert(sign);
// Protect against overflow in the following negation.
if (x == std.math.minInt(i64)) return 64;
return Type.smallestUnsignedBits(@intCast(u64, -x - 1)) + 1;
return Type.smallestUnsignedBits(@intCast(u64, -(x + 1))) + 1;
},
.u64 => |x| {
return Type.smallestUnsignedBits(x) + @boolToInt(sign);

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,9 @@
const builtin = @import("builtin");
const std = @import("std");
const assert = std.debug.assert;
const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const expectEqualSlices = std.testing.expectEqualSlices;
const mem = std.mem;
const maxInt = std.math.maxInt;
const native_endian = builtin.target.cpu.arch.endian();
@ -1609,3 +1612,610 @@ test "coercion from single-item pointer to @as to slice" {
try expect(t[0] == 1);
}
test "peer type resolution: const sentinel slice and mutable non-sentinel slice" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest(comptime T: type, comptime s: T) !void {
var a: [:s]const T = @intToPtr(*const [2:s]T, 0x1000);
var b: []T = @intToPtr(*[3]T, 0x2000);
comptime assert(@TypeOf(a, b) == []const T);
comptime assert(@TypeOf(b, a) == []const T);
var t = true;
const r1 = if (t) a else b;
const r2 = if (t) b else a;
const R = @TypeOf(r1);
try expectEqual(@as(R, @intToPtr(*const [2:s]T, 0x1000)), r1);
try expectEqual(@as(R, @intToPtr(*const [3]T, 0x2000)), r2);
}
};
try S.doTheTest(u8, 0);
try S.doTheTest(?*anyopaque, null);
}
test "peer type resolution: float and comptime-known fixed-width integer" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
const i: u8 = 100;
var f: f32 = 1.234;
comptime assert(@TypeOf(i, f) == f32);
comptime assert(@TypeOf(f, i) == f32);
var t = true;
const r1 = if (t) i else f;
const r2 = if (t) f else i;
const T = @TypeOf(r1);
try expectEqual(@as(T, 100.0), r1);
try expectEqual(@as(T, 1.234), r2);
}
test "peer type resolution: same array type with sentinel" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var a: [2:0]u32 = .{ 0, 1 };
var b: [2:0]u32 = .{ 2, 3 };
comptime assert(@TypeOf(a, b) == [2:0]u32);
comptime assert(@TypeOf(b, a) == [2:0]u32);
var t = true;
const r1 = if (t) a else b;
const r2 = if (t) b else a;
const T = @TypeOf(r1);
try expectEqual(T{ 0, 1 }, r1);
try expectEqual(T{ 2, 3 }, r2);
}
test "peer type resolution: array with sentinel and array without sentinel" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var a: [2:0]u32 = .{ 0, 1 };
var b: [2]u32 = .{ 2, 3 };
comptime assert(@TypeOf(a, b) == [2]u32);
comptime assert(@TypeOf(b, a) == [2]u32);
var t = true;
const r1 = if (t) a else b;
const r2 = if (t) b else a;
const T = @TypeOf(r1);
try expectEqual(T{ 0, 1 }, r1);
try expectEqual(T{ 2, 3 }, r2);
}
test "peer type resolution: array and vector with same child type" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var arr: [2]u32 = .{ 0, 1 };
var vec: @Vector(2, u32) = .{ 2, 3 };
comptime assert(@TypeOf(arr, vec) == @Vector(2, u32));
comptime assert(@TypeOf(vec, arr) == @Vector(2, u32));
var t = true;
const r1 = if (t) arr else vec;
const r2 = if (t) vec else arr;
const T = @TypeOf(r1);
try expectEqual(T{ 0, 1 }, r1);
try expectEqual(T{ 2, 3 }, r2);
}
test "peer type resolution: array with smaller child type and vector with larger child type" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var arr: [2]u8 = .{ 0, 1 };
var vec: @Vector(2, u64) = .{ 2, 3 };
comptime assert(@TypeOf(arr, vec) == @Vector(2, u64));
comptime assert(@TypeOf(vec, arr) == @Vector(2, u64));
var t = true;
const r1 = if (t) arr else vec;
const r2 = if (t) vec else arr;
const T = @TypeOf(r1);
try expectEqual(T{ 0, 1 }, r1);
try expectEqual(T{ 2, 3 }, r2);
}
test "peer type resolution: error union and optional of same type" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
const E = error{Foo};
var a: E!*u8 = error.Foo;
var b: ?*u8 = null;
comptime assert(@TypeOf(a, b) == E!?*u8);
comptime assert(@TypeOf(b, a) == E!?*u8);
var t = true;
const r1 = if (t) a else b;
const r2 = if (t) b else a;
const T = @TypeOf(r1);
try expectEqual(@as(T, error.Foo), r1);
try expectEqual(@as(T, null), r2);
}
test "peer type resolution: C pointer and @TypeOf(null)" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var a: [*c]c_int = 0x1000;
const b = null;
comptime assert(@TypeOf(a, b) == [*c]c_int);
comptime assert(@TypeOf(b, a) == [*c]c_int);
var t = true;
const r1 = if (t) a else b;
const r2 = if (t) b else a;
const T = @TypeOf(r1);
try expectEqual(@as(T, 0x1000), r1);
try expectEqual(@as(T, null), r2);
}
test "peer type resolution: three-way resolution combines error set and optional" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
const E = error{Foo};
var a: E = error.Foo;
var b: *const [5:0]u8 = @intToPtr(*const [5:0]u8, 0x1000);
var c: ?[*:0]u8 = null;
comptime assert(@TypeOf(a, b, c) == E!?[*:0]const u8);
comptime assert(@TypeOf(a, c, b) == E!?[*:0]const u8);
comptime assert(@TypeOf(b, a, c) == E!?[*:0]const u8);
comptime assert(@TypeOf(b, c, a) == E!?[*:0]const u8);
comptime assert(@TypeOf(c, a, b) == E!?[*:0]const u8);
comptime assert(@TypeOf(c, b, a) == E!?[*:0]const u8);
var x: u8 = 0;
const r1 = switch (x) {
0 => a,
1 => b,
else => c,
};
const r2 = switch (x) {
0 => b,
1 => a,
else => c,
};
const r3 = switch (x) {
0 => c,
1 => a,
else => b,
};
const T = @TypeOf(r1);
try expectEqual(@as(T, error.Foo), r1);
try expectEqual(@as(T, @intToPtr([*:0]u8, 0x1000)), r2);
try expectEqual(@as(T, null), r3);
}
test "peer type resolution: vector and optional vector" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var a: ?@Vector(3, u32) = .{ 0, 1, 2 };
var b: @Vector(3, u32) = .{ 3, 4, 5 };
comptime assert(@TypeOf(a, b) == ?@Vector(3, u32));
comptime assert(@TypeOf(b, a) == ?@Vector(3, u32));
var t = true;
const r1 = if (t) a else b;
const r2 = if (t) b else a;
const T = @TypeOf(r1);
try expectEqual(@as(T, .{ 0, 1, 2 }), r1);
try expectEqual(@as(T, .{ 3, 4, 5 }), r2);
}
test "peer type resolution: optional fixed-width int and comptime_int" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var a: ?i32 = 42;
const b: comptime_int = 50;
comptime assert(@TypeOf(a, b) == ?i32);
comptime assert(@TypeOf(b, a) == ?i32);
var t = true;
const r1 = if (t) a else b;
const r2 = if (t) b else a;
const T = @TypeOf(r1);
try expectEqual(@as(T, 42), r1);
try expectEqual(@as(T, 50), r2);
}
test "peer type resolution: array and tuple" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var arr: [3]i32 = .{ 1, 2, 3 };
const tup = .{ 4, 5, 6 };
comptime assert(@TypeOf(arr, tup) == [3]i32);
comptime assert(@TypeOf(tup, arr) == [3]i32);
var t = true;
const r1 = if (t) arr else tup;
const r2 = if (t) tup else arr;
const T = @TypeOf(r1);
try expectEqual(T{ 1, 2, 3 }, r1);
try expectEqual(T{ 4, 5, 6 }, r2);
}
test "peer type resolution: vector and tuple" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var vec: @Vector(3, i32) = .{ 1, 2, 3 };
const tup = .{ 4, 5, 6 };
comptime assert(@TypeOf(vec, tup) == @Vector(3, i32));
comptime assert(@TypeOf(tup, vec) == @Vector(3, i32));
var t = true;
const r1 = if (t) vec else tup;
const r2 = if (t) tup else vec;
const T = @TypeOf(r1);
try expectEqual(T{ 1, 2, 3 }, r1);
try expectEqual(T{ 4, 5, 6 }, r2);
}
test "peer type resolution: vector and array and tuple" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var vec: @Vector(2, i8) = .{ 10, 20 };
var arr: [2]i8 = .{ 30, 40 };
const tup = .{ 50, 60 };
comptime assert(@TypeOf(vec, arr, tup) == @Vector(2, i8));
comptime assert(@TypeOf(vec, tup, arr) == @Vector(2, i8));
comptime assert(@TypeOf(arr, vec, tup) == @Vector(2, i8));
comptime assert(@TypeOf(arr, tup, vec) == @Vector(2, i8));
comptime assert(@TypeOf(tup, vec, arr) == @Vector(2, i8));
comptime assert(@TypeOf(tup, arr, vec) == @Vector(2, i8));
var x: u8 = 0;
const r1 = switch (x) {
0 => vec,
1 => arr,
else => tup,
};
const r2 = switch (x) {
0 => arr,
1 => vec,
else => tup,
};
const r3 = switch (x) {
0 => tup,
1 => vec,
else => arr,
};
const T = @TypeOf(r1);
try expectEqual(T{ 10, 20 }, r1);
try expectEqual(T{ 30, 40 }, r2);
try expectEqual(T{ 50, 60 }, r3);
}
test "peer type resolution: empty tuple pointer and slice" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var a: [:0]const u8 = "Hello";
var b = &.{};
comptime assert(@TypeOf(a, b) == []const u8);
comptime assert(@TypeOf(b, a) == []const u8);
var t = true;
const r1 = if (t) a else b;
const r2 = if (t) b else a;
try expectEqualSlices(u8, "Hello", r1);
try expectEqualSlices(u8, "", r2);
}
test "peer type resolution: tuple pointer and slice" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var a: [:0]const u8 = "Hello";
var b = &.{ @as(u8, 'x'), @as(u8, 'y'), @as(u8, 'z') };
comptime assert(@TypeOf(a, b) == []const u8);
comptime assert(@TypeOf(b, a) == []const u8);
var t = true;
const r1 = if (t) a else b;
const r2 = if (t) b else a;
try expectEqualSlices(u8, "Hello", r1);
try expectEqualSlices(u8, "xyz", r2);
}
test "peer type resolution: tuple pointer and optional slice" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var a: ?[:0]const u8 = null;
var b = &.{ @as(u8, 'x'), @as(u8, 'y'), @as(u8, 'z') };
comptime assert(@TypeOf(a, b) == ?[]const u8);
comptime assert(@TypeOf(b, a) == ?[]const u8);
var t = true;
const r1 = if (t) a else b;
const r2 = if (t) b else a;
try expectEqual(@as(?[]const u8, null), r1);
try expectEqualSlices(u8, "xyz", r2 orelse "");
}
test "peer type resolution: many compatible pointers" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var buf = "foo-3".*;
var vals = .{
@as([*]const u8, "foo-0"),
@as([*:0]const u8, "foo-1"),
@as([*:0]const u8, "foo-2"),
@as([*]u8, &buf),
@as(*const [5]u8, "foo-4"),
};
// Check every possible permutation of types in @TypeOf
@setEvalBranchQuota(5000);
comptime var perms = 0; // check the loop is hitting every permutation
inline for (0..5) |i_0| {
inline for (0..5) |i_1| {
if (i_1 == i_0) continue;
inline for (0..5) |i_2| {
if (i_2 == i_0 or i_2 == i_1) continue;
inline for (0..5) |i_3| {
if (i_3 == i_0 or i_3 == i_1 or i_3 == i_2) continue;
inline for (0..5) |i_4| {
if (i_4 == i_0 or i_4 == i_1 or i_4 == i_2 or i_4 == i_3) continue;
perms += 1;
comptime assert(@TypeOf(
vals[i_0],
vals[i_1],
vals[i_2],
vals[i_3],
vals[i_4],
) == [*]const u8);
}
}
}
}
}
comptime assert(perms == 5 * 4 * 3 * 2 * 1);
var x: u8 = 0;
inline for (0..5) |i| {
const r = switch (x) {
0 => vals[i],
1 => vals[0],
2 => vals[1],
3 => vals[2],
4 => vals[3],
else => vals[4],
};
const expected = switch (i) {
0 => "foo-0",
1 => "foo-1",
2 => "foo-2",
3 => "foo-3",
4 => "foo-4",
else => unreachable,
};
try expectEqualSlices(u8, expected, std.mem.span(@ptrCast([*:0]const u8, r)));
}
}
test "peer type resolution: tuples with comptime fields" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
const a = .{ 1, 2 };
const b = .{ @as(u32, 3), @as(i16, 4) };
// TODO: tuple type equality doesn't work properly yet
const ti1 = @typeInfo(@TypeOf(a, b));
const ti2 = @typeInfo(@TypeOf(b, a));
inline for (.{ ti1, ti2 }) |ti| {
const s = ti.Struct;
comptime assert(s.is_tuple);
comptime assert(s.fields.len == 2);
comptime assert(s.fields[0].type == u32);
comptime assert(s.fields[1].type == i16);
}
var t = true;
const r1 = if (t) a else b;
const r2 = if (t) b else a;
try expectEqual(@as(u32, 1), r1[0]);
try expectEqual(@as(i16, 2), r1[1]);
try expectEqual(@as(u32, 3), r2[0]);
try expectEqual(@as(i16, 4), r2[1]);
}
test "peer type resolution: C pointer and many pointer" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var buf = "hello".*;
var a: [*c]u8 = &buf;
var b: [*:0]const u8 = "world";
comptime assert(@TypeOf(a, b) == [*c]const u8);
comptime assert(@TypeOf(b, a) == [*c]const u8);
var t = true;
const r1 = if (t) a else b;
const r2 = if (t) b else a;
try expectEqual(r1, a);
try expectEqual(r2, b);
}
test "peer type resolution: pointer attributes are combined correctly" {
if (builtin.zig_backend == .stage2_aarch64) 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
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
var buf_a align(4) = "foo".*;
var buf_b align(4) = "bar".*;
var buf_c align(4) = "baz".*;
var a: [*:0]align(4) const u8 = &buf_a;
var b: *align(2) volatile [3:0]u8 = &buf_b;
var c: [*:0]align(4) u8 = &buf_c;
comptime assert(@TypeOf(a, b, c) == [*:0]align(2) const volatile u8);
comptime assert(@TypeOf(a, c, b) == [*:0]align(2) const volatile u8);
comptime assert(@TypeOf(b, a, c) == [*:0]align(2) const volatile u8);
comptime assert(@TypeOf(b, c, a) == [*:0]align(2) const volatile u8);
comptime assert(@TypeOf(c, a, b) == [*:0]align(2) const volatile u8);
comptime assert(@TypeOf(c, b, a) == [*:0]align(2) const volatile u8);
var x: u8 = 0;
const r1 = switch (x) {
0 => a,
1 => b,
else => c,
};
const r2 = switch (x) {
0 => b,
1 => a,
else => c,
};
const r3 = switch (x) {
0 => c,
1 => a,
else => b,
};
try expectEqualSlices(u8, std.mem.span(@volatileCast(r1)), "foo");
try expectEqualSlices(u8, std.mem.span(@volatileCast(r2)), "bar");
try expectEqualSlices(u8, std.mem.span(@volatileCast(r3)), "baz");
}

View File

@ -1,37 +0,0 @@
export fn inconsistentChildType() void {
var x: ?i32 = undefined;
const y: comptime_int = 10;
_ = (x == y);
}
export fn optionalToOptional() void {
var x: ?i32 = undefined;
var y: ?i32 = undefined;
_ = (x == y);
}
export fn optionalVector() void {
var x: ?@Vector(10, i32) = undefined;
var y: @Vector(10, i32) = undefined;
_ = (x == y);
}
export fn optionalVector2() void {
var x: ?@Vector(10, i32) = undefined;
var y: @Vector(11, i32) = undefined;
_ = (x == y);
}
export fn invalidChildType() void {
var x: ?[3]i32 = undefined;
var y: [3]i32 = undefined;
_ = (x == y);
}
// error
// backend=llvm
// target=native
//
// :4:12: error: incompatible types: '?i32' and 'comptime_int'
// :4:10: note: type '?i32' here
// :4:15: note: type 'comptime_int' here
// :19:12: error: incompatible types: '?@Vector(10, i32)' and '@Vector(11, i32)'
// :19:10: note: type '?@Vector(10, i32)' here
// :19:15: note: type '@Vector(11, i32)' here
// :24:12: error: operator == not allowed for type '?[3]i32'

View File

@ -0,0 +1,11 @@
export fn entry() void {
var x: ?[3]i32 = undefined;
var y: [3]i32 = undefined;
_ = (x == y);
}
// error
// backend=llvm
// target=native
//
// :4:12: error: operator == not allowed for type '?[3]i32'

View File

@ -0,0 +1,50 @@
export fn optionalVector() void {
var x: ?@Vector(10, i32) = undefined;
var y: @Vector(11, i32) = undefined;
_ = @TypeOf(x, y);
}
export fn badTupleField() void {
var x = .{ @as(u8, 0), @as(u32, 1) };
var y = .{ @as(u8, 1), "hello" };
_ = @TypeOf(x, y);
}
export fn badNestedField() void {
const x = .{ .foo = "hi", .bar = .{ 0, 1 } };
const y = .{ .foo = "hello", .bar = .{ 2, "hi" } };
_ = @TypeOf(x, y);
}
export fn incompatiblePointers() void {
const x: []const u8 = "foo";
const y: [*:0]const u8 = "bar";
_ = @TypeOf(x, y);
}
export fn incompatiblePointers4() void {
const a: *const [5]u8 = "hello";
const b: *const [3:0]u8 = "foo";
const c: []const u8 = "baz"; // The conflict must be reported against this element!
const d: [*]const u8 = "bar";
_ = @TypeOf(a, b, c, d);
}
// error
// backend=llvm
// target=native
//
// :4:9: error: incompatible types: '?@Vector(10, i32)' and '@Vector(11, i32)'
// :4:17: note: type '?@Vector(10, i32)' here
// :4:20: note: type '@Vector(11, i32)' here
// :9:9: error: struct field '1' has conflicting types
// :9:9: note: incompatible types: 'u32' and '*const [5:0]u8'
// :9:17: note: type 'u32' here
// :9:20: note: type '*const [5:0]u8' here
// :14:9: error: struct field 'bar' has conflicting types
// :14:9: note: struct field '1' has conflicting types
// :14:9: note: incompatible types: 'comptime_int' and '*const [2:0]u8'
// :14:17: note: type 'comptime_int' here
// :14:20: note: type '*const [2:0]u8' here
// :19:9: error: incompatible types: '[]const u8' and '[*:0]const u8'
// :19:17: note: type '[]const u8' here
// :19:20: note: type '[*:0]const u8' here
// :26:9: error: incompatible types: '[]const u8' and '[*]const u8'
// :26:23: note: type '[]const u8' here
// :26:26: note: type '[*]const u8' here