Validate unsize coercion in MIR validation
This commit is contained in:
parent
38352b01ae
commit
8fc8e03150
@ -4,7 +4,8 @@
|
|||||||
use rustc_hir::LangItem;
|
use rustc_hir::LangItem;
|
||||||
use rustc_index::IndexVec;
|
use rustc_index::IndexVec;
|
||||||
use rustc_index::bit_set::BitSet;
|
use rustc_index::bit_set::BitSet;
|
||||||
use rustc_infer::traits::Reveal;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
|
use rustc_infer::traits::{Obligation, ObligationCause, Reveal};
|
||||||
use rustc_middle::mir::coverage::CoverageKind;
|
use rustc_middle::mir::coverage::CoverageKind;
|
||||||
use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
|
use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
@ -16,6 +17,8 @@
|
|||||||
use rustc_middle::{bug, span_bug};
|
use rustc_middle::{bug, span_bug};
|
||||||
use rustc_target::abi::{FIRST_VARIANT, Size};
|
use rustc_target::abi::{FIRST_VARIANT, Size};
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
|
use rustc_trait_selection::traits::ObligationCtxt;
|
||||||
|
use rustc_type_ir::Upcast;
|
||||||
|
|
||||||
use crate::util::{is_within_packed, relate_types};
|
use crate::util::{is_within_packed, relate_types};
|
||||||
|
|
||||||
@ -586,6 +589,22 @@ fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
|
|||||||
|
|
||||||
crate::util::relate_types(self.tcx, self.param_env, variance, src, dest)
|
crate::util::relate_types(self.tcx, self.param_env, variance, src, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check that the given predicate definitely holds in the param-env of this MIR body.
|
||||||
|
fn predicate_must_hold_modulo_regions(
|
||||||
|
&self,
|
||||||
|
pred: impl Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>,
|
||||||
|
) -> bool {
|
||||||
|
let infcx = self.tcx.infer_ctxt().build();
|
||||||
|
let ocx = ObligationCtxt::new(&infcx);
|
||||||
|
ocx.register_obligation(Obligation::new(
|
||||||
|
self.tcx,
|
||||||
|
ObligationCause::dummy(),
|
||||||
|
self.param_env,
|
||||||
|
pred,
|
||||||
|
));
|
||||||
|
ocx.select_all_or_error().is_empty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||||
@ -1202,8 +1221,33 @@ macro_rules! check_kinds {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CastKind::PointerCoercion(PointerCoercion::Unsize, _) => {
|
CastKind::PointerCoercion(PointerCoercion::Unsize, _) => {
|
||||||
// This is used for all `CoerceUnsized` types,
|
// Pointers being unsize coerced should at least implement
|
||||||
// not just pointers/references, so is hard to check.
|
// `CoerceUnsized`.
|
||||||
|
if !self.predicate_must_hold_modulo_regions(ty::TraitRef::new(
|
||||||
|
self.tcx,
|
||||||
|
self.tcx.require_lang_item(
|
||||||
|
LangItem::CoerceUnsized,
|
||||||
|
Some(self.body.source_info(location).span),
|
||||||
|
),
|
||||||
|
[op_ty, *target_type],
|
||||||
|
)) {
|
||||||
|
self.fail(location, format!("Unsize coercion, but `{op_ty}` isn't coercible to `{target_type}`"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Codegen has an additional assumption, where if the
|
||||||
|
// principal trait def id of what's being casted doesn't change,
|
||||||
|
// then we don't need to adjust the vtable at all. This
|
||||||
|
// corresponds to the fact that `dyn Tr<A>: Unsize<dyn Tr<B>>`
|
||||||
|
// requires that `A = B`; we don't allow *upcasting* objects
|
||||||
|
// between the same trait with different args. Nothing actually
|
||||||
|
// validates this, though. While it's true right now, if we for
|
||||||
|
// some reason were to relax the `Unsize` trait, it could become
|
||||||
|
// unsound. We should eventually validate that, but it would
|
||||||
|
// require peeling `&Box<Struct<.., dyn Tr<A>, ..>>` down to
|
||||||
|
// the trait object that's being unsized, and that's rather
|
||||||
|
// annoying, and also it would need to be opportunistic since
|
||||||
|
// this MIR is not yet fully monomorphized, so we may bottom
|
||||||
|
// out in an alias or a projection or something.
|
||||||
}
|
}
|
||||||
CastKind::PointerCoercion(PointerCoercion::DynStar, _) => {
|
CastKind::PointerCoercion(PointerCoercion::DynStar, _) => {
|
||||||
// FIXME(dyn-star): make sure nothing needs to be done here.
|
// FIXME(dyn-star): make sure nothing needs to be done here.
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
//@ known-bug: rust-lang/rust#129219
|
|
||||||
//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir --edition=2018
|
|
||||||
|
|
||||||
use core::marker::Unsize;
|
|
||||||
|
|
||||||
pub trait CastTo<T: ?Sized>: Unsize<T> {}
|
|
||||||
|
|
||||||
impl<T: ?Sized, U: ?Sized> CastTo<T> for U {}
|
|
||||||
|
|
||||||
impl<T: ?Sized> Cast for T {}
|
|
||||||
pub trait Cast {
|
|
||||||
fn cast<T: ?Sized>(&self) -> &T
|
|
||||||
where
|
|
||||||
Self: CastTo<T>,
|
|
||||||
{
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Foo {}
|
|
||||||
impl Foo for [i32; 0] {}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let x: &dyn Foo = &[];
|
|
||||||
let x = x.cast::<[i32]>();
|
|
||||||
}
|
|
33
tests/ui/mir/validate/validate-unsize-cast.rs
Normal file
33
tests/ui/mir/validate/validate-unsize-cast.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//@ compile-flags: -Zmir-opt-level=0 -Zmir-enable-passes=+Inline,+GVN -Zvalidate-mir
|
||||||
|
|
||||||
|
#![feature(unsize)]
|
||||||
|
|
||||||
|
use std::marker::Unsize;
|
||||||
|
|
||||||
|
pub trait CastTo<U: ?Sized>: Unsize<U> {}
|
||||||
|
|
||||||
|
// Not well-formed!
|
||||||
|
impl<T: ?Sized, U: ?Sized> CastTo<U> for T {}
|
||||||
|
//~^ ERROR the trait bound `T: Unsize<U>` is not satisfied
|
||||||
|
|
||||||
|
pub trait Cast {
|
||||||
|
fn cast<U: ?Sized>(&self)
|
||||||
|
where
|
||||||
|
Self: CastTo<U>;
|
||||||
|
}
|
||||||
|
impl<T: ?Sized> Cast for T {
|
||||||
|
#[inline(always)]
|
||||||
|
fn cast<U: ?Sized>(&self)
|
||||||
|
where
|
||||||
|
Self: CastTo<U>,
|
||||||
|
{
|
||||||
|
let x: &U = self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// When we inline this call, then we run GVN, then
|
||||||
|
// GVN tries to evaluate the `() -> [i32]` unsize.
|
||||||
|
// That's invalid!
|
||||||
|
().cast::<[i32]>();
|
||||||
|
}
|
20
tests/ui/mir/validate/validate-unsize-cast.stderr
Normal file
20
tests/ui/mir/validate/validate-unsize-cast.stderr
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
error[E0277]: the trait bound `T: Unsize<U>` is not satisfied
|
||||||
|
--> $DIR/validate-unsize-cast.rs:10:42
|
||||||
|
|
|
||||||
|
LL | impl<T: ?Sized, U: ?Sized> CastTo<U> for T {}
|
||||||
|
| ^ the trait `Unsize<U>` is not implemented for `T`
|
||||||
|
|
|
||||||
|
= 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
|
||||||
|
note: required by a bound in `CastTo`
|
||||||
|
--> $DIR/validate-unsize-cast.rs:7:30
|
||||||
|
|
|
||||||
|
LL | pub trait CastTo<U: ?Sized>: Unsize<U> {}
|
||||||
|
| ^^^^^^^^^ required by this bound in `CastTo`
|
||||||
|
help: consider further restricting this bound
|
||||||
|
|
|
||||||
|
LL | impl<T: ?Sized + std::marker::Unsize<U>, U: ?Sized> CastTo<U> for T {}
|
||||||
|
| ++++++++++++++++++++++++
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Reference in New Issue
Block a user