From c1977bf0fbe523afb4721bc8346ee6536e3c0aa2 Mon Sep 17 00:00:00 2001 From: Ali Cheraghi Date: Sun, 9 Mar 2025 06:41:56 +0330 Subject: [PATCH] Sema: error on illegal code when targeting spirv --- src/Sema.zig | 77 +++++++++++++------ src/codegen/spirv.zig | 11 +-- test/behavior/globals.zig | 4 + test/behavior/ptrfromint.zig | 2 + test/behavior/sizeof_and_typeof.zig | 2 + .../illegal_operation_on_logical_ptr.zig | 52 +++++++++++++ 6 files changed, 119 insertions(+), 29 deletions(-) create mode 100644 test/cases/compile_errors/illegal_operation_on_logical_ptr.zig diff --git a/src/Sema.zig b/src/Sema.zig index cd1711c8b7..99e23e6f5a 100644 --- a/src/Sema.zig +++ b/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 diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index aeede443bf..3a00a05823 100644 --- a/src/codegen/spirv.zig +++ b/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 { diff --git a/test/behavior/globals.zig b/test/behavior/globals.zig index c11fa7cb25..1a07eea95f 100644 --- a/test/behavior/globals.zig +++ b/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, diff --git a/test/behavior/ptrfromint.zig b/test/behavior/ptrfromint.zig index 73ecefddb2..564a3c9614 100644 --- a/test/behavior/ptrfromint.zig +++ b/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(); } diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 82fae00a99..099dfd41e6 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/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; diff --git a/test/cases/compile_errors/illegal_operation_on_logical_ptr.zig b/test/cases/compile_errors/illegal_operation_on_logical_ptr.zig new file mode 100644 index 0000000000..d75e7dbd05 --- /dev/null +++ b/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