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