interpret/write_discriminant: when encoding niched variant, ensure th… · rust-lang/rust@18ed966 (original) (raw)
File tree
6 files changed
lines changed
- rustc_middle/src/mir/interpret
- src/tools/miri/tests/fail
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 | + |