Commit c1977bf0fb

Ali Cheraghi <alichraghi@proton.me>
2025-03-09 04:11:56
Sema: error on illegal code when targeting spirv
1 parent 2fc409a
src/codegen/spirv.zig
@@ -464,7 +464,7 @@ const NavGen = struct {
 
         const zcu = self.pt.zcu;
         const ty = Type.fromInterned(zcu.intern_pool.typeOf(val));
-        const decl_ptr_ty_id = try self.ptrType(ty, .Generic, .indirect);
+        const decl_ptr_ty_id = try self.ptrType(ty, self.spvStorageClass(.generic), .indirect);
 
         const spv_decl_index = blk: {
             const entry = try self.object.uav_link.getOrPut(self.object.gpa, .{ val, .Function });
@@ -4230,7 +4230,7 @@ const NavGen = struct {
         defer self.gpa.free(ids);
 
         const result_id = self.spv.allocId();
-        if (self.spv.hasFeature(.kernel)) {
+        if (self.spv.hasFeature(.addresses)) {
             try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{
                 .id_result_type = result_ty_id,
                 .id_result = result_id,
@@ -5293,7 +5293,7 @@ const NavGen = struct {
         /// The final storage class of the pointer. This may be either `.Generic` or `.Function`.
         /// In either case, the local is allocated in the `.Function` storage class, and optionally
         /// cast back to `.Generic`.
-        storage_class: StorageClass = .Generic,
+        storage_class: StorageClass,
     };
 
     // Allocate a function-local variable, with possible initializer.
@@ -5333,9 +5333,10 @@ const NavGen = struct {
     fn airAlloc(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
         const zcu = self.pt.zcu;
         const ptr_ty = self.typeOfIndex(inst);
-        assert(ptr_ty.ptrAddressSpace(zcu) == .generic);
         const child_ty = ptr_ty.childType(zcu);
-        return try self.alloc(child_ty, .{});
+        return try self.alloc(child_ty, .{
+            .storage_class = self.spvStorageClass(ptr_ty.ptrAddressSpace(zcu)),
+        });
     }
 
     fn airArg(self: *NavGen) IdRef {
src/Sema.zig
@@ -3648,7 +3648,7 @@ fn indexablePtrLen(
     const object_ty = sema.typeOf(object);
     const is_pointer_to = object_ty.isSinglePointer(zcu);
     const indexable_ty = if (is_pointer_to) object_ty.childType(zcu) else object_ty;
-    try checkIndexable(sema, block, src, indexable_ty);
+    try sema.checkIndexable(block, src, indexable_ty);
     const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "len", .no_embedded_nulls);
     return sema.fieldVal(block, src, object, field_name, src);
 }
@@ -10103,6 +10103,7 @@ fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
     }
     try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), ptr_src);
     try sema.validateRuntimeValue(block, ptr_src, operand);
+    try sema.checkLogicalPtrOperation(block, ptr_src, ptr_ty);
     if (!is_vector or zcu.backendSupportsFeature(.all_vector_instructions)) {
         return block.addBitCast(dest_ty, operand);
     }
@@ -16389,6 +16390,8 @@ fn analyzeArithmetic(
                 };
 
                 try sema.requireRuntimeBlock(block, src, runtime_src);
+                try sema.checkLogicalPtrOperation(block, src, lhs_ty);
+                try sema.checkLogicalPtrOperation(block, src, rhs_ty);
                 const lhs_int = try block.addBitCast(.usize, lhs);
                 const rhs_int = try block.addBitCast(.usize, rhs);
                 const address = try block.addBinOp(.sub_wrap, lhs_int, rhs_int);
@@ -16620,24 +16623,7 @@ fn analyzePtrArithmetic(
     };
 
     try sema.requireRuntimeBlock(block, op_src, runtime_src);
-
-    const target = zcu.getTarget();
-    if (target_util.arePointersLogical(target, ptr_info.flags.address_space)) {
-        return sema.failWithOwnedErrorMsg(block, msg: {
-            const msg = try sema.errMsg(op_src, "illegal pointer arithmetic on pointer of type '{}'", .{ptr_ty.fmt(pt)});
-            errdefer msg.destroy(sema.gpa);
-
-            const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm);
-            try sema.errNote(op_src, msg, "arithmetic cannot be performed on pointers with address space '{s}' on target {s}-{s} by compiler backend {s}", .{
-                @tagName(ptr_info.flags.address_space),
-                target.cpu.arch.genericName(),
-                @tagName(target.os.tag),
-                @tagName(backend),
-            });
-
-            break :msg msg;
-        });
-    }
+    try sema.checkLogicalPtrOperation(block, op_src, ptr_ty);
 
     return block.addInst(.{
         .tag = air_tag,
@@ -22501,6 +22487,7 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
         });
     }
     try sema.requireRuntimeBlock(block, src, operand_src);
+    try sema.checkLogicalPtrOperation(block, src, ptr_ty);
     if (!is_vector or zcu.backendSupportsFeature(.all_vector_instructions)) {
         if (block.wantSafety() and (try elem_ty.hasRuntimeBitsSema(pt) or elem_ty.zigTypeTag(zcu) == .@"fn")) {
             if (!ptr_ty.isAllowzeroPtr(zcu)) {
@@ -23165,8 +23152,9 @@ fn ptrCastFull(
 
     try sema.validateRuntimeValue(block, operand_src, operand);
 
-    const need_null_check = block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu);
-    const need_align_check = block.wantSafety() and dest_align.compare(.gt, src_align);
+    const can_cast_to_int = !target_util.arePointersLogical(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu));
+    const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu);
+    const need_align_check = can_cast_to_int and block.wantSafety() and dest_align.compare(.gt, src_align);
 
     // `operand` might be a slice. If `need_operand_ptr`, we'll populate `operand_ptr` with the raw pointer.
     const need_operand_ptr = src_info.flags.size != .slice or // we already have it
@@ -23832,6 +23820,32 @@ fn checkPtrType(
     return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(pt)});
 }
 
+fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
+    const pt = sema.pt;
+    const zcu = pt.zcu;
+    if (zcu.intern_pool.indexToKey(ty.toIntern()) == .ptr_type) {
+        const target = zcu.getTarget();
+        const as = ty.ptrAddressSpace(zcu);
+        if (target_util.arePointersLogical(target, as)) {
+            return sema.failWithOwnedErrorMsg(block, msg: {
+                const msg = try sema.errMsg(src, "illegal operation on logical pointer of type '{}'", .{ty.fmt(pt)});
+                errdefer msg.destroy(sema.gpa);
+                try sema.errNote(
+                    src,
+                    msg,
+                    "cannot perform arithmetic on pointers with address space '{s}' on target {s}-{s}",
+                    .{
+                        @tagName(as),
+                        target.cpu.arch.genericName(),
+                        @tagName(target.os.tag),
+                    },
+                );
+                break :msg msg;
+            });
+        }
+    }
+}
+
 fn checkVectorElemType(
     sema: *Sema,
     block: *Block,
@@ -28326,7 +28340,7 @@ fn elemPtr(
         .pointer => indexable_ptr_ty.childType(zcu),
         else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(pt)}),
     };
