Skip to content

Commit 3757136

Browse files
committed
interpret: dyn trait metadata check: equate traits in a proper way
1 parent d041b7c commit 3757136

File tree

3 files changed

+76
-4
lines changed

3 files changed

+76
-4
lines changed

Diff for: compiler/rustc_const_eval/src/interpret/memory.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -884,9 +884,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
884884
throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset)))
885885
};
886886
if let Some(expected_trait) = expected_trait {
887-
if vtable_trait != expected_trait.principal() {
888-
throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait });
889-
}
887+
self.check_vtable_for_type(vtable_trait, expected_trait)?;
890888
}
891889
Ok(ty)
892890
}

Diff for: compiler/rustc_const_eval/src/interpret/traits.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
use rustc_infer::infer::TyCtxtInferExt;
2+
use rustc_infer::traits::ObligationCause;
13
use rustc_middle::mir::interpret::{InterpResult, Pointer};
24
use rustc_middle::ty::layout::LayoutOf;
35
use rustc_middle::ty::{self, Ty};
46
use rustc_target::abi::{Align, Size};
7+
use rustc_trait_selection::traits::ObligationCtxt;
58
use tracing::trace;
69

710
use super::util::ensure_monomorphic_enough;
8-
use super::{InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable};
11+
use super::{throw_ub, InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable};
912

1013
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
1114
/// Creates a dynamic vtable for the given type and vtable origin. This is used only for
@@ -44,6 +47,37 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
4447
Ok((layout.size, layout.align.abi))
4548
}
4649

50+
/// Check that the given vtable trait is valid for a pointer/reference/place with the given
51+
/// expected trait type.
52+
pub(super) fn check_vtable_for_type(
53+
&self,
54+
vtable_trait: Option<ty::PolyExistentialTraitRef<'tcx>>,
55+
expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
56+
) -> InterpResult<'tcx> {
57+
// Fast path: if they are equal, it's all fine.
58+
if expected_trait.principal() == vtable_trait {
59+
return Ok(());
60+
}
61+
if let (Some(expected_trait), Some(vtable_trait)) =
62+
(expected_trait.principal(), vtable_trait)
63+
{
64+
// Slow path: spin up an inference context to check if these traits are sufficiently equal.
65+
let infcx = self.tcx.infer_ctxt().build();
66+
let ocx = ObligationCtxt::new(&infcx);
67+
let cause = ObligationCause::dummy_with_span(self.cur_span());
68+
// equate the two trait refs after normalization
69+
let expected_trait = ocx.normalize(&cause, self.param_env, expected_trait);
70+
let vtable_trait = ocx.normalize(&cause, self.param_env, vtable_trait);
71+
if ocx.eq(&cause, self.param_env, expected_trait, vtable_trait).is_ok() {
72+
if ocx.select_all_or_error().is_empty() {
73+
// All good.
74+
return Ok(());
75+
}
76+
}
77+
}
78+
throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait });
79+
}
80+
4781
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
4882
pub(super) fn unpack_dyn_trait(
4983
&self,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#![feature(ptr_metadata)]
2+
// This test is the result of minimizing the `emplacable` crate to reproduce
3+
// <https://github.com/rust-lang/miri/issues/3541>.
4+
5+
use std::{ops::FnMut, ptr::Pointee};
6+
7+
pub type EmplacerFn<'a, T> = dyn for<'b> FnMut(<T as Pointee>::Metadata) + 'a;
8+
9+
#[repr(transparent)]
10+
pub struct Emplacer<'a, T>(EmplacerFn<'a, T>)
11+
where
12+
T: ?Sized;
13+
14+
impl<'a, T> Emplacer<'a, T>
15+
where
16+
T: ?Sized,
17+
{
18+
pub unsafe fn from_fn<'b>(emplacer_fn: &'b mut EmplacerFn<'a, T>) -> &'b mut Self {
19+
// This used to trigger:
20+
// constructing invalid value: wrong trait in wide pointer vtable: expected
21+
// `std::ops::FnMut(<[std::boxed::Box<i32>] as std::ptr::Pointee>::Metadata)`, but encountered
22+
// `std::ops::FnMut<(usize,)>`.
23+
unsafe { &mut *((emplacer_fn as *mut EmplacerFn<'a, T>) as *mut Self) }
24+
}
25+
}
26+
27+
pub fn box_new_with<T>()
28+
where
29+
T: ?Sized,
30+
{
31+
let emplacer_closure = &mut |_meta| {
32+
unreachable!();
33+
};
34+
35+
unsafe { Emplacer::<T>::from_fn(emplacer_closure) };
36+
}
37+
38+
fn main() {
39+
box_new_with::<[Box<i32>]>();
40+
}

0 commit comments

Comments
 (0)