authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-08-27 21:22:31+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-08-27 21:22:31+02:00
log4c9cdc8b9189d548f00229cdfc21f1850348026f
treee95e83f4e4bc2b064a844b07bfdd8c2ddc2a7a9b
parentaa3b2c557fe443b2dd55725b237d8e3ac804e414
parent62e61e2e9b368bba001b67f4a29681b4bef4b822

Merge pull request 'Sema: improve ptr operation validation for spirv' (#36257) from alichraghi/zig:spirv3 into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36257 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

31 files changed, 354 insertions(+), 196 deletions(-)

src/Sema.zig+79-45
......@@ -22151,34 +22151,7 @@ fn ptrCastFull(
2215122151 }
2215222152
2215322153 try sema.validateRuntimeValue(block, operand_src, operand);
22154
22155 if (zcu.getTarget().cpu.arch.isSpirV() and
22156 src_info.flags.address_space != .physical_storage_buffer and
22157 src_info.flags.address_space == dest_info.flags.address_space and
22158 src_info.child != dest_info.child and
22159 Type.fromInterned(dest_info.child).hasRuntimeBits(zcu))
22160 {
22161 var cur: Type = .fromInterned(src_info.child);
22162 while (cur.toIntern() != dest_info.child) {
22163 cur = switch (cur.zigTypeTag(zcu)) {
22164 .array, .vector => cur.childType(zcu),
22165 .@"struct" => if (cur.structFieldOffset(0, zcu) == 0) cur.fieldType(0, zcu) else null,
22166 else => null,
22167 } orelse return sema.failWithOwnedErrorMsg(block, msg: {
22168 const msg = try sema.errMsg(src, "cannot cast pointer '{f}' to '{f}'", .{
22169 operand_ty.fmt(pt), dest_ty.fmt(pt),
22170 });
22171 errdefer msg.destroy(sema.gpa);
22172 try sema.errNote(src, msg, "'{f}' must appear at offset 0 inside '{f}'", .{
22173 Type.fromInterned(dest_info.child).fmt(pt), Type.fromInterned(src_info.child).fmt(pt),
22174 });
22175 try sema.errNote(src, msg, "'{s}' pointers can only reach nested types through a first struct field or an array element", .{
22176 @tagName(src_info.flags.address_space),
22177 });
22178 break :msg msg;
22179 });
22180 }
22181 }
22154 try sema.checkLogicalPtrCast(block, src, operand_ty, dest_ty);
2218222155
2218322156 const can_cast_to_int = !target_util.shouldBlockPointerOps(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu));
2218422157 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(
2272122694fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
2272222695 const pt = sema.pt;
2272322696 const zcu = pt.zcu;
22697
22698 if (block.isComptime() or block.is_typeof) return;
2272422699 if (zcu.intern_pool.indexToKey(ty.toIntern()) == .ptr_type) {
2272522700 const target = zcu.getTarget();
2272622701 const as = ty.ptrAddressSpace(zcu);
......@@ -22731,12 +22706,8 @@ fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
2273122706 try sema.errNote(
2273222707 src,
2273322708 msg,
22734 "cannot perform arithmetic on pointers with address space '{s}' on target {s}-{s}",
22735 .{
22736 @tagName(as),
22737 @tagName(target.cpu.arch.family()),
22738 @tagName(target.os.tag),
22739 },
22709 "pointers with address space '{t}' do not support arithmetic or indexing on target {t}-{t}",
22710 .{ as, target.cpu.arch.family(), target.os.tag },
2274022711 );
2274122712 break :msg msg;
2274222713 });
......@@ -22744,6 +22715,49 @@ fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
2274422715 }
2274522716}
2274622717
22718fn checkLogicalPtrCast(
22719 sema: *Sema,
22720 block: *Block,
22721 src: LazySrcLoc,
22722 operand_ty: Type,
22723 dest_ty: Type,
22724) CompileError!void {
22725 const pt = sema.pt;
22726 const zcu = pt.zcu;
22727 const src_info = operand_ty.ptrInfo(zcu);
22728 const dest_info = dest_ty.ptrInfo(zcu);
22729
22730 if (block.isComptime() or block.is_typeof) return;
22731 switch (zcu.getTarget().os.tag) {
22732 .vulkan, .opengl => {},
22733 else => return,
22734 }
22735 if (src_info.flags.address_space == .physical_storage_buffer) return;
22736
22737 var cur: Type = .fromInterned(src_info.child);
22738 while (cur.toIntern() != dest_info.child) {
22739 cur = switch (cur.zigTypeTag(zcu)) {
22740 .array, .vector => cur.childType(zcu),
22741 .@"struct" => field: {
22742 for (0..cur.structFieldCount(zcu)) |i| {
22743 const field_ty = cur.fieldType(i, zcu);
22744 if (field_ty.hasRuntimeBits(zcu) and cur.structFieldOffset(i, zcu) == 0) break :field field_ty;
22745 }
22746 break :field null;
22747 },
22748 else => null,
22749 } orelse return sema.failWithOwnedErrorMsg(block, msg: {
22750 const msg = try sema.errMsg(src, "cannot cast pointer '{f}' to '{f}'", .{ operand_ty.fmt(pt), dest_ty.fmt(pt) });
22751 errdefer msg.destroy(sema.gpa);
22752 try sema.errNote(src, msg, "'{f}' must appear at offset 0 inside '{f}'", .{
22753 Type.fromInterned(dest_info.child).fmt(pt),
22754 Type.fromInterned(src_info.child).fmt(pt),
22755 });
22756 break :msg msg;
22757 });
22758 }
22759}
22760
2274722761fn checkVectorElemType(
2274822762 sema: *Sema,
2274922763 block: *Block,
......@@ -25285,17 +25299,30 @@ fn zirBuiltinExtern(
2528525299 .location, .descriptor => {},
2528625300 };
2528725301
25288 switch (zcu.getTarget().os.tag) {
25289 .vulkan, .opengl => switch (ptr_info.flags.address_space) {
25290 .storage_buffer, .uniform, .push_constant => if (ptr_info.flags.size != .one) {
25291 return sema.failWithOwnedErrorMsg(block, msg: {
25292 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)});
25293 errdefer msg.destroy(sema.gpa);
25294 try sema.errNote(ty_src, msg, "wrap the element type in a struct containing a runtime-sized array", .{});
25295 break :msg msg;
25296 });
25297 },
25298 else => {},
25302 const target = zcu.getTarget();
25303 switch (target.os.tag) {
25304 .vulkan, .opengl => {
25305 const pointee = switch (elem_ty.zigTypeTag(zcu)) {
25306 .array => elem_ty.childType(zcu),
25307 .spirv => if (elem_ty.isSpirvRuntimeArray(zcu)) elem_ty.childType(zcu) else elem_ty,
25308 else => elem_ty,
25309 };
25310 switch (ptr_info.flags.address_space) {
25311 .uniform,
25312 .storage_buffer,
25313 => if (ptr_info.flags.size != .one or pointee.zigTypeTag(zcu) != .@"struct") {
25314 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});
25315 },
25316 .push_constant => if (ptr_info.flags.size != .one or elem_ty.zigTypeTag(zcu) != .@"struct") {
25317 return sema.fail(block, ty_src, "extern in 'push_constant' address space must be a single-item pointer to a struct", .{});
25318 },
25319 .constant => if (target.os.tag == .vulkan and (pointee.zigTypeTag(zcu) != .spirv or pointee.isSpirvRuntimeArray(zcu))) {
25320 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", .{});
25321 },
25322 else => if (elem_ty.isSpirvRuntimeArray(zcu)) {
25323 return sema.fail(block, ty_src, "SPIR-V runtime array is not allowed in the '{t}' address space", .{ptr_info.flags.address_space});
25324 },
25325 }
2529925326 },
2530025327 else => {},
2530125328 }
......@@ -25717,6 +25744,9 @@ pub fn explainWhyTypeIsNotExtern(
2571725744 .spirv => {
2571825745 assert(ty.isSpirvRuntimeArray(zcu));
2571925746 try sema.errNote(src_loc, msg, "SPIR-V runtime arrays must be the last field of an extern struct", .{});
25747 if (position == .other) {
25748 try sema.errNote(src_loc, msg, "consider enabling the 'runtime_descriptor_array' feature to use the runtime array as the extern pointee", .{});
25749 }
2572025750 },
2572125751
2572225752 .float => try sema.errNote(src_loc, msg, "'{f}' is not extern compatible on this target", .{ty.fmt(pt)}),
......@@ -27351,6 +27381,7 @@ fn elemPtrOneLayerOnly(
2735127381
2735227382 try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_src);
2735327383 try sema.validateRuntimeValue(block, indexable_src, indexable);
27384 try sema.checkLogicalPtrOperation(block, src, indexable_ty);
2735427385
2735527386 if (child_ty.abiSize(zcu) == 0) {
2735627387 // zero-bit child type; just bitcast the pointer
......@@ -27423,6 +27454,7 @@ fn elemVal(
2742327454 },
2742427455 .partially_comptime, .fully_comptime => unreachable, // caught by `validateRuntimeElemAccess`
2742527456 }
27457 try sema.checkLogicalPtrOperation(block, src, indexable_ty);
2742627458
2742727459 return block.addBinOp(.ptr_elem_val, indexable, elem_index);
2742827460 },
......@@ -27824,6 +27856,7 @@ fn elemValSlice(
2782427856 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
2782527857 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
2782627858 }
27859 try sema.checkLogicalPtrOperation(block, src, slice_ty);
2782727860 return block.addBinOp(.slice_elem_val, slice, elem_index);
2782827861}
2782927862
......@@ -27875,6 +27908,7 @@ fn elemPtrSlice(
2787527908
2787627909 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_src);
2787727910 try sema.validateRuntimeValue(block, slice_src, slice);
27911 try sema.checkLogicalPtrOperation(block, src, slice_ty);
2787827912
2787927913 if (oob_safety and block.wantSafety()) {
2788027914 const len_inst = len: {
src/Sema/type_resolution.zig+11
......@@ -334,6 +334,17 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
334334 break :msg msg;
335335 });
336336 }
337
338 const elem_ty: Type = field_ty.childType(zcu);
339 if (elem_ty.zigTypeTag(zcu) == .spirv) {
340 return sema.failWithOwnedErrorMsg(&block, msg: {
341 const msg = try sema.errMsg(field_ty_src, "cannot embed SPIR-V type '{f}' in struct", .{elem_ty.fmt(pt)});
342 errdefer msg.destroy(gpa);
343 try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{});
344 try sema.addDeclaredHereNote(msg, field_ty);
345 break :msg msg;
346 });
347 }
337348 } else {
338349 return sema.failWithOwnedErrorMsg(&block, msg: {
339350 const msg = try sema.errMsg(field_ty_src, "cannot directly embed SPIR-V type '{f}' in struct", .{field_ty.fmt(pt)});
src/Type.zig+2-1
......@@ -3145,7 +3145,8 @@ pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool
31453145
31463146 .spirv => switch (position) {
31473147 .struct_field, .union_field => true,
3148 .ret_ty, .param_ty, .element, .other => !ty.isSpirvRuntimeArray(zcu),
3148 .ret_ty, .param_ty, .element => !ty.isSpirvRuntimeArray(zcu),
3149 .other => !ty.isSpirvRuntimeArray(zcu) or zcu.getTarget().cpu.has(.spirv, .runtime_descriptor_array),
31493150 },
31503151
31513152 .pointer => {
src/codegen/spirv/CodeGen.zig+52-17
......@@ -617,7 +617,8 @@ pub fn structType(
617617
618618/// Returns the layout-decorated variant of `ty` for use inside a Vulkan/OpenGL
619619/// interface block. Vulkan forbids nested Block decorations, so recursive calls
620/// always pass `false`.
620/// pass `false`, except through an array, whose elements are each a
621/// block of their own.
621622///
622623/// This is distinct from `resolveType` because SPIR-V forbids such decorations
623624/// 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 {
695696 },
696697 .array => id: {
697698 const elem_ty = ty.childType(zcu);
698 const elem_ty_id = try cg.layoutType(elem_ty, false);
699 const elem_ty_id = try cg.layoutType(elem_ty, is_block_root);
699700 const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel(zcu)) orelse
700701 return cg.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel(zcu)});
701702 const id = try cg.arrayType(try cg.constInt(.u32, total_len), elem_ty_id);
702 if (elem_ty.hasRuntimeBits(zcu)) try cg.decorate(id, .{
703 .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) },
704 });
703 if (!is_block_root and elem_ty.hasRuntimeBits(zcu)) {
704 try cg.decorate(id, .{
705 .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) },
706 });
707 }
705708 break :id id;
706709 },
707710 .spirv => if (ty.isSpirvRuntimeArray(zcu)) id: {
708711 const elem_ty = ty.childType(zcu);
709 const elem_ty_id = try cg.layoutType(elem_ty, false);
712 const elem_ty_id = try cg.layoutType(elem_ty, is_block_root);
710713 const id = cg.allocId();
711714 try cg.sections.globals.emit(gpa, .OpTypeRuntimeArray, .{
712715 .id_result = id,
713716 .element_type = elem_ty_id,
714717 });
715 if (elem_ty.hasRuntimeBits(zcu)) try cg.decorate(id, .{
716 .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) },
717 });
718 if (!is_block_root and elem_ty.hasRuntimeBits(zcu)) {
719 try cg.decorate(id, .{
720 .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) },
721 });
722 }
718723 break :id id;
719724 } else return cg.resolveType(ty, .indirect),
720725 else => return cg.resolveType(ty, .indirect),
......@@ -955,11 +960,18 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
955960 switch (target.os.tag) {
956961 .vulkan, .opengl => {
957962 switch (storage_class) {
958 .uniform, .push_constant, .storage_buffer, .physical_storage_buffer => {
963 .uniform,
964 .push_constant,
965 .storage_buffer,
966 .physical_storage_buffer,
967 => {
959968 if (ty.hasRuntimeBits(zcu)) {
960 try cg.decorate(ptr_ty_id, .{
961 .array_stride = .{ .array_stride = @intCast(ty.abiSize(zcu)) },
962 });
969 if (!ty.isSpirvRuntimeArray(zcu)) {
970 try cg.decorate(
971 ptr_ty_id,
972 .{ .array_stride = .{ .array_stride = @intCast(ty.abiSize(zcu)) } },
973 );
974 }
963975 if (!cg.needsLayout(as, ty)) try cg.decorateLayout(ty, ty_id);
964976 }
965977 if (key.is_const and storage_class == .storage_buffer) {
......@@ -2047,6 +2059,13 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id {
20472059 while (cur.toIntern() != dst_child.toIntern()) {
20482060 switch (cur.zigTypeTag(zcu)) {
20492061 .array => {
2062 if (dst_child.zigTypeTag(zcu) == .array and
2063 dst_child.childType(zcu).toIntern() == cur.childType(zcu).toIntern() and
2064 dst_child.arrayLenIncludingSentinel(zcu) <= cur.arrayLenIncludingSentinel(zcu))
2065 {
2066 cur = dst_child;
2067 break;
2068 }
20502069 cur = cur.childType(zcu);
20512070 depth += 1;
20522071 },
......@@ -2086,7 +2105,7 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id {
20862105 }
20872106 }
20882107
2089 return cg.fail("cannot perform pointer cast: '{f}' to '{f}'", .{
2108 return cg.fail("cannot cast pointer '{f}' to '{f}'", .{
20902109 parent_ptr_ty.fmt(pt),
20912110 oac.new_ptr_ty.fmt(pt),
20922111 });
......@@ -6029,7 +6048,13 @@ fn bitCast(
60296048 while (cur.toIntern() != dst_child.toIntern()) : (try indices.append(gpa, 0)) {
60306049 cur = switch (cur.zigTypeTag(zcu)) {
60316050 .array, .vector => cur.childType(zcu),
6032 .@"struct" => cur.fieldType(0, zcu),
6051 .@"struct" => field: {
6052 for (0..cur.structFieldCount(zcu)) |i| {
6053 const field_ty = cur.fieldType(i, zcu);
6054 if (field_ty.hasRuntimeBits(zcu) and cur.structFieldOffset(i, zcu) == 0) break :field field_ty;
6055 }
6056 unreachable;
6057 },
60336058 else => unreachable,
60346059 };
60356060 }
......@@ -6739,9 +6764,19 @@ fn ptrElemPtr(cg: *CodeGen, ptr_ty: Type, ptr_id: Id, index_id: Id) !Id {
67396764 const zcu = cg.zcu;
67406765 // Construct new pointer type for the resulting pointer
67416766 const as = ptr_ty.ptrAddressSpace(zcu);
6742 const elem_ty_id = try cg.pointeeType(as, ptr_ty.indexableElem(zcu), false);
6767 const child_ty = ptr_ty.childType(zcu);
6768 const is_single_ptr = ptr_ty.isSinglePointer(zcu);
6769 const elem_is_block = switch (as) {
6770 .uniform, .storage_buffer => switch (child_ty.zigTypeTag(cg.zcu)) {
6771 .array => is_single_ptr,
6772 .spirv => is_single_ptr and child_ty.isSpirvRuntimeArray(cg.zcu),
6773 else => false,
6774 },
6775 else => false,
6776 };
6777 const elem_ty_id = try cg.pointeeType(as, ptr_ty.indexableElem(zcu), elem_is_block);
67436778 const elem_ptr_ty_id = try cg.ptrType(elem_ty_id, cg.storageClass(as));
6744 if (ptr_ty.isSinglePointer(zcu)) {
6779 if (is_single_ptr) {
67456780 // Pointer-to-array. In this case, the resulting pointer is not of the same type
67466781 // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain.
67476782 return cg.accessChainId(elem_ptr_ty_id, ptr_id, &.{index_id});
test/behavior/array.zig+4
......@@ -7,6 +7,8 @@ const expect = testing.expect;
77const expectEqual = testing.expectEqual;
88
99test "array to slice" {
10 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
11
1012 const a: u32 align(4) = 3;
1113 const b: u32 align(8) = 4;
1214 const a_slice: []align(1) const u32 = @as(*const [1]u32, &a)[0..];
......@@ -19,8 +21,10 @@ test "array to slice" {
1921}
2022
2123test "arrays" {
24 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
2225 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
2326 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
27
2428 var array: [5]u32 = undefined;
2529
2630 var i: u32 = 0;
test/behavior/backing_int.zig+6
......@@ -137,6 +137,8 @@ const T5 = union(E5) {
137137};
138138
139139test "@backingInt with tagged unions" {
140 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
141
140142 const static = struct {
141143 fn doTheTest(v1: T1, v2: T2, v4: T4, v5: T5) !void {
142144 const b1 = @backingInt(v1);
......@@ -311,6 +313,8 @@ const U5 = packed union(u0) {
311313};
312314
313315test "@backingInt with packed unions" {
316 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
317
314318 const static = struct {
315319 fn doTheTest(v1: U1, v2: U2, v3: U3, v4: U4, v5: U5) !void {
316320 const b1 = @backingInt(v1);
......@@ -339,6 +343,8 @@ test "@backingInt with packed unions" {
339343}
340344
341345test "@fromBackingInt with packed unions" {
346 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
347
342348 const static = struct {
343349 fn doTheTest(
344350 b1: @typeInfo(U1).@"union".backing_integer.?,
test/behavior/cast.zig+1
......@@ -1806,6 +1806,7 @@ test "cast compatible optional types" {
18061806
18071807test "coerce undefined single-item pointer of array to error union of slice" {
18081808 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1809 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
18091810
18101811 const a = @as([*]u8, undefined)[0..0];
18111812 var b: error{a}![]const u8 = a;
test/behavior/comptime_memory.zig+2
......@@ -437,6 +437,8 @@ test "type pun null pointer-like optional" {
437437}
438438
439439test "write empty array to end" {
440 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
441
440442 comptime var array: [5]u8 = "hello".*;
441443 array[5..5].* = .{};
442444 array[5..5].* = [0]u8{};
test/behavior/for.zig+1
......@@ -391,6 +391,7 @@ test "raw pointer and counter" {
391391test "inline for with slice as the comptime-known" {
392392 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
393393 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
394 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
394395
395396 const comptime_slice = "hello";
396397 var runtime_i: usize = 3;
test/behavior/math.zig+2
......@@ -456,6 +456,7 @@ test "division" {
456456 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
457457 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
458458 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
459 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
459460 try testIntDivision();
460461 try comptime testIntDivision();
461462
......@@ -649,6 +650,7 @@ test "division half-precision floats" {
649650 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
650651 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
651652 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
653 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
652654
653655 try testDivisionFP16();
654656 try comptime testDivisionFP16();
test/behavior/packed-union.zig+3
......@@ -229,6 +229,8 @@ test "initialize packed union field to undefined at comptime" {
229229}
230230
231231test "convert from/to backing int" {
232 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
233
232234 const U = packed union(u10) {
233235 a: i10,
234236 b: enum(u10) { x, y, z },
......@@ -244,6 +246,7 @@ test "convert from/to backing int" {
244246
245247test "equality with wide backing integer" {
246248 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35982
249 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
247250
248251 const U = packed union(i200) {
249252 x: u200,
test/behavior/pointers.zig+1
......@@ -701,6 +701,7 @@ test "pointer-to-array constness for zero-size elements, var" {
701701
702702test "pointer-to-array constness for zero-size elements, const" {
703703 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
704 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
704705
705706 try constant();
706707 try comptime constant();
test/behavior/ptrcast.zig+2
......@@ -6,6 +6,7 @@ const native_endian = builtin.target.cpu.arch.endian();
66
77test "reinterpret bytes as integer with nonzero offset" {
88 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
910
1011 try testReinterpretBytesAsInteger();
1112 try comptime testReinterpretBytesAsInteger();
......@@ -39,6 +40,7 @@ fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void {
3940
4041test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" {
4142 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
43 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
4244
4345 try testReinterpretStructWrappedBytesAsInteger();
4446 try comptime testReinterpretStructWrappedBytesAsInteger();
test/behavior/sizeof_and_typeof.zig+2
......@@ -405,6 +405,8 @@ test "Extern function calls, dereferences and field access in @TypeOf" {
405405}
406406
407407test "@sizeOf struct is resolved when used as operand of slicing" {
408 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
409
408410 const dummy = struct {};
409411 const S = struct {
410412 var buf: [1]u8 = undefined;
test/behavior/slice.zig+8
......@@ -59,6 +59,8 @@ test "const slice" {
5959}
6060
6161test "comptime slice of undefined pointer of length 0" {
62 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
63
6264 const slice1 = @as([*]i32, undefined)[0..0];
6365 try expect(slice1.len == 0);
6466 const slice2 = @as([*]i32, undefined)[100..100];
......@@ -173,6 +175,8 @@ test "pass a slice of types to a function" {
173175
174176test "generic malloc free" {
175177 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
178 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
179
176180 const a = memAlloc(u8, 10) catch unreachable;
177181 memFree(u8, a);
178182}
......@@ -209,6 +213,8 @@ test "comptime slice of pointer preserves comptime var" {
209213}
210214
211215test "comptime pointer cast array and then slice" {
216 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
217
212218 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
213219
214220 const ptrA: [*]const u8 = @as([*]const u8, @ptrCast(&array));
......@@ -1079,6 +1085,8 @@ test "conditionally return second argument slice" {
10791085}
10801086
10811087test "slice field alignment" {
1088 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1089
10821090 const S = struct {
10831091 fn doTheTest(p: *align(1) const []u8) !void {
10841092 comptime assert(@TypeOf(&p.ptr) == *align(1) const [*]u8);
test/behavior/spirv.zig+5-6
......@@ -49,13 +49,12 @@ test "@SpirvType" {
4949 _ = runtime_array;
5050}
5151
52const InnerStruct = extern struct { x: u32 };
53const OuterStruct = extern struct { inner: InnerStruct, y: u32 };
54const outer_pc = @extern(*addrspace(.push_constant) const OuterStruct, .{ .name = "outer_pc" });
55
5652test "@ptrCast to first field type" {
57 const pc_inner: *addrspace(.push_constant) const InnerStruct = @ptrCast(outer_pc);
58 _ = pc_inner;
53 const Inner = extern struct { a: u32 };
54 const Outer = extern struct { a: Inner, b: u32 };
55 var outer: Outer = undefined;
56 var inner: *Inner = @ptrCast(&outer);
57 _ = &inner;
5958}
6059
6160test "@SpirvType equality" {
test/behavior/string_literals.zig+2
......@@ -84,6 +84,8 @@ test "string literal pointer sentinel" {
8484}
8585
8686test "sentinel slice of string literal" {
87 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
88
8789 const string = "Hello!\x00World!";
8890 try std.testing.expect(@TypeOf(string) == *const [13:0]u8);
8991
test/behavior/vector.zig+2
......@@ -680,6 +680,8 @@ test "vector division operators" {
680680 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
681681 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
682682 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
683 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
684
683685 const S = struct {
684686 fn doTheTestDiv(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void {
685687 const is_signed_int = switch (@typeInfo(T)) {
test/cases/compile_errors/directly_embedding_spirv_type_in_struct_and_union.zig+13-6
......@@ -3,22 +3,27 @@ const RuntimeArray = @SpirvType(.{ .runtime_array = u32 });
33const Foo = struct {
44 s: Sampler,
55};
6const Baz = struct {
6const Bar = struct {
77 a: RuntimeArray,
88};
9const Qux = extern struct {
9const Baz = extern struct {
1010 a: RuntimeArray,
1111 b: u32,
1212};
13const Qux = extern struct { _: @SpirvType(.{ .runtime_array = Sampler }) };
1314export fn a() void {
1415 var foo: Foo = undefined;
1516 _ = &foo;
1617}
1718export fn c() void {
19 var bar: Bar = undefined;
20 _ = &bar;
21}
22export fn d() void {
1823 var baz: Baz = undefined;
1924 _ = &baz;
2025}
21export fn d() void {
26export fn e() void {
2227 var qux: Qux = undefined;
2328 _ = &qux;
2429}
......@@ -27,9 +32,11 @@ export fn d() void {
2732// backend=selfhosted
2833// target=spirv32-vulkan
2934//
30// :4:8: error: cannot directly embed SPIR-V type 'tmp.Sampler__SpirvType_4' in struct
35// :4:8: error: cannot directly embed SPIR-V type '@SpirvType(.sampler)' in struct
3136// :4:8: note: opaque types have unknown size
32// :6:13: error: non-extern struct cannot contain fields of type 'tmp.RuntimeArray__SpirvType_11'
37// :6:13: error: non-extern struct cannot contain fields of type '@SpirvType(.runtime_array, u32)'
3338// :7:5: note: while checking this field
34// :9:20: error: struct field of type 'tmp.RuntimeArray__SpirvType_11' must be the last field
39// :9:20: error: struct field of type '@SpirvType(.runtime_array, u32)' must be the last field
3540// :10:5: note: while checking this field
41// :13:32: error: cannot embed SPIR-V type '@SpirvType(.sampler)' in struct
42// :13:32: note: opaque types have unknown size
test/cases/compile_errors/extern_spirv_decoration_validation.zig+1-1
......@@ -10,4 +10,4 @@ comptime {
1010// backend=selfhosted
1111// target=spirv32-vulkan
1212//
13// :1:45: error: "flat" decoration requires "input" or "output" address space
13// :1:52: error: "flat" decoration requires "input" or "output" address space
test/cases/compile_errors/extern_spirv_storage_buffer_must_be_single_pointer.zig deleted-25
......@@ -1,25 +0,0 @@
1const a = @extern([*]addrspace(.storage_buffer) u32, .{
2 .name = "a",
3 .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } },
4});
5const b = @extern([]addrspace(.uniform) u32, .{
6 .name = "b",
7 .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } },
8});
9const c = @extern([*c]addrspace(.push_constant) u32, .{ .name = "c" });
10comptime {
11 _ = a;
12 _ = b;
13 _ = c;
14}
15
16// error
17// backend=selfhosted
18// target=spirv32-vulkan
19//
20// :1:19: error: extern in 'storage_buffer' address space must be a single-item pointer to a struct
21// :1:19: note: wrap the element type in a struct containing a runtime-sized array
22// :5:19: error: extern in 'uniform' address space must be a single-item pointer to a struct
23// :5:19: note: wrap the element type in a struct containing a runtime-sized array
24// :9:19: error: extern in 'push_constant' address space must be a single-item pointer to a struct
25// :9:19: note: wrap the element type in a struct containing a runtime-sized array
test/cases/compile_errors/illegal_operation_on_logical_ptr.zig+40-4
......@@ -22,14 +22,50 @@ export fn ptrIntArithmetic() void {
2222 _ = ptr0 - 10;
2323}
2424
25const slice: []const u8 = "abc";
26
27export fn sliceElemVal() void {
28 var i: u32 = 0;
29 _ = &i;
30 _ = slice[i];
31}
32
33export fn sliceElemPtr() void {
34 var i: u32 = 0;
35 _ = &i;
36 _ = &slice[i];
37}
38
39export fn manyElemVal() void {
40 var ptr: [*]const u8 = "abc";
41 var i: u32 = 0;
42 _ = .{ &ptr, &i };
43 _ = ptr[i];
44}
45
46export fn manyElemPtr() void {
47 var ptr: [*]const u8 = "abc";
48 var i: u32 = 0;
49 _ = .{ &ptr, &i };
50 _ = &ptr[i];
51}
52
2553// error
2654// target=spirv64-vulkan
2755//
2856// :3:21: error: illegal operation on logical pointer of type '*u8'
29// :3:21: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan
57// :3:21: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan
3058// :8:20: error: illegal operation on logical pointer of type '*u8'
31// :8:20: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan
59// :8:20: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan
3260// :16:17: error: illegal operation on logical pointer of type '*u8'
33// :16:17: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan
61// :16:17: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan
3462// :22:14: error: illegal operation on logical pointer of type '[*]u8'
35// :22:14: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan
63// :22:14: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan
64// :30:14: error: illegal operation on logical pointer of type '[]const u8'
65// :30:14: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan
66// :36:15: error: illegal operation on logical pointer of type '[]const u8'
67// :36:15: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan
68// :43:12: error: illegal operation on logical pointer of type '[*]const u8'
69// :43:12: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan
70// :50:13: error: illegal operation on logical pointer of type '[*]const u8'
71// :50:13: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan
test/cases/compile_errors/loading_spirv_runtime_array_value.zig+2-2
......@@ -19,5 +19,5 @@ export fn main2() callconv(.kernel) void {
1919// backend=selfhosted
2020// target=spirv32-vulkan
2121//
22// :10:15: error: cannot load SPIR-V runtime array value
23// :15:12: error: cannot load SPIR-V runtime array value
22// :10:18: error: cannot load SPIR-V runtime array value
23// :15:10: error: cannot load SPIR-V runtime array value
test/cases/compile_errors/spirv_extern_runtime_descriptor_array.zig created+49
......@@ -0,0 +1,49 @@
1const Buffer = extern struct { value: u32 };
2const Sampler = @SpirvType(.sampler);
3const RuntimeArray = @SpirvType(.{ .runtime_array = u32 });
4const SamplerRuntimeArray = @SpirvType(.{ .runtime_array = Sampler });
5const BufferRuntimeArray = @SpirvType(.{ .runtime_array = Buffer });
6
7const a = @extern(*addrspace(.input) const RuntimeArray, .{ .name = "a" });
8const b = @extern(*addrspace(.uniform) const RuntimeArray, .{ .name = "b" });
9const c = @extern(*addrspace(.storage_buffer) const RuntimeArray, .{ .name = "c" });
10const d = @extern(*addrspace(.constant) const RuntimeArray, .{ .name = "d" });
11const e = @extern(*addrspace(.constant) const SamplerRuntimeArray, .{
12 .name = "samplers",
13 .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } },
14});
15const f = @extern(*addrspace(.storage_buffer) [4]Buffer, .{
16 .name = "sized_buffers",
17 .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } },
18});
19const g = @extern(*addrspace(.storage_buffer) BufferRuntimeArray, .{
20 .name = "buffers",
21 .decoration = .{ .descriptor = .{ .set = 0, .binding = 2 } },
22});
23
24comptime {
25 _ = a;
26}
27comptime {
28 _ = b;
29}
30comptime {
31 _ = c;
32}
33
34export fn main() callconv(.{ .spirv_fragment = .{} }) void {
35 _ = &d[0];
36 _ = &e[0];
37 f[0].value = 1;
38 g[0].value = 2;
39}
40
41// error
42// backend=selfhosted
43// target=spirv32-vulkan
44// cpu_features=baseline+runtime_descriptor_array
45//
46// :7:19: error: SPIR-V runtime array is not allowed in the 'input' address space
47// :8:19: error: extern in 'uniform' address space must be a single-item pointer to a struct
48// :9:19: error: extern in 'storage_buffer' address space must be a single-item pointer to a struct
49// :10:19: error: extern in 'constant' address space must point to an opaque SPIR-V type, or to an array of one
test/cases/compile_errors/spirv_extern_var_addrspace.zig+41-5
......@@ -1,11 +1,47 @@
1extern var x: u32;
1const Block = extern struct { x: u32 };
2const RuntimeArray = @SpirvType(.{ .runtime_array = u32 });
23
3export fn main() callconv(.kernel) void {
4 _ = x;
4extern var implicit_addrspace: u32;
5
6const many = @extern([*]addrspace(.storage_buffer) u32, .{
7 .name = "many",
8 .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } },
9});
10const push_array = @extern(*addrspace(.push_constant) const [4]Block, .{ .name = "push_array" });
11const plain_constant = @extern(*addrspace(.constant) const Block, .{
12 .name = "plain_constant",
13 .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } },
14});
15const runtime_array = @extern(*addrspace(.storage_buffer) RuntimeArray, .{
16 .name = "runtime_array",
17 .decoration = .{ .descriptor = .{ .set = 0, .binding = 2 } },
18});
19
20comptime {
21 _ = implicit_addrspace;
22}
23comptime {
24 _ = many;
25}
26comptime {
27 _ = push_array;
28}
29comptime {
30 _ = plain_constant;
31}
32comptime {
33 _ = runtime_array;
534}
635
736// error
837// backend=selfhosted
9// target=spirv64-vulkan
38// target=spirv32-vulkan
1039//
11// :1:15: error: SPIR-V extern variables require an explicit address space
40// :4:32: error: SPIR-V extern variables require an explicit address space
41// :6:22: error: extern in 'storage_buffer' address space must be a single-item pointer to a struct
42// :10:28: error: extern in 'push_constant' address space must be a single-item pointer to a struct
43// :11:32: error: extern in 'constant' address space must point to an opaque SPIR-V type, or to an array of one
44// :15:31: error: extern symbol cannot have type '*addrspace(.storage_buffer) @SpirvType(.runtime_array, u32)'
45// :15:31: note: pointer element type '@SpirvType(.runtime_array, u32)' is not extern compatible
46// :15:31: note: SPIR-V runtime arrays must be the last field of an extern struct
47// :15:31: note: consider enabling the 'runtime_descriptor_array' feature to use the runtime array as the extern pointee
test/cases/compile_errors/spirv_merge_logical_pointers.zig+1
......@@ -11,6 +11,7 @@ export fn a() void {
1111}
1212
1313// error
14// backend=selfhosted
1415// target=spirv32-vulkan
1516//
1617// :9:13: error: value with non-mergable pointer type '*i32' depends on runtime control flow
test/cases/compile_errors/spirv_pointer_cast_requires_offset_zero.zig+7-12
......@@ -1,20 +1,15 @@
1const A = extern struct { x: u32, y: u32 };
2const B = extern struct { a: u64 };
3
4const a = @extern(*addrspace(.uniform) const A, .{
5 .name = "a",
6 .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } },
7});
1const Inner = extern struct { a: u32 };
2const Outer = extern struct { a: u32, b: Inner };
83
94export fn main() callconv(.kernel) void {
10 const b: *addrspace(.uniform) const B = @ptrCast(a);
11 _ = &b;
5 var outer: Outer = undefined;
6 const inner: *Inner = @ptrCast(&outer);
7 _ = &inner;
128}
139
1410// error
1511// backend=selfhosted
1612// target=spirv32-vulkan
1713//
18// :10:44: error: cannot cast pointer '*addrspace(.uniform) const A' to '*addrspace(.uniform) const B'
19// :10:44: note: 'B' must appear at offset 0 inside 'A'
20// :10:44: note: 'uniform' pointers can only reach nested types through a first struct field or an array element
14// :6:27: error: cannot cast pointer '*tmp.Outer' to '*tmp.Inner'
15// :6:27: note: 'tmp.Inner' must appear at offset 0 inside 'tmp.Outer'
test/cases/compile_errors/spirv_runtime_array_as_value_type.zig deleted-26
......@@ -1,26 +0,0 @@
1const RuntimeArray = @SpirvType(.{ .runtime_array = u32 });
2
3const a = @extern(*addrspace(.storage_buffer) RuntimeArray, .{
4 .name = "a",
5 .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } },
6});
7const b = @extern(*addrspace(.uniform) const RuntimeArray, .{
8 .name = "b",
9 .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } },
10});
11
12comptime {
13 _ = a;
14 _ = b;
15}
16
17// error
18// backend=selfhosted
19// target=spirv32-vulkan
20//
21// :3:19: error: extern symbol cannot have type '*addrspace(.storage_buffer) @SpirvType(.runtime_array, u32)'
22// :3:19: note: pointer element type '@SpirvType(.runtime_array, u32)' is not extern compatible
23// :3:19: note: SPIR-V runtime arrays must be the last field of an extern struct
24// :7:19: error: extern symbol cannot have type '*addrspace(.uniform) const @SpirvType(.runtime_array, u32)'
25// :7:19: note: pointer element type '@SpirvType(.runtime_array, u32)' is not extern compatible
26// :7:19: note: SPIR-V runtime arrays must be the last field of an extern struct
test/cases/compile_errors/spirv_slices.zig+9-25
......@@ -1,6 +1,6 @@
11export fn a() void {
2 var buf: [3]f32 = undefined;
3 takesSlice(&buf); // error
2 var x: [3]f32 = .{ 1, 2, 3 };
3 takesSlice(&x);
44}
55
66fn takesSlice(buf: []f32) void {
......@@ -8,34 +8,18 @@ fn takesSlice(buf: []f32) void {
88}
99
1010export fn b() void {
11 var buf: [3]f32 = undefined;
12 for (buf[0..3]) |_| {} // error
13}
14
15export fn c() void {
16 var buf: [3]f32 = undefined;
17 for (&buf) |_| {} // not an error
18}
19
20export fn d() void {
21 const buf: [3]f32 = .{1, 2, 3};
22 for (comptime buf[0..3]) |_| {} // not an error
23}
24
25export fn e() void {
26 const buf: [3]f32 = .{1, 2, 3};
27 for (comptime buf[0..3]) |_| {} // not an error
28 for (buf[0..3]) |_| {} // error
11 const x: [3]f32 = .{ 1, 2, 3 };
12 for (comptime x[0..3]) |_| {}
13 for (&x) |_| {}
14 for (x[0..3]) |_| {}
2915}
3016
3117// error
32// backend=auto
18// backend=selfhosted
3319// target=spirv32-opengl,spirv32-vulkan
3420// cpu_features=baseline+variable_pointers
3521//
3622// :3:16: error: cannot construct slice from address space 'generic'
3723// :3:16: note: only 'shared' and 'storage_buffer' address spaces support slicing on SPIR-V
38// :12:13: error: cannot construct slice from address space 'generic'
39// :12:13: note: only 'shared' and 'storage_buffer' address spaces support slicing on SPIR-V
40// :28:13: error: cannot construct slice from address space 'generic'
41// :28:13: note: only 'shared' and 'storage_buffer' address spaces support slicing on SPIR-V
24// :14:11: error: cannot construct slice from address space 'generic'
25// :14:11: note: only 'shared' and 'storage_buffer' address spaces support slicing on SPIR-V
test/cases/compile_errors/spirv_unsupported_float_width.zig deleted-16
......@@ -1,16 +0,0 @@
1export fn use_f80() callconv(.kernel) void {
2 var x: f80 = 1.5;
3 _ = &x;
4}
5
6export fn use_f16() callconv(.kernel) void {
7 var x: f16 = 1.5;
8 _ = &x;
9}
10
11// error
12// backend=selfhosted
13// target=spirv32-vulkan
14//
15// :2:5: error: 'f80' is not supported on the current SPIR-V feature set
16// :7:5: error: 'f16' is not supported on the current SPIR-V feature set
test/src/Cases.zig+6-5
......@@ -389,11 +389,12 @@ fn addFromDirInner(
389389 const resolved_target = b.resolveTargetQuery(target_query);
390390 const target = &resolved_target.result;
391391 for (backends) |backend| {
392 if (backend == .selfhosted and
393 target.cpu.arch != .aarch64 and target.cpu.arch != .wasm32 and target.cpu.arch != .x86_64 and target.cpu.arch != .spirv64)
394 {
395 // Other backends don't support new liveness format
396 continue;
392 if (backend == .selfhosted) {
393 switch (target.cpu.arch) {
394 .aarch64, .wasm32, .x86_64, .spirv64, .spirv32 => {},
395 // Other backends don't support new liveness format
396 else => continue,
397 }
397398 }
398399
399400 if (backend == .selfhosted and target.cpu.arch == .aarch64) {