| author | |
| committer | |
| log | 6b7ad22981867cbf6ec40d540f5571f276d6801b |
| tree | 4c1665cf0c03fcaa5d6c2a09aa49c49f5b804236 |
| parent | e712d5f03e6e3ddde6a4a6f7d2d82e786efd543a |
| parent | 629c3108aa71f94bd26dba8d4f20c9f3a3945bd4 |
| signature |
Improve `@ptrCast` errors, fix some bugs30 files changed, 280 insertions(+), 36 deletions(-)
doc/langref.html.in+16| ... | ... | @@ -8809,6 +8809,15 @@ pub const PrefetchOptions = struct { |
| 8809 | 8809 | {#link|Optional Pointers#} are allowed. Casting an optional pointer which is {#link|null#} |
| 8810 | 8810 | to a non-optional pointer invokes safety-checked {#link|Undefined Behavior#}. |
| 8811 | 8811 | </p> |
| 8812 | <p> | |
| 8813 | {#syntax#}@ptrCast{#endsyntax#} cannot be used for: | |
| 8814 | </p> | |
| 8815 | <ul> | |
| 8816 | <li>Removing {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier, use {#link|@qualCast#}.</li> | |
| 8817 | <li>Changing pointer address space, use {#link|@addrSpaceCast#}.</li> | |
| 8818 | <li>Increasing pointer alignment, use {#link|@alignCast#}.</li> | |
| 8819 | <li>Casting a non-slice pointer to a slice, use slicing syntax {#syntax#}ptr[start..end]{#endsyntax#}.</li> | |
| 8820 | </ul> | |
| 8812 | 8821 | {#header_close#} |
| 8813 | 8822 | |
| 8814 | 8823 | {#header_open|@ptrToInt#} |
| ... | ... | @@ -8821,6 +8830,13 @@ pub const PrefetchOptions = struct { |
| 8821 | 8830 | |
| 8822 | 8831 | {#header_close#} |
| 8823 | 8832 | |
| 8833 | {#header_open|@qualCast#} | |
| 8834 | <pre>{#syntax#}@qualCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre> | |
| 8835 | <p> | |
| 8836 | Remove {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier from a pointer. | |
| 8837 | </p> | |
| 8838 | {#header_close#} | |
| 8839 | ||
| 8824 | 8840 | {#header_open|@rem#} |
| 8825 | 8841 | <pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre> |
| 8826 | 8842 | <p> |
lib/std/child_process.zig+2-2| ... | ... | @@ -1164,7 +1164,7 @@ fn windowsCreateProcessPathExt( |
| 1164 | 1164 | var app_name_unicode_string = windows.UNICODE_STRING{ |
| 1165 | 1165 | .Length = app_name_len_bytes, |
| 1166 | 1166 | .MaximumLength = app_name_len_bytes, |
| 1167 | .Buffer = @intToPtr([*]u16, @ptrToInt(app_name_wildcard.ptr)), | |
| 1167 | .Buffer = @qualCast([*:0]u16, app_name_wildcard.ptr), | |
| 1168 | 1168 | }; |
| 1169 | 1169 | const rc = windows.ntdll.NtQueryDirectoryFile( |
| 1170 | 1170 | dir.fd, |
| ... | ... | @@ -1261,7 +1261,7 @@ fn windowsCreateProcessPathExt( |
| 1261 | 1261 | var app_name_unicode_string = windows.UNICODE_STRING{ |
| 1262 | 1262 | .Length = app_name_len_bytes, |
| 1263 | 1263 | .MaximumLength = app_name_len_bytes, |
| 1264 | .Buffer = @intToPtr([*]u16, @ptrToInt(app_name_appended.ptr)), | |
| 1264 | .Buffer = @qualCast([*:0]u16, app_name_appended.ptr), | |
| 1265 | 1265 | }; |
| 1266 | 1266 | |
| 1267 | 1267 | // Re-use the directory handle but this time we call with the appended app name |
lib/std/fs.zig+1-1| ... | ... | @@ -1763,7 +1763,7 @@ pub const Dir = struct { |
| 1763 | 1763 | var nt_name = w.UNICODE_STRING{ |
| 1764 | 1764 | .Length = path_len_bytes, |
| 1765 | 1765 | .MaximumLength = path_len_bytes, |
| 1766 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), | |
| 1766 | .Buffer = @qualCast([*:0]u16, sub_path_w), | |
| 1767 | 1767 | }; |
| 1768 | 1768 | var attr = w.OBJECT_ATTRIBUTES{ |
| 1769 | 1769 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), |
lib/std/os.zig+1-1| ... | ... | @@ -4513,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32 |
| 4513 | 4513 | var nt_name = windows.UNICODE_STRING{ |
| 4514 | 4514 | .Length = path_len_bytes, |
| 4515 | 4515 | .MaximumLength = path_len_bytes, |
| 4516 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), | |
| 4516 | .Buffer = @qualCast([*:0]u16, sub_path_w), | |
| 4517 | 4517 | }; |
| 4518 | 4518 | var attr = windows.OBJECT_ATTRIBUTES{ |
| 4519 | 4519 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), |
lib/std/os/windows.zig+7-7| ... | ... | @@ -85,7 +85,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 85 | 85 | var nt_name = UNICODE_STRING{ |
| 86 | 86 | .Length = path_len_bytes, |
| 87 | 87 | .MaximumLength = path_len_bytes, |
| 88 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)), | |
| 88 | .Buffer = @qualCast([*]u16, sub_path_w.ptr), | |
| 89 | 89 | }; |
| 90 | 90 | var attr = OBJECT_ATTRIBUTES{ |
| 91 | 91 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| ... | ... | @@ -634,7 +634,7 @@ pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void |
| 634 | 634 | var nt_name = UNICODE_STRING{ |
| 635 | 635 | .Length = path_len_bytes, |
| 636 | 636 | .MaximumLength = path_len_bytes, |
| 637 | .Buffer = @intToPtr([*]u16, @ptrToInt(path_name.ptr)), | |
| 637 | .Buffer = @qualCast([*]u16, path_name.ptr), | |
| 638 | 638 | }; |
| 639 | 639 | |
| 640 | 640 | const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name); |
| ... | ... | @@ -766,7 +766,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin |
| 766 | 766 | var nt_name = UNICODE_STRING{ |
| 767 | 767 | .Length = path_len_bytes, |
| 768 | 768 | .MaximumLength = path_len_bytes, |
| 769 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)), | |
| 769 | .Buffer = @qualCast([*]u16, sub_path_w.ptr), | |
| 770 | 770 | }; |
| 771 | 771 | var attr = OBJECT_ATTRIBUTES{ |
| 772 | 772 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| ... | ... | @@ -876,7 +876,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil |
| 876 | 876 | .Length = path_len_bytes, |
| 877 | 877 | .MaximumLength = path_len_bytes, |
| 878 | 878 | // The Windows API makes this mutable, but it will not mutate here. |
| 879 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)), | |
| 879 | .Buffer = @qualCast([*]u16, sub_path_w.ptr), | |
| 880 | 880 | }; |
| 881 | 881 | |
| 882 | 882 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { |
| ... | ... | @@ -1414,7 +1414,7 @@ pub fn sendmsg( |
| 1414 | 1414 | } |
| 1415 | 1415 | |
| 1416 | 1416 | pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 { |
| 1417 | var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @intToPtr([*]u8, @ptrToInt(buf)) }; | |
| 1417 | var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @qualCast([*]u8, buf) }; | |
| 1418 | 1418 | var bytes_send: DWORD = undefined; |
| 1419 | 1419 | if (ws2_32.WSASendTo(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_send, flags, to, @intCast(i32, to_len), null, null) == ws2_32.SOCKET_ERROR) { |
| 1420 | 1420 | return ws2_32.SOCKET_ERROR; |
| ... | ... | @@ -1876,13 +1876,13 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool { |
| 1876 | 1876 | const a_string = UNICODE_STRING{ |
| 1877 | 1877 | .Length = a_bytes, |
| 1878 | 1878 | .MaximumLength = a_bytes, |
| 1879 | .Buffer = @intToPtr([*]u16, @ptrToInt(a.ptr)), | |
| 1879 | .Buffer = @qualCast([*]u16, a.ptr), | |
| 1880 | 1880 | }; |
| 1881 | 1881 | const b_bytes = @intCast(u16, b.len * 2); |
| 1882 | 1882 | const b_string = UNICODE_STRING{ |
| 1883 | 1883 | .Length = b_bytes, |
| 1884 | 1884 | .MaximumLength = b_bytes, |
| 1885 | .Buffer = @intToPtr([*]u16, @ptrToInt(b.ptr)), | |
| 1885 | .Buffer = @qualCast([*]u16, b.ptr), | |
| 1886 | 1886 | }; |
| 1887 | 1887 | return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE; |
| 1888 | 1888 | } |
lib/std/zig/c_translation.zig+1-1| ... | ... | @@ -75,7 +75,7 @@ fn castPtr(comptime DestType: type, target: anytype) DestType { |
| 75 | 75 | const source = ptrInfo(@TypeOf(target)); |
| 76 | 76 | |
| 77 | 77 | if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile) |
| 78 | return @intToPtr(DestType, @ptrToInt(target)) | |
| 78 | return @qualCast(DestType, target) | |
| 79 | 79 | else if (@typeInfo(dest.child) == .Opaque) |
| 80 | 80 | // dest.alignment would error out |
| 81 | 81 | return @ptrCast(DestType, target) |
src/AstGen.zig+37-2| ... | ... | @@ -2530,6 +2530,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2530 | 2530 | .bit_size_of, |
| 2531 | 2531 | .typeof_log2_int_type, |
| 2532 | 2532 | .ptr_to_int, |
| 2533 | .qual_cast, | |
| 2533 | 2534 | .align_of, |
| 2534 | 2535 | .bool_to_int, |
| 2535 | 2536 | .embed_file, |
| ... | ... | @@ -4278,7 +4279,34 @@ fn testDecl( |
| 4278 | 4279 | var num_namespaces_out: u32 = 0; |
| 4279 | 4280 | var capturing_namespace: ?*Scope.Namespace = null; |
| 4280 | 4281 | while (true) switch (s.tag) { |
| 4281 | .local_val, .local_ptr => unreachable, // a test cannot be in a local scope | |
| 4282 | .local_val => { | |
| 4283 | const local_val = s.cast(Scope.LocalVal).?; | |
| 4284 | if (local_val.name == name_str_index) { | |
| 4285 | local_val.used = test_name_token; | |
| 4286 | return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{ | |
| 4287 | @tagName(local_val.id_cat), | |
| 4288 | }, &[_]u32{ | |
| 4289 | try astgen.errNoteTok(local_val.token_src, "{s} declared here", .{ | |
| 4290 | @tagName(local_val.id_cat), | |
| 4291 | }), | |
| 4292 | }); | |
| 4293 | } | |
| 4294 | s = local_val.parent; | |
| 4295 | }, | |
| 4296 | .local_ptr => { | |
| 4297 | const local_ptr = s.cast(Scope.LocalPtr).?; | |
| 4298 | if (local_ptr.name == name_str_index) { | |
| 4299 | local_ptr.used = test_name_token; | |
| 4300 | return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{ | |
| 4301 | @tagName(local_ptr.id_cat), | |
| 4302 | }, &[_]u32{ | |
| 4303 | try astgen.errNoteTok(local_ptr.token_src, "{s} declared here", .{ | |
| 4304 | @tagName(local_ptr.id_cat), | |
| 4305 | }), | |
| 4306 | }); | |
| 4307 | } | |
| 4308 | s = local_ptr.parent; | |
| 4309 | }, | |
| 4282 | 4310 | .gen_zir => s = s.cast(GenZir).?.parent, |
| 4283 | 4311 | .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, |
| 4284 | 4312 | .namespace, .enum_namespace => { |
| ... | ... | @@ -8010,6 +8038,7 @@ fn builtinCall( |
| 8010 | 8038 | .float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast), |
| 8011 | 8039 | .int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast), |
| 8012 | 8040 | .ptr_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_cast), |
| 8041 | .qual_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .qual_cast), | |
| 8013 | 8042 | .truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate), |
| 8014 | 8043 | // zig fmt: on |
| 8015 | 8044 | |
| ... | ... | @@ -8692,6 +8721,7 @@ fn callExpr( |
| 8692 | 8721 | defer arg_block.unstack(); |
| 8693 | 8722 | |
| 8694 | 8723 | // `call_inst` is reused to provide the param type. |
| 8724 | arg_block.rl_ty_inst = call_inst; | |
| 8695 | 8725 | const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node); |
| 8696 | 8726 | _ = try arg_block.addBreak(.break_inline, call_index, arg_ref); |
| 8697 | 8727 | |
| ... | ... | @@ -10840,7 +10870,12 @@ const GenZir = struct { |
| 10840 | 10870 | // we emit ZIR for the block break instructions to have the result values, |
| 10841 | 10871 | // and then rvalue() on that to pass the value to the result location. |
| 10842 | 10872 | switch (parent_ri.rl) { |
| 10843 | .ty, .coerced_ty => |ty_inst| { | |
| 10873 | .coerced_ty => |ty_inst| { | |
| 10874 | // Type coercion needs to happend before breaks. | |
| 10875 | gz.rl_ty_inst = ty_inst; | |
| 10876 | gz.break_result_info = .{ .rl = .{ .ty = ty_inst } }; | |
| 10877 | }, | |
| 10878 | .ty => |ty_inst| { | |
| 10844 | 10879 | gz.rl_ty_inst = ty_inst; |
| 10845 | 10880 | gz.break_result_info = parent_ri; |
| 10846 | 10881 | }, |
src/BuiltinFn.zig+8| ... | ... | @@ -75,6 +75,7 @@ pub const Tag = enum { |
| 75 | 75 | prefetch, |
| 76 | 76 | ptr_cast, |
| 77 | 77 | ptr_to_int, |
| 78 | qual_cast, | |
| 78 | 79 | rem, |
| 79 | 80 | return_address, |
| 80 | 81 | select, |
| ... | ... | @@ -674,6 +675,13 @@ pub const list = list: { |
| 674 | 675 | .param_count = 1, |
| 675 | 676 | }, |
| 676 | 677 | }, |
| 678 | .{ | |
| 679 | "@qualCast", | |
| 680 | .{ | |
| 681 | .tag = .qual_cast, | |
| 682 | .param_count = 2, | |
| 683 | }, | |
| 684 | }, | |
| 677 | 685 | .{ |
| 678 | 686 | "@rem", |
| 679 | 687 | .{ |
src/Sema.zig+92-9| ... | ... | @@ -1015,6 +1015,7 @@ fn analyzeBodyInner( |
| 1015 | 1015 | .float_cast => try sema.zirFloatCast(block, inst), |
| 1016 | 1016 | .int_cast => try sema.zirIntCast(block, inst), |
| 1017 | 1017 | .ptr_cast => try sema.zirPtrCast(block, inst), |
| 1018 | .qual_cast => try sema.zirQualCast(block, inst), | |
| 1018 | 1019 | .truncate => try sema.zirTruncate(block, inst), |
| 1019 | 1020 | .align_cast => try sema.zirAlignCast(block, inst), |
| 1020 | 1021 | .has_decl => try sema.zirHasDecl(block, inst), |
| ... | ... | @@ -3294,7 +3295,7 @@ fn ensureResultUsed( |
| 3294 | 3295 | const msg = msg: { |
| 3295 | 3296 | const msg = try sema.errMsg(block, src, "error is ignored", .{}); |
| 3296 | 3297 | errdefer msg.destroy(sema.gpa); |
| 3297 | try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{}); | |
| 3298 | try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); | |
| 3298 | 3299 | break :msg msg; |
| 3299 | 3300 | }; |
| 3300 | 3301 | return sema.failWithOwnedErrorMsg(msg); |
| ... | ... | @@ -3325,7 +3326,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com |
| 3325 | 3326 | const msg = msg: { |
| 3326 | 3327 | const msg = try sema.errMsg(block, src, "error is discarded", .{}); |
| 3327 | 3328 | errdefer msg.destroy(sema.gpa); |
| 3328 | try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{}); | |
| 3329 | try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); | |
| 3329 | 3330 | break :msg msg; |
| 3330 | 3331 | }; |
| 3331 | 3332 | return sema.failWithOwnedErrorMsg(msg); |
| ... | ... | @@ -6874,6 +6875,8 @@ fn analyzeInlineCallArg( |
| 6874 | 6875 | if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err); |
| 6875 | 6876 | return err; |
| 6876 | 6877 | }; |
| 6878 | } else if (!is_comptime_call and zir_tags[inst] == .param_comptime) { | |
| 6879 | _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime"); | |
| 6877 | 6880 | } |
| 6878 | 6881 | const casted_arg = sema.coerceExtra(arg_block, param_ty, uncasted_arg, arg_src, .{ .param_src = .{ |
| 6879 | 6882 | .func_inst = func_inst, |
| ... | ... | @@ -6947,6 +6950,9 @@ fn analyzeInlineCallArg( |
| 6947 | 6950 | .val = arg_val, |
| 6948 | 6951 | }; |
| 6949 | 6952 | } else { |
| 6953 | if (zir_tags[inst] == .param_anytype_comptime) { | |
| 6954 | _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime"); | |
| 6955 | } | |
| 6950 | 6956 | sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); |
| 6951 | 6957 | } |
| 6952 | 6958 | |
| ... | ... | @@ -8467,7 +8473,7 @@ fn handleExternLibName( |
| 8467 | 8473 | return sema.fail( |
| 8468 | 8474 | block, |
| 8469 | 8475 | src_loc, |
| 8470 | "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", | |
| 8476 | "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by '-l{s}' or '-fPIC'.", | |
| 8471 | 8477 | .{ lib_name, lib_name }, |
| 8472 | 8478 | ); |
| 8473 | 8479 | } |
| ... | ... | @@ -9004,7 +9010,18 @@ fn zirParam( |
| 9004 | 9010 | if (is_comptime and sema.preallocated_new_func != null) { |
| 9005 | 9011 | // We have a comptime value for this parameter so it should be elided from the |
| 9006 | 9012 | // function type of the function instruction in this block. |
| 9007 | const coerced_arg = try sema.coerce(block, param_ty, arg, src); | |
| 9013 | const coerced_arg = sema.coerce(block, param_ty, arg, .unneeded) catch |err| switch (err) { | |
| 9014 | error.NeededSourceLocation => { | |
| 9015 | // We are instantiating a generic function and a comptime arg | |
| 9016 | // cannot be coerced to the param type, but since we don't | |
| 9017 | // have the callee source location return `GenericPoison` | |
| 9018 | // so that the instantiation is failed and the coercion | |
| 9019 | // is handled by comptime call logic instead. | |
| 9020 | assert(sema.is_generic_instantiation); | |
| 9021 | return error.GenericPoison; | |
| 9022 | }, | |
| 9023 | else => return err, | |
| 9024 | }; | |
| 9008 | 9025 | sema.inst_map.putAssumeCapacity(inst, coerced_arg); |
| 9009 | 9026 | return; |
| 9010 | 9027 | } |
| ... | ... | @@ -19519,13 +19536,34 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 19519 | 19536 | const operand_info = operand_ty.ptrInfo().data; |
| 19520 | 19537 | const dest_info = dest_ty.ptrInfo().data; |
| 19521 | 19538 | if (!operand_info.mutable and dest_info.mutable) { |
| 19522 | return sema.fail(block, src, "cast discards const qualifier", .{}); | |
| 19539 | const msg = msg: { | |
| 19540 | const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{}); | |
| 19541 | errdefer msg.destroy(sema.gpa); | |
| 19542 | ||
| 19543 | try sema.errNote(block, src, msg, "consider using '@qualCast'", .{}); | |
| 19544 | break :msg msg; | |
| 19545 | }; | |
| 19546 | return sema.failWithOwnedErrorMsg(msg); | |
| 19523 | 19547 | } |
| 19524 | 19548 | if (operand_info.@"volatile" and !dest_info.@"volatile") { |
| 19525 | return sema.fail(block, src, "cast discards volatile qualifier", .{}); | |
| 19549 | const msg = msg: { | |
| 19550 | const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{}); | |
| 19551 | errdefer msg.destroy(sema.gpa); | |
| 19552 | ||
| 19553 | try sema.errNote(block, src, msg, "consider using '@qualCast'", .{}); | |
| 19554 | break :msg msg; | |
| 19555 | }; | |
| 19556 | return sema.failWithOwnedErrorMsg(msg); | |
| 19526 | 19557 | } |
| 19527 | 19558 | if (operand_info.@"addrspace" != dest_info.@"addrspace") { |
| 19528 | return sema.fail(block, src, "cast changes pointer address space", .{}); | |
| 19559 | const msg = msg: { | |
| 19560 | const msg = try sema.errMsg(block, src, "cast changes pointer address space", .{}); | |
| 19561 | errdefer msg.destroy(sema.gpa); | |
| 19562 | ||
| 19563 | try sema.errNote(block, src, msg, "consider using '@addrSpaceCast'", .{}); | |
| 19564 | break :msg msg; | |
| 19565 | }; | |
| 19566 | return sema.failWithOwnedErrorMsg(msg); | |
| 19529 | 19567 | } |
| 19530 | 19568 | |
| 19531 | 19569 | const dest_is_slice = dest_ty.isSlice(); |
| ... | ... | @@ -19580,6 +19618,8 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 19580 | 19618 | try sema.errNote(block, dest_ty_src, msg, "'{}' has alignment '{d}'", .{ |
| 19581 | 19619 | dest_ty.fmt(sema.mod), dest_align, |
| 19582 | 19620 | }); |
| 19621 | ||
| 19622 | try sema.errNote(block, src, msg, "consider using '@alignCast'", .{}); | |
| 19583 | 19623 | break :msg msg; |
| 19584 | 19624 | }; |
| 19585 | 19625 | return sema.failWithOwnedErrorMsg(msg); |
| ... | ... | @@ -19615,6 +19655,49 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 19615 | 19655 | return block.addBitCast(aligned_dest_ty, ptr); |
| 19616 | 19656 | } |
| 19617 | 19657 | |
| 19658 | fn zirQualCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 19659 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 19660 | const src = inst_data.src(); | |
| 19661 | const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 19662 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 19663 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | |
| 19664 | const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); | |
| 19665 | const operand = try sema.resolveInst(extra.rhs); | |
| 19666 | const operand_ty = sema.typeOf(operand); | |
| 19667 | ||
| 19668 | try sema.checkPtrType(block, dest_ty_src, dest_ty); | |
| 19669 | try sema.checkPtrOperand(block, operand_src, operand_ty); | |
| 19670 | ||
| 19671 | var operand_payload = operand_ty.ptrInfo(); | |
| 19672 | var dest_info = dest_ty.ptrInfo(); | |
| 19673 | ||
| 19674 | operand_payload.data.mutable = dest_info.data.mutable; | |
| 19675 | operand_payload.data.@"volatile" = dest_info.data.@"volatile"; | |
| 19676 | ||
| 19677 | const altered_operand_ty = Type.initPayload(&operand_payload.base); | |
| 19678 | if (!altered_operand_ty.eql(dest_ty, sema.mod)) { | |
| 19679 | const msg = msg: { | |
| 19680 | const msg = try sema.errMsg(block, src, "'@qualCast' can only modify 'const' and 'volatile' qualifiers", .{}); | |
| 19681 | errdefer msg.destroy(sema.gpa); | |
| 19682 | ||
| 19683 | dest_info.data.mutable = !operand_ty.isConstPtr(); | |
| 19684 | dest_info.data.@"volatile" = operand_ty.isVolatilePtr(); | |
| 19685 | const altered_dest_ty = Type.initPayload(&dest_info.base); | |
| 19686 | try sema.errNote(block, src, msg, "expected type '{}'", .{altered_dest_ty.fmt(sema.mod)}); | |
| 19687 | try sema.errNote(block, src, msg, "got type '{}'", .{operand_ty.fmt(sema.mod)}); | |
| 19688 | break :msg msg; | |
| 19689 | }; | |
| 19690 | return sema.failWithOwnedErrorMsg(msg); | |
| 19691 | } | |
| 19692 | ||
| 19693 | if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { | |
| 19694 | return sema.addConstant(dest_ty, operand_val); | |
| 19695 | } | |
| 19696 | ||
| 19697 | try sema.requireRuntimeBlock(block, src, null); | |
| 19698 | return block.addBitCast(dest_ty, operand); | |
| 19699 | } | |
| 19700 | ||
| 19618 | 19701 | fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 19619 | 19702 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 19620 | 19703 | const src = inst_data.src(); |
| ... | ... | @@ -25131,7 +25214,7 @@ fn coerceExtra( |
| 25131 | 25214 | (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) |
| 25132 | 25215 | { |
| 25133 | 25216 | try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{}); |
| 25134 | try sema.errNote(block, inst_src, msg, "consider using `try`, `catch`, or `if`", .{}); | |
| 25217 | try sema.errNote(block, inst_src, msg, "consider using 'try', 'catch', or 'if'", .{}); | |
| 25135 | 25218 | } |
| 25136 | 25219 | |
| 25137 | 25220 | // ?T to T |
| ... | ... | @@ -25140,7 +25223,7 @@ fn coerceExtra( |
| 25140 | 25223 | (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(&buf), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) |
| 25141 | 25224 | { |
| 25142 | 25225 | try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{}); |
| 25143 | try sema.errNote(block, inst_src, msg, "consider using `.?`, `orelse`, or `if`", .{}); | |
| 25226 | try sema.errNote(block, inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{}); | |
| 25144 | 25227 | } |
| 25145 | 25228 | |
| 25146 | 25229 | try in_memory_result.report(sema, block, inst_src, msg); |
src/TypedValue.zig+4-1| ... | ... | @@ -176,7 +176,9 @@ pub fn print( |
| 176 | 176 | |
| 177 | 177 | var i: u32 = 0; |
| 178 | 178 | while (i < max_len) : (i += 1) { |
| 179 | buf[i] = std.math.cast(u8, val.fieldValue(ty, i).toUnsignedInt(target)) orelse break :str; | |
| 179 | const elem = val.fieldValue(ty, i); | |
| 180 | if (elem.isUndef()) break :str; | |
| 181 | buf[i] = std.math.cast(u8, elem.toUnsignedInt(target)) orelse break :str; | |
| 180 | 182 | } |
| 181 | 183 | |
| 182 | 184 | const truncated = if (len > max_string_len) " (truncated)" else ""; |
| ... | ... | @@ -390,6 +392,7 @@ pub fn print( |
| 390 | 392 | while (i < max_len) : (i += 1) { |
| 391 | 393 | var elem_buf: Value.ElemValueBuffer = undefined; |
| 392 | 394 | const elem_val = payload.ptr.elemValueBuffer(mod, i, &elem_buf); |
| 395 | if (elem_val.isUndef()) break :str; | |
| 393 | 396 | buf[i] = std.math.cast(u8, elem_val.toUnsignedInt(target)) orelse break :str; |
| 394 | 397 | } |
| 395 | 398 |
src/Zir.zig+6| ... | ... | @@ -857,6 +857,9 @@ pub const Inst = struct { |
| 857 | 857 | /// Implements the `@ptrCast` builtin. |
| 858 | 858 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. |
| 859 | 859 | ptr_cast, |
| 860 | /// Implements the `@qualCast` builtin. | |
| 861 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. | |
| 862 | qual_cast, | |
| 860 | 863 | /// Implements the `@truncate` builtin. |
| 861 | 864 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. |
| 862 | 865 | truncate, |
| ... | ... | @@ -1195,6 +1198,7 @@ pub const Inst = struct { |
| 1195 | 1198 | .float_cast, |
| 1196 | 1199 | .int_cast, |
| 1197 | 1200 | .ptr_cast, |
| 1201 | .qual_cast, | |
| 1198 | 1202 | .truncate, |
| 1199 | 1203 | .align_cast, |
| 1200 | 1204 | .has_field, |
| ... | ... | @@ -1484,6 +1488,7 @@ pub const Inst = struct { |
| 1484 | 1488 | .float_cast, |
| 1485 | 1489 | .int_cast, |
| 1486 | 1490 | .ptr_cast, |
| 1491 | .qual_cast, | |
| 1487 | 1492 | .truncate, |
| 1488 | 1493 | .align_cast, |
| 1489 | 1494 | .has_field, |
| ... | ... | @@ -1755,6 +1760,7 @@ pub const Inst = struct { |
| 1755 | 1760 | .float_cast = .pl_node, |
| 1756 | 1761 | .int_cast = .pl_node, |
| 1757 | 1762 | .ptr_cast = .pl_node, |
| 1763 | .qual_cast = .pl_node, | |
| 1758 | 1764 | .truncate = .pl_node, |
| 1759 | 1765 | .align_cast = .pl_node, |
| 1760 | 1766 | .typeof_builtin = .pl_node, |
src/print_zir.zig+1| ... | ... | @@ -332,6 +332,7 @@ const Writer = struct { |
| 332 | 332 | .float_cast, |
| 333 | 333 | .int_cast, |
| 334 | 334 | .ptr_cast, |
| 335 | .qual_cast, | |
| 335 | 336 | .truncate, |
| 336 | 337 | .align_cast, |
| 337 | 338 | .div_exact, |
stage1/zig1.wasm| Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ |
test/behavior/basic.zig+18| ... | ... | @@ -1125,3 +1125,21 @@ test "returning an opaque type from a function" { |
| 1125 | 1125 | }; |
| 1126 | 1126 | try expect(S.foo(123).b == 123); |
| 1127 | 1127 | } |
| 1128 | ||
| 1129 | test "orelse coercion as function argument" { | |
| 1130 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 1131 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1132 | ||
| 1133 | const Loc = struct { start: i32 = -1 }; | |
| 1134 | const Container = struct { | |
| 1135 | a: ?Loc = null, | |
| 1136 | fn init(a: Loc) @This() { | |
| 1137 | return .{ | |
| 1138 | .a = a, | |
| 1139 | }; | |
| 1140 | } | |
| 1141 | }; | |
| 1142 | var optional: ?Loc = .{}; | |
| 1143 | var foo = Container.init(optional orelse .{}); | |
| 1144 | try expect(foo.a.?.start == -1); | |
| 1145 | } |
test/behavior/sizeof_and_typeof.zig+9| ... | ... | @@ -292,3 +292,12 @@ test "@sizeOf optional of previously unresolved union" { |
| 292 | 292 | const Node = union { a: usize }; |
| 293 | 293 | try expect(@sizeOf(?Node) == @sizeOf(Node) + @alignOf(Node)); |
| 294 | 294 | } |
| 295 | ||
| 296 | test "@offsetOf zero-bit field" { | |
| 297 | const S = packed struct { | |
| 298 | a: u32, | |
| 299 | b: u0, | |
| 300 | c: u32, | |
| 301 | }; | |
| 302 | try expect(@offsetOf(S, "b") == @offsetOf(S, "c")); | |
| 303 | } |
test/cases/compile_errors/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig+1-1| ... | ... | @@ -20,4 +20,4 @@ export fn entry() void { |
| 20 | 20 | // |
| 21 | 21 | // :11:27: error: expected type 'u8', found '?u8' |
| 22 | 22 | // :11:27: note: cannot convert optional to payload type |
| 23 | // :11:27: note: consider using `.?`, `orelse`, or `if` | |
| 23 | // :11:27: note: consider using '.?', 'orelse', or 'if' |
test/cases/compile_errors/comptime_arg_to_generic_fn_callee_error.zig created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | const std = @import("std"); | |
| 2 | const MyStruct = struct { | |
| 3 | a: i32, | |
| 4 | b: i32, | |
| 5 | ||
| 6 | pub fn getA(self: *List) i32 { | |
| 7 | return self.items(.c); | |
| 8 | } | |
| 9 | }; | |
| 10 | const List = std.MultiArrayList(MyStruct); | |
| 11 | pub export fn entry() void { | |
| 12 | var list = List{}; | |
| 13 | _ = MyStruct.getA(&list); | |
| 14 | } | |
| 15 | ||
| 16 | // error | |
| 17 | // backend=stage2 | |
| 18 | // target=native | |
| 19 | // | |
| 20 | // :7:28: error: no field named 'c' in enum 'meta.FieldEnum(tmp.MyStruct)' | |
| 21 | // :?:?: note: enum declared here |
test/cases/compile_errors/discarding_error_value.zig+1-1| ... | ... | @@ -10,4 +10,4 @@ fn foo() !void { |
| 10 | 10 | // target=native |
| 11 | 11 | // |
| 12 | 12 | // :2:12: error: error is discarded |
| 13 | // :2:12: note: consider using `try`, `catch`, or `if` | |
| 13 | // :2:12: note: consider using 'try', 'catch', or 'if' |
test/cases/compile_errors/helpful_return_type_error_message.zig+2-2| ... | ... | @@ -26,7 +26,7 @@ export fn quux() u32 { |
| 26 | 26 | // :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' |
| 27 | 27 | // :10:17: note: function cannot return an error |
| 28 | 28 | // :11:15: note: cannot convert error union to payload type |
| 29 | // :11:15: note: consider using `try`, `catch`, or `if` | |
| 29 | // :11:15: note: consider using 'try', 'catch', or 'if' | |
| 30 | 30 | // :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' |
| 31 | 31 | // :15:14: note: cannot convert error union to payload type |
| 32 | // :15:14: note: consider using `try`, `catch`, or `if` | |
| 32 | // :15:14: note: consider using 'try', 'catch', or 'if' |
test/cases/compile_errors/ignored_deferred_function_call.zig+1-1| ... | ... | @@ -8,4 +8,4 @@ fn bar() anyerror!i32 { return 0; } |
| 8 | 8 | // target=native |
| 9 | 9 | // |
| 10 | 10 | // :2:14: error: error is ignored |
| 11 | // :2:14: note: consider using `try`, `catch`, or `if` | |
| 11 | // :2:14: note: consider using 'try', 'catch', or 'if' |
test/cases/compile_errors/ignored_expression_in_while_continuation.zig+3-3| ... | ... | @@ -18,8 +18,8 @@ fn bad() anyerror!void { |
| 18 | 18 | // target=native |
| 19 | 19 | // |
| 20 | 20 | // :2:24: error: error is ignored |
| 21 | // :2:24: note: consider using `try`, `catch`, or `if` | |
| 21 | // :2:24: note: consider using 'try', 'catch', or 'if' | |
| 22 | 22 | // :6:25: error: error is ignored |
| 23 | // :6:25: note: consider using `try`, `catch`, or `if` | |
| 23 | // :6:25: note: consider using 'try', 'catch', or 'if' | |
| 24 | 24 | // :10:25: error: error is ignored |
| 25 | // :10:25: note: consider using `try`, `catch`, or `if` | |
| 25 | // :10:25: note: consider using 'try', 'catch', or 'if' |
test/cases/compile_errors/increase_pointer_alignment_in_ptrCast.zig+1| ... | ... | @@ -11,3 +11,4 @@ export fn entry() u32 { |
| 11 | 11 | // :3:17: error: cast increases pointer alignment |
| 12 | 12 | // :3:32: note: '*u8' has alignment '1' |
| 13 | 13 | // :3:26: note: '*u32' has alignment '4' |
| 14 | // :3:17: note: consider using '@alignCast' |
test/cases/compile_errors/inline_call_runtime_value_to_comptime_param.zig created+17| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | inline fn needComptime(comptime a: u64) void { | |
| 2 | if (a != 0) @compileError("foo"); | |
| 3 | } | |
| 4 | fn acceptRuntime(value: u64) void { | |
| 5 | needComptime(value); | |
| 6 | } | |
| 7 | pub export fn entry() void { | |
| 8 | var value: u64 = 0; | |
| 9 | acceptRuntime(value); | |
| 10 | } | |
| 11 | ||
| 12 | // error | |
| 13 | // backend=stage2 | |
| 14 | // target=native | |
| 15 | // | |
| 16 | // :5:18: error: unable to resolve comptime value | |
| 17 | // :5:18: note: parameter is comptime |
test/cases/compile_errors/invalid_decltest.zig created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | export fn foo() void { | |
| 2 | const a = 1; | |
| 3 | struct { | |
| 4 | test a {} | |
| 5 | }; | |
| 6 | } | |
| 7 | ||
| 8 | // error | |
| 9 | // backend=stage2 | |
| 10 | // target=native | |
| 11 | // | |
| 12 | // :4:14: error: cannot test a local constant | |
| 13 | // :2:11: note: local constant declared here |
test/cases/compile_errors/invalid_qualcast.zig created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | pub export fn entry() void { | |
| 2 | var a: [*:0]const volatile u16 = undefined; | |
| 3 | _ = @qualCast([*]u16, a); | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // backend=stage2 | |
| 8 | // target=native | |
| 9 | // | |
| 10 | // :3:9: error: '@qualCast' can only modify 'const' and 'volatile' qualifiers | |
| 11 | // :3:9: note: expected type '[*]const volatile u16' | |
| 12 | // :3:9: note: got type '[*:0]const volatile u16' |
test/cases/compile_errors/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig+1-1| ... | ... | @@ -10,5 +10,5 @@ export fn foo() void { |
| 10 | 10 | // |
| 11 | 11 | // :4:9: error: expected type '*anyopaque', found '?*anyopaque' |
| 12 | 12 | // :4:9: note: cannot convert optional to payload type |
| 13 | // :4:9: note: consider using `.?`, `orelse`, or `if` | |
| 13 | // :4:9: note: consider using '.?', 'orelse', or 'if' | |
| 14 | 14 | // :4:9: note: '?*anyopaque' could have null values which are illegal in type '*anyopaque' |
test/cases/compile_errors/ptrCast_discards_const_qualifier.zig+1| ... | ... | @@ -9,3 +9,4 @@ export fn entry() void { |
| 9 | 9 | // target=native |
| 10 | 10 | // |
| 11 | 11 | // :3:15: error: cast discards const qualifier |
| 12 | // :3:15: note: consider using '@qualCast' |
test/cases/compile_errors/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig+1-1| ... | ... | @@ -20,4 +20,4 @@ export fn entry() void { |
| 20 | 20 | // |
| 21 | 21 | // :12:25: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32' |
| 22 | 22 | // :12:25: note: cannot convert error union to payload type |
| 23 | // :12:25: note: consider using `try`, `catch`, or `if` | |
| 23 | // :12:25: note: consider using 'try', 'catch', or 'if' |
test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr.zig+1-1| ... | ... | @@ -17,4 +17,4 @@ pub const Container = struct { |
| 17 | 17 | // |
| 18 | 18 | // :3:36: error: expected type 'i32', found '?i32' |
| 19 | 19 | // :3:36: note: cannot convert optional to payload type |
| 20 | // :3:36: note: consider using `.?`, `orelse`, or `if` | |
| 20 | // :3:36: note: consider using '.?', 'orelse', or 'if' |
test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig+1-1| ... | ... | @@ -17,4 +17,4 @@ pub const Container = struct { |
| 17 | 17 | // |
| 18 | 18 | // :3:36: error: expected type 'i32', found '?i32' |
| 19 | 19 | // :3:36: note: cannot convert optional to payload type |
| 20 | // :3:36: note: consider using `.?`, `orelse`, or `if` | |
| 20 | // :3:36: note: consider using '.?', 'orelse', or 'if' |