Skip to content

Commit bb7137a

Browse files
Validate unsize coercion in MIR validation
1 parent 702987f commit bb7137a

File tree

4 files changed

+100
-29
lines changed

4 files changed

+100
-29
lines changed

compiler/rustc_mir_transform/src/validate.rs

+47-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
44
use rustc_hir::LangItem;
55
use rustc_index::IndexVec;
66
use rustc_index::bit_set::BitSet;
7-
use rustc_infer::traits::Reveal;
7+
use rustc_infer::infer::TyCtxtInferExt;
8+
use rustc_infer::traits::{Obligation, ObligationCause, Reveal};
89
use rustc_middle::mir::coverage::CoverageKind;
910
use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
1011
use rustc_middle::mir::*;
@@ -16,6 +17,8 @@ use rustc_middle::ty::{
1617
use rustc_middle::{bug, span_bug};
1718
use rustc_target::abi::{FIRST_VARIANT, Size};
1819
use rustc_target::spec::abi::Abi;
20+
use rustc_trait_selection::traits::ObligationCtxt;
21+
use rustc_type_ir::Upcast;
1922

2023
use crate::util::{is_within_packed, relate_types};
2124

@@ -586,6 +589,22 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
586589

587590
crate::util::relate_types(self.tcx, self.param_env, variance, src, dest)
588591
}
592+
593+
/// Check that the given predicate definitely holds in the param-env of this MIR body.
594+
fn predicate_must_hold_modulo_regions(
595+
&self,
596+
pred: impl Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>,
597+
) -> bool {
598+
let infcx = self.tcx.infer_ctxt().build();
599+
let ocx = ObligationCtxt::new(&infcx);
600+
ocx.register_obligation(Obligation::new(
601+
self.tcx,
602+
ObligationCause::dummy(),
603+
self.param_env,
604+
pred,
605+
));
606+
ocx.select_all_or_error().is_empty()
607+
}
589608
}
590609

591610
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
@@ -1205,8 +1224,33 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
12051224
}
12061225
}
12071226
CastKind::PointerCoercion(PointerCoercion::Unsize) => {
1208-
// This is used for all `CoerceUnsized` types,
1209-
// not just pointers/references, so is hard to check.
1227+
// Pointers being unsize coerced should at least implement
1228+
// `CoerceUnsized`.
1229+
if !self.predicate_must_hold_modulo_regions(ty::TraitRef::new(
1230+
self.tcx,
1231+
self.tcx.require_lang_item(
1232+
LangItem::CoerceUnsized,
1233+
Some(self.body.source_info(location).span),
1234+
),
1235+
[op_ty, *target_type],
1236+
)) {
1237+
self.fail(location, format!("Unsize coercion, but `{op_ty}` isn't coercible to `{target_type}`"));
1238+
}
1239+
1240+
// FIXME: Codegen has an additional assumption, where if the
1241+
// principal trait def id of what's being casted doesn't change,
1242+
// then we don't need to adjust the vtable at all. This
1243+
// corresponds to the fact that `dyn Tr<A>: Unsize<dyn Tr<B>>`
1244+
// requires that `A = B`; we don't allow *upcasting* objects
1245+
// between the same trait with different args. Nothing actually
1246+
// validates this, though. While it's true right now, if we for
1247+
// some reason were to relax the `Unsize` trait, it could become
1248+
// unsound. We should eventually validate that, but it would
1249+
// require peeling `&Box<Struct<.., dyn Tr<A>, ..>>` down to
1250+
// the trait object that's being unsized, and that's rather
1251+
// annoying, and also it would need to be opportunistic since
1252+
// this MIR is not yet fully monomorphized, so we may bottom
1253+
// out in an alias or a projection or something.
12101254
}
12111255
CastKind::IntToInt | CastKind::IntToFloat => {
12121256
let input_valid = op_ty.is_integral() || op_ty.is_char() || op_ty.is_bool();

tests/crashes/129219.rs

-26
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//@ compile-flags: -Zmir-opt-level=0 -Zmir-enable-passes=+Inline,+GVN -Zvalidate-mir
2+
3+
#![feature(unsize)]
4+
5+
use std::marker::Unsize;
6+
7+
pub trait CastTo<U: ?Sized>: Unsize<U> {}
8+
9+
// Not well-formed!
10+
impl<T: ?Sized, U: ?Sized> CastTo<U> for T {}
11+
//~^ ERROR the trait bound `T: Unsize<U>` is not satisfied
12+
13+
pub trait Cast {
14+
fn cast<U: ?Sized>(&self)
15+
where
16+
Self: CastTo<U>;
17+
}
18+
impl<T: ?Sized> Cast for T {
19+
#[inline(always)]
20+
fn cast<U: ?Sized>(&self)
21+
where
22+
Self: CastTo<U>,
23+
{
24+
let x: &U = self;
25+
}
26+
}
27+
28+
fn main() {
29+
// When we inline this call, then we run GVN, then
30+
// GVN tries to evaluate the `() -> [i32]` unsize.
31+
// That's invalid!
32+
().cast::<[i32]>();
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0277]: the trait bound `T: Unsize<U>` is not satisfied
2+
--> $DIR/validate-unsize-cast.rs:10:42
3+
|
4+
LL | impl<T: ?Sized, U: ?Sized> CastTo<U> for T {}
5+
| ^ the trait `Unsize<U>` is not implemented for `T`
6+
|
7+
= note: all implementations of `Unsize` are provided automatically by the compiler, see <https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html> for more information
8+
note: required by a bound in `CastTo`
9+
--> $DIR/validate-unsize-cast.rs:7:30
10+
|
11+
LL | pub trait CastTo<U: ?Sized>: Unsize<U> {}
12+
| ^^^^^^^^^ required by this bound in `CastTo`
13+
help: consider further restricting this bound
14+
|
15+
LL | impl<T: ?Sized + std::marker::Unsize<U>, U: ?Sized> CastTo<U> for T {}
16+
| ++++++++++++++++++++++++
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)