]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/_match.rs
Auto merge of #67760 - Mark-Simulacrum:rustc-dirty, r=alexcrichton
[rust.git] / src / librustc_typeck / check / _match.rs
1 use crate::check::coercion::CoerceMany;
2 use crate::check::{Diverges, Expectation, FnCtxt, Needs};
3 use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
4 use rustc::traits::ObligationCauseCode;
5 use rustc::traits::{IfExpressionCause, MatchExpressionArmCause, ObligationCause};
6 use rustc::ty::Ty;
7 use rustc_hir as hir;
8 use rustc_hir::ExprKind;
9 use rustc_span::Span;
10
11 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12     pub fn check_match(
13         &self,
14         expr: &'tcx hir::Expr<'tcx>,
15         scrut: &'tcx hir::Expr<'tcx>,
16         arms: &'tcx [hir::Arm<'tcx>],
17         expected: Expectation<'tcx>,
18         match_src: hir::MatchSource,
19     ) -> Ty<'tcx> {
20         let tcx = self.tcx;
21
22         use hir::MatchSource::*;
23         let (source_if, if_no_else, force_scrutinee_bool) = match match_src {
24             IfDesugar { contains_else_clause } => (true, !contains_else_clause, true),
25             IfLetDesugar { contains_else_clause } => (true, !contains_else_clause, false),
26             WhileDesugar => (false, false, true),
27             _ => (false, false, false),
28         };
29
30         // Type check the descriminant and get its type.
31         let scrut_ty = if force_scrutinee_bool {
32             // Here we want to ensure:
33             //
34             // 1. That default match bindings are *not* accepted in the condition of an
35             //    `if` expression. E.g. given `fn foo() -> &bool;` we reject `if foo() { .. }`.
36             //
37             // 2. By expecting `bool` for `expr` we get nice diagnostics for e.g. `if x = y { .. }`.
38             //
39             // FIXME(60707): Consider removing hack with principled solution.
40             self.check_expr_has_type_or_error(scrut, self.tcx.types.bool, |_| {})
41         } else {
42             self.demand_scrutinee_type(arms, scrut)
43         };
44
45         // If there are no arms, that is a diverging match; a special case.
46         if arms.is_empty() {
47             self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
48             return tcx.types.never;
49         }
50
51         self.warn_arms_when_scrutinee_diverges(arms, match_src);
52
53         // Otherwise, we have to union together the types that the
54         // arms produce and so forth.
55         let scrut_diverges = self.diverges.get();
56         self.diverges.set(Diverges::Maybe);
57
58         // rust-lang/rust#55810: Typecheck patterns first (via eager
59         // collection into `Vec`), so we get types for all bindings.
60         let all_arm_pats_diverge: Vec<_> = arms
61             .iter()
62             .map(|arm| {
63                 let mut all_pats_diverge = Diverges::WarnedAlways;
64                 self.diverges.set(Diverges::Maybe);
65                 self.check_pat_top(&arm.pat, scrut_ty, Some(scrut.span), true);
66                 all_pats_diverge &= self.diverges.get();
67
68                 // As discussed with @eddyb, this is for disabling unreachable_code
69                 // warnings on patterns (they're now subsumed by unreachable_patterns
70                 // warnings).
71                 match all_pats_diverge {
72                     Diverges::Maybe => Diverges::Maybe,
73                     Diverges::Always { .. } | Diverges::WarnedAlways => Diverges::WarnedAlways,
74                 }
75             })
76             .collect();
77
78         // Now typecheck the blocks.
79         //
80         // The result of the match is the common supertype of all the
81         // arms. Start out the value as bottom, since it's the, well,
82         // bottom the type lattice, and we'll be moving up the lattice as
83         // we process each arm. (Note that any match with 0 arms is matching
84         // on any empty type and is therefore unreachable; should the flow
85         // of execution reach it, we will panic, so bottom is an appropriate
86         // type in that case)
87         let mut all_arms_diverge = Diverges::WarnedAlways;
88
89         let expected = expected.adjust_for_branches(self);
90
91         let mut coercion = {
92             let coerce_first = match expected {
93                 // We don't coerce to `()` so that if the match expression is a
94                 // statement it's branches can have any consistent type. That allows
95                 // us to give better error messages (pointing to a usually better
96                 // arm for inconsistent arms or to the whole match when a `()` type
97                 // is required).
98                 Expectation::ExpectHasType(ety) if ety != self.tcx.mk_unit() => ety,
99                 _ => self.next_ty_var(TypeVariableOrigin {
100                     kind: TypeVariableOriginKind::MiscVariable,
101                     span: expr.span,
102                 }),
103             };
104             CoerceMany::with_coercion_sites(coerce_first, arms)
105         };
106
107         let mut other_arms = vec![]; // used only for diagnostics
108         let mut prior_arm_ty = None;
109         for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {
110             if let Some(g) = &arm.guard {
111                 self.diverges.set(pats_diverge);
112                 match g {
113                     hir::Guard::If(e) => {
114                         self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {})
115                     }
116                 };
117             }
118
119             self.diverges.set(pats_diverge);
120             let arm_ty = if source_if
121                 && if_no_else
122                 && i != 0
123                 && self.if_fallback_coercion(expr.span, &arms[0].body, &mut coercion)
124             {
125                 tcx.types.err
126             } else {
127                 // Only call this if this is not an `if` expr with an expected type and no `else`
128                 // clause to avoid duplicated type errors. (#60254)
129                 self.check_expr_with_expectation(&arm.body, expected)
130             };
131             all_arms_diverge &= self.diverges.get();
132             if source_if {
133                 let then_expr = &arms[0].body;
134                 match (i, if_no_else) {
135                     (0, _) => coercion.coerce(self, &self.misc(expr.span), &arm.body, arm_ty),
136                     (_, true) => {} // Handled above to avoid duplicated type errors (#60254).
137                     (_, _) => {
138                         let then_ty = prior_arm_ty.unwrap();
139                         let cause = self.if_cause(expr.span, then_expr, &arm.body, then_ty, arm_ty);
140                         coercion.coerce(self, &cause, &arm.body, arm_ty);
141                     }
142                 }
143             } else {
144                 let arm_span = if let hir::ExprKind::Block(blk, _) = &arm.body.kind {
145                     // Point at the block expr instead of the entire block
146                     blk.expr.as_ref().map(|e| e.span).unwrap_or(arm.body.span)
147                 } else {
148                     arm.body.span
149                 };
150                 let (span, code) = match i {
151                     // The reason for the first arm to fail is not that the match arms diverge,
152                     // but rather that there's a prior obligation that doesn't hold.
153                     0 => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)),
154                     _ => (
155                         expr.span,
156                         ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
157                             arm_span,
158                             source: match_src,
159                             prior_arms: other_arms.clone(),
160                             last_ty: prior_arm_ty.unwrap(),
161                             scrut_hir_id: scrut.hir_id,
162                         }),
163                     ),
164                 };
165                 let cause = self.cause(span, code);
166                 coercion.coerce(self, &cause, &arm.body, arm_ty);
167                 other_arms.push(arm_span);
168                 if other_arms.len() > 5 {
169                     other_arms.remove(0);
170                 }
171             }
172             prior_arm_ty = Some(arm_ty);
173         }
174
175         // If all of the arms in the `match` diverge,
176         // and we're dealing with an actual `match` block
177         // (as opposed to a `match` desugared from something else'),
178         // we can emit a better note. Rather than pointing
179         // at a diverging expression in an arbitrary arm,
180         // we can point at the entire `match` expression
181         if let (Diverges::Always { .. }, hir::MatchSource::Normal) = (all_arms_diverge, match_src) {
182             all_arms_diverge = Diverges::Always {
183                 span: expr.span,
184                 custom_note: Some(
185                     "any code following this `match` expression is unreachable, as all arms diverge",
186                 ),
187             };
188         }
189
190         // We won't diverge unless the scrutinee or all arms diverge.
191         self.diverges.set(scrut_diverges | all_arms_diverge);
192
193         coercion.complete(self)
194     }
195
196     /// When the previously checked expression (the scrutinee) diverges,
197     /// warn the user about the match arms being unreachable.
198     fn warn_arms_when_scrutinee_diverges(
199         &self,
200         arms: &'tcx [hir::Arm<'tcx>],
201         source: hir::MatchSource,
202     ) {
203         if self.diverges.get().is_always() {
204             use hir::MatchSource::*;
205             let msg = match source {
206                 IfDesugar { .. } | IfLetDesugar { .. } => "block in `if` expression",
207                 WhileDesugar { .. } | WhileLetDesugar { .. } => "block in `while` expression",
208                 _ => "arm",
209             };
210             for arm in arms {
211                 self.warn_if_unreachable(arm.body.hir_id, arm.body.span, msg);
212             }
213         }
214     }
215
216     /// Handle the fallback arm of a desugared if(-let) like a missing else.
217     ///
218     /// Returns `true` if there was an error forcing the coercion to the `()` type.
219     fn if_fallback_coercion(
220         &self,
221         span: Span,
222         then_expr: &'tcx hir::Expr<'tcx>,
223         coercion: &mut CoerceMany<'tcx, '_, rustc_hir::Arm<'tcx>>,
224     ) -> bool {
225         // If this `if` expr is the parent's function return expr,
226         // the cause of the type coercion is the return type, point at it. (#25228)
227         let ret_reason = self.maybe_get_coercion_reason(then_expr.hir_id, span);
228         let cause = self.cause(span, ObligationCauseCode::IfExpressionWithNoElse);
229         let mut error = false;
230         coercion.coerce_forced_unit(
231             self,
232             &cause,
233             &mut |err| {
234                 if let Some((span, msg)) = &ret_reason {
235                     err.span_label(*span, msg.as_str());
236                 } else if let ExprKind::Block(block, _) = &then_expr.kind {
237                     if let Some(expr) = &block.expr {
238                         err.span_label(expr.span, "found here".to_string());
239                     }
240                 }
241                 err.note("`if` expressions without `else` evaluate to `()`");
242                 err.help("consider adding an `else` block that evaluates to the expected type");
243                 error = true;
244             },
245             ret_reason.is_none(),
246         );
247         error
248     }
249
250     fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, span: Span) -> Option<(Span, String)> {
251         use hir::Node::{Block, Item, Local};
252
253         let hir = self.tcx.hir();
254         let arm_id = hir.get_parent_node(hir_id);
255         let match_id = hir.get_parent_node(arm_id);
256         let containing_id = hir.get_parent_node(match_id);
257
258         let node = hir.get(containing_id);
259         if let Block(block) = node {
260             // check that the body's parent is an fn
261             let parent = hir.get(hir.get_parent_node(hir.get_parent_node(block.hir_id)));
262             if let (Some(expr), Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })) =
263                 (&block.expr, parent)
264             {
265                 // check that the `if` expr without `else` is the fn body's expr
266                 if expr.span == span {
267                     return self.get_fn_decl(hir_id).map(|(fn_decl, _)| {
268                         (
269                             fn_decl.output.span(),
270                             format!("expected `{}` because of this return type", fn_decl.output),
271                         )
272                     });
273                 }
274             }
275         }
276         if let Local(hir::Local { ty: Some(_), pat, .. }) = node {
277             return Some((pat.span, "expected because of this assignment".to_string()));
278         }
279         None
280     }
281
282     fn if_cause(
283         &self,
284         span: Span,
285         then_expr: &'tcx hir::Expr<'tcx>,
286         else_expr: &'tcx hir::Expr<'tcx>,
287         then_ty: Ty<'tcx>,
288         else_ty: Ty<'tcx>,
289     ) -> ObligationCause<'tcx> {
290         let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) {
291             // The `if`/`else` isn't in one line in the output, include some context to make it
292             // clear it is an if/else expression:
293             // ```
294             // LL |      let x = if true {
295             //    | _____________-
296             // LL ||         10i32
297             //    ||         ----- expected because of this
298             // LL ||     } else {
299             // LL ||         10u32
300             //    ||         ^^^^^ expected `i32`, found `u32`
301             // LL ||     };
302             //    ||_____- `if` and `else` have incompatible types
303             // ```
304             Some(span)
305         } else {
306             // The entire expression is in one line, only point at the arms
307             // ```
308             // LL |     let x = if true { 10i32 } else { 10u32 };
309             //    |                       -----          ^^^^^ expected `i32`, found `u32`
310             //    |                       |
311             //    |                       expected because of this
312             // ```
313             None
314         };
315
316         let mut remove_semicolon = None;
317         let error_sp = if let ExprKind::Block(block, _) = &else_expr.kind {
318             if let Some(expr) = &block.expr {
319                 expr.span
320             } else if let Some(stmt) = block.stmts.last() {
321                 // possibly incorrect trailing `;` in the else arm
322                 remove_semicolon = self.could_remove_semicolon(block, then_ty);
323                 stmt.span
324             } else {
325                 // empty block; point at its entirety
326                 // Avoid overlapping spans that aren't as readable:
327                 // ```
328                 // 2 |        let x = if true {
329                 //   |   _____________-
330                 // 3 |  |         3
331                 //   |  |         - expected because of this
332                 // 4 |  |     } else {
333                 //   |  |____________^
334                 // 5 | ||
335                 // 6 | ||     };
336                 //   | ||     ^
337                 //   | ||_____|
338                 //   | |______if and else have incompatible types
339                 //   |        expected integer, found `()`
340                 // ```
341                 // by not pointing at the entire expression:
342                 // ```
343                 // 2 |       let x = if true {
344                 //   |               ------- `if` and `else` have incompatible types
345                 // 3 |           3
346                 //   |           - expected because of this
347                 // 4 |       } else {
348                 //   |  ____________^
349                 // 5 | |
350                 // 6 | |     };
351                 //   | |_____^ expected integer, found `()`
352                 // ```
353                 if outer_sp.is_some() {
354                     outer_sp = Some(self.tcx.sess.source_map().def_span(span));
355                 }
356                 else_expr.span
357             }
358         } else {
359             // shouldn't happen unless the parser has done something weird
360             else_expr.span
361         };
362
363         // Compute `Span` of `then` part of `if`-expression.
364         let then_sp = if let ExprKind::Block(block, _) = &then_expr.kind {
365             if let Some(expr) = &block.expr {
366                 expr.span
367             } else if let Some(stmt) = block.stmts.last() {
368                 // possibly incorrect trailing `;` in the else arm
369                 remove_semicolon = remove_semicolon.or(self.could_remove_semicolon(block, else_ty));
370                 stmt.span
371             } else {
372                 // empty block; point at its entirety
373                 outer_sp = None; // same as in `error_sp`; cleanup output
374                 then_expr.span
375             }
376         } else {
377             // shouldn't happen unless the parser has done something weird
378             then_expr.span
379         };
380
381         // Finally construct the cause:
382         self.cause(
383             error_sp,
384             ObligationCauseCode::IfExpression(box IfExpressionCause {
385                 then: then_sp,
386                 outer: outer_sp,
387                 semicolon: remove_semicolon,
388             }),
389         )
390     }
391
392     fn demand_scrutinee_type(
393         &self,
394         arms: &'tcx [hir::Arm<'tcx>],
395         scrut: &'tcx hir::Expr<'tcx>,
396     ) -> Ty<'tcx> {
397         // Not entirely obvious: if matches may create ref bindings, we want to
398         // use the *precise* type of the scrutinee, *not* some supertype, as
399         // the "scrutinee type" (issue #23116).
400         //
401         // arielb1 [writes here in this comment thread][c] that there
402         // is certainly *some* potential danger, e.g., for an example
403         // like:
404         //
405         // [c]: https://github.com/rust-lang/rust/pull/43399#discussion_r130223956
406         //
407         // ```
408         // let Foo(x) = f()[0];
409         // ```
410         //
411         // Then if the pattern matches by reference, we want to match
412         // `f()[0]` as a lexpr, so we can't allow it to be
413         // coerced. But if the pattern matches by value, `f()[0]` is
414         // still syntactically a lexpr, but we *do* want to allow
415         // coercions.
416         //
417         // However, *likely* we are ok with allowing coercions to
418         // happen if there are no explicit ref mut patterns - all
419         // implicit ref mut patterns must occur behind a reference, so
420         // they will have the "correct" variance and lifetime.
421         //
422         // This does mean that the following pattern would be legal:
423         //
424         // ```
425         // struct Foo(Bar);
426         // struct Bar(u32);
427         // impl Deref for Foo {
428         //     type Target = Bar;
429         //     fn deref(&self) -> &Bar { &self.0 }
430         // }
431         // impl DerefMut for Foo {
432         //     fn deref_mut(&mut self) -> &mut Bar { &mut self.0 }
433         // }
434         // fn foo(x: &mut Foo) {
435         //     {
436         //         let Bar(z): &mut Bar = x;
437         //         *z = 42;
438         //     }
439         //     assert_eq!(foo.0.0, 42);
440         // }
441         // ```
442         //
443         // FIXME(tschottdorf): don't call contains_explicit_ref_binding, which
444         // is problematic as the HIR is being scraped, but ref bindings may be
445         // implicit after #42640. We need to make sure that pat_adjustments
446         // (once introduced) is populated by the time we get here.
447         //
448         // See #44848.
449         let contains_ref_bindings = arms
450             .iter()
451             .filter_map(|a| a.pat.contains_explicit_ref_binding())
452             .max_by_key(|m| match *m {
453                 hir::Mutability::Mut => 1,
454                 hir::Mutability::Not => 0,
455             });
456
457         if let Some(m) = contains_ref_bindings {
458             self.check_expr_with_needs(scrut, Needs::maybe_mut_place(m))
459         } else {
460             // ...but otherwise we want to use any supertype of the
461             // scrutinee. This is sort of a workaround, see note (*) in
462             // `check_pat` for some details.
463             let scrut_ty = self.next_ty_var(TypeVariableOrigin {
464                 kind: TypeVariableOriginKind::TypeInference,
465                 span: scrut.span,
466             });
467             self.check_expr_has_type_or_error(scrut, scrut_ty, |_| {});
468             scrut_ty
469         }
470     }
471 }