]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #89028 - Aaron1011:coercion-cause, r=nagisa
authorbors <bors@rust-lang.org>
Sun, 19 Sep 2021 04:34:16 +0000 (04:34 +0000)
committerbors <bors@rust-lang.org>
Sun, 19 Sep 2021 04:34:16 +0000 (04:34 +0000)
Propagate coercion cause into `try_coerce`

Currently, `coerce_inner` discards its `ObligationCause`
when calling `try_coerce`. This interfers with other
diagnostc improvements I'm working on, since we will lose
the original span by the time the actual coercion occurs.

Additionally, we now use the span of the trailing expression
(rather than the span of the entire function) when performing
a coercion in `check_return_expr`. This currently has no visible
effect on any of the unit tests, but will unblock future
diagnostic improvements.

1  2 
compiler/rustc_typeck/src/check/coercion.rs
compiler/rustc_typeck/src/check/expr.rs

index 013aecae586ca93369917a4e7008377de62e4dbb,e47c7e64ab5524e2971134d17db3fd84545dfd3f..92d0470bc2f58ceb92550ad72a12130192b8bb1b
@@@ -707,7 -707,13 +707,7 @@@ impl<'f, 'tcx> Coerce<'f, 'tcx> 
  
                  // Object safety violations or miscellaneous.
                  Err(err) => {
 -                    self.report_selection_error(
 -                        obligation.clone(),
 -                        &obligation,
 -                        &err,
 -                        false,
 -                        false,
 -                    );
 +                    self.report_selection_error(obligation.clone(), &obligation, &err, false);
                      // Treat this like an obligation and follow through
                      // with the unsizing - the lack of a coercion should
                      // be silent, as it causes a type mismatch later.
@@@ -935,11 -941,13 +935,13 @@@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> 
          expr_ty: Ty<'tcx>,
          target: Ty<'tcx>,
          allow_two_phase: AllowTwoPhase,
+         cause: Option<ObligationCause<'tcx>>,
      ) -> RelateResult<'tcx, Ty<'tcx>> {
          let source = self.resolve_vars_with_obligations(expr_ty);
          debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
  
-         let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
+         let cause =
+             cause.unwrap_or_else(|| self.cause(expr.span, ObligationCauseCode::ExprAssignable));
          let coerce = Coerce::new(self, cause, allow_two_phase);
          let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?;
  
@@@ -1363,7 -1371,13 +1365,13 @@@ impl<'tcx, 'exprs, E: AsCoercionSite> C
                  // Special-case the first expression we are coercing.
                  // To be honest, I'm not entirely sure why we do this.
                  // We don't allow two-phase borrows, see comment in try_find_coercion_lub for why
-                 fcx.try_coerce(expression, expression_ty, self.expected_ty, AllowTwoPhase::No)
+                 fcx.try_coerce(
+                     expression,
+                     expression_ty,
+                     self.expected_ty,
+                     AllowTwoPhase::No,
+                     Some(cause.clone()),
+                 )
              } else {
                  match self.expressions {
                      Expressions::Dynamic(ref exprs) => fcx.try_find_coercion_lub(
index 917adf0e2b9bfa17b489ed22f99c0b243be9d3e3,0c011b85b0685ae92b06c5d742c7380b8e7dd0b1..53c1470c601d21c87dbbfba1899b68ddb54372b0
@@@ -161,17 -161,6 +161,17 @@@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> 
          &self,
          expr: &'tcx hir::Expr<'tcx>,
          expected: Expectation<'tcx>,
 +    ) -> Ty<'tcx> {
 +        self.check_expr_with_expectation_and_args(expr, expected, &[])
 +    }
 +
 +    /// Same as `check_expr_with_expectation`, but allows us to pass in the arguments of a
 +    /// `ExprKind::Call` when evaluating its callee when it is an `ExprKind::Path`.
 +    pub(super) fn check_expr_with_expectation_and_args(
 +        &self,
 +        expr: &'tcx hir::Expr<'tcx>,
 +        expected: Expectation<'tcx>,
 +        args: &'tcx [hir::Expr<'tcx>],
      ) -> Ty<'tcx> {
          if self.tcx().sess.verbose() {
              // make this code only run with -Zverbose because it is probably slow
          let old_diverges = self.diverges.replace(Diverges::Maybe);
          let old_has_errors = self.has_errors.replace(false);
  
 -        let ty = ensure_sufficient_stack(|| self.check_expr_kind(expr, expected));
 +        let ty = ensure_sufficient_stack(|| match &expr.kind {
 +            hir::ExprKind::Path(
 +                qpath @ hir::QPath::Resolved(..) | qpath @ hir::QPath::TypeRelative(..),
 +            ) => self.check_expr_path(qpath, expr, args),
 +            _ => self.check_expr_kind(expr, expected),
 +        });
  
          // Warn for non-block expressions with diverging children.
          match expr.kind {
              ExprKind::Path(QPath::LangItem(lang_item, _)) => {
                  self.check_lang_item_path(lang_item, expr)
              }
 -            ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr),
 +            ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, &[]),
              ExprKind::InlineAsm(asm) => self.check_expr_asm(asm),
              ExprKind::LlvmInlineAsm(asm) => {
                  for expr in asm.outputs_exprs.iter().chain(asm.inputs_exprs.iter()) {
          self.resolve_lang_item_path(lang_item, expr.span, expr.hir_id).1
      }
  
 -    fn check_expr_path(
 +    pub(crate) fn check_expr_path(
          &self,
          qpath: &'tcx hir::QPath<'tcx>,
          expr: &'tcx hir::Expr<'tcx>,
 +        args: &'tcx [hir::Expr<'tcx>],
      ) -> Ty<'tcx> {
          let tcx = self.tcx;
          let (res, opt_ty, segs) =
                      // We just want to check sizedness, so instead of introducing
                      // placeholder lifetimes with probing, we just replace higher lifetimes
                      // with fresh vars.
 +                    let span = args.get(i).map(|a| a.span).unwrap_or(expr.span);
                      let input = self
                          .replace_bound_vars_with_fresh_vars(
 -                            expr.span,
 +                            span,
                              infer::LateBoundRegionConversionTime::FnCall,
                              fn_sig.input(i),
                          )
                          .0;
                      self.require_type_is_sized_deferred(
                          input,
 -                        expr.span,
 +                        span,
                          traits::SizedArgumentType(None),
                      );
                  }
              if self.ret_coercion_span.get().is_none() {
                  self.ret_coercion_span.set(Some(e.span));
              }
