]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_typeck/src/check/cast.rs
Typecheck dyn* coercions
[rust.git] / compiler / rustc_typeck / src / check / cast.rs
index a3fcda5864b3f600239614176393790e779093dd..fa6224f1915a9ae2e425a8cc96997d8f0552fe8e 100644 (file)
 use hir::def_id::LOCAL_CRATE;
 use rustc_errors::{struct_span_err, Applicability, 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, Ty, TypeAndMut, TypeVisitable, VariantDef};
+use rustc_middle::ty::{
+    self, Binder, TraitObjectRepresentation, Ty, TypeAndMut, TypeVisitable, VariantDef,
+};
 use rustc_session::lint;
 use rustc_session::Session;
 use rustc_span::symbol::sym;
 /// a function context.
 #[derive(Debug)]
 pub struct CastCheck<'tcx> {
+    /// The expression whose value is being casted
     expr: &'tcx hir::Expr<'tcx>,
+    /// The source type for the cast expression
     expr_ty: Ty<'tcx>,
     expr_span: Span,
+    /// The target type. That is, the type we are casting to.
     cast_ty: Ty<'tcx>,
     cast_span: Span,
     span: Span,
@@ -199,8 +205,76 @@ 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, TraitObjectRepresentation::Sized) => (predicates, region),
+        _ => panic!("Invalid dyn* cast_ty"),
+    };
+
+    let cause = ObligationCause::new(
+        expr.span,
+        fcx.body_id,
+        // FIXME: 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> {
-    pub fn new(
+    fn new(
         fcx: &FnCtxt<'a, 'tcx>,
         expr: &'tcx hir::Expr<'tcx>,
         expr_ty: Ty<'tcx>,
@@ -215,7 +289,7 @@ pub fn new(
         // cases now. We do a more thorough check at the end, once
         // inference is more completely known.
         match cast_ty.kind() {
-            ty::Dynamic(..) | ty::Slice(..) => {
+            ty::Dynamic(_, _, TraitObjectRepresentation::Unsized) | ty::Slice(..) => {
                 let reported = check.report_cast_to_unsized_type(fcx);
                 Err(reported)
             }