interpret/write_discriminant: when encoding niched variant, ensure th… · rust-lang/rust@18ed966 (original) (raw)

File tree

6 files changed

lines changed

6 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -170,6 +170,10 @@ const_eval_invalid_meta =
170 170 invalid metadata in wide pointer: total size is bigger than largest supported object
171 171 const_eval_invalid_meta_slice =
172 172 invalid metadata in wide pointer: slice is bigger than largest supported object
173 +
174 +const_eval_invalid_niched_enum_variant_written =
175 + trying to set discriminant of a {$ty} to the niched variant, but the value does not match
176 +
173 177 const_eval_invalid_str =
174 178 this string is not valid UTF-8: {$err}
175 179 const_eval_invalid_tag =
Original file line number Diff line number Diff line change
@@ -509,6 +509,9 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
509 509 ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch,
510 510 UninhabitedEnumVariantWritten(_) => const_eval_uninhabited_enum_variant_written,
511 511 UninhabitedEnumVariantRead(_) => const_eval_uninhabited_enum_variant_read,
512 +InvalidNichedEnumVariantWritten { .. } => {
513 + const_eval_invalid_niched_enum_variant_written
514 +}
512 515 AbiMismatchArgument { .. } => const_eval_incompatible_types,
513 516 AbiMismatchReturn { .. } => const_eval_incompatible_return_types,
514 517 }
@@ -597,6 +600,9 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
597 600 builder.arg("target_size", info.target_size);
598 601 builder.arg("data_size", info.data_size);
599 602 }
603 +InvalidNichedEnumVariantWritten { enum_ty } => {
604 + builder.arg("ty", enum_ty.to_string());
605 +}
600 606 AbiMismatchArgument { caller_ty, callee_ty }
601 607 | AbiMismatchReturn { caller_ty, callee_ty } => {
602 608 builder.arg("caller_ty", caller_ty.to_string());
Original file line number Diff line number Diff line change
@@ -85,6 +85,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
85 85 // Write result.
86 86 let niche_dest = self.project_field(dest, tag_field)?;
87 87 self.write_immediate(*tag_val, &niche_dest)?;
88 +} else {
89 +// The untagged variant is implicitly encoded simply by having a value that is
90 +// outside the niche variants. But what if the data stored here does not
91 +// actually encode this variant? That would be bad! So let's double-check...
92 +let actual_variant = self.read_discriminant(&dest.to_op(self)?)?;
93 +if actual_variant != variant_index {
94 +throw_ub!(InvalidNichedEnumVariantWritten { enum_ty: dest.layout().ty });
95 +}
88 96 }
89 97 }
90 98 }
Original file line number Diff line number Diff line change
@@ -356,6 +356,8 @@ pub enum UndefinedBehaviorInfo<'tcx> {
356 356 UninhabitedEnumVariantWritten(VariantIdx),
357 357 /// An uninhabited enum variant is projected.
358 358 UninhabitedEnumVariantRead(VariantIdx),
359 +/// Trying to set discriminant to the niched variant, but the value does not match.
360 + InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> },
359 361 /// ABI-incompatible argument types.
360 362 AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
361 363 /// ABI-incompatible return types.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
1 +#![feature(core_intrinsics)]
2 +#![feature(custom_mir)]
3 +
4 +use std::intrinsics::mir::*;
5 +use std::num::NonZeroI32;
6 +
7 +// We define our own option type so that we can control the varian indices.
8 +#[allow(unused)]
9 +enum Option<T> {
10 +None,
11 +Some(T),
12 +}
13 +use Option::*;
14 +
15 +#[custom_mir(dialect = "runtime", phase = "optimized")]
16 +fn set_discriminant(ptr: &mut Option<NonZeroI32>) {
17 +mir! {
18 +{
19 +// We set the discriminant to `Some`, which is a NOP since this is the niched variant.
20 +// However, the enum is actually encoding `None` currently! That's not good...
21 +SetDiscriminant(*ptr, 1);
22 +//~^ ERROR: trying to set discriminant of a Option<std::num::NonZero> to the niched variant, but the value does not match
23 +Return()
24 +}
25 +}
26 +}
27 +
28 +pub fn main() {
29 +let mut v = None;
30 +set_discriminant(&mut v);
31 +}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
1 +error: Undefined Behavior: trying to set discriminant of a Option<std::num::NonZero> to the niched variant, but the value does not match
2 + --> $DIR/enum-set-discriminant-niche-variant-wrong.rs:LL:CC
3 + |
4 +LL | SetDiscriminant(*ptr, 1);
5 + | ^^^^^^^^^^^^^^^^^^^^^^^^ trying to set discriminant of a Option<std::num::NonZero> to the niched variant, but the value does not match
6 + |
7 + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8 + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9 + = note: BACKTRACE:
10 + = note: inside `set_discriminant` at $DIR/enum-set-discriminant-niche-variant-wrong.rs:LL:CC
11 +note: inside `main`
12 + --> $DIR/enum-set-discriminant-niche-variant-wrong.rs:LL:CC
13 + |
14 +LL | set_discriminant(&mut v);
15 + | ^^^^^^^^^^^^^^^^^^^^^^^^
16 +
17 +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
18 +
19 +error: aborting due to 1 previous error
20 +