From 41b37712ea0af660365b1638b9b7f742990c175a Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Mon, 22 Nov 2021 02:22:16 +0100 Subject: [PATCH 1/4] sema: function (pointer) in-memory coercion --- src/Sema.zig | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 4efc8846da..4dfccfd03e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12435,16 +12435,70 @@ 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); + } + // TODO: arrays // TODO: non-pointer-like optionals // TODO: error unions // TODO: error sets - // TODO: functions // TODO: vectors 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, From 83a0329c92dc8302b7098f718ceed2ed00e9fda2 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Mon, 22 Nov 2021 02:38:28 +0100 Subject: [PATCH 2/4] sema: move error set coercion to coerceInMemoryAlloed --- src/Sema.zig | 79 ++++++++++++++++++++++------------------------------ 1 file changed, 33 insertions(+), 46 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 4dfccfd03e..06bd74c67d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12358,31 +12358,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 => {}, @@ -12440,15 +12415,47 @@ fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target: return coerceInMemoryAllowedFns(dest_ty, src_ty, 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: 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, @@ -13224,26 +13231,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, From cb248898ab41378c2e9bcf94d05c7c42577a7bab Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Mon, 22 Nov 2021 02:52:26 +0100 Subject: [PATCH 3/4] sema: error union in-memory coercion --- src/Sema.zig | 10 +++++++++- test/behavior/error.zig | 16 ++++++++++++++++ test/behavior/error_stage1.zig | 16 ---------------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 06bd74c67d..376dc25278 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12415,6 +12415,15 @@ fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target: 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); @@ -12422,7 +12431,6 @@ fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target: // TODO: arrays // TODO: non-pointer-like optionals - // TODO: error unions // TODO: vectors return .no_match; diff --git a/test/behavior/error.zig b/test/behavior/error.zig index edbe866b95..b7d4511fe9 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -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) {} diff --git a/test/behavior/error_stage1.zig b/test/behavior/error_stage1.zig index c379257b99..b279927eb8 100644 --- a/test/behavior/error_stage1.zig +++ b/test/behavior/error_stage1.zig @@ -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 {}; From 2b589d71fbcacb2e8bc8746dd4b675e57b3a53df Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Mon, 22 Nov 2021 03:32:28 +0100 Subject: [PATCH 4/4] stage2: move some tests which are now passing --- test/behavior.zig | 2 +- test/behavior/cast.zig | 29 ++++++ test/behavior/cast_llvm.zig | 132 +++++++++++++++++++++++++ test/behavior/cast_stage1.zig | 159 ------------------------------ test/behavior/fn.zig | 42 ++++++++ test/behavior/fn_stage1.zig | 42 -------- test/behavior/generics.zig | 29 ++++++ test/behavior/generics_llvm.zig | 42 ++++++++ test/behavior/generics_stage1.zig | 73 -------------- 9 files changed, 275 insertions(+), 275 deletions(-) create mode 100644 test/behavior/generics_llvm.zig delete mode 100644 test/behavior/generics_stage1.zig diff --git a/test/behavior.zig b/test/behavior.zig index bb155a7de4..21398cfddb 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -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"); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 607df6a8e8..c4ebbc7643 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.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); +} diff --git a/test/behavior/cast_llvm.zig b/test/behavior/cast_llvm.zig index 6c41152fe9..df8d0ba515 100644 --- a/test/behavior/cast_llvm.zig +++ b/test/behavior/cast_llvm.zig @@ -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); +} diff --git a/test/behavior/cast_stage1.zig b/test/behavior/cast_stage1.zig index 157a830f3c..7f1fe41a50 100644 --- a/test/behavior/cast_stage1.zig +++ b/test/behavior/cast_stage1.zig @@ -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); -} diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 80f1f4d919..db1059a427 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -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); +} diff --git a/test/behavior/fn_stage1.zig b/test/behavior/fn_stage1.zig index 9368b39a46..66eb40296e 100644 --- a/test/behavior/fn_stage1.zig +++ b/test/behavior/fn_stage1.zig @@ -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); -} diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 76ad5cede7..6cb1a82e3e 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -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); +} diff --git a/test/behavior/generics_llvm.zig b/test/behavior/generics_llvm.zig new file mode 100644 index 0000000000..bb4c1b2ef4 --- /dev/null +++ b/test/behavior/generics_llvm.zig @@ -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; + } + }; +} diff --git a/test/behavior/generics_stage1.zig b/test/behavior/generics_stage1.zig deleted file mode 100644 index c4b9687aa6..0000000000 --- a/test/behavior/generics_stage1.zig +++ /dev/null @@ -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); -}