Auto merge of #101832 - compiler-errors:dyn-star-plus, r=eholk
Make `dyn*` casts into a coercion, allow `dyn*` upcasting I know that `dyn*` is likely not going to be a feature exposed to surface Rust, but this makes it slightly more ergonomic to write tests for these types anyways. ... and this was just fun to implement anyways. 1. Make `dyn*` into a coercion instead of a cast 2. Enable `dyn*` upcasting since we basically get it for free 3. Simplify some of the cast checking code since we're using the coercion path now r? `@eholk` but feel free to reassign cc `@nikomatsakis` and `@tmandry` who might care about making `dyn*` casts into a coercion
This commit is contained in:
commit
b15e2c129e
21 changed files with 233 additions and 136 deletions
|
@ -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;
|
||||
|
@ -204,76 +203,8 @@ fn make_invalid_casting_error<'a, 'tcx>(
|
|||
)
|
||||
}
|
||||
|
||||
pub enum CastCheckResult<'tcx> {
|
||||
Ok,
|
||||
Deferred(CastCheck<'tcx>),
|
||||
Err(ErrorGuaranteed),
|
||||
}
|
||||
|
||||
pub fn check_cast<'tcx>(
|
||||
fcx: &FnCtxt<'_, 'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
expr_ty: Ty<'tcx>,
|
||||
cast_ty: Ty<'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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
pub fn new(
|
||||
fcx: &FnCtxt<'a, 'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
expr_ty: Ty<'tcx>,
|
||||
|
@ -934,11 +865,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`"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,63 @@ 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 if !self.tcx().features().trait_upcasting {
|
||||
let mut err = feature_err(
|
||||
&self.tcx.sess.parse_sess,
|
||||
sym::trait_upcasting,
|
||||
self.cause.span,
|
||||
&format!(
|
||||
"cannot cast `{a}` to `{b}`, trait upcasting coercion is experimental"
|
||||
),
|
||||
);
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
|
||||
// Check the obligations of the cast -- for example, when casting
|
||||
// `usize` to `dyn* Clone + 'static`:
|
||||
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 (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>,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//! See `mod.rs` for more context on type checking in general.
|
||||
|
||||
use crate::astconv::AstConv as _;
|
||||
use crate::check::cast::{self, CastCheckResult};
|
||||
use crate::check::cast;
|
||||
use crate::check::coercion::CoerceMany;
|
||||
use crate::check::fatally_break_rust;
|
||||
use crate::check::method::SelfSource;
|
||||
|
@ -1270,9 +1270,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
} else {
|
||||
// Defer other checks until we're done type checking.
|
||||
let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
|
||||
match cast::check_cast(self, e, t_expr, t_cast, t.span, expr.span) {
|
||||
CastCheckResult::Ok => t_cast,
|
||||
CastCheckResult::Deferred(cast_check) => {
|
||||
match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) {
|
||||
Ok(cast_check) => {
|
||||
debug!(
|
||||
"check_expr_cast: deferring cast from {:?} to {:?}: {:?}",
|
||||
t_cast, t_expr, cast_check,
|
||||
|
@ -1280,7 +1279,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
deferred_cast_checks.push(cast_check);
|
||||
t_cast
|
||||
}
|
||||
CastCheckResult::Err(ErrorGuaranteed { .. }) => self.tcx.ty_error(),
|
||||
Err(_) => self.tcx.ty_error(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue