authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2026-08-25 21:55:19+03:30
committergravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2026-08-26 08:13:06+03:30
log21575a44837861c0c9a7b2b42904afb0933995ea
treec65c98a1864841211311f2f6824be0f990f02f00
parente955d137e7d23eacd6aeb0eeb406d9fd9cfa9526

spirv: validate extern var address space


8 files changed, 143 insertions(+), 74 deletions(-)

src/Sema.zig+27-11
......@@ -25285,17 +25285,30 @@ fn zirBuiltinExtern(
2528525285 .location, .descriptor => {},
2528625286 };
2528725287
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 => {},
25288 const target = zcu.getTarget();
25289 switch (target.os.tag) {
25290 .vulkan, .opengl => {
25291 const pointee = switch (elem_ty.zigTypeTag(zcu)) {
25292 .array => elem_ty.childType(zcu),
25293 .spirv => if (elem_ty.isSpirvRuntimeArray(zcu)) elem_ty.childType(zcu) else elem_ty,
25294 else => elem_ty,
25295 };
25296 switch (ptr_info.flags.address_space) {
25297 .uniform,
25298 .storage_buffer,
25299 => if (ptr_info.flags.size != .one or pointee.zigTypeTag(zcu) != .@"struct") {
25300 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});
25301 },
25302 .push_constant => if (ptr_info.flags.size != .one or elem_ty.zigTypeTag(zcu) != .@"struct") {
25303 return sema.fail(block, ty_src, "extern in 'push_constant' address space must be a single-item pointer to a struct", .{});
25304 },
25305 .constant => if (target.os.tag == .vulkan and (pointee.zigTypeTag(zcu) != .spirv or pointee.isSpirvRuntimeArray(zcu))) {
25306 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", .{});
25307 },
25308 else => if (elem_ty.isSpirvRuntimeArray(zcu)) {
25309 return sema.fail(block, ty_src, "SPIR-V runtime array is not allowed in the '{t}' address space", .{ptr_info.flags.address_space});
25310 },
25311 }
2529925312 },
2530025313 else => {},
2530125314 }
......@@ -25717,6 +25730,9 @@ pub fn explainWhyTypeIsNotExtern(
2571725730 .spirv => {
2571825731 assert(ty.isSpirvRuntimeArray(zcu));
2571925732 try sema.errNote(src_loc, msg, "SPIR-V runtime arrays must be the last field of an extern struct", .{});
25733 if (position == .other) {
25734 try sema.errNote(src_loc, msg, "consider enabling the 'runtime_descriptor_array' feature to use the runtime array as the extern pointee", .{});
25735 }
2572025736 },
2572125737
2572225738 .float => try sema.errNote(src_loc, msg, "'{f}' is not extern compatible on this target", .{ty.fmt(pt)}),
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 => {
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_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/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_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