Skip to content

Commit 611a991

Browse files
committed
fix safe-transmute handling of enums
1 parent a36652c commit 611a991

File tree

7 files changed

+74
-63
lines changed

7 files changed

+74
-63
lines changed

Diff for: compiler/rustc_abi/src/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1505,7 +1505,11 @@ impl BackendRepr {
15051505
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
15061506
pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
15071507
/// Single enum variants, structs/tuples, unions, and all non-ADTs.
1508-
Single { index: VariantIdx },
1508+
Single {
1509+
/// Always 0 for non-enums/generators.
1510+
/// For enums without a variant, this is an invalid index!
1511+
index: VariantIdx,
1512+
},
15091513

15101514
/// Enum-likes with more than one variant: each variant comes with
15111515
/// a *discriminant* (usually the same as the variant index but the user can

Diff for: compiler/rustc_const_eval/src/interpret/discriminant.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
7070
if ty.is_enum() {
7171
// Hilariously, `Single` is used even for 0-variant enums.
7272
// (See https://github.com/rust-lang/rust/issues/89765).
73-
if matches!(ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) {
73+
if ty.ty_adt_def().unwrap().variants().is_empty() {
7474
throw_ub!(UninhabitedEnumVariantRead(index))
7575
}
7676
// For consistency with `write_discriminant`, and to make sure that

Diff for: compiler/rustc_middle/src/query/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,8 @@ rustc_queries! {
10861086
}
10871087

10881088
/// Computes the tag (if any) for a given type and variant.
1089+
/// `None` means that the variant doesn't need a tag (because it is niched).
1090+
/// Will panic for uninhabited variants.
10891091
query tag_for_variant(
10901092
key: (Ty<'tcx>, abi::VariantIdx)
10911093
) -> Option<ty::ScalarInt> {

Diff for: compiler/rustc_transmute/src/layout/tree.rs

+27-30
Original file line numberDiff line numberDiff line change
@@ -319,38 +319,35 @@ pub(crate) mod rustc {
319319
) -> Result<Self, Err> {
320320
assert!(def.is_enum());
321321

322-
// Computes the variant of a given index.
323-
let layout_of_variant = |index, encoding: Option<TagEncoding<VariantIdx>>| {
324-
let tag = cx.tcx().tag_for_variant((cx.tcx().erase_regions(ty), index));
325-
let variant_def = Def::Variant(def.variant(index));
326-
let variant_layout = ty_variant(cx, (ty, layout), index);
327-
Self::from_variant(
328-
variant_def,
329-
tag.map(|tag| (tag, index, encoding.unwrap())),
330-
(ty, variant_layout),
331-
layout.size,
332-
cx,
333-
)
334-
};
322+
// Computes the layout of a variant.
323+
let layout_of_variant =
324+
|index, encoding: Option<TagEncoding<VariantIdx>>| -> Result<Self, Err> {
325+
let variant_layout = ty_variant(cx, (ty, layout), index);
326+
if variant_layout.is_uninhabited() {
327+
return Ok(Self::uninhabited());
328+
}
329+
let tag = cx.tcx().tag_for_variant((cx.tcx().erase_regions(ty), index));
330+
let variant_def = Def::Variant(def.variant(index));
331+
Self::from_variant(
332+
variant_def,
333+
tag.map(|tag| (tag, index, encoding.unwrap())),
334+
(ty, variant_layout),
335+
layout.size,
336+
cx,
337+
)
338+
};
335339

336-
// We consider three kinds of enums, each demanding a different
337-
// treatment of their layout computation:
338-
// 1. enums that are uninhabited ZSTs
339-
// 2. enums that delegate their layout to a variant
340-
// 3. enums with multiple variants
341340
match layout.variants() {
342-
Variants::Single { .. } if layout.is_uninhabited() && layout.size == Size::ZERO => {
343-
// The layout representation of uninhabited, ZST enums is
344-
// defined to be like that of the `!` type, as opposed of a
345-
// typical enum. Consequently, they cannot be descended into
346-
// as if they typical enums. We therefore special-case this
347-
// scenario and simply return an uninhabited `Tree`.
348-
Ok(Self::uninhabited())
349-
}
350341
Variants::Single { index } => {
351-
// `Variants::Single` on enums with variants denotes that
352-
// the enum delegates its layout to the variant at `index`.
353-
layout_of_variant(*index, None)
342+
// Hilariously, `Single` is used even for 0-variant enums;
343+
// `index` is just junk in that case.
344+
if ty.ty_adt_def().unwrap().variants().is_empty() {
345+
Ok(Self::uninhabited())
346+
} else {
347+
// `Variants::Single` on enums with variants denotes that
348+
// the enum delegates its layout to the variant at `index`.
349+
layout_of_variant(*index, None)
350+
}
354351
}
355352
Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
356353
// `Variants::Multiple` denotes an enum with multiple
@@ -369,7 +366,7 @@ pub(crate) mod rustc {
369366
},
370367
)?;
371368

372-
return Ok(Self::def(Def::Adt(def)).then(variants));
369+
Ok(Self::def(Def::Adt(def)).then(variants))
373370
}
374371
}
375372
}

Diff for: tests/crashes/126267.rs

-30
This file was deleted.

Diff for: tests/ui/transmutability/uninhabited.rs

+16
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,19 @@ fn distant_void() {
9191
assert::is_maybe_transmutable::<DistantVoid, &'static Void>();
9292
assert::is_maybe_transmutable::<u128, DistantVoid>(); //~ ERROR: cannot be safely transmuted
9393
}
94+
95+
fn issue_126267() {
96+
pub enum ApiError {}
97+
pub struct TokioError {
98+
b: bool,
99+
}
100+
pub enum Error {
101+
Api { source: ApiError }, // this variant is uninhabited
102+
Ethereum,
103+
Tokio { source: TokioError },
104+
}
105+
106+
struct Src;
107+
type Dst = Error;
108+
assert::is_maybe_transmutable::<Src, Dst>(); //~ERROR: cannot be safely transmuted
109+
}

Diff for: tests/ui/transmutability/uninhabited.stderr

+23-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,29 @@ LL | | }
110110
LL | | }>
111111
| |__________^ required by this bound in `is_maybe_transmutable`
112112

113-
error: aborting due to 7 previous errors
113+
error[E0277]: `Src` cannot be safely transmuted into `issue_126267::Error`
114+
--> $DIR/uninhabited.rs:108:42
115+
|
116+
LL | assert::is_maybe_transmutable::<Src, Dst>();
117+
| ^^^ the size of `Src` is smaller than the size of `issue_126267::Error`
118+
|
119+
note: required by a bound in `is_maybe_transmutable`
120+
--> $DIR/uninhabited.rs:10:14
121+
|
122+
LL | pub fn is_maybe_transmutable<Src, Dst>()
123+
| --------------------- required by a bound in this function
124+
LL | where
125+
LL | Dst: TransmuteFrom<Src, {
126+
| ______________^
127+
LL | | Assume {
128+
LL | | alignment: true,
129+
LL | | lifetimes: true,
130+
... |
131+
LL | | }
132+
LL | | }>
133+
| |__________^ required by this bound in `is_maybe_transmutable`
134+
135+
error: aborting due to 8 previous errors
114136

115137
Some errors have detailed explanations: E0080, E0277.
116138
For more information about an error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)