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