Skip to content

Commit 05a4d16

Browse files
committed
fix safe-transmute handling of enums
1 parent a36652c commit 05a4d16

File tree

7 files changed

+82
-73
lines changed

7 files changed

+82
-73
lines changed

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+
/// The index of the only (inhabited) variant. Can be an invalid index if there
1510+
/// is no variant!
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

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

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> {

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
}

tests/crashes/126267.rs

-30
This file was deleted.

tests/ui/transmutability/uninhabited.rs

+21
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ mod assert {
1616
}
1717
}>
1818
{}
19+
20+
pub fn is_transmutable<Src, Dst>()
21+
where
22+
Dst: TransmuteFrom<Src>, // safety is NOT assumed, but proven
23+
{}
1924
}
2025

2126
fn void() {
@@ -91,3 +96,19 @@ fn distant_void() {
9196
assert::is_maybe_transmutable::<DistantVoid, &'static Void>();
9297
assert::is_maybe_transmutable::<u128, DistantVoid>(); //~ ERROR: cannot be safely transmuted
9398
}
99+
100+
fn issue_126267() {
101+
pub enum ApiError {}
102+
pub struct TokioError {
103+
b: bool,
104+
}
105+
pub enum Error {
106+
Api { source: ApiError }, // this variant is uninhabited
107+
Ethereum,
108+
Tokio { source: TokioError },
109+
}
110+
111+
struct Src;
112+
type Dst = Error;
113+
assert::is_transmutable::<Src, Dst>(); //~ERROR: cannot be safely transmuted
114+
}

tests/ui/transmutability/uninhabited.stderr

+26-11
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
error[E0080]: evaluation of constant value failed
2-
--> $DIR/uninhabited.rs:41:9
2+
--> $DIR/uninhabited.rs:46:9
33
|
44
LL | assert!(false);
5-
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:41:9
5+
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:46:9
66
|
77
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
88

99
error[E0080]: evaluation of constant value failed
10-
--> $DIR/uninhabited.rs:63:9
10+
--> $DIR/uninhabited.rs:68:9
1111
|
1212
LL | assert!(false);
13-
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:63:9
13+
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:68:9
1414
|
1515
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
1616

1717
error[E0080]: evaluation of constant value failed
18-
--> $DIR/uninhabited.rs:87:9
18+
--> $DIR/uninhabited.rs:92:9
1919
|
2020
LL | assert!(false);
21-
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:87:9
21+
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:92:9
2222
|
2323
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
2424

2525
error[E0277]: `()` cannot be safely transmuted into `void::Void`
26-
--> $DIR/uninhabited.rs:29:41
26+
--> $DIR/uninhabited.rs:34:41
2727
|
2828
LL | assert::is_maybe_transmutable::<(), Void>();
2929
| ^^^^ `void::Void` is uninhabited
@@ -45,7 +45,7 @@ LL | | }>
4545
| |__________^ required by this bound in `is_maybe_transmutable`
4646

4747
error[E0277]: `()` cannot be safely transmuted into `yawning_void_struct::Void`
48-
--> $DIR/uninhabited.rs:49:41
48+
--> $DIR/uninhabited.rs:54:41
4949
|
5050
LL | assert::is_maybe_transmutable::<(), Void>();
5151
| ^^^^ `yawning_void_struct::Void` is uninhabited
@@ -67,7 +67,7 @@ LL | | }>
6767
| |__________^ required by this bound in `is_maybe_transmutable`
6868

6969
error[E0277]: `()` cannot be safely transmuted into `yawning_void_enum::Void`
70-
--> $DIR/uninhabited.rs:71:41
70+
--> $DIR/uninhabited.rs:76:41
7171
|
7272
LL | assert::is_maybe_transmutable::<(), Void>();
7373
| ^^^^ `yawning_void_enum::Void` is uninhabited
@@ -89,7 +89,7 @@ LL | | }>
8989
| |__________^ required by this bound in `is_maybe_transmutable`
9090

9191
error[E0277]: `u128` cannot be safely transmuted into `DistantVoid`
92-
--> $DIR/uninhabited.rs:92:43
92+
--> $DIR/uninhabited.rs:97:43
9393
|
9494
LL | assert::is_maybe_transmutable::<u128, DistantVoid>();
9595
| ^^^^^^^^^^^ at least one value of `u128` isn't a bit-valid value of `DistantVoid`
@@ -110,7 +110,22 @@ 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:113:36
115+
|
116+
LL | assert::is_transmutable::<Src, Dst>();
117+
| ^^^ `issue_126267::Error` may carry safety invariants
118+
|
119+
note: required by a bound in `is_transmutable`
120+
--> $DIR/uninhabited.rs:22:18
121+
|
122+
LL | pub fn is_transmutable<Src, Dst>()
123+
| --------------- required by a bound in this function
124+
LL | where
125+
LL | Dst: TransmuteFrom<Src>, // safety is NOT assumed, but proven
126+
| ^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
127+
128+
error: aborting due to 8 previous errors
114129

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

0 commit comments

Comments
 (0)