From 21575a44837861c0c9a7b2b42904afb0933995ea Mon Sep 17 00:00:00 2001 From: Ali Cheraghi Date: Tue, 25 Aug 2026 21:55:19 +0330 Subject: [PATCH 1/5] spirv: validate extern var address space --- src/Sema.zig | 38 +++++++++----- src/Sema/type_resolution.zig | 11 +++++ src/Type.zig | 3 +- ...bedding_spirv_type_in_struct_and_union.zig | 19 ++++--- ..._storage_buffer_must_be_single_pointer.zig | 25 ---------- .../spirv_extern_runtime_descriptor_array.zig | 49 +++++++++++++++++++ .../spirv_extern_var_addrspace.zig | 46 +++++++++++++++-- .../spirv_runtime_array_as_value_type.zig | 26 ---------- 8 files changed, 143 insertions(+), 74 deletions(-) delete mode 100644 test/cases/compile_errors/extern_spirv_storage_buffer_must_be_single_pointer.zig create mode 100644 test/cases/compile_errors/spirv_extern_runtime_descriptor_array.zig delete mode 100644 test/cases/compile_errors/spirv_runtime_array_as_value_type.zig diff --git a/src/Sema.zig b/src/Sema.zig index 33746cd9817b99ed88cf68090239901706533e8c..48756b0607c2041ca10d35f95b3a60782435e0ca 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -25285,17 +25285,30 @@ fn zirBuiltinExtern( .location, .descriptor => {}, }; - switch (zcu.getTarget().os.tag) { - .vulkan, .opengl => switch (ptr_info.flags.address_space) { - .storage_buffer, .uniform, .push_constant => if (ptr_info.flags.size != .one) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(ty_src, "extern in '{s}' address space must be a single-item pointer to a struct", .{@tagName(ptr_info.flags.address_space)}); - errdefer msg.destroy(sema.gpa); - try sema.errNote(ty_src, msg, "wrap the element type in a struct containing a runtime-sized array", .{}); - break :msg msg; - }); - }, - else => {}, + const target = zcu.getTarget(); + switch (target.os.tag) { + .vulkan, .opengl => { + const pointee = switch (elem_ty.zigTypeTag(zcu)) { + .array => elem_ty.childType(zcu), + .spirv => if (elem_ty.isSpirvRuntimeArray(zcu)) elem_ty.childType(zcu) else elem_ty, + else => elem_ty, + }; + switch (ptr_info.flags.address_space) { + .uniform, + .storage_buffer, + => if (ptr_info.flags.size != .one or pointee.zigTypeTag(zcu) != .@"struct") { + return sema.fail(block, ty_src, "extern in '{t}' address space must be a single-item pointer to a struct", .{ptr_info.flags.address_space}); + }, + .push_constant => if (ptr_info.flags.size != .one or elem_ty.zigTypeTag(zcu) != .@"struct") { + return sema.fail(block, ty_src, "extern in 'push_constant' address space must be a single-item pointer to a struct", .{}); + }, + .constant => if (target.os.tag == .vulkan and (pointee.zigTypeTag(zcu) != .spirv or pointee.isSpirvRuntimeArray(zcu))) { + return sema.fail(block, ty_src, "extern in 'constant' address space must point to an opaque SPIR-V type, or to an array of one", .{}); + }, + else => if (elem_ty.isSpirvRuntimeArray(zcu)) { + return sema.fail(block, ty_src, "SPIR-V runtime array is not allowed in the '{t}' address space", .{ptr_info.flags.address_space}); + }, + } }, else => {}, } @@ -25717,6 +25730,9 @@ pub fn explainWhyTypeIsNotExtern( .spirv => { assert(ty.isSpirvRuntimeArray(zcu)); try sema.errNote(src_loc, msg, "SPIR-V runtime arrays must be the last field of an extern struct", .{}); + if (position == .other) { + try sema.errNote(src_loc, msg, "consider enabling the 'runtime_descriptor_array' feature to use the runtime array as the extern pointee", .{}); + } }, .float => try sema.errNote(src_loc, msg, "'{f}' is not extern compatible on this target", .{ty.fmt(pt)}), diff --git a/src/Sema/type_resolution.zig b/src/Sema/type_resolution.zig index 1e089955e59e2efcbd3b40185cab8289460c956f..66f4aa63bb6a27fb96d24b0fcc702800b7d55bbf 100644 --- a/src/Sema/type_resolution.zig +++ b/src/Sema/type_resolution.zig @@ -334,6 +334,17 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { break :msg msg; }); } + + const elem_ty: Type = field_ty.childType(zcu); + if (elem_ty.zigTypeTag(zcu) == .spirv) { + return sema.failWithOwnedErrorMsg(&block, msg: { + const msg = try sema.errMsg(field_ty_src, "cannot embed SPIR-V type '{f}' in struct", .{elem_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{}); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } } else { return sema.failWithOwnedErrorMsg(&block, msg: { const msg = try sema.errMsg(field_ty_src, "cannot directly embed SPIR-V type '{f}' in struct", .{field_ty.fmt(pt)}); diff --git a/src/Type.zig b/src/Type.zig index 3774049cc616c35c02f2e32b17ef3c5a99c743ad..f455bfa8ddee03df753592c9303d1ed0fb5a5df2 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -3145,7 +3145,8 @@ pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool .spirv => switch (position) { .struct_field, .union_field => true, - .ret_ty, .param_ty, .element, .other => !ty.isSpirvRuntimeArray(zcu), + .ret_ty, .param_ty, .element => !ty.isSpirvRuntimeArray(zcu), + .other => !ty.isSpirvRuntimeArray(zcu) or zcu.getTarget().cpu.has(.spirv, .runtime_descriptor_array), }, .pointer => { diff --git a/test/cases/compile_errors/directly_embedding_spirv_type_in_struct_and_union.zig b/test/cases/compile_errors/directly_embedding_spirv_type_in_struct_and_union.zig index 732db0b4408883a1e0aaacf019bb6c459d62205f..369cd99f891eecc1dff6ae3f15182a6d3edf3ae9 100644 --- a/test/cases/compile_errors/directly_embedding_spirv_type_in_struct_and_union.zig +++ b/test/cases/compile_errors/directly_embedding_spirv_type_in_struct_and_union.zig @@ -3,22 +3,27 @@ const RuntimeArray = @SpirvType(.{ .runtime_array = u32 }); const Foo = struct { s: Sampler, }; -const Baz = struct { +const Bar = struct { a: RuntimeArray, }; -const Qux = extern struct { +const Baz = extern struct { a: RuntimeArray, b: u32, }; +const Qux = extern struct { _: @SpirvType(.{ .runtime_array = Sampler }) }; export fn a() void { var foo: Foo = undefined; _ = &foo; } export fn c() void { + var bar: Bar = undefined; + _ = &bar; +} +export fn d() void { var baz: Baz = undefined; _ = &baz; } -export fn d() void { +export fn e() void { var qux: Qux = undefined; _ = &qux; } @@ -27,9 +32,11 @@ export fn d() void { // backend=selfhosted // target=spirv32-vulkan // -// :4:8: error: cannot directly embed SPIR-V type 'tmp.Sampler__SpirvType_4' in struct +// :4:8: error: cannot directly embed SPIR-V type '@SpirvType(.sampler)' in struct // :4:8: note: opaque types have unknown size -// :6:13: error: non-extern struct cannot contain fields of type 'tmp.RuntimeArray__SpirvType_11' +// :6:13: error: non-extern struct cannot contain fields of type '@SpirvType(.runtime_array, u32)' // :7:5: note: while checking this field -// :9:20: error: struct field of type 'tmp.RuntimeArray__SpirvType_11' must be the last field +// :9:20: error: struct field of type '@SpirvType(.runtime_array, u32)' must be the last field // :10:5: note: while checking this field +// :13:32: error: cannot embed SPIR-V type '@SpirvType(.sampler)' in struct +// :13:32: note: opaque types have unknown size diff --git a/test/cases/compile_errors/extern_spirv_storage_buffer_must_be_single_pointer.zig b/test/cases/compile_errors/extern_spirv_storage_buffer_must_be_single_pointer.zig deleted file mode 100644 index 3b8ac3c8338afff5ca93773f973d399fd6c51d1b..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/extern_spirv_storage_buffer_must_be_single_pointer.zig +++ /dev/null @@ -1,25 +0,0 @@ -const a = @extern([*]addrspace(.storage_buffer) u32, .{ - .name = "a", - .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, -}); -const b = @extern([]addrspace(.uniform) u32, .{ - .name = "b", - .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } }, -}); -const c = @extern([*c]addrspace(.push_constant) u32, .{ .name = "c" }); -comptime { - _ = a; - _ = b; - _ = c; -} - -// error -// backend=selfhosted -// target=spirv32-vulkan -// -// :1:19: error: extern in 'storage_buffer' address space must be a single-item pointer to a struct -// :1:19: note: wrap the element type in a struct containing a runtime-sized array -// :5:19: error: extern in 'uniform' address space must be a single-item pointer to a struct -// :5:19: note: wrap the element type in a struct containing a runtime-sized array -// :9:19: error: extern in 'push_constant' address space must be a single-item pointer to a struct -// :9:19: note: wrap the element type in a struct containing a runtime-sized array diff --git a/test/cases/compile_errors/spirv_extern_runtime_descriptor_array.zig b/test/cases/compile_errors/spirv_extern_runtime_descriptor_array.zig new file mode 100644 index 0000000000000000000000000000000000000000..87c0087d9a668c246079ecba42c7dc3516cd48f1 --- /dev/null +++ b/test/cases/compile_errors/spirv_extern_runtime_descriptor_array.zig @@ -0,0 +1,49 @@ +const Buffer = extern struct { value: u32 }; +const Sampler = @SpirvType(.sampler); +const RuntimeArray = @SpirvType(.{ .runtime_array = u32 }); +const SamplerRuntimeArray = @SpirvType(.{ .runtime_array = Sampler }); +const BufferRuntimeArray = @SpirvType(.{ .runtime_array = Buffer }); + +const a = @extern(*addrspace(.input) const RuntimeArray, .{ .name = "a" }); +const b = @extern(*addrspace(.uniform) const RuntimeArray, .{ .name = "b" }); +const c = @extern(*addrspace(.storage_buffer) const RuntimeArray, .{ .name = "c" }); +const d = @extern(*addrspace(.constant) const RuntimeArray, .{ .name = "d" }); +const e = @extern(*addrspace(.constant) const SamplerRuntimeArray, .{ + .name = "samplers", + .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, +}); +const f = @extern(*addrspace(.storage_buffer) [4]Buffer, .{ + .name = "sized_buffers", + .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } }, +}); +const g = @extern(*addrspace(.storage_buffer) BufferRuntimeArray, .{ + .name = "buffers", + .decoration = .{ .descriptor = .{ .set = 0, .binding = 2 } }, +}); + +comptime { + _ = a; +} +comptime { + _ = b; +} +comptime { + _ = c; +} + +export fn main() callconv(.{ .spirv_fragment = .{} }) void { + _ = &d[0]; + _ = &e[0]; + f[0].value = 1; + g[0].value = 2; +} + +// error +// backend=selfhosted +// target=spirv32-vulkan +// cpu_features=baseline+runtime_descriptor_array +// +// :7:19: error: SPIR-V runtime array is not allowed in the 'input' address space +// :8:19: error: extern in 'uniform' address space must be a single-item pointer to a struct +// :9:19: error: extern in 'storage_buffer' address space must be a single-item pointer to a struct +// :10:19: error: extern in 'constant' address space must point to an opaque SPIR-V type, or to an array of one diff --git a/test/cases/compile_errors/spirv_extern_var_addrspace.zig b/test/cases/compile_errors/spirv_extern_var_addrspace.zig index b750eda17fda0f48874a05697ca37b0570d66995..c83bfdcf94b0f90394c06867eb696841ccbb1ea5 100644 --- a/test/cases/compile_errors/spirv_extern_var_addrspace.zig +++ b/test/cases/compile_errors/spirv_extern_var_addrspace.zig @@ -1,11 +1,47 @@ -extern var x: u32; +const Block = extern struct { x: u32 }; +const RuntimeArray = @SpirvType(.{ .runtime_array = u32 }); -export fn main() callconv(.kernel) void { - _ = x; +extern var implicit_addrspace: u32; + +const many = @extern([*]addrspace(.storage_buffer) u32, .{ + .name = "many", + .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, +}); +const push_array = @extern(*addrspace(.push_constant) const [4]Block, .{ .name = "push_array" }); +const plain_constant = @extern(*addrspace(.constant) const Block, .{ + .name = "plain_constant", + .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } }, +}); +const runtime_array = @extern(*addrspace(.storage_buffer) RuntimeArray, .{ + .name = "runtime_array", + .decoration = .{ .descriptor = .{ .set = 0, .binding = 2 } }, +}); + +comptime { + _ = implicit_addrspace; +} +comptime { + _ = many; +} +comptime { + _ = push_array; +} +comptime { + _ = plain_constant; +} +comptime { + _ = runtime_array; } // error // backend=selfhosted -// target=spirv64-vulkan +// target=spirv32-vulkan // -// :1:15: error: SPIR-V extern variables require an explicit address space +// :4:32: error: SPIR-V extern variables require an explicit address space +// :6:22: error: extern in 'storage_buffer' address space must be a single-item pointer to a struct +// :10:28: error: extern in 'push_constant' address space must be a single-item pointer to a struct +// :11:32: error: extern in 'constant' address space must point to an opaque SPIR-V type, or to an array of one +// :15:31: error: extern symbol cannot have type '*addrspace(.storage_buffer) @SpirvType(.runtime_array, u32)' +// :15:31: note: pointer element type '@SpirvType(.runtime_array, u32)' is not extern compatible +// :15:31: note: SPIR-V runtime arrays must be the last field of an extern struct +// :15:31: note: consider enabling the 'runtime_descriptor_array' feature to use the runtime array as the extern pointee diff --git a/test/cases/compile_errors/spirv_runtime_array_as_value_type.zig b/test/cases/compile_errors/spirv_runtime_array_as_value_type.zig deleted file mode 100644 index 634837d3cf86d1b8b9ed8d3288d4ee1f099ebe20..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/spirv_runtime_array_as_value_type.zig +++ /dev/null @@ -1,26 +0,0 @@ -const RuntimeArray = @SpirvType(.{ .runtime_array = u32 }); - -const a = @extern(*addrspace(.storage_buffer) RuntimeArray, .{ - .name = "a", - .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, -}); -const b = @extern(*addrspace(.uniform) const RuntimeArray, .{ - .name = "b", - .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } }, -}); - -comptime { - _ = a; - _ = b; -} - -// error -// backend=selfhosted -// target=spirv32-vulkan -// -// :3:19: error: extern symbol cannot have type '*addrspace(.storage_buffer) @SpirvType(.runtime_array, u32)' -// :3:19: note: pointer element type '@SpirvType(.runtime_array, u32)' is not extern compatible -// :3:19: note: SPIR-V runtime arrays must be the last field of an extern struct -// :7:19: error: extern symbol cannot have type '*addrspace(.uniform) const @SpirvType(.runtime_array, u32)' -// :7:19: note: pointer element type '@SpirvType(.runtime_array, u32)' is not extern compatible -// :7:19: note: SPIR-V runtime arrays must be the last field of an extern struct -- 2.54.0 From 591b2a47f79fa07e87d3b625320135de8446a274 Mon Sep 17 00:00:00 2001 From: Ali Cheraghi Date: Tue, 25 Aug 2026 21:55:19 +0330 Subject: [PATCH 2/5] spirv: emit descriptor arrays for uniform and storage buffers --- src/codegen/spirv/CodeGen.zig | 38 +++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig index 67bd36cfa6a3b298b05ca8df76050487af984df5..42f65121aba5309348a701ea7454008d85385a62 100644 --- a/src/codegen/spirv/CodeGen.zig +++ b/src/codegen/spirv/CodeGen.zig @@ -617,7 +617,8 @@ pub fn structType( /// Returns the layout-decorated variant of `ty` for use inside a Vulkan/OpenGL /// interface block. Vulkan forbids nested Block decorations, so recursive calls -/// always pass `false`. +/// pass `false`, except through an array, whose elements are each a +/// block of their own. /// /// This is distinct from `resolveType` because SPIR-V forbids such decorations /// on the pointee of a Function-scope variable. @@ -695,26 +696,30 @@ pub fn layoutType(cg: *CodeGen, ty: Type, is_block_root: bool) Error!Id { }, .array => id: { const elem_ty = ty.childType(zcu); - const elem_ty_id = try cg.layoutType(elem_ty, false); + const elem_ty_id = try cg.layoutType(elem_ty, is_block_root); const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel(zcu)) orelse return cg.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel(zcu)}); const id = try cg.arrayType(try cg.constInt(.u32, total_len), elem_ty_id); - if (elem_ty.hasRuntimeBits(zcu)) try cg.decorate(id, .{ - .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) }, - }); + if (!is_block_root and elem_ty.hasRuntimeBits(zcu)) { + try cg.decorate(id, .{ + .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) }, + }); + } break :id id; }, .spirv => if (ty.isSpirvRuntimeArray(zcu)) id: { const elem_ty = ty.childType(zcu); - const elem_ty_id = try cg.layoutType(elem_ty, false); + const elem_ty_id = try cg.layoutType(elem_ty, is_block_root); const id = cg.allocId(); try cg.sections.globals.emit(gpa, .OpTypeRuntimeArray, .{ .id_result = id, .element_type = elem_ty_id, }); - if (elem_ty.hasRuntimeBits(zcu)) try cg.decorate(id, .{ - .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) }, - }); + if (!is_block_root and elem_ty.hasRuntimeBits(zcu)) { + try cg.decorate(id, .{ + .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) }, + }); + } break :id id; } else return cg.resolveType(ty, .indirect), else => return cg.resolveType(ty, .indirect), @@ -955,11 +960,18 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { switch (target.os.tag) { .vulkan, .opengl => { switch (storage_class) { - .uniform, .push_constant, .storage_buffer, .physical_storage_buffer => { + .uniform, + .push_constant, + .storage_buffer, + .physical_storage_buffer, + => { if (ty.hasRuntimeBits(zcu)) { - try cg.decorate(ptr_ty_id, .{ - .array_stride = .{ .array_stride = @intCast(ty.abiSize(zcu)) }, - }); + if (!ty.isSpirvRuntimeArray(zcu)) { + try cg.decorate( + ptr_ty_id, + .{ .array_stride = .{ .array_stride = @intCast(ty.abiSize(zcu)) } }, + ); + } if (!cg.needsLayout(as, ty)) try cg.decorateLayout(ty, ty_id); } if (key.is_const and storage_class == .storage_buffer) { -- 2.54.0 From e8e47b3428845a176eb5ee1c373063102ab7f6e2 Mon Sep 17 00:00:00 2001 From: Ali Cheraghi Date: Tue, 25 Aug 2026 21:55:19 +0330 Subject: [PATCH 3/5] spirv: reject unrepresentable pointer casts and operations --- src/Sema.zig | 86 +++++++++++-------- src/codegen/spirv/CodeGen.zig | 31 ++++++- test/behavior/spirv.zig | 11 ++- .../illegal_operation_on_logical_ptr.zig | 44 +++++++++- ...pirv_pointer_cast_requires_offset_zero.zig | 19 ++-- 5 files changed, 131 insertions(+), 60 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 48756b0607c2041ca10d35f95b3a60782435e0ca..63bee88f76f40e607532964b911b6ec2030f6e0f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22151,34 +22151,7 @@ fn ptrCastFull( } try sema.validateRuntimeValue(block, operand_src, operand); - - if (zcu.getTarget().cpu.arch.isSpirV() and - src_info.flags.address_space != .physical_storage_buffer and - src_info.flags.address_space == dest_info.flags.address_space and - src_info.child != dest_info.child and - Type.fromInterned(dest_info.child).hasRuntimeBits(zcu)) - { - var cur: Type = .fromInterned(src_info.child); - while (cur.toIntern() != dest_info.child) { - cur = switch (cur.zigTypeTag(zcu)) { - .array, .vector => cur.childType(zcu), - .@"struct" => if (cur.structFieldOffset(0, zcu) == 0) cur.fieldType(0, zcu) else null, - else => null, - } orelse return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "cannot cast pointer '{f}' to '{f}'", .{ - operand_ty.fmt(pt), dest_ty.fmt(pt), - }); - errdefer msg.destroy(sema.gpa); - try sema.errNote(src, msg, "'{f}' must appear at offset 0 inside '{f}'", .{ - Type.fromInterned(dest_info.child).fmt(pt), Type.fromInterned(src_info.child).fmt(pt), - }); - try sema.errNote(src, msg, "'{s}' pointers can only reach nested types through a first struct field or an array element", .{ - @tagName(src_info.flags.address_space), - }); - break :msg msg; - }); - } - } + try sema.checkLogicalPtrCast(block, src, operand_ty, dest_ty); const can_cast_to_int = !target_util.shouldBlockPointerOps(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); @@ -22721,6 +22694,8 @@ fn checkPtrType( fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { const pt = sema.pt; const zcu = pt.zcu; + + if (block.isComptime() or block.is_typeof) return; if (zcu.intern_pool.indexToKey(ty.toIntern()) == .ptr_type) { const target = zcu.getTarget(); const as = ty.ptrAddressSpace(zcu); @@ -22731,12 +22706,8 @@ fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ try sema.errNote( src, msg, - "cannot perform arithmetic on pointers with address space '{s}' on target {s}-{s}", - .{ - @tagName(as), - @tagName(target.cpu.arch.family()), - @tagName(target.os.tag), - }, + "pointers with address space '{t}' do not support arithmetic or indexing on target {t}-{t}", + .{ as, target.cpu.arch.family(), target.os.tag }, ); break :msg msg; }); @@ -22744,6 +22715,49 @@ fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ } } +fn checkLogicalPtrCast( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + operand_ty: Type, + dest_ty: Type, +) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const src_info = operand_ty.ptrInfo(zcu); + const dest_info = dest_ty.ptrInfo(zcu); + + if (block.isComptime() or block.is_typeof) return; + switch (zcu.getTarget().os.tag) { + .vulkan, .opengl => {}, + else => return, + } + if (src_info.flags.address_space == .physical_storage_buffer) return; + + var cur: Type = .fromInterned(src_info.child); + while (cur.toIntern() != dest_info.child) { + cur = switch (cur.zigTypeTag(zcu)) { + .array, .vector => cur.childType(zcu), + .@"struct" => field: { + for (0..cur.structFieldCount(zcu)) |i| { + const field_ty = cur.fieldType(i, zcu); + if (field_ty.hasRuntimeBits(zcu) and cur.structFieldOffset(i, zcu) == 0) break :field field_ty; + } + break :field null; + }, + else => null, + } orelse return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(src, "cannot cast pointer '{f}' to '{f}'", .{ operand_ty.fmt(pt), dest_ty.fmt(pt) }); + errdefer msg.destroy(sema.gpa); + try sema.errNote(src, msg, "'{f}' must appear at offset 0 inside '{f}'", .{ + Type.fromInterned(dest_info.child).fmt(pt), + Type.fromInterned(src_info.child).fmt(pt), + }); + break :msg msg; + }); + } +} + fn checkVectorElemType( sema: *Sema, block: *Block, @@ -27367,6 +27381,7 @@ fn elemPtrOneLayerOnly( try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_src); try sema.validateRuntimeValue(block, indexable_src, indexable); + try sema.checkLogicalPtrOperation(block, src, indexable_ty); if (child_ty.abiSize(zcu) == 0) { // zero-bit child type; just bitcast the pointer @@ -27439,6 +27454,7 @@ fn elemVal( }, .partially_comptime, .fully_comptime => unreachable, // caught by `validateRuntimeElemAccess` } + try sema.checkLogicalPtrOperation(block, src, indexable_ty); return block.addBinOp(.ptr_elem_val, indexable, elem_index); }, @@ -27840,6 +27856,7 @@ fn elemValSlice( const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); } + try sema.checkLogicalPtrOperation(block, src, slice_ty); return block.addBinOp(.slice_elem_val, slice, elem_index); } @@ -27891,6 +27908,7 @@ fn elemPtrSlice( try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_src); try sema.validateRuntimeValue(block, slice_src, slice); + try sema.checkLogicalPtrOperation(block, src, slice_ty); if (oob_safety and block.wantSafety()) { const len_inst = len: { diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig index 42f65121aba5309348a701ea7454008d85385a62..574284d5cdd0991210c2f58697805ffe62e1ca1f 100644 --- a/src/codegen/spirv/CodeGen.zig +++ b/src/codegen/spirv/CodeGen.zig @@ -2059,6 +2059,13 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { while (cur.toIntern() != dst_child.toIntern()) { switch (cur.zigTypeTag(zcu)) { .array => { + if (dst_child.zigTypeTag(zcu) == .array and + dst_child.childType(zcu).toIntern() == cur.childType(zcu).toIntern() and + dst_child.arrayLenIncludingSentinel(zcu) <= cur.arrayLenIncludingSentinel(zcu)) + { + cur = dst_child; + break; + } cur = cur.childType(zcu); depth += 1; }, @@ -2098,7 +2105,7 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { } } - return cg.fail("cannot perform pointer cast: '{f}' to '{f}'", .{ + return cg.fail("cannot cast pointer '{f}' to '{f}'", .{ parent_ptr_ty.fmt(pt), oac.new_ptr_ty.fmt(pt), }); @@ -6041,7 +6048,13 @@ fn bitCast( while (cur.toIntern() != dst_child.toIntern()) : (try indices.append(gpa, 0)) { cur = switch (cur.zigTypeTag(zcu)) { .array, .vector => cur.childType(zcu), - .@"struct" => cur.fieldType(0, zcu), + .@"struct" => field: { + for (0..cur.structFieldCount(zcu)) |i| { + const field_ty = cur.fieldType(i, zcu); + if (field_ty.hasRuntimeBits(zcu) and cur.structFieldOffset(i, zcu) == 0) break :field field_ty; + } + unreachable; + }, else => unreachable, }; } @@ -6751,9 +6764,19 @@ fn ptrElemPtr(cg: *CodeGen, ptr_ty: Type, ptr_id: Id, index_id: Id) !Id { const zcu = cg.zcu; // Construct new pointer type for the resulting pointer const as = ptr_ty.ptrAddressSpace(zcu); - const elem_ty_id = try cg.pointeeType(as, ptr_ty.indexableElem(zcu), false); + const child_ty = ptr_ty.childType(zcu); + const is_single_ptr = ptr_ty.isSinglePointer(zcu); + const elem_is_block = switch (as) { + .uniform, .storage_buffer => switch (child_ty.zigTypeTag(cg.zcu)) { + .array => is_single_ptr, + .spirv => is_single_ptr and child_ty.isSpirvRuntimeArray(cg.zcu), + else => false, + }, + else => false, + }; + const elem_ty_id = try cg.pointeeType(as, ptr_ty.indexableElem(zcu), elem_is_block); const elem_ptr_ty_id = try cg.ptrType(elem_ty_id, cg.storageClass(as)); - if (ptr_ty.isSinglePointer(zcu)) { + if (is_single_ptr) { // Pointer-to-array. In this case, the resulting pointer is not of the same type // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain. return cg.accessChainId(elem_ptr_ty_id, ptr_id, &.{index_id}); diff --git a/test/behavior/spirv.zig b/test/behavior/spirv.zig index ed07aa33c5455bbf5b5a08093dfc7fe561cb36ab..dd8b6d093e01625c361288c76d1a6dce033d7561 100644 --- a/test/behavior/spirv.zig +++ b/test/behavior/spirv.zig @@ -49,13 +49,12 @@ test "@SpirvType" { _ = runtime_array; } -const InnerStruct = extern struct { x: u32 }; -const OuterStruct = extern struct { inner: InnerStruct, y: u32 }; -const outer_pc = @extern(*addrspace(.push_constant) const OuterStruct, .{ .name = "outer_pc" }); - test "@ptrCast to first field type" { - const pc_inner: *addrspace(.push_constant) const InnerStruct = @ptrCast(outer_pc); - _ = pc_inner; + const Inner = extern struct { a: u32 }; + const Outer = extern struct { a: Inner, b: u32 }; + var outer: Outer = undefined; + var inner: *Inner = @ptrCast(&outer); + _ = &inner; } test "@SpirvType equality" { diff --git a/test/cases/compile_errors/illegal_operation_on_logical_ptr.zig b/test/cases/compile_errors/illegal_operation_on_logical_ptr.zig index 859f0be6d37f1961be9f2bc295daeb330858b3cd..b8441f0466f8e4088d8ade8de1e2a97a1424f0c8 100644 --- a/test/cases/compile_errors/illegal_operation_on_logical_ptr.zig +++ b/test/cases/compile_errors/illegal_operation_on_logical_ptr.zig @@ -22,14 +22,50 @@ export fn ptrIntArithmetic() void { _ = ptr0 - 10; } +const slice: []const u8 = "abc"; + +export fn sliceElemVal() void { + var i: u32 = 0; + _ = &i; + _ = slice[i]; +} + +export fn sliceElemPtr() void { + var i: u32 = 0; + _ = &i; + _ = &slice[i]; +} + +export fn manyElemVal() void { + var ptr: [*]const u8 = "abc"; + var i: u32 = 0; + _ = .{ &ptr, &i }; + _ = ptr[i]; +} + +export fn manyElemPtr() void { + var ptr: [*]const u8 = "abc"; + var i: u32 = 0; + _ = .{ &ptr, &i }; + _ = &ptr[i]; +} + // error // target=spirv64-vulkan // // :3:21: error: illegal operation on logical pointer of type '*u8' -// :3:21: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan +// :3:21: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan // :8:20: error: illegal operation on logical pointer of type '*u8' -// :8:20: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan +// :8:20: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan // :16:17: error: illegal operation on logical pointer of type '*u8' -// :16:17: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan +// :16:17: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan // :22:14: error: illegal operation on logical pointer of type '[*]u8' -// :22:14: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan +// :22:14: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan +// :30:14: error: illegal operation on logical pointer of type '[]const u8' +// :30:14: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan +// :36:15: error: illegal operation on logical pointer of type '[]const u8' +// :36:15: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan +// :43:12: error: illegal operation on logical pointer of type '[*]const u8' +// :43:12: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan +// :50:13: error: illegal operation on logical pointer of type '[*]const u8' +// :50:13: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan diff --git a/test/cases/compile_errors/spirv_pointer_cast_requires_offset_zero.zig b/test/cases/compile_errors/spirv_pointer_cast_requires_offset_zero.zig index 8eda14440d21815df25b8e9e509a46f9b1eb1db6..48b47202a7ae95f93d2a3746de8ea60d26e44c1d 100644 --- a/test/cases/compile_errors/spirv_pointer_cast_requires_offset_zero.zig +++ b/test/cases/compile_errors/spirv_pointer_cast_requires_offset_zero.zig @@ -1,20 +1,15 @@ -const A = extern struct { x: u32, y: u32 }; -const B = extern struct { a: u64 }; - -const a = @extern(*addrspace(.uniform) const A, .{ - .name = "a", - .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, -}); +const Inner = extern struct { a: u32 }; +const Outer = extern struct { a: u32, b: Inner }; export fn main() callconv(.kernel) void { - const b: *addrspace(.uniform) const B = @ptrCast(a); - _ = &b; + var outer: Outer = undefined; + const inner: *Inner = @ptrCast(&outer); + _ = &inner; } // error // backend=selfhosted // target=spirv32-vulkan // -// :10:44: error: cannot cast pointer '*addrspace(.uniform) const A' to '*addrspace(.uniform) const B' -// :10:44: note: 'B' must appear at offset 0 inside 'A' -// :10:44: note: 'uniform' pointers can only reach nested types through a first struct field or an array element +// :6:27: error: cannot cast pointer '*tmp.Outer' to '*tmp.Inner' +// :6:27: note: 'tmp.Inner' must appear at offset 0 inside 'tmp.Outer' -- 2.54.0 From d95c7db6ec9f17e0c212c010385ac61ad7340d43 Mon Sep 17 00:00:00 2001 From: Ali Cheraghi Date: Tue, 25 Aug 2026 21:55:19 +0330 Subject: [PATCH 4/5] test: skip failing behavior tests for spirv --- test/behavior/array.zig | 4 ++++ test/behavior/backing_int.zig | 6 ++++++ test/behavior/cast.zig | 1 + test/behavior/comptime_memory.zig | 2 ++ test/behavior/for.zig | 1 + test/behavior/math.zig | 2 ++ test/behavior/packed-union.zig | 3 +++ test/behavior/pointers.zig | 1 + test/behavior/ptrcast.zig | 2 ++ test/behavior/sizeof_and_typeof.zig | 2 ++ test/behavior/slice.zig | 8 ++++++++ test/behavior/string_literals.zig | 2 ++ test/behavior/vector.zig | 2 ++ 13 files changed, 36 insertions(+) diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 55024fdf559ebb8521737017252e07f096c107ab..6c228f76eba5fed6d0899a74e0a5544cb94efa66 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -7,6 +7,8 @@ const expect = testing.expect; const expectEqual = testing.expectEqual; test "array to slice" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + const a: u32 align(4) = 3; const b: u32 align(8) = 4; const a_slice: []align(1) const u32 = @as(*const [1]u32, &a)[0..]; @@ -19,8 +21,10 @@ test "array to slice" { } test "arrays" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + var array: [5]u32 = undefined; var i: u32 = 0; diff --git a/test/behavior/backing_int.zig b/test/behavior/backing_int.zig index 1ab8b5970e1d93eaf7b7b1196cee5cf51ab5622f..78602fc3e97308a687dbc001a5252d4ec2c9c205 100644 --- a/test/behavior/backing_int.zig +++ b/test/behavior/backing_int.zig @@ -137,6 +137,8 @@ const T5 = union(E5) { }; test "@backingInt with tagged unions" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + const static = struct { fn doTheTest(v1: T1, v2: T2, v4: T4, v5: T5) !void { const b1 = @backingInt(v1); @@ -311,6 +313,8 @@ const U5 = packed union(u0) { }; test "@backingInt with packed unions" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + const static = struct { fn doTheTest(v1: U1, v2: U2, v3: U3, v4: U4, v5: U5) !void { const b1 = @backingInt(v1); @@ -339,6 +343,8 @@ test "@backingInt with packed unions" { } test "@fromBackingInt with packed unions" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + const static = struct { fn doTheTest( b1: @typeInfo(U1).@"union".backing_integer.?, diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 2a79566b74f6fad214b6324ff3e41b5ae087ae6d..f229ec8af57feae23e2aafd57007f0ed1a6bf1b6 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1806,6 +1806,7 @@ test "cast compatible optional types" { test "coerce undefined single-item pointer of array to error union of slice" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const a = @as([*]u8, undefined)[0..0]; var b: error{a}![]const u8 = a; diff --git a/test/behavior/comptime_memory.zig b/test/behavior/comptime_memory.zig index d11a7cb0165dca71e86447ec8a059257001d4cb4..2e4989a7e6ee1f0065469b23aa04e631a7b56c6b 100644 --- a/test/behavior/comptime_memory.zig +++ b/test/behavior/comptime_memory.zig @@ -437,6 +437,8 @@ test "type pun null pointer-like optional" { } test "write empty array to end" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + comptime var array: [5]u8 = "hello".*; array[5..5].* = .{}; array[5..5].* = [0]u8{}; diff --git a/test/behavior/for.zig b/test/behavior/for.zig index f4b2ea923f0e7407030b4dc661de7ebf154bfaec..f64271480a65d0e7a1e8125b37f78106ba4b71a0 100644 --- a/test/behavior/for.zig +++ b/test/behavior/for.zig @@ -391,6 +391,7 @@ test "raw pointer and counter" { test "inline for with slice as the comptime-known" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const comptime_slice = "hello"; var runtime_i: usize = 3; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index aafcd83b04fa770ba9620ce354569877d413f5bd..259b9dfe7c3aa908713374a60e21116d6e1edb73 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -456,6 +456,7 @@ test "division" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; try testIntDivision(); try comptime testIntDivision(); @@ -649,6 +650,7 @@ test "division half-precision floats" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; try testDivisionFP16(); try comptime testDivisionFP16(); diff --git a/test/behavior/packed-union.zig b/test/behavior/packed-union.zig index 4fd25526ce4a37deef7233b5b71120742604ce80..91166a0a596640a92d882b5b8aa0b1a835391e2d 100644 --- a/test/behavior/packed-union.zig +++ b/test/behavior/packed-union.zig @@ -229,6 +229,8 @@ test "initialize packed union field to undefined at comptime" { } test "convert from/to backing int" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + const U = packed union(u10) { a: i10, b: enum(u10) { x, y, z }, @@ -244,6 +246,7 @@ test "convert from/to backing int" { test "equality with wide backing integer" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35982 + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const U = packed union(i200) { x: u200, diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 807f4f5450d3fb372e18e2fc78f71fea9ce4d5bb..7f75454495996b29620880b5ee180d8261261970 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -701,6 +701,7 @@ test "pointer-to-array constness for zero-size elements, var" { test "pointer-to-array constness for zero-size elements, const" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; try constant(); try comptime constant(); diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig index 31de4f08b474620680a5d43252530124e12b736d..123b9be3d8d4f28767dc3ed7a1ec636811ec7693 100644 --- a/test/behavior/ptrcast.zig +++ b/test/behavior/ptrcast.zig @@ -6,6 +6,7 @@ const native_endian = builtin.target.cpu.arch.endian(); test "reinterpret bytes as integer with nonzero offset" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; try testReinterpretBytesAsInteger(); try comptime testReinterpretBytesAsInteger(); @@ -39,6 +40,7 @@ fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void { test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; try testReinterpretStructWrappedBytesAsInteger(); try comptime testReinterpretStructWrappedBytesAsInteger(); diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 36288f2668777bfe0f2df7780531b2ebaf58c7c1..96b17e2151712787ebf63f4f1cc7b8cbaecaf139 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -405,6 +405,8 @@ test "Extern function calls, dereferences and field access in @TypeOf" { } test "@sizeOf struct is resolved when used as operand of slicing" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + const dummy = struct {}; const S = struct { var buf: [1]u8 = undefined; diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 247c3d1be8f3a15ab8b53f9d0a47f2bdea02cc4f..e3685562e5259913edfcd99590a1452bbafb7075 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -59,6 +59,8 @@ test "const slice" { } test "comptime slice of undefined pointer of length 0" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + const slice1 = @as([*]i32, undefined)[0..0]; try expect(slice1.len == 0); const slice2 = @as([*]i32, undefined)[100..100]; @@ -173,6 +175,8 @@ test "pass a slice of types to a function" { test "generic malloc free" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + const a = memAlloc(u8, 10) catch unreachable; memFree(u8, a); } @@ -209,6 +213,8 @@ test "comptime slice of pointer preserves comptime var" { } test "comptime pointer cast array and then slice" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; const ptrA: [*]const u8 = @as([*]const u8, @ptrCast(&array)); @@ -1079,6 +1085,8 @@ test "conditionally return second argument slice" { } test "slice field alignment" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + const S = struct { fn doTheTest(p: *align(1) const []u8) !void { comptime assert(@TypeOf(&p.ptr) == *align(1) const [*]u8); diff --git a/test/behavior/string_literals.zig b/test/behavior/string_literals.zig index ff5f57f23510f598127e950a828b41a40dbcac5a..68861deda6462f3c46e799da19b9825572eaaab5 100644 --- a/test/behavior/string_literals.zig +++ b/test/behavior/string_literals.zig @@ -84,6 +84,8 @@ test "string literal pointer sentinel" { } test "sentinel slice of string literal" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + const string = "Hello!\x00World!"; try std.testing.expect(@TypeOf(string) == *const [13:0]u8); diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 9b77994848b2bfbfab21b015c437ad2b6f5a6f54..0566293f9066147a7a717866b2be28f739555fb2 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -680,6 +680,8 @@ test "vector division operators" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + const S = struct { fn doTheTestDiv(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void { const is_signed_int = switch (@typeInfo(T)) { -- 2.54.0 From 62e61e2e9b368bba001b67f4a29681b4bef4b822 Mon Sep 17 00:00:00 2001 From: Ali Cheraghi Date: Tue, 25 Aug 2026 21:55:19 +0330 Subject: [PATCH 5/5] test: do not skip spirv32 in selfhosted backend allowlist These cases were never ran! --- .../extern_spirv_decoration_validation.zig | 2 +- .../loading_spirv_runtime_array_value.zig | 4 +-- .../spirv_merge_logical_pointers.zig | 1 + test/cases/compile_errors/spirv_slices.zig | 34 +++++-------------- .../spirv_unsupported_float_width.zig | 16 --------- test/src/Cases.zig | 11 +++--- 6 files changed, 19 insertions(+), 49 deletions(-) delete mode 100644 test/cases/compile_errors/spirv_unsupported_float_width.zig diff --git a/test/cases/compile_errors/extern_spirv_decoration_validation.zig b/test/cases/compile_errors/extern_spirv_decoration_validation.zig index ae64963849857b2ba6b95f45b33c179b6700870b..a7cdd98b58e6195fe62281c9ed025659bc9c6a57 100644 --- a/test/cases/compile_errors/extern_spirv_decoration_validation.zig +++ b/test/cases/compile_errors/extern_spirv_decoration_validation.zig @@ -10,4 +10,4 @@ comptime { // backend=selfhosted // target=spirv32-vulkan // -// :1:45: error: "flat" decoration requires "input" or "output" address space +// :1:52: error: "flat" decoration requires "input" or "output" address space diff --git a/test/cases/compile_errors/loading_spirv_runtime_array_value.zig b/test/cases/compile_errors/loading_spirv_runtime_array_value.zig index 39cd38b36f0e3bce36884911de9b9e2f4f03c55f..19bdcefc4f208f262693c3503e5024e837f82592 100644 --- a/test/cases/compile_errors/loading_spirv_runtime_array_value.zig +++ b/test/cases/compile_errors/loading_spirv_runtime_array_value.zig @@ -19,5 +19,5 @@ export fn main2() callconv(.kernel) void { // backend=selfhosted // target=spirv32-vulkan // -// :10:15: error: cannot load SPIR-V runtime array value -// :15:12: error: cannot load SPIR-V runtime array value +// :10:18: error: cannot load SPIR-V runtime array value +// :15:10: error: cannot load SPIR-V runtime array value diff --git a/test/cases/compile_errors/spirv_merge_logical_pointers.zig b/test/cases/compile_errors/spirv_merge_logical_pointers.zig index ccceef8097c8418dcfc363f6baa87c0e04073b3c..640129575d3103ae9c8ac2b5bd636e7a9a1021ab 100644 --- a/test/cases/compile_errors/spirv_merge_logical_pointers.zig +++ b/test/cases/compile_errors/spirv_merge_logical_pointers.zig @@ -11,6 +11,7 @@ export fn a() void { } // error +// backend=selfhosted // target=spirv32-vulkan // // :9:13: error: value with non-mergable pointer type '*i32' depends on runtime control flow diff --git a/test/cases/compile_errors/spirv_slices.zig b/test/cases/compile_errors/spirv_slices.zig index 8e6e657025982ad316bc058025e1374e30520aec..73554699caf09fb3a1c7cf86fa3a19863ddd767e 100644 --- a/test/cases/compile_errors/spirv_slices.zig +++ b/test/cases/compile_errors/spirv_slices.zig @@ -1,6 +1,6 @@ export fn a() void { - var buf: [3]f32 = undefined; - takesSlice(&buf); // error + var x: [3]f32 = .{ 1, 2, 3 }; + takesSlice(&x); } fn takesSlice(buf: []f32) void { @@ -8,34 +8,18 @@ fn takesSlice(buf: []f32) void { } export fn b() void { - var buf: [3]f32 = undefined; - for (buf[0..3]) |_| {} // error -} - -export fn c() void { - var buf: [3]f32 = undefined; - for (&buf) |_| {} // not an error -} - -export fn d() void { - const buf: [3]f32 = .{1, 2, 3}; - for (comptime buf[0..3]) |_| {} // not an error -} - -export fn e() void { - const buf: [3]f32 = .{1, 2, 3}; - for (comptime buf[0..3]) |_| {} // not an error - for (buf[0..3]) |_| {} // error + const x: [3]f32 = .{ 1, 2, 3 }; + for (comptime x[0..3]) |_| {} + for (&x) |_| {} + for (x[0..3]) |_| {} } // error -// backend=auto +// backend=selfhosted // target=spirv32-opengl,spirv32-vulkan // cpu_features=baseline+variable_pointers // // :3:16: error: cannot construct slice from address space 'generic' // :3:16: note: only 'shared' and 'storage_buffer' address spaces support slicing on SPIR-V -// :12:13: error: cannot construct slice from address space 'generic' -// :12:13: note: only 'shared' and 'storage_buffer' address spaces support slicing on SPIR-V -// :28:13: error: cannot construct slice from address space 'generic' -// :28:13: note: only 'shared' and 'storage_buffer' address spaces support slicing on SPIR-V +// :14:11: error: cannot construct slice from address space 'generic' +// :14:11: note: only 'shared' and 'storage_buffer' address spaces support slicing on SPIR-V diff --git a/test/cases/compile_errors/spirv_unsupported_float_width.zig b/test/cases/compile_errors/spirv_unsupported_float_width.zig deleted file mode 100644 index 364e9f8385f607af4f766bdd742c261a782635a8..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/spirv_unsupported_float_width.zig +++ /dev/null @@ -1,16 +0,0 @@ -export fn use_f80() callconv(.kernel) void { - var x: f80 = 1.5; - _ = &x; -} - -export fn use_f16() callconv(.kernel) void { - var x: f16 = 1.5; - _ = &x; -} - -// error -// backend=selfhosted -// target=spirv32-vulkan -// -// :2:5: error: 'f80' is not supported on the current SPIR-V feature set -// :7:5: error: 'f16' is not supported on the current SPIR-V feature set diff --git a/test/src/Cases.zig b/test/src/Cases.zig index f16a73121c6a885ce09a1d69a39f53d85b737863..26cb1ae14be419ca3d7347c773b2d1ec567a0331 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -389,11 +389,12 @@ fn addFromDirInner( const resolved_target = b.resolveTargetQuery(target_query); const target = &resolved_target.result; for (backends) |backend| { - if (backend == .selfhosted and - target.cpu.arch != .aarch64 and target.cpu.arch != .wasm32 and target.cpu.arch != .x86_64 and target.cpu.arch != .spirv64) - { - // Other backends don't support new liveness format - continue; + if (backend == .selfhosted) { + switch (target.cpu.arch) { + .aarch64, .wasm32, .x86_64, .spirv64, .spirv32 => {}, + // Other backends don't support new liveness format + else => continue, + } } if (backend == .selfhosted and target.cpu.arch == .aarch64) { -- 2.54.0