// 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.
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))?;
// 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(
&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,
);