Skip to content

Commit 6289d4f

Browse files
committed
report UB when the niche value refers to the untagged variant
1 parent c3f1129 commit 6289d4f

File tree

4 files changed

+29
-21
lines changed

4 files changed

+29
-21
lines changed

compiler/rustc_const_eval/src/const_eval/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use rustc_abi::VariantIdx;
44
use rustc_middle::query::{Key, TyCtxtAt};
5+
use rustc_middle::ty::layout::LayoutOf;
56
use rustc_middle::ty::{self, Ty, TyCtxt};
67
use rustc_middle::{bug, mir};
78
use tracing::instrument;
@@ -85,5 +86,6 @@ pub fn tag_for_variant_provider<'tcx>(
8586
crate::const_eval::DummyMachine,
8687
);
8788

88-
ecx.tag_for_variant(ty, variant_index).unwrap().map(|(tag, _tag_field)| tag)
89+
let layout = ecx.layout_of(ty).unwrap();
90+
ecx.tag_for_variant(layout, variant_index).unwrap().map(|(tag, _tag_field)| tag)
8991
}

compiler/rustc_const_eval/src/interpret/discriminant.rs

+24-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Functions for reading and writing discriminants of multi-variant layouts (enums and coroutines).
22
33
use rustc_abi::{self as abi, TagEncoding, VariantIdx, Variants};
4-
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt};
4+
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
55
use rustc_middle::ty::{self, CoroutineArgsExt, ScalarInt, Ty};
66
use rustc_middle::{mir, span_bug};
77
use tracing::{instrument, trace};
@@ -21,17 +21,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
2121
variant_index: VariantIdx,
2222
dest: &impl Writeable<'tcx, M::Provenance>,
2323
) -> InterpResult<'tcx> {
24-
// Layout computation excludes uninhabited variants from consideration
25-
// therefore there's no way to represent those variants in the given layout.
26-
// Essentially, uninhabited variants do not have a tag that corresponds to their
27-
// discriminant, so we cannot do anything here.
28-
// When evaluating we will always error before even getting here, but ConstProp 'executes'
29-
// dead code, so we cannot ICE here.
30-
if dest.layout().for_variant(self, variant_index).is_uninhabited() {
31-
throw_ub!(UninhabitedEnumVariantWritten(variant_index))
32-
}
33-
34-
match self.tag_for_variant(dest.layout().ty, variant_index)? {
24+
match self.tag_for_variant(dest.layout(), variant_index)? {
3525
Some((tag, tag_field)) => {
3626
// No need to validate that the discriminant here because the
3727
// `TyAndLayout::for_variant()` call earlier already checks the
@@ -188,6 +178,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
188178
let variants =
189179
ty.ty_adt_def().expect("tagged layout for non adt").variants();
190180
assert!(variant_index < variants.next_index());
181+
if variant_index == untagged_variant {
182+
// The untagged variant can be in the niche range, but even then it
183+
// is not a valid encoding.
184+
throw_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size)))
185+
}
191186
variant_index
192187
} else {
193188
untagged_variant
@@ -236,10 +231,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
236231
/// given field index.
237232
pub(crate) fn tag_for_variant(
238233
&self,
239-
ty: Ty<'tcx>,
234+
layout: TyAndLayout<'tcx>,
240235
variant_index: VariantIdx,
241236
) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> {
242-
match self.layout_of(ty)?.variants {
237+
// Layout computation excludes uninhabited variants from consideration.
238+
// Therefore, there's no way to represent those variants in the given layout.
239+
// Essentially, uninhabited variants do not have a tag that corresponds to their
240+
// discriminant, so we have to bail out here.
241+
if layout.for_variant(self, variant_index).is_uninhabited() {
242+
throw_ub!(UninhabitedEnumVariantWritten(variant_index))
243+
}
244+
245+
match layout.variants {
243246
abi::Variants::Single { .. } => {
244247
// The tag of a `Single` enum is like the tag of the niched
245248
// variant: there's no tag as the discriminant is encoded
@@ -260,7 +263,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
260263
// raw discriminants for enums are isize or bigger during
261264
// their computation, but the in-memory tag is the smallest possible
262265
// representation
263-
let discr = self.discriminant_for_variant(ty, variant_index)?;
266+
let discr = self.discriminant_for_variant(layout.ty, variant_index)?;
264267
let discr_size = discr.layout.size;
265268
let discr_val = discr.to_scalar().to_bits(discr_size)?;
266269
let tag_size = tag_layout.size(self);
@@ -286,11 +289,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
286289
..
287290
} => {
288291
assert!(variant_index != untagged_variant);
292+
// We checked that this variant is inhabited, so it must be in the niche range.
293+
assert!(
294+
niche_variants.contains(&variant_index),
295+
"invalid variant index for this enum"
296+
);
289297
let variants_start = niche_variants.start().as_u32();
290-
let variant_index_relative = variant_index
291-
.as_u32()
292-
.checked_sub(variants_start)
293-
.expect("overflow computing relative variant idx");
298+
let variant_index_relative = variant_index.as_u32().strict_sub(variants_start);
294299
// We need to use machine arithmetic when taking into account `niche_start`:
295300
// tag_val = variant_index_relative + niche_start_val
296301
let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?;

compiler/rustc_const_eval/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#![feature(never_type)]
1111
#![feature(rustdoc_internals)]
1212
#![feature(slice_ptr_get)]
13+
#![feature(strict_overflow_ops)]
1314
#![feature(trait_alias)]
1415
#![feature(try_blocks)]
1516
#![feature(unqualified_local_imports)]

compiler/rustc_ty_utils/src/layout.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ fn layout_of<'tcx>(
8181
record_layout_for_printing(&cx, layout);
8282
}
8383

84-
invariant::partially_check_layout(&cx, &layout);
84+
invariant::layout_sanity_check(&cx, &layout);
8585

8686
Ok(layout)
8787
}

0 commit comments

Comments
 (0)