-             self.check_return_expr(e);
+             self.check_return_expr(e, true);
          } else {
              let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut();
              if self.ret_coercion_span.get().is_none() {
          self.tcx.types.never
      }
  
-     pub(super) fn check_return_expr(&self, return_expr: &'tcx hir::Expr<'tcx>) {
+     /// `explicit_return` is `true` if we're checkng an explicit `return expr`,
+     /// and `false` if we're checking a trailing expression.
+     pub(super) fn check_return_expr(
+         &self,
+         return_expr: &'tcx hir::Expr<'tcx>,
+         explicit_return: bool,
+     ) {
          let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| {
              span_bug!(return_expr.span, "check_return_expr called outside fn body")
          });
  
          let ret_ty = ret_coercion.borrow().expected_ty();
          let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty);
+         let mut span = return_expr.span;
+         // Use the span of the trailing expression for our cause,
+         // not the span of the entire function
+         if !explicit_return {
+             if let ExprKind::Block(body, _) = return_expr.kind {
+                 if let Some(last_expr) = body.expr {
+                     span = last_expr.span;
+                 }
+             }
+         }
          ret_coercion.borrow_mut().coerce(
              self,
-             &self.cause(return_expr.span, ObligationCauseCode::ReturnValue(return_expr.hir_id)),
+             &self.cause(span, ObligationCauseCode::ReturnValue(return_expr.hir_id)),
              return_expr,
              return_expr_ty,
          );