]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/_match.rs
Rollup merge of #67594 - oxalica:update-libc, r=Dylan-DPC
[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<'tcx>,
14         discrim: &'tcx hir::Expr<'tcx>,
15         arms: &'tcx [hir::Arm<'tcx>],
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(
198         &self,
199         arms: &'tcx [hir::Arm<'tcx>],
200         source: hir::MatchSource,
201     ) {
202         if self.diverges.get().is_always() {
203             use hir::MatchSource::*;
204             let msg = match source {
205                 IfDesugar { .. } | IfLetDesugar { .. } => "block in `if` expression",
206                 WhileDesugar { .. } | WhileLetDesugar { .. } => "block in `while` expression",
207                 _ => "arm",
208             };
209             for arm in arms {
210                 self.warn_if_unreachable(arm.body.hir_id, arm.body.span, msg);
211             }
212         }
213     }
214
215     /// Handle the fallback arm of a desugared if(-let) like a missing else.
216     ///
217     /// Returns `true` if there was an error forcing the coercion to the `()` type.
218     fn if_fallback_coercion(
219         &self,
220         span: Span,
221         then_expr: &'tcx hir::Expr<'tcx>,
222         coercion: &mut CoerceMany<'tcx, '_, rustc::hir::Arm<'tcx>>,
223     ) -> bool {
224         // If this `if` expr is the parent's function return expr,
225         // the cause of the type coercion is the return type, point at it. (#25228)
226         let ret_reason = self.maybe_get_coercion_reason(then_expr.hir_id, span);
227         let cause = self.cause(span, ObligationCauseCode::IfExpressionWithNoElse);
228         let mut error = false;
229         coercion.coerce_forced_unit(
230             self,
231             &cause,
232             &mut |err| {
233                 if let Some((span, msg)) = &ret_reason {
234                     err.span_label(*span, msg.as_str());
235                 } else if let ExprKind::Block(block, _) = &then_expr.kind {
236                     if let Some(expr) = &block.expr {
237                         err.span_label(expr.span, "found here".to_string());
238                     }
239                 }
240                 err.note("`if` expressions without `else` evaluate to `()`");
241                 err.help("consider adding an `else` block that evaluates to the expected type");
242                 error = true;
243             },
244             ret_reason.is_none(),
245         );
246         error
247     }
248
249     fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, span: Span) -> Option<(Span, String)> {
250         use hir::Node::{Block, Item, Local};
251
252         let hir = self.tcx.hir();
253         let arm_id = hir.get_parent_node(hir_id);
254         let match_id = hir.get_parent_node(arm_id);
255         let containing_id = hir.get_parent_node(match_id);
256
257         let node = hir.get(containing_id);
258         if let Block(block) = node {
259             // check that the body's parent is an fn
260             let parent = hir.get(hir.get_parent_node(hir.get_parent_node(block.hir_id)));
261             if let (Some(expr), Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })) =
262                 (&block.expr, parent)
263             {
264                 // check that the `if` expr without `else` is the fn body's expr
265                 if expr.span == span {
266                     return self.get_fn_decl(hir_id).map(|(fn_decl, _)| {
267                         (
268                             fn_decl.output.span(),
269                             format!("expected `{}` because of this return type", fn_decl.output),
270                         )
271                     });
272                 }
273             }
274         }
275         if let Local(hir::Local { ty: Some(_), pat, .. }) = node {
276             return Some((pat.span, "expected because of this assignment".to_string()));
277         }
278         None
279     }
280
281     fn if_cause(
282         &self,
283         span: Span,
284         then_expr: &'tcx hir::Expr<'tcx>,
285         else_expr: &'tcx hir::Expr<'tcx>,
286         then_ty: Ty<'tcx>,
287         else_ty: Ty<'tcx>,
288     ) -> ObligationCause<'tcx> {
289         let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) {
290             // The `if`/`else` isn't in one line in the output, include some context to make it
291             // clear it is an if/else expression:
292             // ```
293             // LL |      let x = if true {
294             //    | _____________-
295             // LL ||         10i32
296             //    ||         ----- expected because of this
297             // LL ||     } else {
298             // LL ||         10u32
299             //    ||         ^^^^^ expected `i32`, found `u32`
300             // LL ||     };
301             //    ||_____- if and else have incompatible types
302             // ```
303             Some(span)
304         } else {
305             // The entire expression is in one line, only point at the arms
306             // ```
307             // LL |     let x = if true { 10i32 } else { 10u32 };
308             //    |                       -----          ^^^^^ expected `i32`, found `u32`
309             //    |                       |
310             //    |                       expected because of this
311             // ```
312             None
313         };
314
315         let mut remove_semicolon = None;
316         let error_sp = if let ExprKind::Block(block, _) = &else_expr.kind {
317             if let Some(expr) = &block.expr {
318                 expr.span
319             } else if let Some(stmt) = block.stmts.last() {
320                 // possibly incorrect trailing `;` in the else arm
321                 remove_semicolon = self.could_remove_semicolon(block, then_ty);
322                 stmt.span
323             } else {
324                 // empty block; point at its entirety
325                 // Avoid overlapping spans that aren't as readable:
326                 // ```
327                 // 2 |        let x = if true {
328                 //   |   _____________-
329                 // 3 |  |         3
330                 //   |  |         - expected because of this
331                 // 4 |  |     } else {
332                 //   |  |____________^
333                 // 5 | ||
334                 // 6 | ||     };
335                 //   | ||     ^
336                 //   | ||_____|
337                 //   | |______if and else have incompatible types
338                 //   |        expected integer, found `()`
339                 // ```
340                 // by not pointing at the entire expression:
341                 // ```
342                 // 2 |       let x = if true {
343                 //   |               ------- if and else have incompatible types
344                 // 3 |           3
345                 //   |           - expected because of this
346                 // 4 |       } else {
347                 //   |  ____________^
348                 // 5 | |
349                 // 6 | |     };
350                 //   | |_____^ expected integer, found `()`
351                 // ```
352                 if outer_sp.is_some() {
353                     outer_sp = Some(self.tcx.sess.source_map().def_span(span));
354                 }
355                 else_expr.span
356             }
357         } else {
358             // shouldn't happen unless the parser has done something weird
359             else_expr.span
360         };
361
362         // Compute `Span` of `then` part of `if`-expression.
363         let then_sp = if let ExprKind::Block(block, _) = &then_expr.kind {
364             if let Some(expr) = &block.expr {
365                 expr.span
366             } else if let Some(stmt) = block.stmts.last() {
367                 // possibly incorrect trailing `;` in the else arm
368                 remove_semicolon = remove_semicolon.or(self.could_remove_semicolon(block, else_ty));
369                 stmt.span
370             } else {
371                 // empty block; point at its entirety
372                 outer_sp = None; // same as in `error_sp`; cleanup output
373                 then_expr.span
374             }
375         } else {
376             // shouldn't happen unless the parser has done something weird
377             then_expr.span
378         };
379
380         // Finally construct the cause:
381         self.cause(
382             error_sp,
383             ObligationCauseCode::IfExpression(box IfExpressionCause {
384                 then: then_sp,
385                 outer: outer_sp,
386                 semicolon: remove_semicolon,
387             }),
388         )
389     }
390
391     fn demand_discriminant_type(
392         &self,
393         arms: &'tcx [hir::Arm<'tcx>],
394         discrim: &'tcx hir::Expr<'tcx>,
395     ) -> Ty<'tcx> {
396         // Not entirely obvious: if matches may create ref bindings, we want to
397         // use the *precise* type of the discriminant, *not* some supertype, as
398         // the "discriminant type" (issue #23116).
399         //
400         // arielb1 [writes here in this comment thread][c] that there
401         // is certainly *some* potential danger, e.g., for an example
402         // like:
403         //
404         // [c]: https://github.com/rust-lang/rust/pull/43399#discussion_r130223956
405         //
406         // ```
407         // let Foo(x) = f()[0];
408         // ```
409         //
410         // Then if the pattern matches by reference, we want to match
411         // `f()[0]` as a lexpr, so we can't allow it to be
412         // coerced. But if the pattern matches by value, `f()[0]` is
413         // still syntactically a lexpr, but we *do* want to allow
414         // coercions.
415         //
416         // However, *likely* we are ok with allowing coercions to
417         // happen if there are no explicit ref mut patterns - all
418         // implicit ref mut patterns must occur behind a reference, so
419         // they will have the "correct" variance and lifetime.
420         //
421         // This does mean that the following pattern would be legal:
422         //
423         // ```
424         // struct Foo(Bar);
425         // struct Bar(u32);
426         // impl Deref for Foo {
427         //     type Target = Bar;
428         //     fn deref(&self) -> &Bar { &self.0 }
429         // }
430         // impl DerefMut for Foo {
431         //     fn deref_mut(&mut self) -> &mut Bar { &mut self.0 }
432         // }
433         // fn foo(x: &mut Foo) {
434         //     {
435         //         let Bar(z): &mut Bar = x;
436         //         *z = 42;
437         //     }
438         //     assert_eq!(foo.0.0, 42);
439         // }
440         // ```
441         //
442         // FIXME(tschottdorf): don't call contains_explicit_ref_binding, which
443         // is problematic as the HIR is being scraped, but ref bindings may be
444         // implicit after #42640. We need to make sure that pat_adjustments
445         // (once introduced) is populated by the time we get here.
446         //
447         // See #44848.
448         let contains_ref_bindings = arms
449             .iter()
450             .filter_map(|a| a.pat.contains_explicit_ref_binding())
451             .max_by_key(|m| match *m {
452                 hir::Mutability::Mut => 1,
453                 hir::Mutability::Not => 0,
454             });
455
456         if let Some(m) = contains_ref_bindings {
457             self.check_expr_with_needs(discrim, Needs::maybe_mut_place(m))
458         } else {
459             // ...but otherwise we want to use any supertype of the
460             // discriminant. This is sort of a workaround, see note (*) in
461             // `check_pat` for some details.
462             let discrim_ty = self.next_ty_var(TypeVariableOrigin {
463                 kind: TypeVariableOriginKind::TypeInference,
464                 span: discrim.span,
465             });
466             self.check_expr_has_type_or_error(discrim, discrim_ty, |_| {});
467             discrim_ty
468         }
469     }
470 }