Commit 72ba7f7e98

mlugg <mlugg@mlugg.co.uk>
2024-12-15 12:08:07
Sema: disallow runtime stores to pointers with comptime-only element types
1 parent b5d3db5
Changed files (2)
src/Sema.zig
@@ -3628,6 +3628,10 @@ fn zirAllocExtended(
         }
     }
 
+    if (small.has_type and try var_ty.comptimeOnlySema(pt)) {
+        return sema.analyzeComptimeAlloc(block, var_ty, alignment);
+    }
+
     if (small.has_type) {
         if (!small.is_const) {
             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
     const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
 
     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
-    if (block.is_comptime) {
+    if (block.is_comptime or try var_ty.comptimeOnlySema(pt)) {
         return sema.analyzeComptimeAlloc(block, var_ty, .none);
     }
     if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) {
@@ -31938,6 +31942,17 @@ fn storePtr2(
         } else break :rs ptr_src;
     } else ptr_src;
 
+    // We're performing the store at runtime; as such, we need to make sure the pointee type
+    // is not comptime-only. We can hit this case with a `@ptrFromInt` pointer.
+    if (try elem_ty.comptimeOnlySema(pt)) {
+        return sema.failWithOwnedErrorMsg(block, msg: {
+            const msg = try sema.errMsg(src, "cannot store comptime-only type '{}' at runtime", .{elem_ty.fmt(pt)});
+            errdefer msg.destroy(sema.gpa);
+            try sema.errNote(ptr_src, msg, "operation is runtime due to this pointer", .{});
+            break :msg msg;
+        });
+    }
+
     // We do this after the possible comptime store above, for the case of field_ptr stores
     // to unions because we want the comptime tag to be set, even if the field type is void.
     if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) {
test/cases/compile_errors/store_comptime_only_type_to_runtime_pointer.zig
@@ -0,0 +1,42 @@
+export fn a() void {
+    const p: *fn () void = @ptrFromInt(4);
+    p.* = undefined;
+}
+
+export fn b(p: *anyopaque) void {
+    p.* = undefined;
+}
+
+export fn c(p: *anyopaque, q: *anyopaque) void {
+    p.* = q.*;
+}
+
+const Opaque = opaque {};
+export fn d(p: *Opaque) void {
+    p.* = undefined;
+}
+
+export fn e() void {
+    const p: *comptime_int = @ptrFromInt(16);
+    p.* = undefined;
+}
+
+export fn f() void {
+    const p: **comptime_int = @ptrFromInt(16); // double pointer ('*comptime_int' is comptime-only)
+    p.* = undefined;
+}
+
+// error
+//
+// :3:9: error: cannot store comptime-only type 'fn () void' at runtime
+// :3:6: note: operation is runtime due to this pointer
+// :7:11: error: expected type 'anyopaque', found '@TypeOf(undefined)'
+// :7:11: note: cannot coerce to 'anyopaque'
+// :11:12: error: cannot load opaque type 'anyopaque'
+// :16:11: error: expected type 'tmp.Opaque', found '@TypeOf(undefined)'
+// :16:11: note: cannot coerce to 'tmp.Opaque'
+// :14:16: note: opaque declared here
+// :21:9: error: cannot store comptime-only type 'comptime_int' at runtime
+// :21:6: note: operation is runtime due to this pointer
+// :26:9: error: cannot store comptime-only type '*comptime_int' at runtime
+// :26:6: note: operation is runtime due to this pointer