diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 3372cae7a5107..20c2dfc01d467 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -15,7 +15,7 @@ use crate::{Diverges, Expectation, FnCtxt, Needs}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug", ret)] - pub(crate) fn check_match( + pub(crate) fn check_expr_match( &self, expr: &'tcx hir::Expr<'tcx>, scrut: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index ed56bb9c455cc..782b4884f2a36 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -63,7 +63,7 @@ enum CallStep<'tcx> { } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub(crate) fn check_call( + pub(crate) fn check_expr_call( &self, call_expr: &'tcx hir::Expr<'tcx>, callee_expr: &'tcx hir::Expr<'tcx>, @@ -75,8 +75,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .check_expr_with_expectation_and_args( callee_expr, Expectation::NoExpectation, - arg_exprs, - Some(call_expr), + Some((call_expr, arg_exprs)), ), _ => self.check_expr(callee_expr), }; diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 2d8943c615920..569a04f350097 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -137,7 +137,7 @@ pub(super) fn check_fn<'a, 'tcx>( } fcx.is_whole_body.set(true); - fcx.check_return_expr(body.value, false); + fcx.check_return_or_body_tail(body.value, false); // Finalize the return check by taking the LUB of the return types // we saw and assigning it to the expected return type. This isn't diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d6e5fab610ed6..e067ba6408ae2 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -55,6 +55,9 @@ use crate::{ }; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Check an expr with an expectation type, and also demand that the expr's + /// evaluated type is a subtype of the expectation at the end. This is a + /// *hard* requirement. pub(crate) fn check_expr_has_type_or_error( &self, expr: &'tcx hir::Expr<'tcx>, @@ -97,6 +100,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } + /// Check an expr with an expectation type, and also demand that the expr's + /// evaluated type is a coercible to the expectation at the end. This is a + /// *hard* requirement. pub(super) fn check_expr_coercible_to_type( &self, expr: &'tcx hir::Expr<'tcx>, @@ -108,6 +114,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_coerce(expr, ty, expected, expected_ty_expr, AllowTwoPhase::No) } + /// Check an expr with an expectation type. Don't actually enforce that expectation + /// is related to the expr's evaluated type via subtyping or coercion. This is + /// usually called because we want to do that subtype/coerce call manually for better + /// diagnostics. pub(super) fn check_expr_with_hint( &self, expr: &'tcx hir::Expr<'tcx>, @@ -116,6 +126,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_with_expectation(expr, ExpectHasType(expected)) } + /// Check an expr with an expectation type, and also [`Needs`] which will + /// prompt typeck to convert any implicit immutable derefs to mutable derefs. fn check_expr_with_expectation_and_needs( &self, expr: &'tcx hir::Expr<'tcx>, @@ -133,10 +145,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } + /// Check an expr with no expectations. pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { self.check_expr_with_expectation(expr, NoExpectation) } + /// Check an expr with no expectations, but with [`Needs`] which will + /// prompt typeck to convert any implicit immutable derefs to mutable derefs. pub(super) fn check_expr_with_needs( &self, expr: &'tcx hir::Expr<'tcx>, @@ -145,33 +160,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_with_expectation_and_needs(expr, NoExpectation, needs) } - /// Invariant: - /// If an expression has any sub-expressions that result in a type error, - /// inspecting that expression's type with `ty.references_error()` will return - /// true. Likewise, if an expression is known to diverge, inspecting its - /// type with `ty::type_is_bot` will return true (n.b.: since Rust is - /// strict, _|_ can appear in the type of an expression that does not, - /// itself, diverge: for example, fn() -> _|_.) - /// Note that inspecting a type's structure *directly* may expose the fact - /// that there are actually multiple representations for `Error`, so avoid - /// that when err needs to be handled differently. + /// Check an expr with an expectation type which may be used to eagerly + /// guide inference when evaluating that expr. #[instrument(skip(self, expr), level = "debug")] pub(super) fn check_expr_with_expectation( &self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - self.check_expr_with_expectation_and_args(expr, expected, &[], None) + self.check_expr_with_expectation_and_args(expr, expected, None) } - /// 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`. + /// Same as [`Self::check_expr_with_expectation`], but allows us to pass in + /// the arguments of a [`ExprKind::Call`] when evaluating its callee that + /// is an [`ExprKind::Path`]. We use this to refine the spans for certain + /// well-formedness guarantees for the path expr. pub(super) fn check_expr_with_expectation_and_args( &self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, - args: &'tcx [hir::Expr<'tcx>], - call: Option<&'tcx hir::Expr<'tcx>>, + call_expr_and_args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, ) -> Ty<'tcx> { if self.tcx().sess.verbose_internals() { // make this code only run with -Zverbose-internals because it is probably slow @@ -216,9 +224,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let ty = ensure_sufficient_stack(|| match &expr.kind { + // Intercept the callee path expr and give it better spans. hir::ExprKind::Path( qpath @ (hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)), - ) => self.check_expr_path(qpath, expr, Some(args), call), + ) => self.check_expr_path(qpath, expr, call_expr_and_args), _ => self.check_expr_kind(expr, expected), }); let ty = self.resolve_vars_if_possible(ty); @@ -452,28 +461,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; match expr.kind { - ExprKind::Lit(ref lit) => self.check_lit(lit, expected), - ExprKind::Binary(op, lhs, rhs) => self.check_binop(expr, op, lhs, rhs, expected), + ExprKind::Lit(ref lit) => self.check_expr_lit(lit, expected), + ExprKind::Binary(op, lhs, rhs) => self.check_expr_binop(expr, op, lhs, rhs, expected), ExprKind::Assign(lhs, rhs, span) => { self.check_expr_assign(expr, expected, lhs, rhs, span) } ExprKind::AssignOp(op, lhs, rhs) => { - self.check_binop_assign(expr, op, lhs, rhs, expected) + self.check_expr_binop_assign(expr, op, lhs, rhs, expected) } - ExprKind::Unary(unop, oprnd) => self.check_expr_unary(unop, oprnd, expected, expr), + ExprKind::Unary(unop, oprnd) => self.check_expr_unop(unop, oprnd, expected, expr), ExprKind::AddrOf(kind, mutbl, oprnd) => { self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) } ExprKind::Path(QPath::LangItem(lang_item, _)) => { self.check_lang_item_path(lang_item, expr) } - ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, None, None), + ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, None), ExprKind::InlineAsm(asm) => { // We defer some asm checks as we may not have resolved the input and output types yet (they may still be infer vars). self.deferred_asm_checks.borrow_mut().push((asm, expr.hir_id)); self.check_expr_asm(asm) } - ExprKind::OffsetOf(container, fields) => self.check_offset_of(container, fields, expr), + ExprKind::OffsetOf(container, fields) => { + self.check_expr_offset_of(container, fields, expr) + } ExprKind::Break(destination, ref expr_opt) => { self.check_expr_break(destination, expr_opt.as_deref(), expr) } @@ -492,13 +503,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_loop(body, source, expected, expr) } ExprKind::Match(discrim, arms, match_src) => { - self.check_match(expr, discrim, arms, expected, match_src) + self.check_expr_match(expr, discrim, arms, expected, match_src) } ExprKind::Closure(closure) => self.check_expr_closure(closure, expr.span, expected), - ExprKind::Block(body, _) => self.check_block_with_expected(body, expected), - ExprKind::Call(callee, args) => self.check_call(expr, callee, args, expected), + ExprKind::Block(body, _) => self.check_expr_block(body, expected), + ExprKind::Call(callee, args) => self.check_expr_call(expr, callee, args, expected), ExprKind::MethodCall(segment, receiver, args, _) => { - self.check_method_call(expr, segment, receiver, args, expected) + self.check_expr_method_call(expr, segment, receiver, args, expected) } ExprKind::Cast(e, t) => self.check_expr_cast(e, t, expr), ExprKind::Type(e, t) => { @@ -508,7 +519,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ascribed_ty } ExprKind::If(cond, then_expr, opt_else_expr) => { - self.check_then_else(cond, then_expr, opt_else_expr, expr.span, expected) + self.check_expr_if(cond, then_expr, opt_else_expr, expr.span, expected) } ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected), ExprKind::Array(args) => self.check_expr_array(args, expected, expr), @@ -520,7 +531,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Struct(qpath, fields, ref base_expr) => { self.check_expr_struct(expr, expected, qpath, fields, base_expr) } - ExprKind::Field(base, field) => self.check_field(expr, base, field, expected), + ExprKind::Field(base, field) => self.check_expr_field(expr, base, field, expected), ExprKind::Index(base, idx, brackets_span) => { self.check_expr_index(base, idx, expr, brackets_span) } @@ -529,7 +540,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_expr_unary( + fn check_expr_unop( &self, unop: hir::UnOp, oprnd: &'tcx hir::Expr<'tcx>, @@ -679,8 +690,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, qpath: &'tcx hir::QPath<'tcx>, expr: &'tcx hir::Expr<'tcx>, - args: Option<&'tcx [hir::Expr<'tcx>]>, - call: Option<&'tcx hir::Expr<'tcx>>, + call_expr_and_args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, ) -> Ty<'tcx> { let tcx = self.tcx; let (res, opt_ty, segs) = @@ -710,7 +720,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { segs, opt_ty, res, - call.map_or(expr.span, |e| e.span), + call_expr_and_args.map_or(expr.span, |(e, _)| e.span), expr.span, expr.hir_id, ) @@ -749,7 +759,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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.and_then(|args| args.get(i)).map_or(expr.span, |arg| arg.span); + let span = call_expr_and_args + .and_then(|(_, args)| args.get(i)) + .map_or(expr.span, |arg| arg.span); let input = self.instantiate_binder_with_fresh_vars( span, infer::BoundRegionConversionTime::FnCall, @@ -775,7 +787,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); self.require_type_is_sized_deferred( output, - call.map_or(expr.span, |e| e.span), + call_expr_and_args.map_or(expr.span, |(e, _)| e.span), ObligationCauseCode::SizedCallReturnType, ); } @@ -952,7 +964,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.ret_coercion_span.get().is_none() { self.ret_coercion_span.set(Some(e.span)); } - self.check_return_expr(e, true); + self.check_return_or_body_tail(e, true); } else { let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut(); if self.ret_coercion_span.get().is_none() { @@ -1015,7 +1027,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// `explicit_return` is `true` if we're checking an explicit `return expr`, /// and `false` if we're checking a trailing expression. - pub(super) fn check_return_expr( + pub(super) fn check_return_or_body_tail( &self, return_expr: &'tcx hir::Expr<'tcx>, explicit_return: bool, @@ -1239,7 +1251,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // A generic function for checking the 'then' and 'else' clauses in an 'if' // or 'if-else' expression. - fn check_then_else( + fn check_expr_if( &self, cond_expr: &'tcx hir::Expr<'tcx>, then_expr: &'tcx hir::Expr<'tcx>, @@ -1522,7 +1534,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Checks a method call. - fn check_method_call( + fn check_expr_method_call( &self, expr: &'tcx hir::Expr<'tcx>, segment: &'tcx hir::PathSegment<'tcx>, @@ -2574,7 +2586,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Check field access expressions - fn check_field( + fn check_expr_field( &self, expr: &'tcx hir::Expr<'tcx>, base: &'tcx hir::Expr<'tcx>, @@ -3515,8 +3527,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let previous_diverges = self.diverges.get(); // The label blocks should have unit return value or diverge. - let ty = - self.check_block_with_expected(block, ExpectHasType(self.tcx.types.unit)); + let ty = self.check_expr_block(block, ExpectHasType(self.tcx.types.unit)); if !ty.is_never() { self.demand_suptype(block.span, self.tcx.types.unit, ty); diverge = false; @@ -3531,7 +3542,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if diverge { self.tcx.types.never } else { self.tcx.types.unit } } - fn check_offset_of( + fn check_expr_offset_of( &self, container: &'tcx hir::Ty<'tcx>, fields: &[Ident], diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index a6c249da103f3..50d1322eba649 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1565,7 +1565,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // AST fragment checking - pub(in super::super) fn check_lit( + pub(in super::super) fn check_expr_lit( &self, lit: &hir::Lit, expected: Expectation<'tcx>, @@ -1747,7 +1747,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(blk) = decl.origin.try_get_else() { let previous_diverges = self.diverges.get(); - let else_ty = self.check_block_with_expected(blk, NoExpectation); + let else_ty = self.check_expr_block(blk, NoExpectation); let cause = self.cause(blk.span, ObligationCauseCode::LetElse); if let Err(err) = self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) { @@ -1805,7 +1805,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { let unit = self.tcx.types.unit; - let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); + let ty = self.check_expr_block(blk, ExpectHasType(unit)); // if the block produces a `!` value, that can always be // (effectively) coerced to unit. @@ -1814,7 +1814,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(in super::super) fn check_block_with_expected( + pub(in super::super) fn check_expr_block( &self, blk: &'tcx hir::Block<'tcx>, expected: Expectation<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 1574e9e98d4d8..c1a7340ced92f 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -26,7 +26,7 @@ use crate::Expectation; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Checks a `a = b` - pub(crate) fn check_binop_assign( + pub(crate) fn check_expr_binop_assign( &self, expr: &'tcx hir::Expr<'tcx>, op: hir::BinOp, @@ -85,7 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Checks a potentially overloaded binary operator. - pub(crate) fn check_binop( + pub(crate) fn check_expr_binop( &self, expr: &'tcx hir::Expr<'tcx>, op: hir::BinOp,