mirror of
https://github.com/ziglang/zig.git
synced 2025-12-27 08:33:15 +00:00
Merge pull request #10201 from Snektron/stage2-more-coercion
stage2: more in-memory coercion
This commit is contained in:
commit
11330cbcc5
145
src/Sema.zig
145
src/Sema.zig
@ -12364,31 +12364,6 @@ fn coerce(
|
||||
// T to E!T or E to E!T
|
||||
return sema.wrapErrorUnion(block, dest_ty, inst, inst_src);
|
||||
},
|
||||
.ErrorSet => switch (inst_ty.zigTypeTag()) {
|
||||
.ErrorSet => {
|
||||
// Coercion to `anyerror`. Note that this check can return false positives
|
||||
// in case the error sets did not get resolved.
|
||||
if (dest_ty.isAnyError()) {
|
||||
return sema.coerceCompatibleErrorSets(block, inst, inst_src);
|
||||
}
|
||||
// If both are inferred error sets of functions, and
|
||||
// the dest includes the source function, the coercion is OK.
|
||||
// This check is important because it works without forcing a full resolution
|
||||
// of inferred error sets.
|
||||
if (inst_ty.castTag(.error_set_inferred)) |src_payload| {
|
||||
if (dest_ty.castTag(.error_set_inferred)) |dst_payload| {
|
||||
const src_func = src_payload.data.func;
|
||||
const dst_func = dst_payload.data.func;
|
||||
|
||||
if (src_func == dst_func or dst_payload.data.functions.contains(src_func)) {
|
||||
return sema.coerceCompatibleErrorSets(block, inst, inst_src);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO full error set resolution and compare sets by names.
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
.Union => switch (inst_ty.zigTypeTag()) {
|
||||
.Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
|
||||
else => {},
|
||||
@ -12441,16 +12416,110 @@ fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target:
|
||||
return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target);
|
||||
}
|
||||
|
||||
// Functions
|
||||
if (dest_ty.zigTypeTag() == .Fn and src_ty.zigTypeTag() == .Fn) {
|
||||
return coerceInMemoryAllowedFns(dest_ty, src_ty, target);
|
||||
}
|
||||
|
||||
// Error Unions
|
||||
if (dest_ty.zigTypeTag() == .ErrorUnion and src_ty.zigTypeTag() == .ErrorUnion) {
|
||||
const child = coerceInMemoryAllowed(dest_ty.errorUnionPayload(), src_ty.errorUnionPayload(), dest_is_mut, target);
|
||||
if (child == .no_match) {
|
||||
return child;
|
||||
}
|
||||
return coerceInMemoryAllowed(dest_ty.errorUnionSet(), src_ty.errorUnionSet(), dest_is_mut, target);
|
||||
}
|
||||
|
||||
// Error Sets
|
||||
if (dest_ty.zigTypeTag() == .ErrorSet and src_ty.zigTypeTag() == .ErrorSet) {
|
||||
return coerceInMemoryAllowedErrorSets(dest_ty, src_ty);
|
||||
}
|
||||
|
||||
// TODO: arrays
|
||||
// TODO: non-pointer-like optionals
|
||||
// TODO: error unions
|
||||
// TODO: error sets
|
||||
// TODO: functions
|
||||
// TODO: vectors
|
||||
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
fn coerceInMemoryAllowedErrorSets(
|
||||
dest_ty: Type,
|
||||
src_ty: Type,
|
||||
) InMemoryCoercionResult {
|
||||
// Coercion to `anyerror`. Note that this check can return false positives
|
||||
// in case the error sets did not get resolved.
|
||||
if (dest_ty.isAnyError()) {
|
||||
return .ok;
|
||||
}
|
||||
// If both are inferred error sets of functions, and
|
||||
// the dest includes the source function, the coercion is OK.
|
||||
// This check is important because it works without forcing a full resolution
|
||||
// of inferred error sets.
|
||||
if (src_ty.castTag(.error_set_inferred)) |src_payload| {
|
||||
if (dest_ty.castTag(.error_set_inferred)) |dst_payload| {
|
||||
const src_func = src_payload.data.func;
|
||||
const dst_func = dst_payload.data.func;
|
||||
|
||||
if (src_func == dst_func or dst_payload.data.functions.contains(src_func)) {
|
||||
return .ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO full error set resolution and compare sets by names.
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
fn coerceInMemoryAllowedFns(
|
||||
dest_ty: Type,
|
||||
src_ty: Type,
|
||||
target: std.Target,
|
||||
) InMemoryCoercionResult {
|
||||
const dest_info = dest_ty.fnInfo();
|
||||
const src_info = src_ty.fnInfo();
|
||||
|
||||
if (dest_info.is_var_args != src_info.is_var_args) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
if (dest_info.is_generic != src_info.is_generic) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
if (!src_info.return_type.isNoReturn()) {
|
||||
const rt = coerceInMemoryAllowed(dest_info.return_type, src_info.return_type, false, target);
|
||||
if (rt == .no_match) {
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
if (dest_info.param_types.len != src_info.param_types.len) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
for (dest_info.param_types) |dest_param_ty, i| {
|
||||
const src_param_ty = src_info.param_types[i];
|
||||
|
||||
if (dest_info.comptime_params[i] != src_info.comptime_params[i]) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
// TODO: nolias
|
||||
|
||||
// Note: Cast direction is reversed here.
|
||||
const param = coerceInMemoryAllowed(src_param_ty, dest_param_ty, false, target);
|
||||
if (param == .no_match) {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
|
||||
if (dest_info.cc != src_info.cc) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
return .ok;
|
||||
}
|
||||
|
||||
fn coerceInMemoryAllowedPtrs(
|
||||
dest_ty: Type,
|
||||
src_ty: Type,
|
||||
@ -13198,26 +13267,6 @@ fn coerceVectorInMemory(
|
||||
return block.addBitCast(dest_ty, inst);
|
||||
}
|
||||
|
||||
fn coerceCompatibleErrorSets(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
err_set: Air.Inst.Ref,
|
||||
err_set_src: LazySrcLoc,
|
||||
) !Air.Inst.Ref {
|
||||
if (try sema.resolveDefinedValue(block, err_set_src, err_set)) |err_set_val| {
|
||||
// Same representation works.
|
||||
return sema.addConstant(Type.anyerror, err_set_val);
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, err_set_src);
|
||||
return block.addInst(.{
|
||||
.tag = .bitcast,
|
||||
.data = .{ .ty_op = .{
|
||||
.ty = Air.Inst.Ref.anyerror_type,
|
||||
.operand = err_set,
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
fn analyzeDeclVal(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
|
||||
@ -58,6 +58,7 @@ test {
|
||||
_ = @import("behavior/floatop.zig");
|
||||
_ = @import("behavior/fn.zig");
|
||||
_ = @import("behavior/for.zig");
|
||||
_ = @import("behavior/generics_llvm.zig");
|
||||
_ = @import("behavior/math.zig");
|
||||
_ = @import("behavior/maximum_minimum.zig");
|
||||
_ = @import("behavior/null_llvm.zig");
|
||||
@ -145,7 +146,6 @@ test {
|
||||
_ = @import("behavior/fn_delegation.zig");
|
||||
_ = @import("behavior/fn_in_struct_in_comptime.zig");
|
||||
_ = @import("behavior/for_stage1.zig");
|
||||
_ = @import("behavior/generics_stage1.zig");
|
||||
_ = @import("behavior/if_stage1.zig");
|
||||
_ = @import("behavior/import.zig");
|
||||
_ = @import("behavior/incomplete_struct_param_tld.zig");
|
||||
|
||||
@ -266,3 +266,32 @@ test "array coersion to undefined at runtime" {
|
||||
array = undefined;
|
||||
try expect(std.mem.eql(u8, &array, &undefined_val));
|
||||
}
|
||||
|
||||
test "implicitly cast from int to anyerror!?T" {
|
||||
implicitIntLitToOptional();
|
||||
comptime implicitIntLitToOptional();
|
||||
}
|
||||
fn implicitIntLitToOptional() void {
|
||||
const f: ?i32 = 1;
|
||||
_ = f;
|
||||
const g: anyerror!?i32 = 1;
|
||||
_ = g catch {};
|
||||
}
|
||||
|
||||
test "return u8 coercing into ?u32 return type" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try expect(foo(123).? == 123);
|
||||
}
|
||||
fn foo(arg: u8) ?u32 {
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "cast from ?[*]T to ??[*]T" {
|
||||
const a: ??[*]u8 = @as(?[*]u8, null);
|
||||
try expect(a != null and a.? == null);
|
||||
}
|
||||
|
||||
@ -65,3 +65,135 @@ test "implicit ptr to *c_void" {
|
||||
var c: *u32 = @ptrCast(*u32, ptr2.?);
|
||||
try expect(c.* == 1);
|
||||
}
|
||||
|
||||
const A = struct {
|
||||
a: i32,
|
||||
};
|
||||
test "return null from fn() anyerror!?&T" {
|
||||
const a = returnNullFromOptionalTypeErrorRef();
|
||||
const b = returnNullLitFromOptionalTypeErrorRef();
|
||||
try expect((try a) == null and (try b) == null);
|
||||
}
|
||||
fn returnNullFromOptionalTypeErrorRef() anyerror!?*A {
|
||||
const a: ?*A = null;
|
||||
return a;
|
||||
}
|
||||
fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A {
|
||||
return null;
|
||||
}
|
||||
|
||||
test "peer type resolution: [0]u8 and []const u8" {
|
||||
try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
|
||||
try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
|
||||
comptime {
|
||||
try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
|
||||
try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
|
||||
}
|
||||
}
|
||||
fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {
|
||||
if (a) {
|
||||
return &[_]u8{};
|
||||
}
|
||||
|
||||
return slice[0..1];
|
||||
}
|
||||
|
||||
test "implicitly cast from [N]T to ?[]const T" {
|
||||
try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
|
||||
comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
|
||||
}
|
||||
|
||||
fn castToOptionalSlice() ?[]const u8 {
|
||||
return "hi";
|
||||
}
|
||||
|
||||
test "cast u128 to f128 and back" {
|
||||
comptime try testCast128();
|
||||
try testCast128();
|
||||
}
|
||||
|
||||
fn testCast128() !void {
|
||||
try expect(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000);
|
||||
}
|
||||
|
||||
fn cast128Int(x: f128) u128 {
|
||||
return @bitCast(u128, x);
|
||||
}
|
||||
|
||||
fn cast128Float(x: u128) f128 {
|
||||
return @bitCast(f128, x);
|
||||
}
|
||||
|
||||
test "implicit cast from *[N]T to ?[*]T" {
|
||||
var x: ?[*]u16 = null;
|
||||
var y: [4]u16 = [4]u16{ 0, 1, 2, 3 };
|
||||
|
||||
x = &y;
|
||||
try expect(std.mem.eql(u16, x.?[0..4], y[0..4]));
|
||||
x.?[0] = 8;
|
||||
y[3] = 6;
|
||||
try expect(std.mem.eql(u16, x.?[0..4], y[0..4]));
|
||||
}
|
||||
|
||||
test "implicit cast from *T to ?*c_void" {
|
||||
var a: u8 = 1;
|
||||
incrementVoidPtrValue(&a);
|
||||
try std.testing.expect(a == 2);
|
||||
}
|
||||
|
||||
fn incrementVoidPtrValue(value: ?*c_void) void {
|
||||
@ptrCast(*u8, value.?).* += 1;
|
||||
}
|
||||
|
||||
test "implicit cast *[0]T to E![]const u8" {
|
||||
var x = @as(anyerror![]const u8, &[0]u8{});
|
||||
try expect((x catch unreachable).len == 0);
|
||||
}
|
||||
|
||||
var global_array: [4]u8 = undefined;
|
||||
test "cast from array reference to fn" {
|
||||
const f = @ptrCast(fn () callconv(.C) void, &global_array);
|
||||
try expect(@ptrToInt(f) == @ptrToInt(&global_array));
|
||||
}
|
||||
|
||||
test "*const [N]null u8 to ?[]const u8" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var a = "Hello";
|
||||
var b: ?[]const u8 = a;
|
||||
try expect(mem.eql(u8, b.?, "Hello"));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "cast between [*c]T and ?[*:0]T on fn parameter" {
|
||||
const S = struct {
|
||||
const Handler = ?fn ([*c]const u8) callconv(.C) void;
|
||||
fn addCallback(handler: Handler) void {
|
||||
_ = handler;
|
||||
}
|
||||
|
||||
fn myCallback(cstr: ?[*:0]const u8) callconv(.C) void {
|
||||
_ = cstr;
|
||||
}
|
||||
|
||||
fn doTheTest() void {
|
||||
addCallback(myCallback);
|
||||
}
|
||||
};
|
||||
S.doTheTest();
|
||||
}
|
||||
|
||||
var global_struct: struct { f0: usize } = undefined;
|
||||
test "assignment to optional pointer result loc" {
|
||||
var foo: struct { ptr: ?*c_void } = .{ .ptr = &global_struct };
|
||||
try expect(foo.ptr.? == @ptrCast(*c_void, &global_struct));
|
||||
}
|
||||
|
||||
test "cast between *[N]void and []void" {
|
||||
var a: [4]void = undefined;
|
||||
var b: []void = &a;
|
||||
try expect(b.len == 4);
|
||||
}
|
||||
|
||||
@ -58,55 +58,6 @@ fn castToOptionalTypeError(z: i32) !void {
|
||||
try expect((b catch unreachable).?.a == 1);
|
||||
}
|
||||
|
||||
test "implicitly cast from int to anyerror!?T" {
|
||||
implicitIntLitToOptional();
|
||||
comptime implicitIntLitToOptional();
|
||||
}
|
||||
fn implicitIntLitToOptional() void {
|
||||
const f: ?i32 = 1;
|
||||
_ = f;
|
||||
const g: anyerror!?i32 = 1;
|
||||
_ = g catch {};
|
||||
}
|
||||
|
||||
test "return null from fn() anyerror!?&T" {
|
||||
const a = returnNullFromOptionalTypeErrorRef();
|
||||
const b = returnNullLitFromOptionalTypeErrorRef();
|
||||
try expect((try a) == null and (try b) == null);
|
||||
}
|
||||
fn returnNullFromOptionalTypeErrorRef() anyerror!?*A {
|
||||
const a: ?*A = null;
|
||||
return a;
|
||||
}
|
||||
fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A {
|
||||
return null;
|
||||
}
|
||||
|
||||
test "peer type resolution: [0]u8 and []const u8" {
|
||||
try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
|
||||
try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
|
||||
comptime {
|
||||
try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
|
||||
try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
|
||||
}
|
||||
}
|
||||
fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {
|
||||
if (a) {
|
||||
return &[_]u8{};
|
||||
}
|
||||
|
||||
return slice[0..1];
|
||||
}
|
||||
|
||||
test "implicitly cast from [N]T to ?[]const T" {
|
||||
try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
|
||||
comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
|
||||
}
|
||||
|
||||
fn castToOptionalSlice() ?[]const u8 {
|
||||
return "hi";
|
||||
}
|
||||
|
||||
test "implicitly cast from [0]T to anyerror![]T" {
|
||||
try testCastZeroArrayToErrSliceMut();
|
||||
comptime try testCastZeroArrayToErrSliceMut();
|
||||
@ -191,23 +142,6 @@ fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 {
|
||||
};
|
||||
}
|
||||
|
||||
test "cast u128 to f128 and back" {
|
||||
comptime try testCast128();
|
||||
try testCast128();
|
||||
}
|
||||
|
||||
fn testCast128() !void {
|
||||
try expect(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000);
|
||||
}
|
||||
|
||||
fn cast128Int(x: f128) u128 {
|
||||
return @bitCast(u128, x);
|
||||
}
|
||||
|
||||
fn cast128Float(x: u128) f128 {
|
||||
return @bitCast(f128, x);
|
||||
}
|
||||
|
||||
test "single-item pointer of array to slice to unknown length pointer" {
|
||||
try testCastPtrOfArrayToSliceAndPtr();
|
||||
comptime try testCastPtrOfArrayToSliceAndPtr();
|
||||
@ -316,27 +250,6 @@ test "@floatCast cast down" {
|
||||
}
|
||||
}
|
||||
|
||||
test "implicit cast from *[N]T to ?[*]T" {
|
||||
var x: ?[*]u16 = null;
|
||||
var y: [4]u16 = [4]u16{ 0, 1, 2, 3 };
|
||||
|
||||
x = &y;
|
||||
try expect(std.mem.eql(u16, x.?[0..4], y[0..4]));
|
||||
x.?[0] = 8;
|
||||
y[3] = 6;
|
||||
try expect(std.mem.eql(u16, x.?[0..4], y[0..4]));
|
||||
}
|
||||
|
||||
test "implicit cast from *T to ?*c_void" {
|
||||
var a: u8 = 1;
|
||||
incrementVoidPtrValue(&a);
|
||||
try std.testing.expect(a == 2);
|
||||
}
|
||||
|
||||
fn incrementVoidPtrValue(value: ?*c_void) void {
|
||||
@ptrCast(*u8, value.?).* += 1;
|
||||
}
|
||||
|
||||
test "peer type resolution: unreachable, null, slice" {
|
||||
const S = struct {
|
||||
fn doTheTest(num: usize, word: []const u8) !void {
|
||||
@ -374,11 +287,6 @@ test "peer type resolution: unreachable, error set, unreachable" {
|
||||
try expect(transformed_err == error.SystemResources);
|
||||
}
|
||||
|
||||
test "implicit cast *[0]T to E![]const u8" {
|
||||
var x = @as(anyerror![]const u8, &[0]u8{});
|
||||
try expect((x catch unreachable).len == 0);
|
||||
}
|
||||
|
||||
test "peer cast *[0]T to E![]const T" {
|
||||
var buffer: [5]u8 = "abcde".*;
|
||||
var buf: anyerror![]const u8 = buffer[0..];
|
||||
@ -395,24 +303,6 @@ test "peer cast *[0]T to []const T" {
|
||||
try expect(mem.eql(u8, "abcde", y));
|
||||
}
|
||||
|
||||
var global_array: [4]u8 = undefined;
|
||||
test "cast from array reference to fn" {
|
||||
const f = @ptrCast(fn () callconv(.C) void, &global_array);
|
||||
try expect(@ptrToInt(f) == @ptrToInt(&global_array));
|
||||
}
|
||||
|
||||
test "*const [N]null u8 to ?[]const u8" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var a = "Hello";
|
||||
var b: ?[]const u8 = a;
|
||||
try expect(mem.eql(u8, b.?, "Hello"));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "peer resolution of string literals" {
|
||||
const S = struct {
|
||||
const E = enum { a, b, c, d };
|
||||
@ -502,19 +392,6 @@ test "cast i8 fn call peers to i32 result" {
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "return u8 coercing into ?u32 return type" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try expect(foo(123).? == 123);
|
||||
}
|
||||
fn foo(arg: u8) ?u32 {
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "peer type resolution implicit cast to return type" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
@ -553,24 +430,6 @@ test "variable initialization uses result locations properly with regards to the
|
||||
try expect(x == 1);
|
||||
}
|
||||
|
||||
test "cast between [*c]T and ?[*:0]T on fn parameter" {
|
||||
const S = struct {
|
||||
const Handler = ?fn ([*c]const u8) callconv(.C) void;
|
||||
fn addCallback(handler: Handler) void {
|
||||
_ = handler;
|
||||
}
|
||||
|
||||
fn myCallback(cstr: ?[*:0]const u8) callconv(.C) void {
|
||||
_ = cstr;
|
||||
}
|
||||
|
||||
fn doTheTest() void {
|
||||
addCallback(myCallback);
|
||||
}
|
||||
};
|
||||
S.doTheTest();
|
||||
}
|
||||
|
||||
test "cast between C pointer with different but compatible types" {
|
||||
const S = struct {
|
||||
fn foo(arg: [*]c_ushort) u16 {
|
||||
@ -584,13 +443,6 @@ test "cast between C pointer with different but compatible types" {
|
||||
try S.doTheTest();
|
||||
}
|
||||
|
||||
var global_struct: struct { f0: usize } = undefined;
|
||||
|
||||
test "assignment to optional pointer result loc" {
|
||||
var foo: struct { ptr: ?*c_void } = .{ .ptr = &global_struct };
|
||||
try expect(foo.ptr.? == @ptrCast(*c_void, &global_struct));
|
||||
}
|
||||
|
||||
test "peer type resolve string lit with sentinel-terminated mutable slice" {
|
||||
var array: [4:0]u8 = undefined;
|
||||
array[4] = 0; // TODO remove this when #4372 is solved
|
||||
@ -649,14 +501,3 @@ test "comptime float casts" {
|
||||
fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
|
||||
try expect(@floatToInt(I, f) == i);
|
||||
}
|
||||
|
||||
test "cast from ?[*]T to ??[*]T" {
|
||||
const a: ??[*]u8 = @as(?[*]u8, null);
|
||||
try expect(a != null and a.? == null);
|
||||
}
|
||||
|
||||
test "cast between *[N]void and []void" {
|
||||
var a: [4]void = undefined;
|
||||
var b: []void = &a;
|
||||
try expect(b.len == 4);
|
||||
}
|
||||
|
||||
@ -115,3 +115,19 @@ test "implicit cast to optional to error union to return result loc" {
|
||||
try S.entry();
|
||||
//comptime S.entry(); TODO
|
||||
}
|
||||
|
||||
test "error: fn returning empty error set can be passed as fn returning any error" {
|
||||
entry();
|
||||
comptime entry();
|
||||
}
|
||||
|
||||
fn entry() void {
|
||||
foo2(bar2);
|
||||
}
|
||||
|
||||
fn foo2(f: fn () anyerror!void) void {
|
||||
const x = f();
|
||||
x catch {};
|
||||
}
|
||||
|
||||
fn bar2() (error{}!void) {}
|
||||
|
||||
@ -120,22 +120,6 @@ fn quux_1() !i32 {
|
||||
return error.C;
|
||||
}
|
||||
|
||||
test "error: fn returning empty error set can be passed as fn returning any error" {
|
||||
entry();
|
||||
comptime entry();
|
||||
}
|
||||
|
||||
fn entry() void {
|
||||
foo2(bar2);
|
||||
}
|
||||
|
||||
fn foo2(f: fn () anyerror!void) void {
|
||||
const x = f();
|
||||
x catch {};
|
||||
}
|
||||
|
||||
fn bar2() (error{}!void) {}
|
||||
|
||||
test "error: Zero sized error set returned with value payload crash" {
|
||||
_ = foo3(0) catch {};
|
||||
_ = comptime foo3(0) catch {};
|
||||
|
||||
@ -121,3 +121,45 @@ test "inline function call that calls optional function pointer, return pointer
|
||||
};
|
||||
try S.doTheTest();
|
||||
}
|
||||
|
||||
test "implicit cast function unreachable return" {
|
||||
wantsFnWithVoid(fnWithUnreachable);
|
||||
}
|
||||
|
||||
fn wantsFnWithVoid(f: fn () void) void {
|
||||
_ = f;
|
||||
}
|
||||
|
||||
fn fnWithUnreachable() noreturn {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
test "extern struct with stdcallcc fn pointer" {
|
||||
const S = extern struct {
|
||||
ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32,
|
||||
|
||||
fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 {
|
||||
return 1234;
|
||||
}
|
||||
};
|
||||
|
||||
var s: S = undefined;
|
||||
s.ptr = S.foo;
|
||||
try expect(s.ptr() == 1234);
|
||||
}
|
||||
|
||||
const nComplexCallconv = 100;
|
||||
fn fComplexCallconvRet(x: u32) callconv(blk: {
|
||||
const s: struct { n: u32 } = .{ .n = nComplexCallconv };
|
||||
break :blk switch (s.n) {
|
||||
0 => .C,
|
||||
1 => .Inline,
|
||||
else => .Unspecified,
|
||||
};
|
||||
}) struct { x: u32 } {
|
||||
return .{ .x = x * x };
|
||||
}
|
||||
|
||||
test "function with complex callconv and return type expressions" {
|
||||
try expect(fComplexCallconvRet(3).x == 9);
|
||||
}
|
||||
|
||||
@ -23,18 +23,6 @@ fn acceptsString(foo: []u8) void {
|
||||
_ = foo;
|
||||
}
|
||||
|
||||
test "implicit cast function unreachable return" {
|
||||
wantsFnWithVoid(fnWithUnreachable);
|
||||
}
|
||||
|
||||
fn wantsFnWithVoid(f: fn () void) void {
|
||||
_ = f;
|
||||
}
|
||||
|
||||
fn fnWithUnreachable() noreturn {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
test "function pointers" {
|
||||
const fns = [_]@TypeOf(fn1){
|
||||
fn1,
|
||||
@ -126,20 +114,6 @@ test "pass by non-copying value as method, at comptime" {
|
||||
}
|
||||
}
|
||||
|
||||
test "extern struct with stdcallcc fn pointer" {
|
||||
const S = extern struct {
|
||||
ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32,
|
||||
|
||||
fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 {
|
||||
return 1234;
|
||||
}
|
||||
};
|
||||
|
||||
var s: S = undefined;
|
||||
s.ptr = S.foo;
|
||||
try expect(s.ptr() == 1234);
|
||||
}
|
||||
|
||||
test "implicit cast fn call result to optional in field result" {
|
||||
const S = struct {
|
||||
fn entry() !void {
|
||||
@ -204,19 +178,3 @@ test "function with inferred error set but returning no error" {
|
||||
const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?;
|
||||
try expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len);
|
||||
}
|
||||
|
||||
const nComplexCallconv = 100;
|
||||
fn fComplexCallconvRet(x: u32) callconv(blk: {
|
||||
const s: struct { n: u32 } = .{ .n = nComplexCallconv };
|
||||
break :blk switch (s.n) {
|
||||
0 => .C,
|
||||
1 => .Inline,
|
||||
else => .Unspecified,
|
||||
};
|
||||
}) struct { x: u32 } {
|
||||
return .{ .x = x * x };
|
||||
}
|
||||
|
||||
test "function with complex callconv and return type expressions" {
|
||||
try expect(fComplexCallconvRet(3).x == 9);
|
||||
}
|
||||
|
||||
@ -134,3 +134,32 @@ test "use generic param in generic param" {
|
||||
fn aGenericFn(comptime T: type, comptime a: T, b: T) T {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
test "generic fn with implicit cast" {
|
||||
try expect(getFirstByte(u8, &[_]u8{13}) == 13);
|
||||
try expect(getFirstByte(u16, &[_]u16{
|
||||
0,
|
||||
13,
|
||||
}) == 0);
|
||||
}
|
||||
fn getByte(ptr: ?*const u8) u8 {
|
||||
return ptr.?.*;
|
||||
}
|
||||
fn getFirstByte(comptime T: type, mem: []const T) u8 {
|
||||
return getByte(@ptrCast(*const u8, &mem[0]));
|
||||
}
|
||||
|
||||
test "generic fn keeps non-generic parameter types" {
|
||||
const A = 128;
|
||||
|
||||
const S = struct {
|
||||
fn f(comptime T: type, s: []T) !void {
|
||||
try expect(A != @typeInfo(@TypeOf(s)).Pointer.alignment);
|
||||
}
|
||||
};
|
||||
|
||||
// The compiler monomorphizes `S.f` for `T=u8` on its first use, check that
|
||||
// `x` type not affect `s` parameter type.
|
||||
var x: [16]u8 align(A) = undefined;
|
||||
try S.f(u8, &x);
|
||||
}
|
||||
|
||||
42
test/behavior/generics_llvm.zig
Normal file
42
test/behavior/generics_llvm.zig
Normal file
@ -0,0 +1,42 @@
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
const foos = [_]fn (anytype) bool{
|
||||
foo1,
|
||||
foo2,
|
||||
};
|
||||
|
||||
fn foo1(arg: anytype) bool {
|
||||
return arg;
|
||||
}
|
||||
fn foo2(arg: anytype) bool {
|
||||
return !arg;
|
||||
}
|
||||
|
||||
test "array of generic fns" {
|
||||
try expect(foos[0](true));
|
||||
try expect(!foos[1](true));
|
||||
}
|
||||
|
||||
test "generic struct" {
|
||||
var a1 = GenNode(i32){
|
||||
.value = 13,
|
||||
.next = null,
|
||||
};
|
||||
var b1 = GenNode(bool){
|
||||
.value = true,
|
||||
.next = null,
|
||||
};
|
||||
try expect(a1.value == 13);
|
||||
try expect(a1.value == a1.getVal());
|
||||
try expect(b1.getVal());
|
||||
}
|
||||
fn GenNode(comptime T: type) type {
|
||||
return struct {
|
||||
value: T,
|
||||
next: ?*GenNode(T),
|
||||
fn getVal(n: *const GenNode(T)) T {
|
||||
return n.value;
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const expect = testing.expect;
|
||||
const expectEqual = testing.expectEqual;
|
||||
|
||||
test "generic struct" {
|
||||
var a1 = GenNode(i32){
|
||||
.value = 13,
|
||||
.next = null,
|
||||
};
|
||||
var b1 = GenNode(bool){
|
||||
.value = true,
|
||||
.next = null,
|
||||
};
|
||||
try expect(a1.value == 13);
|
||||
try expect(a1.value == a1.getVal());
|
||||
try expect(b1.getVal());
|
||||
}
|
||||
fn GenNode(comptime T: type) type {
|
||||
return struct {
|
||||
value: T,
|
||||
next: ?*GenNode(T),
|
||||
fn getVal(n: *const GenNode(T)) T {
|
||||
return n.value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "generic fn with implicit cast" {
|
||||
try expect(getFirstByte(u8, &[_]u8{13}) == 13);
|
||||
try expect(getFirstByte(u16, &[_]u16{
|
||||
0,
|
||||
13,
|
||||
}) == 0);
|
||||
}
|
||||
fn getByte(ptr: ?*const u8) u8 {
|
||||
return ptr.?.*;
|
||||
}
|
||||
fn getFirstByte(comptime T: type, mem: []const T) u8 {
|
||||
return getByte(@ptrCast(*const u8, &mem[0]));
|
||||
}
|
||||
|
||||
const foos = [_]fn (anytype) bool{
|
||||
foo1,
|
||||
foo2,
|
||||
};
|
||||
|
||||
fn foo1(arg: anytype) bool {
|
||||
return arg;
|
||||
}
|
||||
fn foo2(arg: anytype) bool {
|
||||
return !arg;
|
||||
}
|
||||
|
||||
test "array of generic fns" {
|
||||
try expect(foos[0](true));
|
||||
try expect(!foos[1](true));
|
||||
}
|
||||
|
||||
test "generic fn keeps non-generic parameter types" {
|
||||
const A = 128;
|
||||
|
||||
const S = struct {
|
||||
fn f(comptime T: type, s: []T) !void {
|
||||
try expect(A != @typeInfo(@TypeOf(s)).Pointer.alignment);
|
||||
}
|
||||
};
|
||||
|
||||
// The compiler monomorphizes `S.f` for `T=u8` on its first use, check that
|
||||
// `x` type not affect `s` parameter type.
|
||||
var x: [16]u8 align(A) = undefined;
|
||||
try S.f(u8, &x);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user