]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_hir_typeck/src/_match.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / compiler / rustc_hir_typeck / src / _match.rs
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};
9 use rustc_span::Span;
10 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
11 use rustc_trait_selection::traits::{
12 IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
13 };
14
15 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16 #[instrument(skip(self), level = "debug", ret)]
17 pub fn check_match(
18 &self,
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,
24 ) -> Ty<'tcx> {
25 let tcx = self.tcx;
26
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);
30
31 // If there are no arms, that is a diverging match; a special case.
32 if arms.is_empty() {
33 self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
34 return tcx.types.never;
35 }
36
37 self.warn_arms_when_scrutinee_diverges(arms);
38
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);
41
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);
44 for arm in arms {
45 self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut), None);
46 }
47
48 // Now typecheck the blocks.
49 //
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
56 // type in that case)
57 let mut all_arms_diverge = Diverges::WarnedAlways;
58
59 let expected = orig_expected.adjust_for_branches(self);
60 debug!(?expected);
61
62 let mut coercion = {
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
68 // is required).
69 Expectation::ExpectHasType(ety) if ety != Ty::new_unit(self.tcx) => ety,
70 _ => self.next_ty_var(TypeVariableOrigin {
71 kind: TypeVariableOriginKind::MiscVariable,
72 span: expr.span,
73 }),
74 };
75 CoerceMany::with_coercion_sites(coerce_first, arms)
76 };
77
78 let mut other_arms = vec![]; // Used only for diagnostics.
79 let mut prior_arm = None;
80 for arm in arms {
81 if let Some(g) = &arm.guard {
82 self.diverges.set(Diverges::Maybe);
83 match g {
84 hir::Guard::If(e) => {
85 self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {});
86 }
87 hir::Guard::IfLet(l) => {
88 self.check_expr_let(l);
89 }
90 };
91 }
92
93 self.diverges.set(Diverges::Maybe);
94
95 let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
96 all_arms_diverge &= self.diverges.get();
97
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)
100 });
101
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))
104 } else {
105 (None, arm.body.span)
106 };
107
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.
111 None => {
112 (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id, match_src))
113 }
114 Some((prior_arm_block_id, prior_arm_ty, prior_arm_span)) => (
115 expr.span,
116 ObligationCauseCode::MatchExpressionArm(Box::new(MatchExpressionArmCause {
117 arm_block_id,
118 arm_span,
119 arm_ty,
120 prior_arm_block_id,
121 prior_arm_ty,
122 prior_arm_span,
123 scrut_span: scrut.span,
124 source: match_src,
125 prior_arms: other_arms.clone(),
126 opt_suggest_box_span,
127 })),
128 ),
129 };
130 let cause = self.cause(span, code);
131
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(
137 self,
138 &cause,
139 Some(&arm.body),
140 arm_ty,
141 |err| self.suggest_removing_semicolon_for_coerce(err, expr, arm_ty, prior_arm),
142 false,
143 );
144
145 other_arms.push(arm_span);
146 if other_arms.len() > 5 {
147 other_arms.remove(0);
148 }
149
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));
155 }
156 }
157
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 {
166 span: expr.span,
167 custom_note: Some(
168 "any code following this `match` expression is unreachable, as all arms diverge",
169 ),
170 };
171 }
172
173 // We won't diverge unless the scrutinee or all arms diverge.
174 self.diverges.set(scrut_diverges | all_arms_diverge);
175
176 coercion.complete(self)
177 }
178
179 fn suggest_removing_semicolon_for_coerce(
180 &self,
181 diag: &mut Diagnostic,
182 expr: &hir::Expr<'tcx>,
183 arm_ty: Ty<'tcx>,
184 prior_arm: Option<(Option<hir::HirId>, Ty<'tcx>, Span)>,
185 ) {
186 let hir = self.tcx.hir();
187
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 {
190 return;
191 };
192 let body = hir.body(body_id);
193 let hir::ExprKind::Block(block, _) = body.value.kind else {
194 return;
195 };
196 let Some(hir::Stmt { kind: hir::StmtKind::Semi(last_expr), span: semi_span, .. }) =
197 block.innermost_block().stmts.last()
198 else {
199 return;
200 };
201 if last_expr.hir_id != expr.hir_id {
202 return;
203 }
204
205 // Next, make sure that we have no type expectation.
206 let Some(ret) = hir
207 .find_by_def_id(self.body_id)
208 .and_then(|owner| owner.fn_decl())
209 .map(|decl| decl.output.span())
210 else {
211 return;
212 };
213
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, ..))
222 }
223 _ => false,
224 };
225 if !can_coerce_to_return_ty {
226 return;
227 }
228
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);
232 }
233
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>]) {
237 for arm in arms {
238 self.warn_if_unreachable(arm.body.hir_id, arm.body.span, "arm");
239 }
240 }
241
242 /// Handle the fallback arm of a desugared if(-let) like a missing else.
243 ///
244 /// Returns `true` if there was an error forcing the coercion to the `()` type.
245 pub(super) fn if_fallback_coercion<T>(
246 &self,
247 span: Span,
248 then_expr: &'tcx hir::Expr<'tcx>,
249 coercion: &mut CoerceMany<'tcx, '_, T>,
250 ) -> bool
251 where
252 T: AsCoercionSite,
253 {
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(
261 self,
262 &cause,
263 |err| {
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
268 {
269 err.span_label(expr.span, "found here");
270 }
271 err.note("`if` expressions without `else` evaluate to `()`");
272 err.help("consider adding an `else` block that evaluates to the expected type");
273 error = true;
274 },
275 false,
276 );
277 error
278 }
279
280 pub fn maybe_get_coercion_reason(
281 &self,
282 hir_id: hir::HirId,
283 sp: Span,
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)
291 {
292 // check that the `if` expr without `else` is the fn body's expr
293 if expr.span == sp {
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),
298 };
299 Some((span, format!("expected `{ty}` because of this return type")))
300 });
301 }
302 }
303 }
304 if let hir::Node::Local(hir::Local { ty: Some(_), pat, .. }) = node {
305 return Some((pat.span, "expected because of this assignment".to_string()));
306 }
307 None
308 }
309
310 pub(crate) fn if_cause(
311 &self,
312 span: Span,
313 cond_span: Span,
314 then_expr: &'tcx hir::Expr<'tcx>,
315 else_expr: &'tcx hir::Expr<'tcx>,
316 then_ty: Ty<'tcx>,
317 else_ty: Ty<'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:
323 // ```
324 // LL | let x = if true {
325 // | _____________-
326 // LL || 10i32
327 // || ----- expected because of this
328 // LL || } else {
329 // LL || 10u32
330 // || ^^^^^ expected `i32`, found `u32`
331 // LL || };
332 // ||_____- `if` and `else` have incompatible types
333 // ```
334 Some(span)
335 } else {
336 // The entire expression is in one line, only point at the arms
337 // ```
338 // LL | let x = if true { 10i32 } else { 10u32 };
339 // | ----- ^^^^^ expected `i32`, found `u32`
340 // | |
341 // | expected because of this
342 // ```
343 None
344 };
345
346 let (error_sp, else_id) = if let ExprKind::Block(block, _) = &else_expr.kind {
347 let block = block.innermost_block();
348
349 // Avoid overlapping spans that aren't as readable:
350 // ```
351 // 2 | let x = if true {
352 // | _____________-
353 // 3 | | 3
354 // | | - expected because of this
355 // 4 | | } else {
356 // | |____________^
357 // 5 | ||
358 // 6 | || };
359 // | || ^
360 // | ||_____|
361 // | |______if and else have incompatible types
362 // | expected integer, found `()`
363 // ```
364 // by not pointing at the entire expression:
365 // ```
366 // 2 | let x = if true {
367 // | ------- `if` and `else` have incompatible types
368 // 3 | 3
369 // | - expected because of this
370 // 4 | } else {
371 // | ____________^
372 // 5 | |
373 // 6 | | };
374 // | |_____^ expected integer, found `()`
375 // ```
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)
379 {
380 *outer_span = outer_span.with_hi(cond_span.hi())
381 }
382
383 (self.find_block_span(block), block.hir_id)
384 } else {
385 (else_expr.span, else_expr.hir_id)
386 };
387
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() {
392 outer_span = None;
393 }
394 block.hir_id
395 } else {
396 then_expr.hir_id
397 };
398
399 // Finally construct the cause:
400 self.cause(
401 error_sp,
402 ObligationCauseCode::IfExpression(Box::new(IfExpressionCause {
403 else_id,
404 then_id,
405 then_ty,
406 else_ty,
407 outer_span,
408 opt_suggest_box_span,
409 })),
410 )
411 }
412
413 pub(super) fn demand_scrutinee_type(
414 &self,
415 scrut: &'tcx hir::Expr<'tcx>,
416 contains_ref_bindings: Option<hir::Mutability>,
417 no_arms: bool,
418 ) -> Ty<'tcx> {
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).
422 //
423 // arielb1 [writes here in this comment thread][c] that there
424 // is certainly *some* potential danger, e.g., for an example
425 // like:
426 //
427 // [c]: https://github.com/rust-lang/rust/pull/43399#discussion_r130223956
428 //
429 // ```
430 // let Foo(x) = f()[0];
431 // ```
432 //
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
437 // coercions.
438 //
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.
443 //
444 // This does mean that the following pattern would be legal:
445 //
446 // ```
447 // struct Foo(Bar);
448 // struct Bar(u32);
449 // impl Deref for Foo {
450 // type Target = Bar;
451 // fn deref(&self) -> &Bar { &self.0 }
452 // }
453 // impl DerefMut for Foo {
454 // fn deref_mut(&mut self) -> &mut Bar { &mut self.0 }
455 // }
456 // fn foo(x: &mut Foo) {
457 // {
458 // let Bar(z): &mut Bar = x;
459 // *z = 42;
460 // }
461 // assert_eq!(foo.0.0, 42);
462 // }
463 // ```
464 //
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.
469 //
470 // See #44848.
471 if let Some(m) = contains_ref_bindings {
472 self.check_expr_with_needs(scrut, Needs::maybe_mut_place(m))
473 } else if no_arms {
474 self.check_expr(scrut)
475 } else {
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,
481 span: scrut.span,
482 });
483 self.check_expr_has_type_or_error(scrut, scrut_ty, |_| {});
484 scrut_ty
485 }
486 }
487
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(
492 &self,
493 first_ty: Ty<'tcx>,
494 second_ty: Ty<'tcx>,
495 orig_expected: Expectation<'tcx>,
496 ) -> Option<Span> {
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 {
502 span,
503 kind: TypeVariableOriginKind::OpaqueTypeInference(rpit_def_id),
504 ..
505 } = self.type_var_origin(expected)?
506 else {
507 return None;
508 };
509
510 let Some(rpit_local_def_id) = rpit_def_id.as_local() else {
511 return None;
512 };
513 if !matches!(
514 self.tcx.hir().expect_item(rpit_local_def_id).expect_opaque_ty().origin,
515 hir::OpaqueTyOrigin::FnReturn(..)
516 ) {
517 return None;
518 }
519
520 let sig = self.body_fn_sig()?;
521
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
526 {
527 Some(args)
528 } else {
529 None
530 }
531 })?;
532
533 if !self.can_coerce(first_ty, expected) || !self.can_coerce(second_ty, expected) {
534 return None;
535 }
536
537 for ty in [first_ty, second_ty] {
538 for (clause, _) in self
539 .tcx
540 .explicit_item_bounds(rpit_def_id)
541 .iter_instantiated_copied(self.tcx, args)
542 {
543 let pred = clause.kind().rebind(match clause.kind().skip_binder() {
544 ty::ClauseKind::Trait(trait_pred) => {
545 assert!(matches!(
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
549 ));
550 ty::ClauseKind::Trait(trait_pred.with_self_ty(self.tcx, ty))
551 }
552 ty::ClauseKind::Projection(mut proj_pred) => {
553 assert!(matches!(
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
557 ));
558 proj_pred = proj_pred.with_self_ty(self.tcx, ty);
559 ty::ClauseKind::Projection(proj_pred)
560 }
561 _ => continue,
562 });
563 if !self.predicate_must_hold_modulo_regions(&Obligation::new(
564 self.tcx,
565 ObligationCause::misc(span, self.body_id),
566 self.param_env,
567 pred,
568 )) {
569 return None;
570 }
571 }
572 }
573
574 Some(span)
575 }
576 _ => None,
577 }
578 }
579 }
580
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()
583 }