Skip to content

Commit 18ed966

Browse files
committed
interpret/write_discriminant: when encoding niched variant, ensure the stored value matches
1 parent 0809f78 commit 18ed966

File tree

6 files changed

+71
-0
lines changed

6 files changed

+71
-0
lines changed

compiler/rustc_const_eval/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ const_eval_invalid_meta =
170170
invalid metadata in wide pointer: total size is bigger than largest supported object
171171
const_eval_invalid_meta_slice =
172172
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+
173177
const_eval_invalid_str =
174178
this string is not valid UTF-8: {$err}
175179
const_eval_invalid_tag =

compiler/rustc_const_eval/src/errors.rs

+6
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,9 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
509509
ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch,
510510
UninhabitedEnumVariantWritten(_) => const_eval_uninhabited_enum_variant_written,
511511
UninhabitedEnumVariantRead(_) => const_eval_uninhabited_enum_variant_read,
512+
InvalidNichedEnumVariantWritten { .. } => {
513+
const_eval_invalid_niched_enum_variant_written
514+
}
512515
AbiMismatchArgument { .. } => const_eval_incompatible_types,
513516
AbiMismatchReturn { .. } => const_eval_incompatible_return_types,
514517
}
@@ -597,6 +600,9 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
597600
builder.arg("target_size", info.target_size);
598601
builder.arg("data_size", info.data_size);
599602
}
603+
InvalidNichedEnumVariantWritten { enum_ty } => {
604+
builder.arg("ty", enum_ty.to_string());
605+
}
600606
AbiMismatchArgument { caller_ty, callee_ty }
601607
| AbiMismatchReturn { caller_ty, callee_ty } => {
602608
builder.arg("caller_ty", caller_ty.to_string());

compiler/rustc_const_eval/src/interpret/discriminant.rs

+8
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
8585
// Write result.
8686
let niche_dest = self.project_field(dest, tag_field)?;
8787
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+
}
8896
}
8997
}
9098
}

compiler/rustc_middle/src/mir/interpret/error.rs

+2
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,8 @@ pub enum UndefinedBehaviorInfo<'tcx> {
356356
UninhabitedEnumVariantWritten(VariantIdx),
357357
/// An uninhabited enum variant is projected.
358358
UninhabitedEnumVariantRead(VariantIdx),
359+
/// Trying to set discriminant to the niched variant, but the value does not match.
360+
InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> },
359361
/// ABI-incompatible argument types.
360362
AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
361363
/// ABI-incompatible return types.
Original file line numberDiff line numberDiff 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<i32>> 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 numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: Undefined Behavior: trying to set discriminant of a Option<std::num::NonZero<i32>> 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<i32>> 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+

0 commit comments

Comments
 (0)