Make dyn* cast into a coercion

This commit is contained in:
Michael Goulet 2022-09-14 23:20:03 +00:00
parent edabf59ca4
commit 76386bd65e
13 changed files with 73 additions and 68 deletions

View File

@ -35,13 +35,12 @@ use crate::type_error_struct;
use hir::def_id::LOCAL_CRATE;
use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode};
use rustc_middle::mir::Mutability;
use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::cast::{CastKind, CastTy};
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, Binder, Ty, TypeAndMut, TypeVisitable, VariantDef};
use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable, VariantDef};
use rustc_session::lint;
use rustc_session::Session;
use rustc_span::symbol::sym;
@ -218,60 +217,12 @@ pub fn check_cast<'tcx>(
cast_span: Span,
span: Span,
) -> CastCheckResult<'tcx> {
if cast_ty.is_dyn_star() {
check_dyn_star_cast(fcx, expr, expr_ty, cast_ty)
} else {
match CastCheck::new(fcx, expr, expr_ty, cast_ty, cast_span, span) {
Ok(check) => CastCheckResult::Deferred(check),
Err(e) => CastCheckResult::Err(e),
}
match CastCheck::new(fcx, expr, expr_ty, cast_ty, cast_span, span) {
Ok(check) => CastCheckResult::Deferred(check),
Err(e) => CastCheckResult::Err(e),
}
}
fn check_dyn_star_cast<'tcx>(
fcx: &FnCtxt<'_, 'tcx>,
expr: &'tcx hir::Expr<'tcx>,
expr_ty: Ty<'tcx>,
cast_ty: Ty<'tcx>,
) -> CastCheckResult<'tcx> {
// Find the bounds in the dyn*. For eaxmple, if we have
//
// let x = 22_usize as dyn* (Clone + Debug + 'static)
//
// this would return `existential_predicates = [?Self: Clone, ?Self: Debug]` and `region = 'static`.
let (existential_predicates, region) = match cast_ty.kind() {
ty::Dynamic(predicates, region, ty::DynStar) => (predicates, region),
_ => panic!("Invalid dyn* cast_ty"),
};
let cause = ObligationCause::new(
expr.span,
fcx.body_id,
// FIXME(dyn-star): Use a better obligation cause code
ObligationCauseCode::MiscObligation,
);
// For each existential predicate (e.g., `?Self: Clone`) substitute
// the type of the expression (e.g., `usize` in our example above)
// and then require that the resulting predicate (e.g., `usize: Clone`)
// holds (it does).
for existential_predicate in existential_predicates.iter() {
let predicate = existential_predicate.with_self_ty(fcx.tcx, expr_ty);
fcx.register_predicate(Obligation::new(cause.clone(), fcx.param_env, predicate));
}
// Enforce the region bound `'static` (e.g., `usize: 'static`, in our example).
fcx.register_predicate(Obligation::new(
cause,
fcx.param_env,
fcx.tcx.mk_predicate(Binder::dummy(ty::PredicateKind::TypeOutlives(
ty::OutlivesPredicate(expr_ty, *region),
))),
));
CastCheckResult::Ok
}
impl<'a, 'tcx> CastCheck<'tcx> {
fn new(
fcx: &FnCtxt<'a, 'tcx>,
@ -934,11 +885,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
(Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast),
// FIXME(dyn-star): this needs more conditions...
(_, DynStar) => Ok(CastKind::DynStarCast),
// FIXME(dyn-star): do we want to allow dyn* upcasting or other casts?
(DynStar, _) => Err(CastError::IllegalCast),
(_, DynStar) | (DynStar, _) => bug!("should be handled by `try_coerce`"),
}
}

View File