-    try checkIndexable(sema, block, src, indexable_ty);
+    try sema.checkIndexable(block, src, indexable_ty);
 
     const elem_ptr = switch (indexable_ty.zigTypeTag(zcu)) {
         .array, .vector => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
@@ -28362,7 +28376,7 @@ fn elemPtrOneLayerOnly(
     const pt = sema.pt;
     const zcu = pt.zcu;
 
-    try checkIndexable(sema, block, src, indexable_ty);
+    try sema.checkIndexable(block, src, indexable_ty);
 
     switch (indexable_ty.ptrSize(zcu)) {
         .slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
@@ -28376,6 +28390,8 @@ fn elemPtrOneLayerOnly(
                 const elem_ptr = try ptr_val.ptrElem(index, pt);
                 return Air.internedToRef(elem_ptr.toIntern());
             }
+
+            try sema.checkLogicalPtrOperation(block, src, indexable_ty);
             const result_ty = try indexable_ty.elemPtrType(null, pt);
 
             return block.addPtrElemPtr(indexable, elem_index, result_ty);
@@ -28412,7 +28428,7 @@ fn elemVal(
     const pt = sema.pt;
     const zcu = pt.zcu;
 
-    try checkIndexable(sema, block, src, indexable_ty);
+    try sema.checkIndexable(block, src, indexable_ty);
 
     // TODO in case of a vector of pointers, we need to detect whether the element
     // index is a scalar or vector instead of unconditionally casting to usize.
@@ -28438,6 +28454,7 @@ fn elemVal(
                     return Air.internedToRef((try pt.getCoerced(elem_val, elem_ty)).toIntern());
                 }
 
+                try sema.checkLogicalPtrOperation(block, src, indexable_ty);
                 return block.addBinOp(.ptr_elem_val, indexable, elem_index);
             },
             .one => {
@@ -28477,6 +28494,9 @@ fn validateRuntimeElemAccess(
     parent_ty: Type,
     parent_src: LazySrcLoc,
 ) CompileError!void {
+    const pt = sema.pt;
+    const zcu = pt.zcu;
+
     if (try elem_ty.comptimeOnlySema(sema.pt)) {
         const msg = msg: {
             const msg = try sema.errMsg(
@@ -28492,6 +28512,14 @@ fn validateRuntimeElemAccess(
         };
         return sema.failWithOwnedErrorMsg(block, msg);
     }
+
+    if (zcu.intern_pool.indexToKey(parent_ty.toIntern()) == .ptr_type) {
+        const target = zcu.getTarget();
+        const as = parent_ty.ptrAddressSpace(zcu);
+        if (target_util.arePointersLogical(target, as)) {
+            return sema.fail(block, elem_index_src, "cannot access element of logical pointer '{}'", .{parent_ty.fmt(pt)});
+        }
+    }
 }
 
 fn tupleFieldPtr(
@@ -31158,6 +31186,7 @@ fn coerceCompatiblePtrs(
     if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(zcu) and
         (try dest_ty.elemType2(zcu).hasRuntimeBitsSema(pt) or dest_ty.elemType2(zcu).zigTypeTag(zcu) == .@"fn"))
     {
+        try sema.checkLogicalPtrOperation(block, inst_src, inst_ty);
         const actual_ptr = if (inst_ty.isSlice(zcu))
             try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty)
         else
test/behavior/globals.zig
@@ -69,6 +69,8 @@ test "global loads can affect liveness" {
 }
 
 test "global const can be self-referential" {
+    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+
     const S = struct {
         self: *const @This(),
         x: u32,
@@ -113,6 +115,8 @@ test "global var can be self-referential" {
 }
 
 test "global const can be indirectly self-referential" {
+    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+
     const S = struct {
         other: *const @This(),
         x: u32,
test/behavior/ptrfromint.zig
@@ -3,6 +3,8 @@ const builtin = @import("builtin");
 const expectEqual = std.testing.expectEqual;
 
 test "casting integer address to function pointer" {
+    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+
     addressToFunction();
     comptime addressToFunction();
 }
test/behavior/sizeof_and_typeof.zig
@@ -233,6 +233,8 @@ test "@sizeOf comparison against zero" {
 }
 
 test "hardcoded address in typeof expression" {
+    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+
     const S = struct {
         fn func() @TypeOf(@as(*[]u8, @ptrFromInt(0x10)).*[0]) {
             return 0;
test/cases/compile_errors/illegal_operation_on_logical_ptr.zig
@@ -0,0 +1,52 @@
+export fn elemPtr() void {
+    var ptr: [*]u8 = undefined;
+    ptr[0] = 0;
+}
+
+export fn elemVal() void {
+    var ptr: [*]u8 = undefined;
+    var val = ptr[0];
+    _ = &ptr;
+    _ = &val;
+}
+
+export fn intFromPtr() void {
+    var value: u8 = 0;
+    _ = @intFromPtr(&value);
+}
+
+export fn ptrFromInt() void {
+    var v: u32 = 0x1234;
+    var ptr: *u8 = @ptrFromInt(v);
+    _ = &v;
+    _ = &ptr;
+}
+
+export fn ptrPtrArithmetic() void {
+    var value0: u8 = 0;
+    var value1: u8 = 0;
+    _ = &value0 - &value1;
+}
+
+export fn ptrIntArithmetic() void {
+    var ptr0: [*]u8 = undefined;
+    _ = &ptr0;
+    _ = ptr0 - 10;
+}
+
+// error
+// backend=stage2
+// target=spirv64-vulkan
+//
+// :3:8: error: illegal operation on logical pointer of type '[*]u8'
+// :3:8: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan
+// :8:18: error: illegal operation on logical pointer of type '[*]u8'
+// :8:18: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan
+// :15:21: error: illegal operation on logical pointer of type '*u8'
+// :15:21: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan
+// :20:20: error: illegal operation on logical pointer of type '*u8'
+// :20:20: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan
+// :28:17: error: illegal operation on logical pointer of type '*u8'
+// :28:17: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan
+// :34:14: error: illegal operation on logical pointer of type '[*]u8'
+// :34:14: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan