authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-12-15 16:04:12+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-12-15 16:04:12+00:00
log12f0c383475df1d0bd0e1a7aee58090e2cb23eb7
tree7ad2a593be813469b9ac72f8542acb00625d6273
parentaf89bb05d392b34a9ac257d166df1c794542f2e8
parent72ba7f7e98b8cd00787e2903641e752e15264522
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #22239 from mlugg/runtime-store-comptime-only

Sema: disallow runtime stores to pointers with comptime-only element types Resolves: #22175

2 files changed, 74 insertions(+), 6 deletions(-)

src/Sema.zig+32-6
......@@ -3628,6 +3628,10 @@ fn zirAllocExtended(
36283628 }
36293629 }
36303630
3631 if (small.has_type and try var_ty.comptimeOnlySema(pt)) {
3632 return sema.analyzeComptimeAlloc(block, var_ty, alignment);
3633 }
3634
36313635 if (small.has_type) {
36323636 if (!small.is_const) {
36333637 try sema.validateVarType(block, ty_src, var_ty, false);
......@@ -4075,7 +4079,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
40754079 const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
40764080
40774081 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
4078 if (block.is_comptime) {
4082 if (block.is_comptime or try var_ty.comptimeOnlySema(pt)) {
40794083 return sema.analyzeComptimeAlloc(block, var_ty, .none);
40804084 }
40814085 if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) {
......@@ -30675,6 +30679,18 @@ fn coerceExtra(
3067530679 else => {},
3067630680 }
3067730681
30682 const can_coerce_to = switch (dest_ty.zigTypeTag(zcu)) {
30683 .noreturn, .@"opaque" => false,
30684 else => true,
30685 };
30686
30687 if (can_coerce_to) {
30688 // undefined to anything. We do this after the big switch above so that
30689 // special logic has a chance to run first, such as `*[N]T` to `[]T` which
30690 // should initialize the length field of the slice.
30691 if (maybe_inst_val) |val| if (val.toIntern() == .undef) return pt.undefRef(dest_ty);
30692 }
30693
3067830694 if (!opts.report_err) return error.NotCoercible;
3067930695
3068030696 if (opts.is_ret and dest_ty.zigTypeTag(zcu) == .noreturn) {
......@@ -30692,15 +30708,14 @@ fn coerceExtra(
3069230708 return sema.failWithOwnedErrorMsg(block, msg);
3069330709 }
3069430710
30695 // undefined to anything. We do this after the big switch above so that
30696 // special logic has a chance to run first, such as `*[N]T` to `[]T` which
30697 // should initialize the length field of the slice.
30698 if (maybe_inst_val) |val| if (val.toIntern() == .undef) return pt.undefRef(dest_ty);
30699
3070030711 const msg = msg: {
3070130712 const msg = try sema.errMsg(inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(pt), inst_ty.fmt(pt) });
3070230713 errdefer msg.destroy(sema.gpa);
3070330714
30715 if (!can_coerce_to) {
30716 try sema.errNote(inst_src, msg, "cannot coerce to '{}'", .{dest_ty.fmt(pt)});
30717 }
30718
3070430719 // E!T to T
3070530720 if (inst_ty.zigTypeTag(zcu) == .error_union and
3070630721 (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok)
......@@ -31927,6 +31942,17 @@ fn storePtr2(
3192731942 } else break :rs ptr_src;
3192831943 } else ptr_src;
3192931944
31945 // We're performing the store at runtime; as such, we need to make sure the pointee type
31946 // is not comptime-only. We can hit this case with a `@ptrFromInt` pointer.
31947 if (try elem_ty.comptimeOnlySema(pt)) {
31948 return sema.failWithOwnedErrorMsg(block, msg: {
31949 const msg = try sema.errMsg(src, "cannot store comptime-only type '{}' at runtime", .{elem_ty.fmt(pt)});
31950 errdefer msg.destroy(sema.gpa);
31951 try sema.errNote(ptr_src, msg, "operation is runtime due to this pointer", .{});
31952 break :msg msg;
31953 });
31954 }
31955
3193031956 // We do this after the possible comptime store above, for the case of field_ptr stores
3193131957 // to unions because we want the comptime tag to be set, even if the field type is void.
3193231958 if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) {
test/cases/compile_errors/store_comptime_only_type_to_runtime_pointer.zig created+42
......@@ -0,0 +1,42 @@
1export fn a() void {
2 const p: *fn () void = @ptrFromInt(4);
3 p.* = undefined;
4}
5
6export fn b(p: *anyopaque) void {
7 p.* = undefined;
8}
9
10export fn c(p: *anyopaque, q: *anyopaque) void {
11 p.* = q.*;
12}
13
14const Opaque = opaque {};
15export fn d(p: *Opaque) void {
16 p.* = undefined;
17}
18
19export fn e() void {
20 const p: *comptime_int = @ptrFromInt(16);
21 p.* = undefined;
22}
23
24export fn f() void {
25 const p: **comptime_int = @ptrFromInt(16); // double pointer ('*comptime_int' is comptime-only)
26 p.* = undefined;
27}
28
29// error
30//
31// :3:9: error: cannot store comptime-only type 'fn () void' at runtime
32// :3:6: note: operation is runtime due to this pointer
33// :7:11: error: expected type 'anyopaque', found '@TypeOf(undefined)'
34// :7:11: note: cannot coerce to 'anyopaque'
35// :11:12: error: cannot load opaque type 'anyopaque'
36// :16:11: error: expected type 'tmp.Opaque', found '@TypeOf(undefined)'
37// :16:11: note: cannot coerce to 'tmp.Opaque'
38// :14:16: note: opaque declared here
39// :21:9: error: cannot store comptime-only type 'comptime_int' at runtime
40// :21:6: note: operation is runtime due to this pointer
41// :26:9: error: cannot store comptime-only type '*comptime_int' at runtime
42// :26:6: note: operation is runtime due to this pointer