diff --git a/src/Sema.zig b/src/Sema.zig index 33746cd9817b99ed88cf68090239901706533e8c..48756b0607c2041ca10d35f95b3a60782435e0ca 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -25285,17 +25285,30 @@ fn zirBuiltinExtern( .location, .descriptor => {}, }; - switch (zcu.getTarget().os.tag) { - .vulkan, .opengl => switch (ptr_info.flags.address_space) { - .storage_buffer, .uniform, .push_constant => if (ptr_info.flags.size != .one) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(ty_src, "extern in '{s}' address space must be a single-item pointer to a struct", .{@tagName(ptr_info.flags.address_space)}); - errdefer msg.destroy(sema.gpa); - try sema.errNote(ty_src, msg, "wrap the element type in a struct containing a runtime-sized array", .{}); - break :msg msg; - }); - }, - else => {}, + const target = zcu.getTarget(); + switch (target.os.tag) { + .vulkan, .opengl => { + const pointee = switch (elem_ty.zigTypeTag(zcu)) { + .array => elem_ty.childType(zcu), + .spirv => if (elem_ty.isSpirvRuntimeArray(zcu)) elem_ty.childType(zcu) else elem_ty, + else => elem_ty, + }; + switch (ptr_info.flags.address_space) { + .uniform, + .storage_buffer, + => if (ptr_info.flags.size != .one or pointee.zigTypeTag(zcu) != .@"struct") { + return sema.fail(block, ty_src, "extern in '{t}' address space must be a single-item pointer to a struct", .{ptr_info.flags.address_space}); + }, + .push_constant => if (ptr_info.flags.size != .one or elem_ty.zigTypeTag(zcu) != .@"struct") { + return sema.fail(block, ty_src, "extern in 'push_constant' address space must be a single-item pointer to a struct", .{}); + }, + .constant => if (target.os.tag == .vulkan and (pointee.zigTypeTag(zcu) != .spirv or pointee.isSpirvRuntimeArray(zcu))) { + return sema.fail(block, ty_src, "extern in 'constant' address space must point to an opaque SPIR-V type, or to an array of one", .{}); + }, + else => if (elem_ty.isSpirvRuntimeArray(zcu)) { + return sema.fail(block, ty_src, "SPIR-V runtime array is not allowed in the '{t}' address space", .{ptr_info.flags.address_space}); + }, + } }, else => {}, } @@ -25717,6 +25730,9 @@ pub fn explainWhyTypeIsNotExtern( .spirv => { assert(ty.isSpirvRuntimeArray(zcu)); try sema.errNote(src_loc, msg, "SPIR-V runtime arrays must be the last field of an extern struct", .{}); + if (position == .other) { + try sema.errNote(src_loc, msg, "consider enabling the 'runtime_descriptor_array' feature to use the runtime array as the extern pointee", .{}); + } }, .float => try sema.errNote(src_loc, msg, "'{f}' is not extern compatible on this target", .{ty.fmt(pt)}), diff --git a/src/Sema/type_resolution.zig b/src/Sema/type_resolution.zig index 1e089955e59e2efcbd3b40185cab8289460c956f..66f4aa63bb6a27fb96d24b0fcc702800b7d55bbf 100644 --- a/src/Sema/type_resolution.zig +++ b/src/Sema/type_resolution.zig @@ -334,6 +334,17 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { break :msg msg; }); } + + const elem_ty: Type = field_ty.childType(zcu); + if (elem_ty.zigTypeTag(zcu) == .spirv) { + return sema.failWithOwnedErrorMsg(&block, msg: { + const msg = try sema.errMsg(field_ty_src, "cannot embed SPIR-V type '{f}' in struct", .{elem_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{}); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } } else { return sema.failWithOwnedErrorMsg(&block, msg: { const msg = try sema.errMsg(field_ty_src, "cannot directly embed SPIR-V type '{f}' in struct", .{field_ty.fmt(pt)}); diff --git a/src/Type.zig b/src/Type.zig index 3774049cc616c35c02f2e32b17ef3c5a99c743ad..f455bfa8ddee03df753592c9303d1ed0fb5a5df2 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -3145,7 +3145,8 @@ pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool .spirv => switch (position) { .struct_field, .union_field => true, - .ret_ty, .param_ty, .element, .other => !ty.isSpirvRuntimeArray(zcu), + .ret_ty, .param_ty, .element => !ty.isSpirvRuntimeArray(zcu), + .other => !ty.isSpirvRuntimeArray(zcu) or zcu.getTarget().cpu.has(.spirv, .runtime_descriptor_array), }, .pointer => { diff --git a/test/cases/compile_errors/directly_embedding_spirv_type_in_struct_and_union.zig b/test/cases/compile_errors/directly_embedding_spirv_type_in_struct_and_union.zig index 732db0b4408883a1e0aaacf019bb6c459d62205f..369cd99f891eecc1dff6ae3f15182a6d3edf3ae9 100644 --- a/test/cases/compile_errors/directly_embedding_spirv_type_in_struct_and_union.zig +++ b/test/cases/compile_errors/directly_embedding_spirv_type_in_struct_and_union.zig @@ -3,22 +3,27 @@ const RuntimeArray = @SpirvType(.{ .runtime_array = u32 }); const Foo = struct { s: Sampler, }; -const Baz = struct { +const Bar = struct { a: RuntimeArray, }; -const Qux = extern struct { +const Baz = extern struct { a: RuntimeArray, b: u32, }; +const Qux = extern struct { _: @SpirvType(.{ .runtime_array = Sampler }) }; export fn a() void { var foo: Foo = undefined; _ = &foo; } export fn c() void { + var bar: Bar = undefined; + _ = &bar; +} +export fn d() void { var baz: Baz = undefined; _ = &baz; } -export fn d() void { +export fn e() void { var qux: Qux = undefined; _ = &qux; } @@ -27,9 +32,11 @@ export fn d() void { // backend=selfhosted // target=spirv32-vulkan // -// :4:8: error: cannot directly embed SPIR-V type 'tmp.Sampler__SpirvType_4' in struct +// :4:8: error: cannot directly embed SPIR-V type '@SpirvType(.sampler)' in struct // :4:8: note: opaque types have unknown size -// :6:13: error: non-extern struct cannot contain fields of type 'tmp.RuntimeArray__SpirvType_11' +// :6:13: error: non-extern struct cannot contain fields of type '@SpirvType(.runtime_array, u32)' // :7:5: note: while checking this field -// :9:20: error: struct field of type 'tmp.RuntimeArray__SpirvType_11' must be the last field +// :9:20: error: struct field of type '@SpirvType(.runtime_array, u32)' must be the last field // :10:5: note: while checking this field +// :13:32: error: cannot embed SPIR-V type '@SpirvType(.sampler)' in struct +// :13:32: note: opaque types have unknown size diff --git a/test/cases/compile_errors/extern_spirv_storage_buffer_must_be_single_pointer.zig b/test/cases/compile_errors/extern_spirv_storage_buffer_must_be_single_pointer.zig deleted file mode 100644 index 3b8ac3c8338afff5ca93773f973d399fd6c51d1b..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/extern_spirv_storage_buffer_must_be_single_pointer.zig +++ /dev/null @@ -1,25 +0,0 @@ -const a = @extern([*]addrspace(.storage_buffer) u32, .{ - .name = "a", - .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, -}); -const b = @extern([]addrspace(.uniform) u32, .{ - .name = "b", - .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } }, -}); -const c = @extern([*c]addrspace(.push_constant) u32, .{ .name = "c" }); -comptime { - _ = a; - _ = b; - _ = c; -} - -// error -// backend=selfhosted -// target=spirv32-vulkan -// -// :1:19: error: extern in 'storage_buffer' address space must be a single-item pointer to a struct -// :1:19: note: wrap the element type in a struct containing a runtime-sized array -// :5:19: error: extern in 'uniform' address space must be a single-item pointer to a struct -// :5:19: note: wrap the element type in a struct containing a runtime-sized array -// :9:19: error: extern in 'push_constant' address space must be a single-item pointer to a struct -// :9:19: note: wrap the element type in a struct containing a runtime-sized array diff --git a/test/cases/compile_errors/spirv_extern_runtime_descriptor_array.zig b/test/cases/compile_errors/spirv_extern_runtime_descriptor_array.zig new file mode 100644 index 0000000000000000000000000000000000000000..87c0087d9a668c246079ecba42c7dc3516cd48f1 --- /dev/null +++ b/test/cases/compile_errors/spirv_extern_runtime_descriptor_array.zig @@ -0,0 +1,49 @@ +const Buffer = extern struct { value: u32 }; +const Sampler = @SpirvType(.sampler); +const RuntimeArray = @SpirvType(.{ .runtime_array = u32 }); +const SamplerRuntimeArray = @SpirvType(.{ .runtime_array = Sampler }); +const BufferRuntimeArray = @SpirvType(.{ .runtime_array = Buffer }); + +const a = @extern(*addrspace(.input) const RuntimeArray, .{ .name = "a" }); +const b = @extern(*addrspace(.uniform) const RuntimeArray, .{ .name = "b" }); +const c = @extern(*addrspace(.storage_buffer) const RuntimeArray, .{ .name = "c" }); +const d = @extern(*addrspace(.constant) const RuntimeArray, .{ .name = "d" }); +const e = @extern(*addrspace(.constant) const SamplerRuntimeArray, .{ + .name = "samplers", + .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, +}); +const f = @extern(*addrspace(.storage_buffer) [4]Buffer, .{ + .name = "sized_buffers", + .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } }, +}); +const g = @extern(*addrspace(.storage_buffer) BufferRuntimeArray, .{ + .name = "buffers", + .decoration = .{ .descriptor = .{ .set = 0, .binding = 2 } }, +}); + +comptime { + _ = a; +} +comptime { + _ = b; +} +comptime { + _ = c; +} + +export fn main() callconv(.{ .spirv_fragment = .{} }) void { + _ = &d[0]; + _ = &e[0]; + f[0].value = 1; + g[0].value = 2; +} + +// error +// backend=selfhosted +// target=spirv32-vulkan +// cpu_features=baseline+runtime_descriptor_array +// +// :7:19: error: SPIR-V runtime array is not allowed in the 'input' address space +// :8:19: error: extern in 'uniform' address space must be a single-item pointer to a struct +// :9:19: error: extern in 'storage_buffer' address space must be a single-item pointer to a struct +// :10:19: error: extern in 'constant' address space must point to an opaque SPIR-V type, or to an array of one diff --git a/test/cases/compile_errors/spirv_extern_var_addrspace.zig b/test/cases/compile_errors/spirv_extern_var_addrspace.zig index b750eda17fda0f48874a05697ca37b0570d66995..c83bfdcf94b0f90394c06867eb696841ccbb1ea5 100644 --- a/test/cases/compile_errors/spirv_extern_var_addrspace.zig +++ b/test/cases/compile_errors/spirv_extern_var_addrspace.zig @@ -1,11 +1,47 @@ -extern var x: u32; +const Block = extern struct { x: u32 }; +const RuntimeArray = @SpirvType(.{ .runtime_array = u32 }); -export fn main() callconv(.kernel) void { - _ = x; +extern var implicit_addrspace: u32; + +const many = @extern([*]addrspace(.storage_buffer) u32, .{ + .name = "many", + .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, +}); +const push_array = @extern(*addrspace(.push_constant) const [4]Block, .{ .name = "push_array" }); +const plain_constant = @extern(*addrspace(.constant) const Block, .{ + .name = "plain_constant", + .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } }, +}); +const runtime_array = @extern(*addrspace(.storage_buffer) RuntimeArray, .{ + .name = "runtime_array", + .decoration = .{ .descriptor = .{ .set = 0, .binding = 2 } }, +}); + +comptime { + _ = implicit_addrspace; +} +comptime { + _ = many; +} +comptime { + _ = push_array; +} +comptime { + _ = plain_constant; +} +comptime { + _ = runtime_array; } // error // backend=selfhosted -// target=spirv64-vulkan +// target=spirv32-vulkan // -// :1:15: error: SPIR-V extern variables require an explicit address space +// :4:32: error: SPIR-V extern variables require an explicit address space +// :6:22: error: extern in 'storage_buffer' address space must be a single-item pointer to a struct +// :10:28: error: extern in 'push_constant' address space must be a single-item pointer to a struct +// :11:32: error: extern in 'constant' address space must point to an opaque SPIR-V type, or to an array of one +// :15:31: error: extern symbol cannot have type '*addrspace(.storage_buffer) @SpirvType(.runtime_array, u32)' +// :15:31: note: pointer element type '@SpirvType(.runtime_array, u32)' is not extern compatible +// :15:31: note: SPIR-V runtime arrays must be the last field of an extern struct +// :15:31: note: consider enabling the 'runtime_descriptor_array' feature to use the runtime array as the extern pointee diff --git a/test/cases/compile_errors/spirv_runtime_array_as_value_type.zig b/test/cases/compile_errors/spirv_runtime_array_as_value_type.zig deleted file mode 100644 index 634837d3cf86d1b8b9ed8d3288d4ee1f099ebe20..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/spirv_runtime_array_as_value_type.zig +++ /dev/null @@ -1,26 +0,0 @@ -const RuntimeArray = @SpirvType(.{ .runtime_array = u32 }); - -const a = @extern(*addrspace(.storage_buffer) RuntimeArray, .{ - .name = "a", - .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, -}); -const b = @extern(*addrspace(.uniform) const RuntimeArray, .{ - .name = "b", - .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } }, -}); - -comptime { - _ = a; - _ = b; -} - -// error -// backend=selfhosted -// target=spirv32-vulkan -// -// :3:19: error: extern symbol cannot have type '*addrspace(.storage_buffer) @SpirvType(.runtime_array, u32)' -// :3:19: note: pointer element type '@SpirvType(.runtime_array, u32)' is not extern compatible -// :3:19: note: SPIR-V runtime arrays must be the last field of an extern struct -// :7:19: error: extern symbol cannot have type '*addrspace(.uniform) const @SpirvType(.runtime_array, u32)' -// :7:19: note: pointer element type '@SpirvType(.runtime_array, u32)' is not extern compatible -// :7:19: note: SPIR-V runtime arrays must be the last field of an extern struct