| author | |
| committer | |
| log | 11330cbcc55f7d7dbd2de2f5acd7b097cd19788c |
| tree | 68f25f32b59d803986f67f80e660fb8b76fc3025 |
| parent | 691090f3429b8625252dd5cfec101f2a9e171463 |
| parent | 2b589d71fbcacb2e8bc8746dd4b675e57b3a53df |
| signature |
stage2: more in-memory coercion12 files changed, 388 insertions(+), 339 deletions(-)
src/Sema.zig+97-48| ... | ... | @@ -12364,31 +12364,6 @@ fn coerce( |
| 12364 | 12364 | // T to E!T or E to E!T |
| 12365 | 12365 | return sema.wrapErrorUnion(block, dest_ty, inst, inst_src); |
| 12366 | 12366 | }, |
| 12367 | .ErrorSet => switch (inst_ty.zigTypeTag()) { | |
| 12368 | .ErrorSet => { | |
| 12369 | // Coercion to `anyerror`. Note that this check can return false positives | |
| 12370 | // in case the error sets did not get resolved. | |
| 12371 | if (dest_ty.isAnyError()) { | |
| 12372 | return sema.coerceCompatibleErrorSets(block, inst, inst_src); | |
| 12373 | } | |
| 12374 | // If both are inferred error sets of functions, and | |
| 12375 | // the dest includes the source function, the coercion is OK. | |
| 12376 | // This check is important because it works without forcing a full resolution | |
| 12377 | // of inferred error sets. | |
| 12378 | if (inst_ty.castTag(.error_set_inferred)) |src_payload| { | |
| 12379 | if (dest_ty.castTag(.error_set_inferred)) |dst_payload| { | |
| 12380 | const src_func = src_payload.data.func; | |
| 12381 | const dst_func = dst_payload.data.func; | |
| 12382 | ||
| 12383 | if (src_func == dst_func or dst_payload.data.functions.contains(src_func)) { | |
| 12384 | return sema.coerceCompatibleErrorSets(block, inst, inst_src); | |
| 12385 | } | |
| 12386 | } | |
| 12387 | } | |
| 12388 | // TODO full error set resolution and compare sets by names. | |
| 12389 | }, | |
| 12390 | else => {}, | |
| 12391 | }, | |
| 12392 | 12367 | .Union => switch (inst_ty.zigTypeTag()) { |
| 12393 | 12368 | .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), |
| 12394 | 12369 | else => {}, |
| ... | ... | @@ -12441,16 +12416,110 @@ fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target: |
| 12441 | 12416 | return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target); |
| 12442 | 12417 | } |
| 12443 | 12418 | |
| 12419 | // Functions | |
| 12420 | if (dest_ty.zigTypeTag() == .Fn and src_ty.zigTypeTag() == .Fn) { | |
| 12421 | return coerceInMemoryAllowedFns(dest_ty, src_ty, target); | |
| 12422 | } | |
| 12423 | ||
| 12424 | // Error Unions | |
| 12425 | if (dest_ty.zigTypeTag() == .ErrorUnion and src_ty.zigTypeTag() == .ErrorUnion) { | |
| 12426 | const child = coerceInMemoryAllowed(dest_ty.errorUnionPayload(), src_ty.errorUnionPayload(), dest_is_mut, target); | |
| 12427 | if (child == .no_match) { | |
| 12428 | return child; | |
| 12429 | } | |
| 12430 | return coerceInMemoryAllowed(dest_ty.errorUnionSet(), src_ty.errorUnionSet(), dest_is_mut, target); | |
| 12431 | } | |
| 12432 | ||
| 12433 | // Error Sets | |
| 12434 | if (dest_ty.zigTypeTag() == .ErrorSet and src_ty.zigTypeTag() == .ErrorSet) { | |
| 12435 | return coerceInMemoryAllowedErrorSets(dest_ty, src_ty); | |
| 12436 | } | |
| 12437 | ||
| 12444 | 12438 | // TODO: arrays |
| 12445 | 12439 | // TODO: non-pointer-like optionals |
| 12446 | // TODO: error unions | |
| 12447 | // TODO: error sets | |
| 12448 | // TODO: functions | |
| 12449 | 12440 | // TODO: vectors |
| 12450 | 12441 | |
| 12451 | 12442 | return .no_match; |
| 12452 | 12443 | } |
| 12453 | 12444 | |
| 12445 | fn coerceInMemoryAllowedErrorSets( | |
| 12446 | dest_ty: Type, | |
| 12447 | src_ty: Type, | |
| 12448 | ) InMemoryCoercionResult { | |
| 12449 | // Coercion to `anyerror`. Note that this check can return false positives | |
| 12450 | // in case the error sets did not get resolved. | |
| 12451 | if (dest_ty.isAnyError()) { | |
| 12452 | return .ok; | |
| 12453 | } | |
| 12454 | // If both are inferred error sets of functions, and | |
| 12455 | // the dest includes the source function, the coercion is OK. | |
| 12456 | // This check is important because it works without forcing a full resolution | |
| 12457 | // of inferred error sets. | |
| 12458 | if (src_ty.castTag(.error_set_inferred)) |src_payload| { | |
| 12459 | if (dest_ty.castTag(.error_set_inferred)) |dst_payload| { | |
| 12460 | const src_func = src_payload.data.func; | |
| 12461 | const dst_func = dst_payload.data.func; | |
| 12462 | ||
| 12463 | if (src_func == dst_func or dst_payload.data.functions.contains(src_func)) { | |
| 12464 | return .ok; | |
| 12465 | } | |
| 12466 | } | |
| 12467 | } | |
| 12468 | ||
| 12469 | // TODO full error set resolution and compare sets by names. | |
| 12470 | return .no_match; | |
| 12471 | } | |
| 12472 | ||
| 12473 | fn coerceInMemoryAllowedFns( | |
| 12474 | dest_ty: Type, | |
| 12475 | src_ty: Type, | |
| 12476 | target: std.Target, | |
| 12477 | ) InMemoryCoercionResult { | |
| 12478 | const dest_info = dest_ty.fnInfo(); | |
| 12479 | const src_info = src_ty.fnInfo(); | |
| 12480 | ||
| 12481 | if (dest_info.is_var_args != src_info.is_var_args) { | |
| 12482 | return .no_match; | |
| 12483 | } | |
| 12484 | ||
| 12485 | if (dest_info.is_generic != src_info.is_generic) { | |
| 12486 | return .no_match; | |
| 12487 | } | |
| 12488 | ||
| 12489 | if (!src_info.return_type.isNoReturn()) { | |
| 12490 | const rt = coerceInMemoryAllowed(dest_info.return_type, src_info.return_type, false, target); | |
| 12491 | if (rt == .no_match) { | |
| 12492 | return rt; | |
| 12493 | } | |
| 12494 | } | |
| 12495 | ||
| 12496 | if (dest_info.param_types.len != src_info.param_types.len) { | |
| 12497 | return .no_match; | |
| 12498 | } | |
| 12499 | ||
| 12500 | for (dest_info.param_types) |dest_param_ty, i| { | |
| 12501 | const src_param_ty = src_info.param_types[i]; | |
| 12502 | ||
| 12503 | if (dest_info.comptime_params[i] != src_info.comptime_params[i]) { | |
| 12504 | return .no_match; | |
| 12505 | } | |
| 12506 | ||
| 12507 | // TODO: nolias | |
| 12508 | ||
| 12509 | // Note: Cast direction is reversed here. | |
| 12510 | const param = coerceInMemoryAllowed(src_param_ty, dest_param_ty, false, target); | |
| 12511 | if (param == .no_match) { | |
| 12512 | return param; | |
| 12513 | } | |
| 12514 | } | |
| 12515 | ||
| 12516 | if (dest_info.cc != src_info.cc) { | |
| 12517 | return .no_match; | |
| 12518 | } | |
| 12519 | ||
| 12520 | return .ok; | |
| 12521 | } | |
| 12522 | ||
| 12454 | 12523 | fn coerceInMemoryAllowedPtrs( |
| 12455 | 12524 | dest_ty: Type, |
| 12456 | 12525 | src_ty: Type, |
| ... | ... | @@ -13198,26 +13267,6 @@ fn coerceVectorInMemory( |
| 13198 | 13267 | return block.addBitCast(dest_ty, inst); |
| 13199 | 13268 | } |
| 13200 | 13269 | |
| 13201 | fn coerceCompatibleErrorSets( | |
| 13202 | sema: *Sema, | |
| 13203 | block: *Block, | |
| 13204 | err_set: Air.Inst.Ref, | |
| 13205 | err_set_src: LazySrcLoc, | |
| 13206 | ) !Air.Inst.Ref { | |
| 13207 | if (try sema.resolveDefinedValue(block, err_set_src, err_set)) |err_set_val| { | |
| 13208 | // Same representation works. | |
| 13209 | return sema.addConstant(Type.anyerror, err_set_val); | |
| 13210 | } | |
| 13211 | try sema.requireRuntimeBlock(block, err_set_src); | |
| 13212 | return block.addInst(.{ | |
| 13213 | .tag = .bitcast, | |
| 13214 | .data = .{ .ty_op = .{ | |
| 13215 | .ty = Air.Inst.Ref.anyerror_type, | |
| 13216 | .operand = err_set, | |
| 13217 | } }, | |
| 13218 | }); | |
| 13219 | } | |
| 13220 | ||
| 13221 | 13270 | fn analyzeDeclVal( |
| 13222 | 13271 | sema: *Sema, |
| 13223 | 13272 | block: *Block, |
test/behavior.zig+1-1| ... | ... | @@ -58,6 +58,7 @@ test { |
| 58 | 58 | _ = @import("behavior/floatop.zig"); |
| 59 | 59 | _ = @import("behavior/fn.zig"); |
| 60 | 60 | _ = @import("behavior/for.zig"); |
| 61 | _ = @import("behavior/generics_llvm.zig"); | |
| 61 | 62 | _ = @import("behavior/math.zig"); |
| 62 | 63 | _ = @import("behavior/maximum_minimum.zig"); |
| 63 | 64 | _ = @import("behavior/null_llvm.zig"); |
| ... | ... | @@ -145,7 +146,6 @@ test { |
| 145 | 146 | _ = @import("behavior/fn_delegation.zig"); |
| 146 | 147 | _ = @import("behavior/fn_in_struct_in_comptime.zig"); |
| 147 | 148 | _ = @import("behavior/for_stage1.zig"); |
| 148 | _ = @import("behavior/generics_stage1.zig"); | |
| 149 | 149 | _ = @import("behavior/if_stage1.zig"); |
| 150 | 150 | _ = @import("behavior/import.zig"); |
| 151 | 151 | _ = @import("behavior/incomplete_struct_param_tld.zig"); |
test/behavior/cast.zig+29| ... | ... | @@ -266,3 +266,32 @@ test "array coersion to undefined at runtime" { |
| 266 | 266 | array = undefined; |
| 267 | 267 | try expect(std.mem.eql(u8, &array, &undefined_val)); |
| 268 | 268 | } |
| 269 | ||
| 270 | test "implicitly cast from int to anyerror!?T" { | |
| 271 | implicitIntLitToOptional(); | |
| 272 | comptime implicitIntLitToOptional(); | |
| 273 | } | |
| 274 | fn implicitIntLitToOptional() void { | |
| 275 | const f: ?i32 = 1; | |
| 276 | _ = f; | |
| 277 | const g: anyerror!?i32 = 1; | |
| 278 | _ = g catch {}; | |
| 279 | } | |
| 280 | ||
| 281 | test "return u8 coercing into ?u32 return type" { | |
| 282 | const S = struct { | |
| 283 | fn doTheTest() !void { | |
| 284 | try expect(foo(123).? == 123); | |
| 285 | } | |
| 286 | fn foo(arg: u8) ?u32 { | |
| 287 | return arg; | |
| 288 | } | |
| 289 | }; | |
| 290 | try S.doTheTest(); | |
| 291 | comptime try S.doTheTest(); | |
| 292 | } | |
| 293 | ||
| 294 | test "cast from ?[*]T to ??[*]T" { | |
| 295 | const a: ??[*]u8 = @as(?[*]u8, null); | |
| 296 | try expect(a != null and a.? == null); | |
| 297 | } |
test/behavior/cast_llvm.zig+132| ... | ... | @@ -65,3 +65,135 @@ test "implicit ptr to *c_void" { |
| 65 | 65 | var c: *u32 = @ptrCast(*u32, ptr2.?); |
| 66 | 66 | try expect(c.* == 1); |
| 67 | 67 | } |
| 68 | ||
| 69 | const A = struct { | |
| 70 | a: i32, | |
| 71 | }; | |
| 72 | test "return null from fn() anyerror!?&T" { | |
| 73 | const a = returnNullFromOptionalTypeErrorRef(); | |
| 74 | const b = returnNullLitFromOptionalTypeErrorRef(); | |
| 75 | try expect((try a) == null and (try b) == null); | |
| 76 | } | |
| 77 | fn returnNullFromOptionalTypeErrorRef() anyerror!?*A { | |
| 78 | const a: ?*A = null; | |
| 79 | return a; | |
| 80 | } | |
| 81 | fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { | |
| 82 | return null; | |
| 83 | } | |
| 84 | ||
| 85 | test "peer type resolution: [0]u8 and []const u8" { | |
| 86 | try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); | |
| 87 | try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); | |
| 88 | comptime { | |
| 89 | try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); | |
| 90 | try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); | |
| 91 | } | |
| 92 | } | |
| 93 | fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { | |
| 94 | if (a) { | |
| 95 | return &[_]u8{}; | |
| 96 | } | |
| 97 | ||
| 98 | return slice[0..1]; | |
| 99 | } | |
| 100 | ||
| 101 | test "implicitly cast from [N]T to ?[]const T" { | |
| 102 | try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); | |
| 103 | comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); | |
| 104 | } | |
| 105 | ||
| 106 | fn castToOptionalSlice() ?[]const u8 { | |
| 107 | return "hi"; | |
| 108 | } | |
| 109 | ||
| 110 | test "cast u128 to f128 and back" { | |
| 111 | comptime try testCast128(); | |
| 112 | try testCast128(); | |
| 113 | } | |
| 114 | ||
| 115 | fn testCast128() !void { | |
| 116 | try expect(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); | |
| 117 | } | |
| 118 | ||
| 119 | fn cast128Int(x: f128) u128 { | |
| 120 | return @bitCast(u128, x); | |
| 121 | } | |
| 122 | ||
| 123 | fn cast128Float(x: u128) f128 { | |
| 124 | return @bitCast(f128, x); | |
| 125 | } | |
| 126 | ||
| 127 | test "implicit cast from *[N]T to ?[*]T" { | |
| 128 | var x: ?[*]u16 = null; | |
| 129 | var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; | |
| 130 | ||
| 131 | x = &y; | |
| 132 | try expect(std.mem.eql(u16, x.?[0..4], y[0..4])); | |
| 133 | x.?[0] = 8; | |
| 134 | y[3] = 6; | |
| 135 | try expect(std.mem.eql(u16, x.?[0..4], y[0..4])); | |
| 136 | } | |
| 137 | ||
| 138 | test "implicit cast from *T to ?*c_void" { | |
| 139 | var a: u8 = 1; | |
| 140 | incrementVoidPtrValue(&a); | |
| 141 | try std.testing.expect(a == 2); | |
| 142 | } | |
| 143 | ||
| 144 | fn incrementVoidPtrValue(value: ?*c_void) void { | |
| 145 | @ptrCast(*u8, value.?).* += 1; | |
| 146 | } | |
| 147 | ||
| 148 | test "implicit cast *[0]T to E![]const u8" { | |
| 149 | var x = @as(anyerror![]const u8, &[0]u8{}); | |
| 150 | try expect((x catch unreachable).len == 0); | |
| 151 | } | |
| 152 | ||
| 153 | var global_array: [4]u8 = undefined; | |
| 154 | test "cast from array reference to fn" { | |
| 155 | const f = @ptrCast(fn () callconv(.C) void, &global_array); | |
| 156 | try expect(@ptrToInt(f) == @ptrToInt(&global_array)); | |
| 157 | } | |
| 158 | ||
| 159 | test "*const [N]null u8 to ?[]const u8" { | |
| 160 | const S = struct { | |
| 161 | fn doTheTest() !void { | |
| 162 | var a = "Hello"; | |
| 163 | var b: ?[]const u8 = a; | |
| 164 | try expect(mem.eql(u8, b.?, "Hello")); | |
| 165 | } | |
| 166 | }; | |
| 167 | try S.doTheTest(); | |
| 168 | comptime try S.doTheTest(); | |
| 169 | } | |
| 170 | ||
| 171 | test "cast between [*c]T and ?[*:0]T on fn parameter" { | |
| 172 | const S = struct { | |
| 173 | const Handler = ?fn ([*c]const u8) callconv(.C) void; | |
| 174 | fn addCallback(handler: Handler) void { | |
| 175 | _ = handler; | |
| 176 | } | |
| 177 | ||
| 178 | fn myCallback(cstr: ?[*:0]const u8) callconv(.C) void { | |
| 179 | _ = cstr; | |
| 180 | } | |
| 181 | ||
| 182 | fn doTheTest() void { | |
| 183 | addCallback(myCallback); | |
| 184 | } | |
| 185 | }; | |
| 186 | S.doTheTest(); | |
| 187 | } | |
| 188 | ||
| 189 | var global_struct: struct { f0: usize } = undefined; | |
| 190 | test "assignment to optional pointer result loc" { | |
| 191 | var foo: struct { ptr: ?*c_void } = .{ .ptr = &global_struct }; | |
| 192 | try expect(foo.ptr.? == @ptrCast(*c_void, &global_struct)); | |
| 193 | } | |
| 194 | ||
| 195 | test "cast between *[N]void and []void" { | |
| 196 | var a: [4]void = undefined; | |
| 197 | var b: []void = &a; | |
| 198 | try expect(b.len == 4); | |
| 199 | } |
test/behavior/cast_stage1.zig-159| ... | ... | @@ -58,55 +58,6 @@ fn castToOptionalTypeError(z: i32) !void { |
| 58 | 58 | try expect((b catch unreachable).?.a == 1); |
| 59 | 59 | } |
| 60 | 60 | |
| 61 | test "implicitly cast from int to anyerror!?T" { | |
| 62 | implicitIntLitToOptional(); | |
| 63 | comptime implicitIntLitToOptional(); | |
| 64 | } | |
| 65 | fn implicitIntLitToOptional() void { | |
| 66 | const f: ?i32 = 1; | |
| 67 | _ = f; | |
| 68 | const g: anyerror!?i32 = 1; | |
| 69 | _ = g catch {}; | |
| 70 | } | |
| 71 | ||
| 72 | test "return null from fn() anyerror!?&T" { | |
| 73 | const a = returnNullFromOptionalTypeErrorRef(); | |
| 74 | const b = returnNullLitFromOptionalTypeErrorRef(); | |
| 75 | try expect((try a) == null and (try b) == null); | |
| 76 | } | |
| 77 | fn returnNullFromOptionalTypeErrorRef() anyerror!?*A { | |
| 78 | const a: ?*A = null; | |
| 79 | return a; | |
| 80 | } | |
| 81 | fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { | |
| 82 | return null; | |
| 83 | } | |
| 84 | ||
| 85 | test "peer type resolution: [0]u8 and []const u8" { | |
| 86 | try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); | |
| 87 | try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); | |
| 88 | comptime { | |
| 89 | try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); | |
| 90 | try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); | |
| 91 | } | |
| 92 | } | |
| 93 | fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { | |
| 94 | if (a) { | |
| 95 | return &[_]u8{}; | |
| 96 | } | |
| 97 | ||
| 98 | return slice[0..1]; | |
| 99 | } | |
| 100 | ||
| 101 | test "implicitly cast from [N]T to ?[]const T" { | |
| 102 | try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); | |
| 103 | comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); | |
| 104 | } | |
| 105 | ||
| 106 | fn castToOptionalSlice() ?[]const u8 { | |
| 107 | return "hi"; | |
| 108 | } | |
| 109 | ||
| 110 | 61 | test "implicitly cast from [0]T to anyerror![]T" { |
| 111 | 62 | try testCastZeroArrayToErrSliceMut(); |
| 112 | 63 | comptime try testCastZeroArrayToErrSliceMut(); |
| ... | ... | @@ -191,23 +142,6 @@ fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 { |
| 191 | 142 | }; |
| 192 | 143 | } |
| 193 | 144 | |
| 194 | test "cast u128 to f128 and back" { | |
| 195 | comptime try testCast128(); | |
| 196 | try testCast128(); | |
| 197 | } | |
| 198 | ||
| 199 | fn testCast128() !void { | |
| 200 | try expect(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); | |
| 201 | } | |
| 202 | ||
| 203 | fn cast128Int(x: f128) u128 { | |
| 204 | return @bitCast(u128, x); | |
| 205 | } | |
| 206 | ||
| 207 | fn cast128Float(x: u128) f128 { | |
| 208 | return @bitCast(f128, x); | |
| 209 | } | |
| 210 | ||
| 211 | 145 | test "single-item pointer of array to slice to unknown length pointer" { |
| 212 | 146 | try testCastPtrOfArrayToSliceAndPtr(); |
| 213 | 147 | comptime try testCastPtrOfArrayToSliceAndPtr(); |
| ... | ... | @@ -316,27 +250,6 @@ test "@floatCast cast down" { |
| 316 | 250 | } |
| 317 | 251 | } |
| 318 | 252 | |
| 319 | test "implicit cast from *[N]T to ?[*]T" { | |
| 320 | var x: ?[*]u16 = null; | |
| 321 | var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; | |
| 322 | ||
| 323 | x = &y; | |
| 324 | try expect(std.mem.eql(u16, x.?[0..4], y[0..4])); | |
| 325 | x.?[0] = 8; | |
| 326 | y[3] = 6; | |
| 327 | try expect(std.mem.eql(u16, x.?[0..4], y[0..4])); | |
| 328 | } | |
| 329 | ||
| 330 | test "implicit cast from *T to ?*c_void" { | |
| 331 | var a: u8 = 1; | |
| 332 | incrementVoidPtrValue(&a); | |
| 333 | try std.testing.expect(a == 2); | |
| 334 | } | |
| 335 | ||
| 336 | fn incrementVoidPtrValue(value: ?*c_void) void { | |
| 337 | @ptrCast(*u8, value.?).* += 1; | |
| 338 | } | |
| 339 | ||
| 340 | 253 | test "peer type resolution: unreachable, null, slice" { |
| 341 | 254 | const S = struct { |
| 342 | 255 | fn doTheTest(num: usize, word: []const u8) !void { |
| ... | ... | @@ -374,11 +287,6 @@ test "peer type resolution: unreachable, error set, unreachable" { |
| 374 | 287 | try expect(transformed_err == error.SystemResources); |
| 375 | 288 | } |
| 376 | 289 | |
| 377 | test "implicit cast *[0]T to E![]const u8" { | |
| 378 | var x = @as(anyerror![]const u8, &[0]u8{}); | |
| 379 | try expect((x catch unreachable).len == 0); | |
| 380 | } | |
| 381 | ||
| 382 | 290 | test "peer cast *[0]T to E![]const T" { |
| 383 | 291 | var buffer: [5]u8 = "abcde".*; |
| 384 | 292 | var buf: anyerror![]const u8 = buffer[0..]; |
| ... | ... | @@ -395,24 +303,6 @@ test "peer cast *[0]T to []const T" { |
| 395 | 303 | try expect(mem.eql(u8, "abcde", y)); |
| 396 | 304 | } |
| 397 | 305 | |
| 398 | var global_array: [4]u8 = undefined; | |
| 399 | test "cast from array reference to fn" { | |
| 400 | const f = @ptrCast(fn () callconv(.C) void, &global_array); | |
| 401 | try expect(@ptrToInt(f) == @ptrToInt(&global_array)); | |
| 402 | } | |
| 403 | ||
| 404 | test "*const [N]null u8 to ?[]const u8" { | |
| 405 | const S = struct { | |
| 406 | fn doTheTest() !void { | |
| 407 | var a = "Hello"; | |
| 408 | var b: ?[]const u8 = a; | |
| 409 | try expect(mem.eql(u8, b.?, "Hello")); | |
| 410 | } | |
| 411 | }; | |
| 412 | try S.doTheTest(); | |
| 413 | comptime try S.doTheTest(); | |
| 414 | } | |
| 415 | ||
| 416 | 306 | test "peer resolution of string literals" { |
| 417 | 307 | const S = struct { |
| 418 | 308 | const E = enum { a, b, c, d }; |
| ... | ... | @@ -502,19 +392,6 @@ test "cast i8 fn call peers to i32 result" { |
| 502 | 392 | comptime try S.doTheTest(); |
| 503 | 393 | } |
| 504 | 394 | |
| 505 | test "return u8 coercing into ?u32 return type" { | |
| 506 | const S = struct { | |
| 507 | fn doTheTest() !void { | |
| 508 | try expect(foo(123).? == 123); | |
| 509 | } | |
| 510 | fn foo(arg: u8) ?u32 { | |
| 511 | return arg; | |
| 512 | } | |
| 513 | }; | |
| 514 | try S.doTheTest(); | |
| 515 | comptime try S.doTheTest(); | |
| 516 | } | |
| 517 | ||
| 518 | 395 | test "peer type resolution implicit cast to return type" { |
| 519 | 396 | const S = struct { |
| 520 | 397 | fn doTheTest() !void { |
| ... | ... | @@ -553,24 +430,6 @@ test "variable initialization uses result locations properly with regards to the |
| 553 | 430 | try expect(x == 1); |
| 554 | 431 | } |
| 555 | 432 | |
| 556 | test "cast between [*c]T and ?[*:0]T on fn parameter" { | |
| 557 | const S = struct { | |
| 558 | const Handler = ?fn ([*c]const u8) callconv(.C) void; | |
| 559 | fn addCallback(handler: Handler) void { | |
| 560 | _ = handler; | |
| 561 | } | |
| 562 | ||
| 563 | fn myCallback(cstr: ?[*:0]const u8) callconv(.C) void { | |
| 564 | _ = cstr; | |
| 565 | } | |
| 566 | ||
| 567 | fn doTheTest() void { | |
| 568 | addCallback(myCallback); | |
| 569 | } | |
| 570 | }; | |
| 571 | S.doTheTest(); | |
| 572 | } | |
| 573 | ||
| 574 | 433 | test "cast between C pointer with different but compatible types" { |
| 575 | 434 | const S = struct { |
| 576 | 435 | fn foo(arg: [*]c_ushort) u16 { |
| ... | ... | @@ -584,13 +443,6 @@ test "cast between C pointer with different but compatible types" { |
| 584 | 443 | try S.doTheTest(); |
| 585 | 444 | } |
| 586 | 445 | |
| 587 | var global_struct: struct { f0: usize } = undefined; | |
| 588 | ||
| 589 | test "assignment to optional pointer result loc" { | |
| 590 | var foo: struct { ptr: ?*c_void } = .{ .ptr = &global_struct }; | |
| 591 | try expect(foo.ptr.? == @ptrCast(*c_void, &global_struct)); | |
| 592 | } | |
| 593 | ||
| 594 | 446 | test "peer type resolve string lit with sentinel-terminated mutable slice" { |
| 595 | 447 | var array: [4:0]u8 = undefined; |
| 596 | 448 | array[4] = 0; // TODO remove this when #4372 is solved |
| ... | ... | @@ -649,14 +501,3 @@ test "comptime float casts" { |
| 649 | 501 | fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { |
| 650 | 502 | try expect(@floatToInt(I, f) == i); |
| 651 | 503 | } |
| 652 | ||
| 653 | test "cast from ?[*]T to ??[*]T" { | |
| 654 | const a: ??[*]u8 = @as(?[*]u8, null); | |
| 655 | try expect(a != null and a.? == null); | |
| 656 | } | |
| 657 | ||
| 658 | test "cast between *[N]void and []void" { | |
| 659 | var a: [4]void = undefined; | |
| 660 | var b: []void = &a; | |
| 661 | try expect(b.len == 4); | |
| 662 | } |
test/behavior/error.zig+16| ... | ... | @@ -115,3 +115,19 @@ test "implicit cast to optional to error union to return result loc" { |
| 115 | 115 | try S.entry(); |
| 116 | 116 | //comptime S.entry(); TODO |
| 117 | 117 | } |
| 118 | ||
| 119 | test "error: fn returning empty error set can be passed as fn returning any error" { | |
| 120 | entry(); | |
| 121 | comptime entry(); | |
| 122 | } | |
| 123 | ||
| 124 | fn entry() void { | |
| 125 | foo2(bar2); | |
| 126 | } | |
| 127 | ||
| 128 | fn foo2(f: fn () anyerror!void) void { | |
| 129 | const x = f(); | |
| 130 | x catch {}; | |
| 131 | } | |
| 132 | ||
| 133 | fn bar2() (error{}!void) {} |
test/behavior/error_stage1.zig-16| ... | ... | @@ -120,22 +120,6 @@ fn quux_1() !i32 { |
| 120 | 120 | return error.C; |
| 121 | 121 | } |
| 122 | 122 | |
| 123 | test "error: fn returning empty error set can be passed as fn returning any error" { | |
| 124 | entry(); | |
| 125 | comptime entry(); | |
| 126 | } | |
| 127 | ||
| 128 | fn entry() void { | |
| 129 | foo2(bar2); | |
| 130 | } | |
| 131 | ||
| 132 | fn foo2(f: fn () anyerror!void) void { | |
| 133 | const x = f(); | |
| 134 | x catch {}; | |
| 135 | } | |
| 136 | ||
| 137 | fn bar2() (error{}!void) {} | |
| 138 | ||
| 139 | 123 | test "error: Zero sized error set returned with value payload crash" { |
| 140 | 124 | _ = foo3(0) catch {}; |
| 141 | 125 | _ = comptime foo3(0) catch {}; |
test/behavior/fn.zig+42| ... | ... | @@ -121,3 +121,45 @@ test "inline function call that calls optional function pointer, return pointer |
| 121 | 121 | }; |
| 122 | 122 | try S.doTheTest(); |
| 123 | 123 | } |
| 124 | ||
| 125 | test "implicit cast function unreachable return" { | |
| 126 | wantsFnWithVoid(fnWithUnreachable); | |
| 127 | } | |
| 128 | ||
| 129 | fn wantsFnWithVoid(f: fn () void) void { | |
| 130 | _ = f; | |
| 131 | } | |
| 132 | ||
| 133 | fn fnWithUnreachable() noreturn { | |
| 134 | unreachable; | |
| 135 | } | |
| 136 | ||
| 137 | test "extern struct with stdcallcc fn pointer" { | |
| 138 | const S = extern struct { | |
| 139 | ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32, | |
| 140 | ||
| 141 | fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 { | |
| 142 | return 1234; | |
| 143 | } | |
| 144 | }; | |
| 145 | ||
| 146 | var s: S = undefined; | |
| 147 | s.ptr = S.foo; | |
| 148 | try expect(s.ptr() == 1234); | |
| 149 | } | |
| 150 | ||
| 151 | const nComplexCallconv = 100; | |
| 152 | fn fComplexCallconvRet(x: u32) callconv(blk: { | |
| 153 | const s: struct { n: u32 } = .{ .n = nComplexCallconv }; | |
| 154 | break :blk switch (s.n) { | |
| 155 | 0 => .C, | |
| 156 | 1 => .Inline, | |
| 157 | else => .Unspecified, | |
| 158 | }; | |
| 159 | }) struct { x: u32 } { | |
| 160 | return .{ .x = x * x }; | |
| 161 | } | |
| 162 | ||
| 163 | test "function with complex callconv and return type expressions" { | |
| 164 | try expect(fComplexCallconvRet(3).x == 9); | |
| 165 | } |
test/behavior/fn_stage1.zig-42| ... | ... | @@ -23,18 +23,6 @@ fn acceptsString(foo: []u8) void { |
| 23 | 23 | _ = foo; |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | test "implicit cast function unreachable return" { | |
| 27 | wantsFnWithVoid(fnWithUnreachable); | |
| 28 | } | |
| 29 | ||
| 30 | fn wantsFnWithVoid(f: fn () void) void { | |
| 31 | _ = f; | |
| 32 | } | |
| 33 | ||
| 34 | fn fnWithUnreachable() noreturn { | |
| 35 | unreachable; | |
| 36 | } | |
| 37 | ||
| 38 | 26 | test "function pointers" { |
| 39 | 27 | const fns = [_]@TypeOf(fn1){ |
| 40 | 28 | fn1, |
| ... | ... | @@ -126,20 +114,6 @@ test "pass by non-copying value as method, at comptime" { |
| 126 | 114 | } |
| 127 | 115 | } |
| 128 | 116 | |
| 129 | test "extern struct with stdcallcc fn pointer" { | |
| 130 | const S = extern struct { | |
| 131 | ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32, | |
| 132 | ||
| 133 | fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 { | |
| 134 | return 1234; | |
| 135 | } | |
| 136 | }; | |
| 137 | ||
| 138 | var s: S = undefined; | |
| 139 | s.ptr = S.foo; | |
| 140 | try expect(s.ptr() == 1234); | |
| 141 | } | |
| 142 | ||
| 143 | 117 | test "implicit cast fn call result to optional in field result" { |
| 144 | 118 | const S = struct { |
| 145 | 119 | fn entry() !void { |
| ... | ... | @@ -204,19 +178,3 @@ test "function with inferred error set but returning no error" { |
| 204 | 178 | const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?; |
| 205 | 179 | try expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len); |
| 206 | 180 | } |
| 207 | ||
| 208 | const nComplexCallconv = 100; | |
| 209 | fn fComplexCallconvRet(x: u32) callconv(blk: { | |
| 210 | const s: struct { n: u32 } = .{ .n = nComplexCallconv }; | |
| 211 | break :blk switch (s.n) { | |
| 212 | 0 => .C, | |
| 213 | 1 => .Inline, | |
| 214 | else => .Unspecified, | |
| 215 | }; | |
| 216 | }) struct { x: u32 } { | |
| 217 | return .{ .x = x * x }; | |
| 218 | } | |
| 219 | ||
| 220 | test "function with complex callconv and return type expressions" { | |
| 221 | try expect(fComplexCallconvRet(3).x == 9); | |
| 222 | } |
test/behavior/generics.zig+29| ... | ... | @@ -134,3 +134,32 @@ test "use generic param in generic param" { |
| 134 | 134 | fn aGenericFn(comptime T: type, comptime a: T, b: T) T { |
| 135 | 135 | return a + b; |
| 136 | 136 | } |
| 137 | ||
| 138 | test "generic fn with implicit cast" { | |
| 139 | try expect(getFirstByte(u8, &[_]u8{13}) == 13); | |
| 140 | try expect(getFirstByte(u16, &[_]u16{ | |
| 141 | 0, | |
| 142 | 13, | |
| 143 | }) == 0); | |
| 144 | } | |
| 145 | fn getByte(ptr: ?*const u8) u8 { | |
| 146 | return ptr.?.*; | |
| 147 | } | |
| 148 | fn getFirstByte(comptime T: type, mem: []const T) u8 { | |
| 149 | return getByte(@ptrCast(*const u8, &mem[0])); | |
| 150 | } | |
| 151 | ||
| 152 | test "generic fn keeps non-generic parameter types" { | |
| 153 | const A = 128; | |
| 154 | ||
| 155 | const S = struct { | |
| 156 | fn f(comptime T: type, s: []T) !void { | |
| 157 | try expect(A != @typeInfo(@TypeOf(s)).Pointer.alignment); | |
| 158 | } | |
| 159 | }; | |
| 160 | ||
| 161 | // The compiler monomorphizes `S.f` for `T=u8` on its first use, check that | |
| 162 | // `x` type not affect `s` parameter type. | |
| 163 | var x: [16]u8 align(A) = undefined; | |
| 164 | try S.f(u8, &x); | |
| 165 | } |
test/behavior/generics_llvm.zig created+42| ... | ... | @@ -0,0 +1,42 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | ||
| 4 | const foos = [_]fn (anytype) bool{ | |
| 5 | foo1, | |
| 6 | foo2, | |
| 7 | }; | |
| 8 | ||
| 9 | fn foo1(arg: anytype) bool { | |
| 10 | return arg; | |
| 11 | } | |
| 12 | fn foo2(arg: anytype) bool { | |
| 13 | return !arg; | |
| 14 | } | |
| 15 | ||
| 16 | test "array of generic fns" { | |
| 17 | try expect(foos[0](true)); | |
| 18 | try expect(!foos[1](true)); | |
| 19 | } | |
| 20 | ||
| 21 | test "generic struct" { | |
| 22 | var a1 = GenNode(i32){ | |
| 23 | .value = 13, | |
| 24 | .next = null, | |
| 25 | }; | |
| 26 | var b1 = GenNode(bool){ | |
| 27 | .value = true, | |
| 28 | .next = null, | |
| 29 | }; | |
| 30 | try expect(a1.value == 13); | |
| 31 | try expect(a1.value == a1.getVal()); | |
| 32 | try expect(b1.getVal()); | |
| 33 | } | |
| 34 | fn GenNode(comptime T: type) type { | |
| 35 | return struct { | |
| 36 | value: T, | |
| 37 | next: ?*GenNode(T), | |
| 38 | fn getVal(n: *const GenNode(T)) T { | |
| 39 | return n.value; | |
| 40 | } | |
| 41 | }; | |
| 42 | } |
test/behavior/generics_stage1.zig deleted-73| ... | ... | @@ -1,73 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const testing = std.testing; | |
| 3 | const expect = testing.expect; | |
| 4 | const expectEqual = testing.expectEqual; | |
| 5 | ||
| 6 | test "generic struct" { | |
| 7 | var a1 = GenNode(i32){ | |
| 8 | .value = 13, | |
| 9 | .next = null, | |
| 10 | }; | |
| 11 | var b1 = GenNode(bool){ | |
| 12 | .value = true, | |
| 13 | .next = null, | |
| 14 | }; | |
| 15 | try expect(a1.value == 13); | |
| 16 | try expect(a1.value == a1.getVal()); | |
| 17 | try expect(b1.getVal()); | |
| 18 | } | |
| 19 | fn GenNode(comptime T: type) type { | |
| 20 | return struct { | |
| 21 | value: T, | |
| 22 | next: ?*GenNode(T), | |
| 23 | fn getVal(n: *const GenNode(T)) T { | |
| 24 | return n.value; | |
| 25 | } | |
| 26 | }; | |
| 27 | } | |
| 28 | ||
| 29 | test "generic fn with implicit cast" { | |
| 30 | try expect(getFirstByte(u8, &[_]u8{13}) == 13); | |
| 31 | try expect(getFirstByte(u16, &[_]u16{ | |
| 32 | 0, | |
| 33 | 13, | |
| 34 | }) == 0); | |
| 35 | } | |
| 36 | fn getByte(ptr: ?*const u8) u8 { | |
| 37 | return ptr.?.*; | |
| 38 | } | |
| 39 | fn getFirstByte(comptime T: type, mem: []const T) u8 { | |
| 40 | return getByte(@ptrCast(*const u8, &mem[0])); | |
| 41 | } | |
| 42 | ||
| 43 | const foos = [_]fn (anytype) bool{ | |
| 44 | foo1, | |
| 45 | foo2, | |
| 46 | }; | |
| 47 | ||
| 48 | fn foo1(arg: anytype) bool { | |
| 49 | return arg; | |
| 50 | } | |
| 51 | fn foo2(arg: anytype) bool { | |
| 52 | return !arg; | |
| 53 | } | |
| 54 | ||
| 55 | test "array of generic fns" { | |
| 56 | try expect(foos[0](true)); | |
| 57 | try expect(!foos[1](true)); | |
| 58 | } | |
| 59 | ||
| 60 | test "generic fn keeps non-generic parameter types" { | |
| 61 | const A = 128; | |
| 62 | ||
| 63 | const S = struct { | |
| 64 | fn f(comptime T: type, s: []T) !void { | |
| 65 | try expect(A != @typeInfo(@TypeOf(s)).Pointer.alignment); | |
| 66 | } | |
| 67 | }; | |
| 68 | ||
| 69 | // The compiler monomorphizes `S.f` for `T=u8` on its first use, check that | |
| 70 | // `x` type not affect `s` parameter type. | |
| 71 | var x: [16]u8 align(A) = undefined; | |
| 72 | try S.f(u8, &x); | |
| 73 | } |