Skip to content

Commit c6c8056

Browse files
committed
Fix union field access by representing unions as structs
When a union has multiple fields, represent it as a struct with all fields at offset 0 instead of just using the largest field. This allows pointer casts between union fields to work correctly by enabling the recover_access_chain_from_offset function to find valid access chains. Fixes #241.
1 parent e4375b1 commit c6c8056

File tree

2 files changed

+48
-15
lines changed

2 files changed

+48
-15
lines changed

crates/rustc_codegen_spirv/src/abi.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -718,21 +718,22 @@ fn trans_aggregate<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>
718718
FieldsShape::Union(_) => {
719719
assert!(!ty.is_unsized(), "{ty:#?}");
720720

721-
// Represent the `union` with its largest case, which should work
722-
// for at least `MaybeUninit<T>` (which is between `T` and `()`),
723-
// but also potentially some other ones as well.
724-
// NOTE(eddyb) even if long-term this may become a byte array, that
725-
// only works for "data types" and not "opaque handles" (images etc.).
726-
let largest_case = (0..ty.fields.count())
727-
.map(|i| ty.field(cx, i))
728-
.max_by_key(|case| case.size);
729-
730-
if let Some(case) = largest_case {
731-
assert_eq!(ty.size, case.size);
732-
case.spirv_type(span, cx)
733-
} else {
734-
assert_eq!(ty.size, Size::ZERO);
735-
create_zst(cx, span, ty)
721+
// NOTE(eddyb) even if long-term this may become a byte array, that only
722+
// works for "data types" and not "opaque handles" (images etc.).
723+
match ty.fields.count() {
724+
// For unions with no fields, represent as a zero-sized type.
725+
0 => {
726+
assert_eq!(ty.size, Size::ZERO);
727+
create_zst(cx, span, ty)
728+
}
729+
// For unions with a single field, represent as the field itself.
730+
1 => {
731+
let field = ty.field(cx, 0);
732+
assert_eq!(ty.size, field.size);
733+
field.spirv_type(span, cx)
734+
}
735+
// For unions with multiple fields, represent as struct with all fields at offset 0
736+
_ => trans_struct(cx, span, ty),
736737
}
737738
}
738739
FieldsShape::Array { stride, count } => {

tests/ui/lang/core/union_cast.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// build-pass
2+
3+
use spirv_std::spirv;
4+
5+
#[repr(C)]
6+
#[derive(Clone, Copy)]
7+
struct Data {
8+
a: f32,
9+
b: [f32; 3],
10+
c: f32,
11+
}
12+
13+
#[repr(C)]
14+
union DataOrArray {
15+
arr: [f32; 5],
16+
str: Data,
17+
}
18+
19+
impl DataOrArray {
20+
fn arr(&self) -> [f32; 5] {
21+
unsafe { self.arr }
22+
}
23+
fn new(arr: [f32; 5]) -> Self {
24+
Self { arr }
25+
}
26+
}
27+
28+
#[spirv(fragment)]
29+
pub fn main() {
30+
let dora = DataOrArray::new([0.0, 0.0, 0.0, 0.0, 0.0]);
31+
let _arr = dora.arr();
32+
}

0 commit comments

Comments
 (0)