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(...@@ -22151,34 +22151,7 @@ fn ptrCastFull(
22151 }22151 }
2215222152
22153 try sema.validateRuntimeValue(block, operand_src, operand);22153 try sema.validateRuntimeValue(block, operand_src, operand);
2215422154 try sema.checkLogicalPtrCast(block, src, operand_ty, dest_ty);
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 }
2218222155
22183 const can_cast_to_int = !target_util.shouldBlockPointerOps(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu));22156 const can_cast_to_int = !target_util.shouldBlockPointerOps(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu));
22184 const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu);22157 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(...@@ -22721,6 +22694,8 @@ fn checkPtrType(
22721fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {22694fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
22722 const pt = sema.pt;22695 const pt = sema.pt;
22723 const zcu = pt.zcu;22696 const zcu = pt.zcu;
22697
22698 if (block.isComptime() or block.is_typeof) return;
22724 if (zcu.intern_pool.indexToKey(ty.toIntern()) == .ptr_type) {22699 if (zcu.intern_pool.indexToKey(ty.toIntern()) == .ptr_type) {
22725 const target = zcu.getTarget();22700 const target = zcu.getTarget();
22726 const as = ty.ptrAddressSpace(zcu);22701 const as = ty.ptrAddressSpace(zcu);
...@@ -22731,12 +22706,8 @@ fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ...@@ -22731,12 +22706,8 @@ fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
22731 try sema.errNote(22706 try sema.errNote(
22732 src,22707 src,
22733 msg,22708 msg,
22734 "cannot perform arithmetic on pointers with address space '{s}' on target {s}-{s}",22709 "pointers with address space '{t}' do not support arithmetic or indexing on target {t}-{t}",
22735 .{22710 .{ as, target.cpu.arch.family(), target.os.tag },
22736 @tagName(as),
22737 @tagName(target.cpu.arch.family()),
22738 @tagName(target.os.tag),
22739 },
22740 );22711 );
22741 break :msg msg;22712 break :msg msg;
22742 });22713 });
...@@ -22744,6 +22715,49 @@ fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ...@@ -22744,6 +22715,49 @@ fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
22744 }22715 }
22745}22716}
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
22747fn checkVectorElemType(22761fn checkVectorElemType(
22748 sema: *Sema,22762 sema: *Sema,
22749 block: *Block,22763 block: *Block,
...@@ -25285,17 +25299,30 @@ fn zirBuiltinExtern(...@@ -25285,17 +25299,30 @@ fn zirBuiltinExtern(
25285 .location, .descriptor => {},25299 .location, .descriptor => {},
25286 };25300 };
2528725301
25288 switch (zcu.getTarget().os.tag) {25302 const target = zcu.getTarget();
25289 .vulkan, .opengl => switch (ptr_info.flags.address_space) {25303 switch (target.os.tag) {
25290 .storage_buffer, .uniform, .push_constant => if (ptr_info.flags.size != .one) {25304 .vulkan, .opengl => {
25291 return sema.failWithOwnedErrorMsg(block, msg: {25305 const pointee = switch (elem_ty.zigTypeTag(zcu)) {
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)});25306 .array => elem_ty.childType(zcu),
25293 errdefer msg.destroy(sema.gpa);25307 .spirv => if (elem_ty.isSpirvRuntimeArray(zcu)) elem_ty.childType(zcu) else elem_ty,
25294 try sema.errNote(ty_src, msg, "wrap the element type in a struct containing a runtime-sized array", .{});25308 else => elem_ty,
25295 break :msg msg;25309 };
25296 });25310 switch (ptr_info.flags.address_space) {
25297 },25311 .uniform,
25298 else => {},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 }
25299 },25326 },
25300 else => {},25327 else => {},
25301 }25328 }
...@@ -25717,6 +25744,9 @@ pub fn explainWhyTypeIsNotExtern(...@@ -25717,6 +25744,9 @@ pub fn explainWhyTypeIsNotExtern(
25717 .spirv => {25744 .spirv => {
25718 assert(ty.isSpirvRuntimeArray(zcu));25745 assert(ty.isSpirvRuntimeArray(zcu));
25719 try sema.errNote(src_loc, msg, "SPIR-V runtime arrays must be the last field of an extern struct", .{});25746 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 }
25720 },25750 },
2572125751
25722 .float => try sema.errNote(src_loc, msg, "'{f}' is not extern compatible on this target", .{ty.fmt(pt)}),25752 .float => try sema.errNote(src_loc, msg, "'{f}' is not extern compatible on this target", .{ty.fmt(pt)}),
...@@ -27351,6 +27381,7 @@ fn elemPtrOneLayerOnly(...@@ -27351,6 +27381,7 @@ fn elemPtrOneLayerOnly(
2735127381
27352 try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_src);27382 try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_src);
27353 try sema.validateRuntimeValue(block, indexable_src, indexable);27383 try sema.validateRuntimeValue(block, indexable_src, indexable);
27384 try sema.checkLogicalPtrOperation(block, src, indexable_ty);
2735427385
27355 if (child_ty.abiSize(zcu) == 0) {27386 if (child_ty.abiSize(zcu) == 0) {
27356 // zero-bit child type; just bitcast the pointer27387 // zero-bit child type; just bitcast the pointer
...@@ -27423,6 +27454,7 @@ fn elemVal(...@@ -27423,6 +27454,7 @@ fn elemVal(
27423 },27454 },
27424 .partially_comptime, .fully_comptime => unreachable, // caught by `validateRuntimeElemAccess`27455 .partially_comptime, .fully_comptime => unreachable, // caught by `validateRuntimeElemAccess`
27425 }27456 }
27457 try sema.checkLogicalPtrOperation(block, src, indexable_ty);
2742627458
27427 return block.addBinOp(.ptr_elem_val, indexable, elem_index);27459 return block.addBinOp(.ptr_elem_val, indexable, elem_index);
27428 },27460 },
...@@ -27824,6 +27856,7 @@ fn elemValSlice(...@@ -27824,6 +27856,7 @@ fn elemValSlice(
27824 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;27856 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
27825 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);27857 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
27826 }27858 }
27859 try sema.checkLogicalPtrOperation(block, src, slice_ty);
27827 return block.addBinOp(.slice_elem_val, slice, elem_index);27860 return block.addBinOp(.slice_elem_val, slice, elem_index);
27828}27861}
2782927862
...@@ -27875,6 +27908,7 @@ fn elemPtrSlice(...@@ -27875,6 +27908,7 @@ fn elemPtrSlice(
2787527908
27876 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_src);27909 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_src);
27877 try sema.validateRuntimeValue(block, slice_src, slice);27910 try sema.validateRuntimeValue(block, slice_src, slice);
27911 try sema.checkLogicalPtrOperation(block, src, slice_ty);
2787827912
27879 if (oob_safety and block.wantSafety()) {27913 if (oob_safety and block.wantSafety()) {
27880 const len_inst = len: {27914 const len_inst = len: {
src/Sema/type_resolution.zig+11
...@@ -334,6 +334,17 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {...@@ -334,6 +334,17 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
334 break :msg msg;334 break :msg msg;
335 });335 });
336 }336 }
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 }
337 } else {348 } else {
338 return sema.failWithOwnedErrorMsg(&block, msg: {349 return sema.failWithOwnedErrorMsg(&block, msg: {
339 const msg = try sema.errMsg(field_ty_src, "cannot directly embed SPIR-V type '{f}' in struct", .{field_ty.fmt(pt)});350 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...@@ -3145,7 +3145,8 @@ pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool
31453145
3146 .spirv => switch (position) {3146 .spirv => switch (position) {
3147 .struct_field, .union_field => true,3147 .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),
3149 },3150 },
31503151
3151 .pointer => {3152 .pointer => {
src/codegen/spirv/CodeGen.zig+52-17
...@@ -617,7 +617,8 @@ pub fn structType(...@@ -617,7 +617,8 @@ pub fn structType(
617617
618/// Returns the layout-decorated variant of `ty` for use inside a Vulkan/OpenGL618/// Returns the layout-decorated variant of `ty` for use inside a Vulkan/OpenGL
619/// interface block. Vulkan forbids nested Block decorations, so recursive calls619/// 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.
621///622///
622/// This is distinct from `resolveType` because SPIR-V forbids such decorations623/// This is distinct from `resolveType` because SPIR-V forbids such decorations
623/// on the pointee of a Function-scope variable.624/// 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 {...@@ -695,26 +696,30 @@ pub fn layoutType(cg: *CodeGen, ty: Type, is_block_root: bool) Error!Id {
695 },696 },
696 .array => id: {697 .array => id: {
697 const elem_ty = ty.childType(zcu);698 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);
699 const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel(zcu)) orelse700 const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel(zcu)) orelse
700 return cg.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel(zcu)});701 return cg.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel(zcu)});
701 const id = try cg.arrayType(try cg.constInt(.u32, total_len), elem_ty_id);702 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 if (!is_block_root and elem_ty.hasRuntimeBits(zcu)) {
703 .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) },704 try cg.decorate(id, .{
704 });705 .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) },
706 });
707 }
705 break :id id;708 break :id id;
706 },709 },
707 .spirv => if (ty.isSpirvRuntimeArray(zcu)) id: {710 .spirv => if (ty.isSpirvRuntimeArray(zcu)) id: {
708 const elem_ty = ty.childType(zcu);711 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);
710 const id = cg.allocId();713 const id = cg.allocId();
711 try cg.sections.globals.emit(gpa, .OpTypeRuntimeArray, .{714 try cg.sections.globals.emit(gpa, .OpTypeRuntimeArray, .{
712 .id_result = id,715 .id_result = id,
713 .element_type = elem_ty_id,716 .element_type = elem_ty_id,
714 });717 });
715 if (elem_ty.hasRuntimeBits(zcu)) try cg.decorate(id, .{718 if (!is_block_root and elem_ty.hasRuntimeBits(zcu)) {
716 .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) },719 try cg.decorate(id, .{
717 });720 .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) },
721 });
722 }
718 break :id id;723 break :id id;
719 } else return cg.resolveType(ty, .indirect),724 } else return cg.resolveType(ty, .indirect),
720 else => return cg.resolveType(ty, .indirect),725 else => return cg.resolveType(ty, .indirect),
...@@ -955,11 +960,18 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {...@@ -955,11 +960,18 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
955 switch (target.os.tag) {960 switch (target.os.tag) {
956 .vulkan, .opengl => {961 .vulkan, .opengl => {
957 switch (storage_class) {962 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 => {
959 if (ty.hasRuntimeBits(zcu)) {968 if (ty.hasRuntimeBits(zcu)) {
960 try cg.decorate(ptr_ty_id, .{969 if (!ty.isSpirvRuntimeArray(zcu)) {
961 .array_stride = .{ .array_stride = @intCast(ty.abiSize(zcu)) },970 try cg.decorate(
962 });971 ptr_ty_id,
972 .{ .array_stride = .{ .array_stride = @intCast(ty.abiSize(zcu)) } },
973 );
974 }
963 if (!cg.needsLayout(as, ty)) try cg.decorateLayout(ty, ty_id);975 if (!cg.needsLayout(as, ty)) try cg.decorateLayout(ty, ty_id);
964 }976 }
965 if (key.is_const and storage_class == .storage_buffer) {977 if (key.is_const and storage_class == .storage_buffer) {
...@@ -2047,6 +2059,13 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id {...@@ -2047,6 +2059,13 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id {
2047 while (cur.toIntern() != dst_child.toIntern()) {2059 while (cur.toIntern() != dst_child.toIntern()) {
2048 switch (cur.zigTypeTag(zcu)) {2060 switch (cur.zigTypeTag(zcu)) {
2049 .array => {2061 .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 }
2050 cur = cur.childType(zcu);2069 cur = cur.childType(zcu);
2051 depth += 1;2070 depth += 1;
2052 },2071 },
...@@ -2086,7 +2105,7 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id {...@@ -2086,7 +2105,7 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id {
2086 }2105 }
2087 }2106 }
20882107
2089 return cg.fail("cannot perform pointer cast: '{f}' to '{f}'", .{2108 return cg.fail("cannot cast pointer '{f}' to '{f}'", .{
2090 parent_ptr_ty.fmt(pt),2109 parent_ptr_ty.fmt(pt),
2091 oac.new_ptr_ty.fmt(pt),2110 oac.new_ptr_ty.fmt(pt),
2092 });2111 });
...@@ -6029,7 +6048,13 @@ fn bitCast(...@@ -6029,7 +6048,13 @@ fn bitCast(
6029 while (cur.toIntern() != dst_child.toIntern()) : (try indices.append(gpa, 0)) {6048 while (cur.toIntern() != dst_child.toIntern()) : (try indices.append(gpa, 0)) {
6030 cur = switch (cur.zigTypeTag(zcu)) {6049 cur = switch (cur.zigTypeTag(zcu)) {
6031 .array, .vector => cur.childType(zcu),6050 .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 },
6033 else => unreachable,6058 else => unreachable,
6034 };6059 };
6035 }6060 }
...@@ -6739,9 +6764,19 @@ fn ptrElemPtr(cg: *CodeGen, ptr_ty: Type, ptr_id: Id, index_id: Id) !Id {...@@ -6739,9 +6764,19 @@ fn ptrElemPtr(cg: *CodeGen, ptr_ty: Type, ptr_id: Id, index_id: Id) !Id {
6739 const zcu = cg.zcu;6764 const zcu = cg.zcu;
6740 // Construct new pointer type for the resulting pointer6765 // Construct new pointer type for the resulting pointer
6741 const as = ptr_ty.ptrAddressSpace(zcu);6766 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);
6743 const elem_ptr_ty_id = try cg.ptrType(elem_ty_id, cg.storageClass(as));6778 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) {
6745 // Pointer-to-array. In this case, the resulting pointer is not of the same type6780 // Pointer-to-array. In this case, the resulting pointer is not of the same type
6746 // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain.6781 // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain.
6747 return cg.accessChainId(elem_ptr_ty_id, ptr_id, &.{index_id});6782 return cg.accessChainId(elem_ptr_ty_id, ptr_id, &.{index_id});
test/behavior/array.zig+4
...@@ -7,6 +7,8 @@ const expect = testing.expect;...@@ -7,6 +7,8 @@ const expect = testing.expect;
7const expectEqual = testing.expectEqual;7const expectEqual = testing.expectEqual;
88
9test "array to slice" {9test "array to slice" {
10 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
11
10 const a: u32 align(4) = 3;12 const a: u32 align(4) = 3;
11 const b: u32 align(8) = 4;13 const b: u32 align(8) = 4;
12 const a_slice: []align(1) const u32 = @as(*const [1]u32, &a)[0..];14 const a_slice: []align(1) const u32 = @as(*const [1]u32, &a)[0..];
...@@ -19,8 +21,10 @@ test "array to slice" {...@@ -19,8 +21,10 @@ test "array to slice" {
19}21}
2022
21test "arrays" {23test "arrays" {
24 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
22 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;25 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
23 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO26 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
27
24 var array: [5]u32 = undefined;28 var array: [5]u32 = undefined;
2529
26 var i: u32 = 0;30 var i: u32 = 0;
test/behavior/backing_int.zig+6
...@@ -137,6 +137,8 @@ const T5 = union(E5) {...@@ -137,6 +137,8 @@ const T5 = union(E5) {
137};137};
138138
139test "@backingInt with tagged unions" {139test "@backingInt with tagged unions" {
140 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
141
140 const static = struct {142 const static = struct {
141 fn doTheTest(v1: T1, v2: T2, v4: T4, v5: T5) !void {143 fn doTheTest(v1: T1, v2: T2, v4: T4, v5: T5) !void {
142 const b1 = @backingInt(v1);144 const b1 = @backingInt(v1);
...@@ -311,6 +313,8 @@ const U5 = packed union(u0) {...@@ -311,6 +313,8 @@ const U5 = packed union(u0) {
311};313};
312314
313test "@backingInt with packed unions" {315test "@backingInt with packed unions" {
316 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
317
314 const static = struct {318 const static = struct {
315 fn doTheTest(v1: U1, v2: U2, v3: U3, v4: U4, v5: U5) !void {319 fn doTheTest(v1: U1, v2: U2, v3: U3, v4: U4, v5: U5) !void {
316 const b1 = @backingInt(v1);320 const b1 = @backingInt(v1);
...@@ -339,6 +343,8 @@ test "@backingInt with packed unions" {...@@ -339,6 +343,8 @@ test "@backingInt with packed unions" {
339}343}
340344
341test "@fromBackingInt with packed unions" {345test "@fromBackingInt with packed unions" {
346 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
347
342 const static = struct {348 const static = struct {
343 fn doTheTest(349 fn doTheTest(
344 b1: @typeInfo(U1).@"union".backing_integer.?,350 b1: @typeInfo(U1).@"union".backing_integer.?,
test/behavior/cast.zig+1
...@@ -1806,6 +1806,7 @@ test "cast compatible optional types" {...@@ -1806,6 +1806,7 @@ test "cast compatible optional types" {
18061806
1807test "coerce undefined single-item pointer of array to error union of slice" {1807test "coerce undefined single-item pointer of array to error union of slice" {
1808 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1808 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1809 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
18091810
1810 const a = @as([*]u8, undefined)[0..0];1811 const a = @as([*]u8, undefined)[0..0];
1811 var b: error{a}![]const u8 = a;1812 var b: error{a}![]const u8 = a;
test/behavior/comptime_memory.zig+2
...@@ -437,6 +437,8 @@ test "type pun null pointer-like optional" {...@@ -437,6 +437,8 @@ test "type pun null pointer-like optional" {
437}437}
438438
439test "write empty array to end" {439test "write empty array to end" {
440 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
441
440 comptime var array: [5]u8 = "hello".*;442 comptime var array: [5]u8 = "hello".*;
441 array[5..5].* = .{};443 array[5..5].* = .{};
442 array[5..5].* = [0]u8{};444 array[5..5].* = [0]u8{};
test/behavior/for.zig+1
...@@ -391,6 +391,7 @@ test "raw pointer and counter" {...@@ -391,6 +391,7 @@ test "raw pointer and counter" {
391test "inline for with slice as the comptime-known" {391test "inline for with slice as the comptime-known" {
392 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO392 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
393 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO393 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
394 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
394395
395 const comptime_slice = "hello";396 const comptime_slice = "hello";
396 var runtime_i: usize = 3;397 var runtime_i: usize = 3;
test/behavior/math.zig+2
...@@ -456,6 +456,7 @@ test "division" {...@@ -456,6 +456,7 @@ test "division" {
456 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO456 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
457 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO457 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
458 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;458 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
459 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
459 try testIntDivision();460 try testIntDivision();
460 try comptime testIntDivision();461 try comptime testIntDivision();
461462
...@@ -649,6 +650,7 @@ test "division half-precision floats" {...@@ -649,6 +650,7 @@ test "division half-precision floats" {
649 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO650 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
650 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO651 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
651 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;652 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
653 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
652654
653 try testDivisionFP16();655 try testDivisionFP16();
654 try comptime testDivisionFP16();656 try comptime testDivisionFP16();
test/behavior/packed-union.zig+3
...@@ -229,6 +229,8 @@ test "initialize packed union field to undefined at comptime" {...@@ -229,6 +229,8 @@ test "initialize packed union field to undefined at comptime" {
229}229}
230230
231test "convert from/to backing int" {231test "convert from/to backing int" {
232 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
233
232 const U = packed union(u10) {234 const U = packed union(u10) {
233 a: i10,235 a: i10,
234 b: enum(u10) { x, y, z },236 b: enum(u10) { x, y, z },
...@@ -244,6 +246,7 @@ test "convert from/to backing int" {...@@ -244,6 +246,7 @@ test "convert from/to backing int" {
244246
245test "equality with wide backing integer" {247test "equality with wide backing integer" {
246 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35982248 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
248 const U = packed union(i200) {251 const U = packed union(i200) {
249 x: u200,252 x: u200,
test/behavior/pointers.zig+1
...@@ -701,6 +701,7 @@ test "pointer-to-array constness for zero-size elements, var" {...@@ -701,6 +701,7 @@ test "pointer-to-array constness for zero-size elements, var" {
701701
702test "pointer-to-array constness for zero-size elements, const" {702test "pointer-to-array constness for zero-size elements, const" {
703 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO703 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
704 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
704705
705 try constant();706 try constant();
706 try comptime constant();707 try comptime constant();
test/behavior/ptrcast.zig+2
...@@ -6,6 +6,7 @@ const native_endian = builtin.target.cpu.arch.endian();...@@ -6,6 +6,7 @@ const native_endian = builtin.target.cpu.arch.endian();
66
7test "reinterpret bytes as integer with nonzero offset" {7test "reinterpret bytes as integer with nonzero offset" {
8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
910
10 try testReinterpretBytesAsInteger();11 try testReinterpretBytesAsInteger();
11 try comptime testReinterpretBytesAsInteger();12 try comptime testReinterpretBytesAsInteger();
...@@ -39,6 +40,7 @@ fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void {...@@ -39,6 +40,7 @@ fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void {
3940
40test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" {41test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" {
41 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO42 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
43 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
4244
43 try testReinterpretStructWrappedBytesAsInteger();45 try testReinterpretStructWrappedBytesAsInteger();
44 try comptime testReinterpretStructWrappedBytesAsInteger();46 try comptime testReinterpretStructWrappedBytesAsInteger();
test/behavior/sizeof_and_typeof.zig+2
...@@ -405,6 +405,8 @@ test "Extern function calls, dereferences and field access in @TypeOf" {...@@ -405,6 +405,8 @@ test "Extern function calls, dereferences and field access in @TypeOf" {
405}405}
406406
407test "@sizeOf struct is resolved when used as operand of slicing" {407test "@sizeOf struct is resolved when used as operand of slicing" {
408 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
409
408 const dummy = struct {};410 const dummy = struct {};
409 const S = struct {411 const S = struct {
410 var buf: [1]u8 = undefined;412 var buf: [1]u8 = undefined;
test/behavior/slice.zig+8
...@@ -59,6 +59,8 @@ test "const slice" {...@@ -59,6 +59,8 @@ test "const slice" {
59}59}
6060
61test "comptime slice of undefined pointer of length 0" {61test "comptime slice of undefined pointer of length 0" {
62 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
63
62 const slice1 = @as([*]i32, undefined)[0..0];64 const slice1 = @as([*]i32, undefined)[0..0];
63 try expect(slice1.len == 0);65 try expect(slice1.len == 0);
64 const slice2 = @as([*]i32, undefined)[100..100];66 const slice2 = @as([*]i32, undefined)[100..100];
...@@ -173,6 +175,8 @@ test "pass a slice of types to a function" {...@@ -173,6 +175,8 @@ test "pass a slice of types to a function" {
173175
174test "generic malloc free" {176test "generic malloc free" {
175 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO177 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
178 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
179
176 const a = memAlloc(u8, 10) catch unreachable;180 const a = memAlloc(u8, 10) catch unreachable;
177 memFree(u8, a);181 memFree(u8, a);
178}182}
...@@ -209,6 +213,8 @@ test "comptime slice of pointer preserves comptime var" {...@@ -209,6 +213,8 @@ test "comptime slice of pointer preserves comptime var" {
209}213}
210214
211test "comptime pointer cast array and then slice" {215test "comptime pointer cast array and then slice" {
216 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
217
212 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };218 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
213219
214 const ptrA: [*]const u8 = @as([*]const u8, @ptrCast(&array));220 const ptrA: [*]const u8 = @as([*]const u8, @ptrCast(&array));
...@@ -1079,6 +1085,8 @@ test "conditionally return second argument slice" {...@@ -1079,6 +1085,8 @@ test "conditionally return second argument slice" {
1079}1085}
10801086
1081test "slice field alignment" {1087test "slice field alignment" {
1088 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1089
1082 const S = struct {1090 const S = struct {
1083 fn doTheTest(p: *align(1) const []u8) !void {1091 fn doTheTest(p: *align(1) const []u8) !void {
1084 comptime assert(@TypeOf(&p.ptr) == *align(1) const [*]u8);1092 comptime assert(@TypeOf(&p.ptr) == *align(1) const [*]u8);
test/behavior/spirv.zig+5-6
...@@ -49,13 +49,12 @@ test "@SpirvType" {...@@ -49,13 +49,12 @@ test "@SpirvType" {
49 _ = runtime_array;49 _ = runtime_array;
50}50}
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
56test "@ptrCast to first field type" {52test "@ptrCast to first field type" {
57 const pc_inner: *addrspace(.push_constant) const InnerStruct = @ptrCast(outer_pc);53 const Inner = extern struct { a: u32 };
58 _ = pc_inner;54 const Outer = extern struct { a: Inner, b: u32 };
55 var outer: Outer = undefined;
56 var inner: *Inner = @ptrCast(&outer);
57 _ = &inner;
59}58}
6059
61test "@SpirvType equality" {60test "@SpirvType equality" {
test/behavior/string_literals.zig+2
...@@ -84,6 +84,8 @@ test "string literal pointer sentinel" {...@@ -84,6 +84,8 @@ test "string literal pointer sentinel" {
84}84}
8585
86test "sentinel slice of string literal" {86test "sentinel slice of string literal" {
87 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
88
87 const string = "Hello!\x00World!";89 const string = "Hello!\x00World!";
88 try std.testing.expect(@TypeOf(string) == *const [13:0]u8);90 try std.testing.expect(@TypeOf(string) == *const [13:0]u8);
8991
test/behavior/vector.zig+2
...@@ -680,6 +680,8 @@ test "vector division operators" {...@@ -680,6 +680,8 @@ test "vector division operators" {
680 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO680 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
681 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO681 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
682 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;682 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
683 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
684
683 const S = struct {685 const S = struct {
684 fn doTheTestDiv(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void {686 fn doTheTestDiv(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void {
685 const is_signed_int = switch (@typeInfo(T)) {687 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 });...@@ -3,22 +3,27 @@ const RuntimeArray = @SpirvType(.{ .runtime_array = u32 });
3const Foo = struct {3const Foo = struct {
4 s: Sampler,4 s: Sampler,
5};5};
6const Baz = struct {6const Bar = struct {
7 a: RuntimeArray,7 a: RuntimeArray,
8};8};
9const Qux = extern struct {9const Baz = extern struct {
10 a: RuntimeArray,10 a: RuntimeArray,
11 b: u32,11 b: u32,
12};12};
13const Qux = extern struct { _: @SpirvType(.{ .runtime_array = Sampler }) };
13export fn a() void {14export fn a() void {
14 var foo: Foo = undefined;15 var foo: Foo = undefined;
15 _ = &foo;16 _ = &foo;
16}17}
17export fn c() void {18export fn c() void {
19 var bar: Bar = undefined;
20 _ = &bar;
21}
22export fn d() void {
18 var baz: Baz = undefined;23 var baz: Baz = undefined;
19 _ = &baz;24 _ = &baz;
20}25}
21export fn d() void {26export fn e() void {
22 var qux: Qux = undefined;27 var qux: Qux = undefined;
23 _ = &qux;28 _ = &qux;
24}29}
...@@ -27,9 +32,11 @@ export fn d() void {...@@ -27,9 +32,11 @@ export fn d() void {
27// backend=selfhosted32// backend=selfhosted
28// target=spirv32-vulkan33// target=spirv32-vulkan
29//34//
30// :4:8: error: cannot directly embed SPIR-V type 'tmp.Sampler__SpirvType_4' in struct35// :4:8: error: cannot directly embed SPIR-V type '@SpirvType(.sampler)' in struct
31// :4:8: note: opaque types have unknown size36// :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)'
33// :7:5: note: while checking this field38// :7:5: note: while checking this field
34// :9:20: error: struct field of type 'tmp.RuntimeArray__SpirvType_11' must be the last field39// :9:20: error: struct field of type '@SpirvType(.runtime_array, u32)' must be the last field
35// :10:5: note: while checking this field40// :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 {...@@ -10,4 +10,4 @@ comptime {
10// backend=selfhosted10// backend=selfhosted
11// target=spirv32-vulkan11// target=spirv32-vulkan
12//12//
13// :1:45: error: "flat" decoration requires "input" or "output" address space13// :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 {...@@ -22,14 +22,50 @@ export fn ptrIntArithmetic() void {
22 _ = ptr0 - 10;22 _ = ptr0 - 10;
23}23}
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
25// error53// error
26// target=spirv64-vulkan54// target=spirv64-vulkan
27//55//
28// :3:21: error: illegal operation on logical pointer of type '*u8'56// :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-vulkan57// :3:21: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan
30// :8:20: error: illegal operation on logical pointer of type '*u8'58// :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-vulkan59// :8:20: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan
32// :16:17: error: illegal operation on logical pointer of type '*u8'60// :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-vulkan61// :16:17: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan
34// :22:14: error: illegal operation on logical pointer of type '[*]u8'62// :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-vulkan63// :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 {...@@ -19,5 +19,5 @@ export fn main2() callconv(.kernel) void {
19// backend=selfhosted19// backend=selfhosted
20// target=spirv32-vulkan20// target=spirv32-vulkan
21//21//
22// :10:15: error: cannot load SPIR-V runtime array value22// :10:18: error: cannot load SPIR-V runtime array value
23// :15:12: error: cannot load SPIR-V runtime array value23// :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 @@...@@ -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 {4extern var implicit_addrspace: u32;
4 _ = x;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;
5}34}
635
7// error36// error
8// backend=selfhosted37// backend=selfhosted
9// target=spirv64-vulkan38// target=spirv32-vulkan
10//39//
11// :1:15: error: SPIR-V extern variables require an explicit address space40// :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 {...@@ -11,6 +11,7 @@ export fn a() void {
11}11}
1212
13// error13// error
14// backend=selfhosted
14// target=spirv32-vulkan15// target=spirv32-vulkan
15//16//
16// :9:13: error: value with non-mergable pointer type '*i32' depends on runtime control flow17// :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 @@...@@ -1,20 +1,15 @@
1const A = extern struct { x: u32, y: u32 };1const Inner = extern struct { a: u32 };
2const B = extern struct { a: u64 };2const Outer = extern struct { a: u32, b: Inner };
3
4const a = @extern(*addrspace(.uniform) const A, .{
5 .name = "a",
6 .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } },
7});
83
9export fn main() callconv(.kernel) void {4export fn main() callconv(.kernel) void {
10 const b: *addrspace(.uniform) const B = @ptrCast(a);5 var outer: Outer = undefined;
11 _ = &b;6 const inner: *Inner = @ptrCast(&outer);
7 _ = &inner;
12}8}
139
14// error10// error
15// backend=selfhosted11// backend=selfhosted
16// target=spirv32-vulkan12// target=spirv32-vulkan
17//13//
18// :10:44: error: cannot cast pointer '*addrspace(.uniform) const A' to '*addrspace(.uniform) const B'14// :6:27: error: cannot cast pointer '*tmp.Outer' to '*tmp.Inner'
19// :10:44: note: 'B' must appear at offset 0 inside 'A'15// :6:27: note: 'tmp.Inner' must appear at offset 0 inside 'tmp.Outer'
20// :10:44: note: 'uniform' pointers can only reach nested types through a first struct field or an array element
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 @@...@@ -1,6 +1,6 @@
1export fn a() void {1export fn a() void {
2 var buf: [3]f32 = undefined;2 var x: [3]f32 = .{ 1, 2, 3 };
3 takesSlice(&buf); // error3 takesSlice(&x);
4}4}
55
6fn takesSlice(buf: []f32) void {6fn takesSlice(buf: []f32) void {
...@@ -8,34 +8,18 @@ fn takesSlice(buf: []f32) void {...@@ -8,34 +8,18 @@ fn takesSlice(buf: []f32) void {
8}8}
99
10export fn b() void {10export fn b() void {
11 var buf: [3]f32 = undefined;11 const x: [3]f32 = .{ 1, 2, 3 };
12 for (buf[0..3]) |_| {} // error12 for (comptime x[0..3]) |_| {}
13}13 for (&x) |_| {}
1414 for (x[0..3]) |_| {}
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
29}15}
3016
31// error17// error
32// backend=auto18// backend=selfhosted
33// target=spirv32-opengl,spirv32-vulkan19// target=spirv32-opengl,spirv32-vulkan
34// cpu_features=baseline+variable_pointers20// cpu_features=baseline+variable_pointers
35//21//
36// :3:16: error: cannot construct slice from address space 'generic'22// :3:16: error: cannot construct slice from address space 'generic'
37// :3:16: note: only 'shared' and 'storage_buffer' address spaces support slicing on SPIR-V23// :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'24// :14:11: error: cannot construct slice from address space 'generic'
39// :12:13: note: only 'shared' and 'storage_buffer' address spaces support slicing on SPIR-V25// :14:11: 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
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(...@@ -389,11 +389,12 @@ fn addFromDirInner(
389 const resolved_target = b.resolveTargetQuery(target_query);389 const resolved_target = b.resolveTargetQuery(target_query);
390 const target = &resolved_target.result;390 const target = &resolved_target.result;
391 for (backends) |backend| {391 for (backends) |backend| {
392 if (backend == .selfhosted and392 if (backend == .selfhosted) {
393 target.cpu.arch != .aarch64 and target.cpu.arch != .wasm32 and target.cpu.arch != .x86_64 and target.cpu.arch != .spirv64)393 switch (target.cpu.arch) {
394 {394 .aarch64, .wasm32, .x86_64, .spirv64, .spirv32 => {},
395 // Other backends don't support new liveness format395 // Other backends don't support new liveness format
396 continue;396 else => continue,
397 }
397 }398 }
398399
399 if (backend == .selfhosted and target.cpu.arch == .aarch64) {400 if (backend == .selfhosted and target.cpu.arch == .aarch64) {