@ -216,6 +216,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
ty::Ref(r_b, _, mutbl_b) => {
return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b);
}
ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star => {
return self.coerce_dyn_star(a, b, predicates, region);
}
_ => {}
}
@ -745,6 +748,53 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
Ok(coercion)
}
fn coerce_dyn_star(
&self,
a: Ty<'tcx>,
b: Ty<'tcx>,
predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
b_region: ty::Region<'tcx>,
) -> CoerceResult<'tcx> {
if !self.tcx.features().dyn_star {
return Err(TypeError::Mismatch);
}
if let ty::Dynamic(a_data, _, _) = a.kind()
&& let ty::Dynamic(b_data, _, _) = b.kind()
{
if a_data.principal_def_id() == b_data.principal_def_id() {
return self.unify_and(a, b, |_| vec![]);
} else {
bug!("dyn* trait upcasting is not supported");
}
}
let obligations = predicates
.iter()
.map(|predicate| {
// For each existential predicate (e.g., `?Self: Clone`) substitute
// the type of the expression (e.g., `usize` in our example above)
// and then require that the resulting predicate (e.g., `usize: Clone`)
// holds (it does).
let predicate = predicate.with_self_ty(self.tcx, a);
Obligation::new(self.cause.clone(), self.param_env, predicate)
})
// Enforce the region bound `'static` (e.g., `usize: 'static`, in our example).
.chain([Obligation::new(
self.cause.clone(),
self.param_env,
self.tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::TypeOutlives(
ty::OutlivesPredicate(a, b_region),
))),
)])
.collect();
Ok(InferOk {
value: (vec![Adjustment { kind: Adjust::DynStar, target: b }], b),
obligations,
})
}
fn coerce_from_safe_fn<F, G>(
&self,
a: Ty<'tcx>,

View File

@ -583,7 +583,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
for adjustment in adjustments {
debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment);
match adjustment.kind {
adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => {
adjustment::Adjust::NeverToAny
| adjustment::Adjust::Pointer(_)
| adjustment::Adjust::DynStar => {
// Creating a closure/fn-pointer or unsizing consumes
// the input and stores it into the resulting rvalue.
self.delegate_consume(&place_with_id, place_with_id.hir_id);

View File

@ -292,7 +292,8 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
adjustment::Adjust::NeverToAny
| adjustment::Adjust::Pointer(_)
| adjustment::Adjust::Borrow(_) => {
| adjustment::Adjust::Borrow(_)
| adjustment::Adjust::DynStar => {
// Result is an rvalue.
Ok(self.cat_rvalue(expr.hir_id, expr.span, target))
}

View File

@ -1824,7 +1824,6 @@ impl<'tcx> Rvalue<'tcx> {
// While the model is undecided, we should be conservative. See
// <https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html>
Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false,
Rvalue::Cast(CastKind::DynStar, _, _) => false,
Rvalue::Use(_)
| Rvalue::CopyForDeref(_)
@ -1841,7 +1840,8 @@ impl<'tcx> Rvalue<'tcx> {
| CastKind::FnPtrToPtr
| CastKind::PtrToPtr
| CastKind::Pointer(_)
| CastKind::PointerFromExposedAddress,
| CastKind::PointerFromExposedAddress
| CastKind::DynStar,
_,
_,
)

View File

@ -101,6 +101,9 @@ pub enum Adjust<'tcx> {
Borrow(AutoBorrow<'tcx>),
Pointer(PointerCast),
/// Cast into a dyn* object.
DynStar,
}
/// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)`

View File

@ -159,6 +159,7 @@ impl<'tcx> Cx<'tcx> {
Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => {
ExprKind::AddressOf { mutability, arg: self.thir.exprs.push(expr) }
}
Adjust::DynStar => ExprKind::Cast { source: self.thir.exprs.push(expr) },
};
Expr { temp_lifetime, ty: adjustment.target, span, kind }

View File

@ -6,7 +6,7 @@ use std::fmt::Debug;
fn make_dyn_star() {
let i = 42usize;
let dyn_i: dyn* Debug = i as dyn* Debug;
let dyn_i: dyn* Debug = i;
}
fn main() {

View File

@ -15,7 +15,7 @@ impl Drop for Foo {
}
fn make_dyn_star(i: Foo) {
let _dyn_i: dyn* Debug = i as dyn* Debug;
let _dyn_i: dyn* Debug = i;
}
fn main() {

View File

@ -7,7 +7,7 @@ trait Foo {}
fn make_dyn_star() {
let i = 42;
let dyn_i: dyn* Foo = i as dyn* Foo; //~ ERROR trait bound `{integer}: Foo` is not satisfied
let dyn_i: dyn* Foo = i; //~ ERROR trait bound `{integer}: Foo` is not satisfied
}
fn main() {}

View File

@ -1,7 +1,7 @@
error[E0277]: the trait bound `{integer}: Foo` is not satisfied
--> $DIR/error.rs:10:27
|
LL | let dyn_i: dyn* Foo = i as dyn* Foo;
LL | let dyn_i: dyn* Foo = i;
| ^ the trait `Foo` is not implemented for `{integer}`
error: aborting due to previous error

View File

@ -5,7 +5,7 @@
use std::fmt::Debug;
fn make_dyn_star(i: usize) {
let _dyn_i: dyn* Debug = i as dyn* Debug;
let _dyn_i: dyn* Debug = i;
}
fn main() {

View File

@ -1,4 +1,5 @@
// run-pass
#![feature(dyn_star)]
#![allow(incomplete_features)]
@ -17,7 +18,7 @@ fn invoke_dyn_star(i: dyn* Foo) -> usize {
}
fn make_and_invoke_dyn_star(i: usize) -> usize {
let dyn_i: dyn* Foo = i as dyn* Foo;
let dyn_i: dyn* Foo = i;
invoke_dyn_star(dyn_i)
}