aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVeikka Tuominen <git@vexu.eu>2023-01-27 20:25:48 +0200
committerVeikka Tuominen <git@vexu.eu>2023-01-30 18:55:57 +0200
commitf16c10a86b7183e99e54a70344f4681211cd52bb (patch)
treeaa6c1f3510696eb5ee63dd4ddb17c96cc6dce45b
parentfe4ea31f7e9e1c8caea6a1df107b91e8ea1a7b8a (diff)
downloadzig-f16c10a86b7183e99e54a70344f4681211cd52bb.tar.gz
zig-f16c10a86b7183e99e54a70344f4681211cd52bb.zip
implement `@qualCast`
-rw-r--r--doc/langref.html.in15
-rw-r--r--lib/std/child_process.zig4
-rw-r--r--lib/std/fs.zig2
-rw-r--r--lib/std/os.zig2
-rw-r--r--lib/std/os/windows.zig14
-rw-r--r--lib/std/zig/c_translation.zig2
-rw-r--r--src/AstGen.zig2
-rw-r--r--src/BuiltinFn.zig8
-rw-r--r--src/Sema.zig62
-rw-r--r--src/Zir.zig6
-rw-r--r--src/print_zir.zig1
-rw-r--r--test/cases/compile_errors/invalid_qualcast.zig12
-rw-r--r--test/cases/compile_errors/ptrCast_discards_const_qualifier.zig1
13 files changed, 113 insertions, 18 deletions
diff --git a/doc/langref.html.in b/doc/langref.html.in
index e1521795ca..8c7781ee42 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -8803,10 +8803,10 @@ pub const PrefetchOptions = struct {
{#syntax#}@ptrCast{#endsyntax#} cannot be used for:
</p>
<ul>
- <li>Removing {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier. TODO add a {#syntax#}@qualCast{#endsyntax#} builtin.</li>
- <li>Changing pointer address space, use {#link|@addrSpaceCast#}</li>
- <li>Increasing pointer alignment, use {#link|@alignCast#}</li>
- <li>Casting a non-slice pointer to a slice, use slicing syntax {#syntax#}ptr[start..end]{#endsyntax#}</li>
+ <li>Removing {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier, use {#link|@qualCast#}.</li>
+ <li>Changing pointer address space, use {#link|@addrSpaceCast#}.</li>
+ <li>Increasing pointer alignment, use {#link|@alignCast#}.</li>
+ <li>Casting a non-slice pointer to a slice, use slicing syntax {#syntax#}ptr[start..end]{#endsyntax#}.</li>
</ul>
{#header_close#}
@@ -8820,6 +8820,13 @@ pub const PrefetchOptions = struct {
{#header_close#}
+ {#header_open|@qualCast#}
+ <pre>{#syntax#}@qualCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre>
+ <p>
+ Remove {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier from a pointer.
+ </p>
+ {#header_close#}
+
{#header_open|@rem#}
<pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre>
<p>
diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig
index 4a816c8318..21d7b4fe3e 100644
--- a/lib/std/child_process.zig
+++ b/lib/std/child_process.zig
@@ -1164,7 +1164,7 @@ fn windowsCreateProcessPathExt(
var app_name_unicode_string = windows.UNICODE_STRING{
.Length = app_name_len_bytes,
.MaximumLength = app_name_len_bytes,
- .Buffer = @intToPtr([*]u16, @ptrToInt(app_name_wildcard.ptr)),
+ .Buffer = @qualCast([*:0]u16, app_name_wildcard.ptr),
};
const rc = windows.ntdll.NtQueryDirectoryFile(
dir.fd,
@@ -1261,7 +1261,7 @@ fn windowsCreateProcessPathExt(
var app_name_unicode_string = windows.UNICODE_STRING{
.Length = app_name_len_bytes,
.MaximumLength = app_name_len_bytes,
- .Buffer = @intToPtr([*]u16, @ptrToInt(app_name_appended.ptr)),
+ .Buffer = @qualCast([*:0]u16, app_name_appended.ptr),
};
// Re-use the directory handle but this time we call with the appended app name
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index 244f3a38ce..2300ad044a 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -1763,7 +1763,7 @@ pub const Dir = struct {
var nt_name = w.UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
- .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
+ .Buffer = @qualCast([*:0]u16, sub_path_w),
};
var attr = w.OBJECT_ATTRIBUTES{
.Length = @sizeOf(w.OBJECT_ATTRIBUTES),
diff --git a/lib/std/os.zig b/lib/std/os.zig
index 32463aa30e..3cee30c32d 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -4513,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32
var nt_name = windows.UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
- .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
+ .Buffer = @qualCast([*:0]u16, sub_path_w),
};
var attr = windows.OBJECT_ATTRIBUTES{
.Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig
index e53387b27c..93e762827b 100644
--- a/lib/std/os/windows.zig
+++ b/lib/std/os/windows.zig
@@ -85,7 +85,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
var nt_name = UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
- .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),
+ .Buffer = @qualCast([*]u16, sub_path_w.ptr),
};
var attr = OBJECT_ATTRIBUTES{
.Length = @sizeOf(OBJECT_ATTRIBUTES),
@@ -634,7 +634,7 @@ pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void
var nt_name = UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
- .Buffer = @intToPtr([*]u16, @ptrToInt(path_name.ptr)),
+ .Buffer = @qualCast([*]u16, path_name.ptr),
};
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
var nt_name = UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
- .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),
+ .Buffer = @qualCast([*]u16, sub_path_w.ptr),
};
var attr = OBJECT_ATTRIBUTES{
.Length = @sizeOf(OBJECT_ATTRIBUTES),
@@ -876,7 +876,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
// The Windows API makes this mutable, but it will not mutate here.
- .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),
+ .Buffer = @qualCast([*]u16, sub_path_w.ptr),
};
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
@@ -1414,7 +1414,7 @@ pub fn sendmsg(
}
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 {
- var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @intToPtr([*]u8, @ptrToInt(buf)) };
+ var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @qualCast([*]u8, buf) };
var bytes_send: DWORD = undefined;
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) {
return ws2_32.SOCKET_ERROR;
@@ -1876,13 +1876,13 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool {
const a_string = UNICODE_STRING{
.Length = a_bytes,
.MaximumLength = a_bytes,
- .Buffer = @intToPtr([*]u16, @ptrToInt(a.ptr)),
+ .Buffer = @qualCast([*]u16, a.ptr),
};
const b_bytes = @intCast(u16, b.len * 2);
const b_string = UNICODE_STRING{
.Length = b_bytes,
.MaximumLength = b_bytes,
- .Buffer = @intToPtr([*]u16, @ptrToInt(b.ptr)),
+ .Buffer = @qualCast([*]u16, b.ptr),
};
return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;
}
diff --git a/lib/std/zig/c_translation.zig b/lib/std/zig/c_translation.zig
index a050e592a2..d33c74d777 100644
--- a/lib/std/zig/c_translation.zig
+++ b/lib/std/zig/c_translation.zig
@@ -75,7 +75,7 @@ fn castPtr(comptime DestType: type, target: anytype) DestType {
const source = ptrInfo(@TypeOf(target));
if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile)
- return @intToPtr(DestType, @ptrToInt(target))
+ return @qualCast(DestType, target)
else if (@typeInfo(dest.child) == .Opaque)
// dest.alignment would error out
return @ptrCast(DestType, target)
diff --git a/src/AstGen.zig b/src/AstGen.zig
index df111906e6..a5667ce9e8 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -2530,6 +2530,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.bit_size_of,
.typeof_log2_int_type,
.ptr_to_int,
+ .qual_cast,
.align_of,
.bool_to_int,
.embed_file,
@@ -8037,6 +8038,7 @@ fn builtinCall(
.float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast),
.int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast),
.ptr_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_cast),
+ .qual_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .qual_cast),
.truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate),
// zig fmt: on
diff --git a/src/BuiltinFn.zig b/src/BuiltinFn.zig
index b71d96c3dd..80eb739185 100644
--- a/src/BuiltinFn.zig
+++ b/src/BuiltinFn.zig
@@ -75,6 +75,7 @@ pub const Tag = enum {
prefetch,
ptr_cast,
ptr_to_int,
+ qual_cast,
rem,
return_address,
select,
@@ -675,6 +676,13 @@ pub const list = list: {
},
},
.{
+ "@qualCast",
+ .{
+ .tag = .qual_cast,
+ .param_count = 2,
+ },
+ },
+ .{
"@rem",
.{
.tag = .rem,
diff --git a/src/Sema.zig b/src/Sema.zig
index b4731d9509..d306c68e08 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -1015,6 +1015,7 @@ fn analyzeBodyInner(
.float_cast => try sema.zirFloatCast(block, inst),
.int_cast => try sema.zirIntCast(block, inst),
.ptr_cast => try sema.zirPtrCast(block, inst),
+ .qual_cast => try sema.zirQualCast(block, inst),
.truncate => try sema.zirTruncate(block, inst),
.align_cast => try sema.zirAlignCast(block, inst),
.has_decl => try sema.zirHasDecl(block, inst),
@@ -19529,10 +19530,24 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const operand_info = operand_ty.ptrInfo().data;
const dest_info = dest_ty.ptrInfo().data;
if (!operand_info.mutable and dest_info.mutable) {
- return sema.fail(block, src, "cast discards const qualifier", .{});
+ const msg = msg: {
+ const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{});
+ errdefer msg.destroy(sema.gpa);
+
+ try sema.errNote(block, src, msg, "consider using '@qualCast'", .{});
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
}
if (operand_info.@"volatile" and !dest_info.@"volatile") {
- return sema.fail(block, src, "cast discards volatile qualifier", .{});
+ const msg = msg: {
+ const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{});
+ errdefer msg.destroy(sema.gpa);
+
+ try sema.errNote(block, src, msg, "consider using '@qualCast'", .{});
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
}
if (operand_info.@"addrspace" != dest_info.@"addrspace") {
const msg = msg: {
@@ -19634,6 +19649,49 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
return block.addBitCast(aligned_dest_ty, ptr);
}
+fn zirQualCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src = inst_data.src();
+ const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+ const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
+ const operand = try sema.resolveInst(extra.rhs);
+ const operand_ty = sema.typeOf(operand);
+
+ try sema.checkPtrType(block, dest_ty_src, dest_ty);
+ try sema.checkPtrOperand(block, operand_src, operand_ty);
+
+ var operand_payload = operand_ty.ptrInfo();
+ var dest_info = dest_ty.ptrInfo();
+
+ operand_payload.data.mutable = dest_info.data.mutable;
+ operand_payload.data.@"volatile" = dest_info.data.@"volatile";
+
+ const altered_operand_ty = Type.initPayload(&operand_payload.base);
+ if (!altered_operand_ty.eql(dest_ty, sema.mod)) {
+ const msg = msg: {
+ const msg = try sema.errMsg(block, src, "'@qualCast' can only modify 'const' and 'volatile' qualifiers", .{});
+ errdefer msg.destroy(sema.gpa);
+
+ dest_info.data.mutable = !operand_ty.isConstPtr();
+ dest_info.data.@"volatile" = operand_ty.isVolatilePtr();
+ const altered_dest_ty = Type.initPayload(&dest_info.base);
+ try sema.errNote(block, src, msg, "expected type '{}'", .{altered_dest_ty.fmt(sema.mod)});
+ try sema.errNote(block, src, msg, "got type '{}'", .{operand_ty.fmt(sema.mod)});
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
+ }
+
+ if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
+ return sema.addConstant(dest_ty, operand_val);
+ }
+
+ try sema.requireRuntimeBlock(block, src, null);
+ return block.addBitCast(dest_ty, operand);
+}
+
fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
diff --git a/src/Zir.zig b/src/Zir.zig
index 94e6a9a11a..b93422177e 100644
--- a/src/Zir.zig
+++ b/src/Zir.zig
@@ -857,6 +857,9 @@ pub const Inst = struct {
/// Implements the `@ptrCast` builtin.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
ptr_cast,
+ /// Implements the `@qualCast` builtin.
+ /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
+ qual_cast,
/// Implements the `@truncate` builtin.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
truncate,
@@ -1195,6 +1198,7 @@ pub const Inst = struct {
.float_cast,
.int_cast,
.ptr_cast,
+ .qual_cast,
.truncate,
.align_cast,
.has_field,
@@ -1484,6 +1488,7 @@ pub const Inst = struct {
.float_cast,
.int_cast,
.ptr_cast,
+ .qual_cast,
.truncate,
.align_cast,
.has_field,
@@ -1755,6 +1760,7 @@ pub const Inst = struct {
.float_cast = .pl_node,
.int_cast = .pl_node,
.ptr_cast = .pl_node,
+ .qual_cast = .pl_node,
.truncate = .pl_node,
.align_cast = .pl_node,
.typeof_builtin = .pl_node,
diff --git a/src/print_zir.zig b/src/print_zir.zig
index 6e8923bed9..e5fc8815ed 100644
--- a/src/print_zir.zig
+++ b/src/print_zir.zig
@@ -332,6 +332,7 @@ const Writer = struct {
.float_cast,
.int_cast,
.ptr_cast,
+ .qual_cast,
.truncate,
.align_cast,
.div_exact,
diff --git a/test/cases/compile_errors/invalid_qualcast.zig b/test/cases/compile_errors/invalid_qualcast.zig
new file mode 100644
index 0000000000..20b223b727
--- /dev/null
+++ b/test/cases/compile_errors/invalid_qualcast.zig
@@ -0,0 +1,12 @@
+pub export fn entry() void {
+ var a: [*:0]const volatile u16 = undefined;
+ _ = @qualCast([*]u16, a);
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :3:9: error: '@qualCast' can only modify 'const' and 'volatile' qualifiers
+// :3:9: note: expected type '[*]const volatile u16'
+// :3:9: note: got type '[*:0]const volatile u16'
diff --git a/test/cases/compile_errors/ptrCast_discards_const_qualifier.zig b/test/cases/compile_errors/ptrCast_discards_const_qualifier.zig
index a2fea4ff11..eedef01234 100644
--- a/test/cases/compile_errors/ptrCast_discards_const_qualifier.zig
+++ b/test/cases/compile_errors/ptrCast_discards_const_qualifier.zig
@@ -9,3 +9,4 @@ export fn entry() void {
// target=native
//
// :3:15: error: cast discards const qualifier
+// :3:15: note: consider using '@qualCast'