| 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,31 +12364,6 @@ fn coerce( |
| 12364 | // T to E!T or E to E!T | 12364 | // T to E!T or E to E!T |
| 12365 | return sema.wrapErrorUnion(block, dest_ty, inst, inst_src); | 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 | .Union => switch (inst_ty.zigTypeTag()) { | 12367 | .Union => switch (inst_ty.zigTypeTag()) { |
| 12393 | .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), | 12368 | .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), |
| 12394 | else => {}, | 12369 | else => {}, |
| ... | @@ -12441,16 +12416,110 @@ fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target: | ... | @@ -12441,16 +12416,110 @@ fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target: |
| 12441 | return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target); | 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 | // TODO: arrays | 12438 | // TODO: arrays |
| 12445 | // TODO: non-pointer-like optionals | 12439 | // TODO: non-pointer-like optionals |
| 12446 | // TODO: error unions | ||
| 12447 | // TODO: error sets | ||
| 12448 | // TODO: functions | ||
| 12449 | // TODO: vectors | 12440 | // TODO: vectors |
| 12450 | 12441 | ||
| 12451 | return .no_match; | 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 | fn coerceInMemoryAllowedPtrs( | 12523 | fn coerceInMemoryAllowedPtrs( |
| 12455 | dest_ty: Type, | 12524 | dest_ty: Type, |
| 12456 | src_ty: Type, | 12525 | src_ty: Type, |
| ... | @@ -13198,26 +13267,6 @@ fn coerceVectorInMemory( | ... | @@ -13198,26 +13267,6 @@ fn coerceVectorInMemory( |
| 13198 | return block.addBitCast(dest_ty, inst); | 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 | fn analyzeDeclVal( | 13270 | fn analyzeDeclVal( |
| 13222 | sema: *Sema, | 13271 | sema: *Sema, |
| 13223 | block: *Block, | 13272 | block: *Block, |
test/behavior.zig+1-1| ... | @@ -58,6 +58,7 @@ test { | ... | @@ -58,6 +58,7 @@ test { |
| 58 | _ = @import("behavior/floatop.zig"); | 58 | _ = @import("behavior/floatop.zig"); |
| 59 | _ = @import("behavior/fn.zig"); | 59 | _ = @import("behavior/fn.zig"); |
| 60 | _ = @import("behavior/for.zig"); | 60 | _ = @import("behavior/for.zig"); |
| 61 | _ = @import("behavior/generics_llvm.zig"); | ||
| 61 | _ = @import("behavior/math.zig"); | 62 | _ = @import("behavior/math.zig"); |
| 62 | _ = @import("behavior/maximum_minimum.zig"); | 63 | _ = @import("behavior/maximum_minimum.zig"); |
| 63 | _ = @import("behavior/null_llvm.zig"); | 64 | _ = @import("behavior/null_llvm.zig"); |
| ... | @@ -145,7 +146,6 @@ test { | ... | @@ -145,7 +146,6 @@ test { |
| 145 | _ = @import("behavior/fn_delegation.zig"); | 146 | _ = @import("behavior/fn_delegation.zig"); |
| 146 | _ = @import("behavior/fn_in_struct_in_comptime.zig"); | 147 | _ = @import("behavior/fn_in_struct_in_comptime.zig"); |
| 147 | _ = @import("behavior/for_stage1.zig"); | 148 | _ = @import("behavior/for_stage1.zig"); |
| 148 | _ = @import("behavior/generics_stage1.zig"); | ||
| 149 | _ = @import("behavior/if_stage1.zig"); | 149 | _ = @import("behavior/if_stage1.zig"); |
| 150 | _ = @import("behavior/import.zig"); | 150 | _ = @import("behavior/import.zig"); |
| 151 | _ = @import("behavior/incomplete_struct_param_tld.zig"); | 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,3 +266,32 @@ test "array coersion to undefined at runtime" { |
| 266 | array = undefined; | 266 | array = undefined; |
| 267 | try expect(std.mem.eql(u8, &array, &undefined_val)); | 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,3 +65,135 @@ test "implicit ptr to *c_void" { |
| 65 | var c: *u32 = @ptrCast(*u32, ptr2.?); | 65 | var c: *u32 = @ptrCast(*u32, ptr2.?); |
| 66 | try expect(c.* == 1); | 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,55 +58,6 @@ fn castToOptionalTypeError(z: i32) !void { |
| 58 | try expect((b catch unreachable).?.a == 1); | 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 | test "implicitly cast from [0]T to anyerror![]T" { | 61 | test "implicitly cast from [0]T to anyerror![]T" { |
| 111 | try testCastZeroArrayToErrSliceMut(); | 62 | try testCastZeroArrayToErrSliceMut(); |
| 112 | comptime try testCastZeroArrayToErrSliceMut(); | 63 | comptime try testCastZeroArrayToErrSliceMut(); |
| ... | @@ -191,23 +142,6 @@ fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 { | ... | @@ -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 | test "single-item pointer of array to slice to unknown length pointer" { | 145 | test "single-item pointer of array to slice to unknown length pointer" { |
| 212 | try testCastPtrOfArrayToSliceAndPtr(); | 146 | try testCastPtrOfArrayToSliceAndPtr(); |
| 213 | comptime try testCastPtrOfArrayToSliceAndPtr(); | 147 | comptime try testCastPtrOfArrayToSliceAndPtr(); |
| ... | @@ -316,27 +250,6 @@ test "@floatCast cast down" { | ... | @@ -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 | test "peer type resolution: unreachable, null, slice" { | 253 | test "peer type resolution: unreachable, null, slice" { |
| 341 | const S = struct { | 254 | const S = struct { |
| 342 | fn doTheTest(num: usize, word: []const u8) !void { | 255 | fn doTheTest(num: usize, word: []const u8) !void { |
| ... | @@ -374,11 +287,6 @@ test "peer type resolution: unreachable, error set, unreachable" { | ... | @@ -374,11 +287,6 @@ test "peer type resolution: unreachable, error set, unreachable" { |
| 374 | try expect(transformed_err == error.SystemResources); | 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 | test "peer cast *[0]T to E![]const T" { | 290 | test "peer cast *[0]T to E![]const T" { |
| 383 | var buffer: [5]u8 = "abcde".*; | 291 | var buffer: [5]u8 = "abcde".*; |
| 384 | var buf: anyerror![]const u8 = buffer[0..]; | 292 | var buf: anyerror![]const u8 = buffer[0..]; |
| ... | @@ -395,24 +303,6 @@ test "peer cast *[0]T to []const T" { | ... | @@ -395,24 +303,6 @@ test "peer cast *[0]T to []const T" { |
| 395 | try expect(mem.eql(u8, "abcde", y)); | 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 | test "peer resolution of string literals" { | 306 | test "peer resolution of string literals" { |
| 417 | const S = struct { | 307 | const S = struct { |
| 418 | const E = enum { a, b, c, d }; | 308 | const E = enum { a, b, c, d }; |
| ... | @@ -502,19 +392,6 @@ test "cast i8 fn call peers to i32 result" { | ... | @@ -502,19 +392,6 @@ test "cast i8 fn call peers to i32 result" { |
| 502 | comptime try S.doTheTest(); | 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 | test "peer type resolution implicit cast to return type" { | 395 | test "peer type resolution implicit cast to return type" { |
| 519 | const S = struct { | 396 | const S = struct { |
| 520 | fn doTheTest() !void { | 397 | fn doTheTest() !void { |
| ... | @@ -553,24 +430,6 @@ test "variable initialization uses result locations properly with regards to the | ... | @@ -553,24 +430,6 @@ test "variable initialization uses result locations properly with regards to the |
| 553 | try expect(x == 1); | 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 | test "cast between C pointer with different but compatible types" { | 433 | test "cast between C pointer with different but compatible types" { |
| 575 | const S = struct { | 434 | const S = struct { |
| 576 | fn foo(arg: [*]c_ushort) u16 { | 435 | fn foo(arg: [*]c_ushort) u16 { |
| ... | @@ -584,13 +443,6 @@ test "cast between C pointer with different but compatible types" { | ... | @@ -584,13 +443,6 @@ test "cast between C pointer with different but compatible types" { |
| 584 | try S.doTheTest(); | 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 | test "peer type resolve string lit with sentinel-terminated mutable slice" { | 446 | test "peer type resolve string lit with sentinel-terminated mutable slice" { |
| 595 | var array: [4:0]u8 = undefined; | 447 | var array: [4:0]u8 = undefined; |
| 596 | array[4] = 0; // TODO remove this when #4372 is solved | 448 | array[4] = 0; // TODO remove this when #4372 is solved |
| ... | @@ -649,14 +501,3 @@ test "comptime float casts" { | ... | @@ -649,14 +501,3 @@ test "comptime float casts" { |
| 649 | fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { | 501 | fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { |
| 650 | try expect(@floatToInt(I, f) == i); | 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,3 +115,19 @@ test "implicit cast to optional to error union to return result loc" { |
| 115 | try S.entry(); | 115 | try S.entry(); |
| 116 | //comptime S.entry(); TODO | 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,22 +120,6 @@ fn quux_1() !i32 { |
| 120 | return error.C; | 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 | test "error: Zero sized error set returned with value payload crash" { | 123 | test "error: Zero sized error set returned with value payload crash" { |
| 140 | _ = foo3(0) catch {}; | 124 | _ = foo3(0) catch {}; |
| 141 | _ = comptime foo3(0) catch {}; | 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,3 +121,45 @@ test "inline function call that calls optional function pointer, return pointer |
| 121 | }; | 121 | }; |
| 122 | try S.doTheTest(); | 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,18 +23,6 @@ fn acceptsString(foo: []u8) void { |
| 23 | _ = foo; | 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 | test "function pointers" { | 26 | test "function pointers" { |
| 39 | const fns = [_]@TypeOf(fn1){ | 27 | const fns = [_]@TypeOf(fn1){ |
| 40 | fn1, | 28 | fn1, |
| ... | @@ -126,20 +114,6 @@ test "pass by non-copying value as method, at comptime" { | ... | @@ -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 | test "implicit cast fn call result to optional in field result" { | 117 | test "implicit cast fn call result to optional in field result" { |
| 144 | const S = struct { | 118 | const S = struct { |
| 145 | fn entry() !void { | 119 | fn entry() !void { |
| ... | @@ -204,19 +178,3 @@ test "function with inferred error set but returning no error" { | ... | @@ -204,19 +178,3 @@ test "function with inferred error set but returning no error" { |
| 204 | const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?; | 178 | const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?; |
| 205 | try expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len); | 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,3 +134,32 @@ test "use generic param in generic param" { |
| 134 | fn aGenericFn(comptime T: type, comptime a: T, b: T) T { | 134 | fn aGenericFn(comptime T: type, comptime a: T, b: T) T { |
| 135 | return a + b; | 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 | } | ||