1 use crate::coercion
::{AsCoercionSite, CoerceMany}
;
2 use crate::{Diverges, Expectation, FnCtxt, Needs}
;
3 use rustc_errors
::Diagnostic
;
4 use rustc_hir
::{self as hir, ExprKind}
;
5 use rustc_hir_pretty
::ty_to_string
;
6 use rustc_infer
::infer
::type_variable
::{TypeVariableOrigin, TypeVariableOriginKind}
;
7 use rustc_infer
::traits
::Obligation
;
8 use rustc_middle
::ty
::{self, Ty}
;
10 use rustc_trait_selection
::traits
::query
::evaluate_obligation
::InferCtxtExt
;
11 use rustc_trait_selection
::traits
::{
12 IfExpressionCause
, MatchExpressionArmCause
, ObligationCause
, ObligationCauseCode
,
15 impl<'a
, 'tcx
> FnCtxt
<'a
, 'tcx
> {
16 #[instrument(skip(self), level = "debug", ret)]
19 expr
: &'tcx hir
::Expr
<'tcx
>,
20 scrut
: &'tcx hir
::Expr
<'tcx
>,
21 arms
: &'tcx
[hir
::Arm
<'tcx
>],
22 orig_expected
: Expectation
<'tcx
>,
23 match_src
: hir
::MatchSource
,
27 let acrb
= arms_contain_ref_bindings(arms
);
28 let scrutinee_ty
= self.demand_scrutinee_type(scrut
, acrb
, arms
.is_empty());
29 debug
!(?scrutinee_ty
);
31 // If there are no arms, that is a diverging match; a special case.
33 self.diverges
.set(self.diverges
.get() | Diverges
::always(expr
.span
));
34 return tcx
.types
.never
;
37 self.warn_arms_when_scrutinee_diverges(arms
);
39 // Otherwise, we have to union together the types that the arms produce and so forth.
40 let scrut_diverges
= self.diverges
.replace(Diverges
::Maybe
);
42 // #55810: Type check patterns first so we get types for all bindings.
43 let scrut_span
= scrut
.span
.find_ancestor_inside(expr
.span
).unwrap_or(scrut
.span
);
45 self.check_pat_top(&arm
.pat
, scrutinee_ty
, Some(scrut_span
), Some(scrut
), None
);
48 // Now typecheck the blocks.
50 // The result of the match is the common supertype of all the
51 // arms. Start out the value as bottom, since it's the, well,
52 // bottom the type lattice, and we'll be moving up the lattice as
53 // we process each arm. (Note that any match with 0 arms is matching
54 // on any empty type and is therefore unreachable; should the flow
55 // of execution reach it, we will panic, so bottom is an appropriate
57 let mut all_arms_diverge
= Diverges
::WarnedAlways
;
59 let expected
= orig_expected
.adjust_for_branches(self);
63 let coerce_first
= match expected
{
64 // We don't coerce to `()` so that if the match expression is a
65 // statement it's branches can have any consistent type. That allows
66 // us to give better error messages (pointing to a usually better
67 // arm for inconsistent arms or to the whole match when a `()` type
69 Expectation
::ExpectHasType(ety
) if ety
!= Ty
::new_unit(self.tcx
) => ety
,
70 _
=> self.next_ty_var(TypeVariableOrigin
{
71 kind
: TypeVariableOriginKind
::MiscVariable
,
75 CoerceMany
::with_coercion_sites(coerce_first
, arms
)
78 let mut other_arms
= vec
![]; // Used only for diagnostics.
79 let mut prior_arm
= None
;
81 if let Some(g
) = &arm
.guard
{
82 self.diverges
.set(Diverges
::Maybe
);
84 hir
::Guard
::If(e
) => {
85 self.check_expr_has_type_or_error(e
, tcx
.types
.bool
, |_
| {}
);
87 hir
::Guard
::IfLet(l
) => {
88 self.check_expr_let(l
);
93 self.diverges
.set(Diverges
::Maybe
);
95 let arm_ty
= self.check_expr_with_expectation(&arm
.body
, expected
);
96 all_arms_diverge
&= self.diverges
.get();
98 let opt_suggest_box_span
= prior_arm
.and_then(|(_
, prior_arm_ty
, _
)| {
99 self.opt_suggest_box_span(prior_arm_ty
, arm_ty
, orig_expected
)
102 let (arm_block_id
, arm_span
) = if let hir
::ExprKind
::Block(blk
, _
) = arm
.body
.kind
{
103 (Some(blk
.hir_id
), self.find_block_span(blk
))
105 (None
, arm
.body
.span
)
108 let (span
, code
) = match prior_arm
{
109 // The reason for the first arm to fail is not that the match arms diverge,
110 // but rather that there's a prior obligation that doesn't hold.
112 (arm_span
, ObligationCauseCode
::BlockTailExpression(arm
.body
.hir_id
, match_src
))
114 Some((prior_arm_block_id
, prior_arm_ty
, prior_arm_span
)) => (
116 ObligationCauseCode
::MatchExpressionArm(Box
::new(MatchExpressionArmCause
{
123 scrut_span
: scrut
.span
,
125 prior_arms
: other_arms
.clone(),
126 opt_suggest_box_span
,
130 let cause
= self.cause(span
, code
);
132 // This is the moral equivalent of `coercion.coerce(self, cause, arm.body, arm_ty)`.
133 // We use it this way to be able to expand on the potential error and detect when a
134 // `match` tail statement could be a tail expression instead. If so, we suggest
135 // removing the stray semicolon.
136 coercion
.coerce_inner(
141 |err
| self.suggest_removing_semicolon_for_coerce(err
, expr
, arm_ty
, prior_arm
),
145 other_arms
.push(arm_span
);
146 if other_arms
.len() > 5 {
147 other_arms
.remove(0);
150 if !arm_ty
.is_never() {
151 // When a match arm has type `!`, then it doesn't influence the expected type for
152 // the following arm. If all of the prior arms are `!`, then the influence comes
153 // from elsewhere and we shouldn't point to any previous arm.
154 prior_arm
= Some((arm_block_id
, arm_ty
, arm_span
));
158 // If all of the arms in the `match` diverge,
159 // and we're dealing with an actual `match` block
160 // (as opposed to a `match` desugared from something else'),
161 // we can emit a better note. Rather than pointing
162 // at a diverging expression in an arbitrary arm,
163 // we can point at the entire `match` expression
164 if let (Diverges
::Always { .. }
, hir
::MatchSource
::Normal
) = (all_arms_diverge
, match_src
) {
165 all_arms_diverge
= Diverges
::Always
{
168 "any code following this `match` expression is unreachable, as all arms diverge",
173 // We won't diverge unless the scrutinee or all arms diverge.
174 self.diverges
.set(scrut_diverges
| all_arms_diverge
);
176 coercion
.complete(self)
179 fn suggest_removing_semicolon_for_coerce(
181 diag
: &mut Diagnostic
,
182 expr
: &hir
::Expr
<'tcx
>,
184 prior_arm
: Option
<(Option
<hir
::HirId
>, Ty
<'tcx
>, Span
)>,
186 let hir
= self.tcx
.hir();
188 // First, check that we're actually in the tail of a function.
189 let Some(body_id
) = hir
.maybe_body_owned_by(self.body_id
) else {
192 let body
= hir
.body(body_id
);
193 let hir
::ExprKind
::Block(block
, _
) = body
.value
.kind
else {
196 let Some(hir
::Stmt { kind: hir::StmtKind::Semi(last_expr), span: semi_span, .. }
) =
197 block
.innermost_block().stmts
.last()
201 if last_expr
.hir_id
!= expr
.hir_id
{
205 // Next, make sure that we have no type expectation.
207 .find_by_def_id(self.body_id
)
208 .and_then(|owner
| owner
.fn_decl())
209 .map(|decl
| decl
.output
.span())
214 let can_coerce_to_return_ty
= match self.ret_coercion
.as_ref() {
215 Some(ret_coercion
) => {
216 let ret_ty
= ret_coercion
.borrow().expected_ty();
217 let ret_ty
= self.inh
.infcx
.shallow_resolve(ret_ty
);
218 self.can_coerce(arm_ty
, ret_ty
)
219 && prior_arm
.map_or(true, |(_
, ty
, _
)| self.can_coerce(ty
, ret_ty
))
220 // The match arms need to unify for the case of `impl Trait`.
221 && !matches
!(ret_ty
.kind(), ty
::Alias(ty
::Opaque
, ..))
225 if !can_coerce_to_return_ty
{
229 let semi
= expr
.span
.shrink_to_hi().with_hi(semi_span
.hi());
230 let sugg
= crate::errors
::RemoveSemiForCoerce { expr: expr.span, ret, semi }
;
231 diag
.subdiagnostic(sugg
);
234 /// When the previously checked expression (the scrutinee) diverges,
235 /// warn the user about the match arms being unreachable.
236 fn warn_arms_when_scrutinee_diverges(&self, arms
: &'tcx
[hir
::Arm
<'tcx
>]) {
238 self.warn_if_unreachable(arm
.body
.hir_id
, arm
.body
.span
, "arm");
242 /// Handle the fallback arm of a desugared if(-let) like a missing else.
244 /// Returns `true` if there was an error forcing the coercion to the `()` type.
245 pub(super) fn if_fallback_coercion
<T
>(
248 then_expr
: &'tcx hir
::Expr
<'tcx
>,
249 coercion
: &mut CoerceMany
<'tcx
, '_
, T
>,
254 // If this `if` expr is the parent's function return expr,
255 // the cause of the type coercion is the return type, point at it. (#25228)
256 let hir_id
= self.tcx
.hir().parent_id(self.tcx
.hir().parent_id(then_expr
.hir_id
));
257 let ret_reason
= self.maybe_get_coercion_reason(hir_id
, span
);
258 let cause
= self.cause(span
, ObligationCauseCode
::IfExpressionWithNoElse
);
259 let mut error
= false;
260 coercion
.coerce_forced_unit(
264 if let Some((span
, msg
)) = &ret_reason
{
265 err
.span_label(*span
, msg
.clone());
266 } else if let ExprKind
::Block(block
, _
) = &then_expr
.kind
267 && let Some(expr
) = &block
.expr
269 err
.span_label(expr
.span
, "found here");
271 err
.note("`if` expressions without `else` evaluate to `()`");
272 err
.help("consider adding an `else` block that evaluates to the expected type");
280 pub fn maybe_get_coercion_reason(
284 ) -> Option
<(Span
, String
)> {
285 let node
= self.tcx
.hir().get(hir_id
);
286 if let hir
::Node
::Block(block
) = node
{
287 // check that the body's parent is an fn
288 let parent
= self.tcx
.hir().get_parent(self.tcx
.hir().parent_id(block
.hir_id
));
289 if let (Some(expr
), hir
::Node
::Item(hir
::Item { kind: hir::ItemKind::Fn(..), .. }
)) =
290 (&block
.expr
, parent
)
292 // check that the `if` expr without `else` is the fn body's expr
294 return self.get_fn_decl(hir_id
).and_then(|(_
, fn_decl
, _
)| {
295 let (ty
, span
) = match fn_decl
.output
{
296 hir
::FnRetTy
::DefaultReturn(span
) => ("()".to_string(), span
),
297 hir
::FnRetTy
::Return(ty
) => (ty_to_string(ty
), ty
.span
),
299 Some((span
, format
!("expected `{ty}` because of this return type")))
304 if let hir
::Node
::Local(hir
::Local { ty: Some(_), pat, .. }
) = node
{
305 return Some((pat
.span
, "expected because of this assignment".to_string()));
310 pub(crate) fn if_cause(
314 then_expr
: &'tcx hir
::Expr
<'tcx
>,
315 else_expr
: &'tcx hir
::Expr
<'tcx
>,
318 opt_suggest_box_span
: Option
<Span
>,
319 ) -> ObligationCause
<'tcx
> {
320 let mut outer_span
= if self.tcx
.sess
.source_map().is_multiline(span
) {
321 // The `if`/`else` isn't in one line in the output, include some context to make it
322 // clear it is an if/else expression:
324 // LL | let x = if true {
327 // || ----- expected because of this
330 // || ^^^^^ expected `i32`, found `u32`
332 // ||_____- `if` and `else` have incompatible types
336 // The entire expression is in one line, only point at the arms
338 // LL | let x = if true { 10i32 } else { 10u32 };
339 // | ----- ^^^^^ expected `i32`, found `u32`
341 // | expected because of this
346 let (error_sp
, else_id
) = if let ExprKind
::Block(block
, _
) = &else_expr
.kind
{
347 let block
= block
.innermost_block();
349 // Avoid overlapping spans that aren't as readable:
351 // 2 | let x = if true {
354 // | | - expected because of this
361 // | |______if and else have incompatible types
362 // | expected integer, found `()`
364 // by not pointing at the entire expression:
366 // 2 | let x = if true {
367 // | ------- `if` and `else` have incompatible types
369 // | - expected because of this
374 // | |_____^ expected integer, found `()`
376 if block
.expr
.is_none() && block
.stmts
.is_empty()
377 && let Some(outer_span
) = &mut outer_span
378 && let Some(cond_span
) = cond_span
.find_ancestor_inside(*outer_span
)
380 *outer_span
= outer_span
.with_hi(cond_span
.hi())
383 (self.find_block_span(block
), block
.hir_id
)
385 (else_expr
.span
, else_expr
.hir_id
)
388 let then_id
= if let ExprKind
::Block(block
, _
) = &then_expr
.kind
{
389 let block
= block
.innermost_block();
390 // Exclude overlapping spans
391 if block
.expr
.is_none() && block
.stmts
.is_empty() {
399 // Finally construct the cause:
402 ObligationCauseCode
::IfExpression(Box
::new(IfExpressionCause
{
408 opt_suggest_box_span
,
413 pub(super) fn demand_scrutinee_type(
415 scrut
: &'tcx hir
::Expr
<'tcx
>,
416 contains_ref_bindings
: Option
<hir
::Mutability
>,
419 // Not entirely obvious: if matches may create ref bindings, we want to
420 // use the *precise* type of the scrutinee, *not* some supertype, as
421 // the "scrutinee type" (issue #23116).
423 // arielb1 [writes here in this comment thread][c] that there
424 // is certainly *some* potential danger, e.g., for an example
427 // [c]: https://github.com/rust-lang/rust/pull/43399#discussion_r130223956
430 // let Foo(x) = f()[0];
433 // Then if the pattern matches by reference, we want to match
434 // `f()[0]` as a lexpr, so we can't allow it to be
435 // coerced. But if the pattern matches by value, `f()[0]` is
436 // still syntactically a lexpr, but we *do* want to allow
439 // However, *likely* we are ok with allowing coercions to
440 // happen if there are no explicit ref mut patterns - all
441 // implicit ref mut patterns must occur behind a reference, so
442 // they will have the "correct" variance and lifetime.
444 // This does mean that the following pattern would be legal:
449 // impl Deref for Foo {
450 // type Target = Bar;
451 // fn deref(&self) -> &Bar { &self.0 }
453 // impl DerefMut for Foo {
454 // fn deref_mut(&mut self) -> &mut Bar { &mut self.0 }
456 // fn foo(x: &mut Foo) {
458 // let Bar(z): &mut Bar = x;
461 // assert_eq!(foo.0.0, 42);
465 // FIXME(tschottdorf): don't call contains_explicit_ref_binding, which
466 // is problematic as the HIR is being scraped, but ref bindings may be
467 // implicit after #42640. We need to make sure that pat_adjustments
468 // (once introduced) is populated by the time we get here.
471 if let Some(m
) = contains_ref_bindings
{
472 self.check_expr_with_needs(scrut
, Needs
::maybe_mut_place(m
))
474 self.check_expr(scrut
)
476 // ...but otherwise we want to use any supertype of the
477 // scrutinee. This is sort of a workaround, see note (*) in
478 // `check_pat` for some details.
479 let scrut_ty
= self.next_ty_var(TypeVariableOrigin
{
480 kind
: TypeVariableOriginKind
::TypeInference
,
483 self.check_expr_has_type_or_error(scrut
, scrut_ty
, |_
| {}
);
488 /// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
489 /// we check if the different arms would work with boxed trait objects instead and
490 /// provide a structured suggestion in that case.
491 pub(crate) fn opt_suggest_box_span(
495 orig_expected
: Expectation
<'tcx
>,
497 // FIXME(compiler-errors): This really shouldn't need to be done during the
498 // "good" path of typeck, but here we are.
499 match orig_expected
{
500 Expectation
::ExpectHasType(expected
) => {
501 let TypeVariableOrigin
{
503 kind
: TypeVariableOriginKind
::OpaqueTypeInference(rpit_def_id
),
505 } = self.type_var_origin(expected
)?
510 let Some(rpit_local_def_id
) = rpit_def_id
.as_local() else {
514 self.tcx
.hir().expect_item(rpit_local_def_id
).expect_opaque_ty().origin
,
515 hir
::OpaqueTyOrigin
::FnReturn(..)
520 let sig
= self.body_fn_sig()?
;
522 let args
= sig
.output().walk().find_map(|arg
| {
523 if let ty
::GenericArgKind
::Type(ty
) = arg
.unpack()
524 && let ty
::Alias(ty
::Opaque
, ty
::AliasTy { def_id, args, .. }
) = *ty
.kind()
525 && def_id
== rpit_def_id
533 if !self.can_coerce(first_ty
, expected
) || !self.can_coerce(second_ty
, expected
) {
537 for ty
in [first_ty
, second_ty
] {
538 for (clause
, _
) in self
540 .explicit_item_bounds(rpit_def_id
)
541 .iter_instantiated_copied(self.tcx
, args
)
543 let pred
= clause
.kind().rebind(match clause
.kind().skip_binder() {
544 ty
::ClauseKind
::Trait(trait_pred
) => {
546 *trait_pred
.trait_ref
.self_ty().kind(),
547 ty
::Alias(ty
::Opaque
, ty
::AliasTy { def_id, args: alias_args, .. }
)
548 if def_id
== rpit_def_id
&& args
== alias_args
550 ty
::ClauseKind
::Trait(trait_pred
.with_self_ty(self.tcx
, ty
))
552 ty
::ClauseKind
::Projection(mut proj_pred
) => {
554 *proj_pred
.projection_ty
.self_ty().kind(),
555 ty
::Alias(ty
::Opaque
, ty
::AliasTy { def_id, args: alias_args, .. }
)
556 if def_id
== rpit_def_id
&& args
== alias_args
558 proj_pred
= proj_pred
.with_self_ty(self.tcx
, ty
);
559 ty
::ClauseKind
::Projection(proj_pred
)
563 if !self.predicate_must_hold_modulo_regions(&Obligation
::new(
565 ObligationCause
::misc(span
, self.body_id
),
581 fn arms_contain_ref_bindings
<'tcx
>(arms
: &'tcx
[hir
::Arm
<'tcx
>]) -> Option
<hir
::Mutability
> {
582 arms
.iter().filter_map(|a
| a
.pat
.contains_explicit_ref_binding()).max()