| author | |
| committer | |
| log | e8e47b3428845a176eb5ee1c373063102ab7f6e2 |
| tree | b1de66ba4eeba3f18412e9e95148f45a04aab8e5 |
| parent | 591b2a47f79fa07e87d3b625320135de8446a274 |
5 files changed, 131 insertions(+), 60 deletions(-)
src/Sema.zig+52-34| ... | @@ -22151,34 +22151,7 @@ fn ptrCastFull( | ... | @@ -22151,34 +22151,7 @@ fn ptrCastFull( |
| 22151 | } | 22151 | } |
| 22152 | 22152 | ||
| 22153 | try sema.validateRuntimeValue(block, operand_src, operand); | 22153 | try sema.validateRuntimeValue(block, operand_src, operand); |
| 22154 | 22154 | 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 | } | ||
| 22182 | 22155 | ||
| 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( |
| 22721 | fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { | 22694 | fn 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 | } |
| 22746 | 22717 | ||
| 22718 | fn 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 | |||
| 22747 | fn checkVectorElemType( | 22761 | fn checkVectorElemType( |
| 22748 | sema: *Sema, | 22762 | sema: *Sema, |
| 22749 | block: *Block, | 22763 | block: *Block, |
| ... | @@ -27367,6 +27381,7 @@ fn elemPtrOneLayerOnly( | ... | @@ -27367,6 +27381,7 @@ fn elemPtrOneLayerOnly( |
| 27367 | 27381 | ||
| 27368 | try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_src); | 27382 | try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_src); |
| 27369 | try sema.validateRuntimeValue(block, indexable_src, indexable); | 27383 | try sema.validateRuntimeValue(block, indexable_src, indexable); |
| 27384 | try sema.checkLogicalPtrOperation(block, src, indexable_ty); | ||
| 27370 | 27385 | ||
| 27371 | if (child_ty.abiSize(zcu) == 0) { | 27386 | if (child_ty.abiSize(zcu) == 0) { |
| 27372 | // zero-bit child type; just bitcast the pointer | 27387 | // zero-bit child type; just bitcast the pointer |
| ... | @@ -27439,6 +27454,7 @@ fn elemVal( | ... | @@ -27439,6 +27454,7 @@ fn elemVal( |
| 27439 | }, | 27454 | }, |
| 27440 | .partially_comptime, .fully_comptime => unreachable, // caught by `validateRuntimeElemAccess` | 27455 | .partially_comptime, .fully_comptime => unreachable, // caught by `validateRuntimeElemAccess` |
| 27441 | } | 27456 | } |
| 27457 | try sema.checkLogicalPtrOperation(block, src, indexable_ty); | ||
| 27442 | 27458 | ||
| 27443 | return block.addBinOp(.ptr_elem_val, indexable, elem_index); | 27459 | return block.addBinOp(.ptr_elem_val, indexable, elem_index); |
| 27444 | }, | 27460 | }, |
| ... | @@ -27840,6 +27856,7 @@ fn elemValSlice( | ... | @@ -27840,6 +27856,7 @@ fn elemValSlice( |
| 27840 | 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; |
| 27841 | try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); | 27857 | try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); |
| 27842 | } | 27858 | } |
| 27859 | try sema.checkLogicalPtrOperation(block, src, slice_ty); | ||
| 27843 | return block.addBinOp(.slice_elem_val, slice, elem_index); | 27860 | return block.addBinOp(.slice_elem_val, slice, elem_index); |
| 27844 | } | 27861 | } |
| 27845 | 27862 | ||
| ... | @@ -27891,6 +27908,7 @@ fn elemPtrSlice( | ... | @@ -27891,6 +27908,7 @@ fn elemPtrSlice( |
| 27891 | 27908 | ||
| 27892 | 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); |
| 27893 | try sema.validateRuntimeValue(block, slice_src, slice); | 27910 | try sema.validateRuntimeValue(block, slice_src, slice); |
| 27911 | try sema.checkLogicalPtrOperation(block, src, slice_ty); | ||
| 27894 | 27912 | ||
| 27895 | if (oob_safety and block.wantSafety()) { | 27913 | if (oob_safety and block.wantSafety()) { |
| 27896 | const len_inst = len: { | 27914 | const len_inst = len: { |
src/codegen/spirv/CodeGen.zig+27-4| ... | @@ -2059,6 +2059,13 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { | ... | @@ -2059,6 +2059,13 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { |
| 2059 | while (cur.toIntern() != dst_child.toIntern()) { | 2059 | while (cur.toIntern() != dst_child.toIntern()) { |
| 2060 | switch (cur.zigTypeTag(zcu)) { | 2060 | switch (cur.zigTypeTag(zcu)) { |
| 2061 | .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 | } | ||
| 2062 | cur = cur.childType(zcu); | 2069 | cur = cur.childType(zcu); |
| 2063 | depth += 1; | 2070 | depth += 1; |
| 2064 | }, | 2071 | }, |
| ... | @@ -2098,7 +2105,7 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { | ... | @@ -2098,7 +2105,7 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { |
| 2098 | } | 2105 | } |
| 2099 | } | 2106 | } |
| 2100 | 2107 | ||
| 2101 | return cg.fail("cannot perform pointer cast: '{f}' to '{f}'", .{ | 2108 | return cg.fail("cannot cast pointer '{f}' to '{f}'", .{ |
| 2102 | parent_ptr_ty.fmt(pt), | 2109 | parent_ptr_ty.fmt(pt), |
| 2103 | oac.new_ptr_ty.fmt(pt), | 2110 | oac.new_ptr_ty.fmt(pt), |
| 2104 | }); | 2111 | }); |
| ... | @@ -6041,7 +6048,13 @@ fn bitCast( | ... | @@ -6041,7 +6048,13 @@ fn bitCast( |
| 6041 | while (cur.toIntern() != dst_child.toIntern()) : (try indices.append(gpa, 0)) { | 6048 | while (cur.toIntern() != dst_child.toIntern()) : (try indices.append(gpa, 0)) { |
| 6042 | cur = switch (cur.zigTypeTag(zcu)) { | 6049 | cur = switch (cur.zigTypeTag(zcu)) { |
| 6043 | .array, .vector => cur.childType(zcu), | 6050 | .array, .vector => cur.childType(zcu), |
| 6044 | .@"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 | }, | ||
| 6045 | else => unreachable, | 6058 | else => unreachable, |
| 6046 | }; | 6059 | }; |
| 6047 | } | 6060 | } |
| ... | @@ -6751,9 +6764,19 @@ fn ptrElemPtr(cg: *CodeGen, ptr_ty: Type, ptr_id: Id, index_id: Id) !Id { | ... | @@ -6751,9 +6764,19 @@ fn ptrElemPtr(cg: *CodeGen, ptr_ty: Type, ptr_id: Id, index_id: Id) !Id { |
| 6751 | const zcu = cg.zcu; | 6764 | const zcu = cg.zcu; |
| 6752 | // Construct new pointer type for the resulting pointer | 6765 | // Construct new pointer type for the resulting pointer |
| 6753 | const as = ptr_ty.ptrAddressSpace(zcu); | 6766 | const as = ptr_ty.ptrAddressSpace(zcu); |
| 6754 | 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); | ||
| 6755 | 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)); |
| 6756 | if (ptr_ty.isSinglePointer(zcu)) { | 6779 | if (is_single_ptr) { |
| 6757 | // Pointer-to-array. In this case, the resulting pointer is not of the same type | 6780 | // Pointer-to-array. In this case, the resulting pointer is not of the same type |
| 6758 | // 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. |
| 6759 | 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/spirv.zig+5-6| ... | @@ -49,13 +49,12 @@ test "@SpirvType" { | ... | @@ -49,13 +49,12 @@ test "@SpirvType" { |
| 49 | _ = runtime_array; | 49 | _ = runtime_array; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | const InnerStruct = extern struct { x: u32 }; | ||
| 53 | const OuterStruct = extern struct { inner: InnerStruct, y: u32 }; | ||
| 54 | const outer_pc = @extern(*addrspace(.push_constant) const OuterStruct, .{ .name = "outer_pc" }); | ||
| 55 | |||
| 56 | test "@ptrCast to first field type" { | 52 | test "@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 | } |
| 60 | 59 | ||
| 61 | test "@SpirvType equality" { | 60 | test "@SpirvType equality" { |
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 | } |
| 24 | 24 | ||
| 25 | const slice: []const u8 = "abc"; | ||
| 26 | |||
| 27 | export fn sliceElemVal() void { | ||
| 28 | var i: u32 = 0; | ||
| 29 | _ = &i; | ||
| 30 | _ = slice[i]; | ||
| 31 | } | ||
| 32 | |||
| 33 | export fn sliceElemPtr() void { | ||
| 34 | var i: u32 = 0; | ||
| 35 | _ = &i; | ||
| 36 | _ = &slice[i]; | ||
| 37 | } | ||
| 38 | |||
| 39 | export fn manyElemVal() void { | ||
| 40 | var ptr: [*]const u8 = "abc"; | ||
| 41 | var i: u32 = 0; | ||
| 42 | _ = .{ &ptr, &i }; | ||
| 43 | _ = ptr[i]; | ||
| 44 | } | ||
| 45 | |||
| 46 | export fn manyElemPtr() void { | ||
| 47 | var ptr: [*]const u8 = "abc"; | ||
| 48 | var i: u32 = 0; | ||
| 49 | _ = .{ &ptr, &i }; | ||
| 50 | _ = &ptr[i]; | ||
| 51 | } | ||
| 52 | |||
| 25 | // error | 53 | // error |
| 26 | // target=spirv64-vulkan | 54 | // 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-vulkan | 57 | // :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-vulkan | 59 | // :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-vulkan | 61 | // :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-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/spirv_pointer_cast_requires_offset_zero.zig+7-12| ... | @@ -1,20 +1,15 @@ | ... | @@ -1,20 +1,15 @@ |
| 1 | const A = extern struct { x: u32, y: u32 }; | 1 | const Inner = extern struct { a: u32 }; |
| 2 | const B = extern struct { a: u64 }; | 2 | const Outer = extern struct { a: u32, b: Inner }; |
| 3 | |||
| 4 | const a = @extern(*addrspace(.uniform) const A, .{ | ||
| 5 | .name = "a", | ||
| 6 | .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, | ||
| 7 | }); | ||
| 8 | 3 | ||
| 9 | export fn main() callconv(.kernel) void { | 4 | export 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 | } |
| 13 | 9 | ||
| 14 | // error | 10 | // error |
| 15 | // backend=selfhosted | 11 | // backend=selfhosted |
| 16 | // target=spirv32-vulkan | 12 | // 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 |