]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir_build/hair/pattern/check_match.rs
New upstream version 1.43.0+dfsg1
[rustc.git] / src / librustc_mir_build / hair / pattern / check_match.rs
1 use super::_match::Usefulness::*;
2 use super::_match::WitnessPreference::*;
3 use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack};
4
5 use super::{PatCtxt, PatKind, PatternError};
6
7 use rustc::hir::map::Map;
8 use rustc::ty::{self, Ty, TyCtxt};
9 use rustc_ast::ast::Mutability;
10 use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder};
11 use rustc_hir as hir;
12 use rustc_hir::def::*;
13 use rustc_hir::def_id::DefId;
14 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
15 use rustc_hir::{HirId, Pat};
16 use rustc_session::lint::builtin::BINDINGS_WITH_VARIANT_NAME;
17 use rustc_session::lint::builtin::{IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS};
18 use rustc_session::parse::feature_err;
19 use rustc_session::Session;
20 use rustc_span::{sym, Span};
21
22 use std::slice;
23
24 crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
25 let body_id = match tcx.hir().as_local_hir_id(def_id) {
26 None => return,
27 Some(id) => tcx.hir().body_owned_by(id),
28 };
29
30 let mut visitor =
31 MatchVisitor { tcx, tables: tcx.body_tables(body_id), param_env: tcx.param_env(def_id) };
32 visitor.visit_body(tcx.hir().body(body_id));
33 }
34
35 fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBuilder<'_> {
36 struct_span_err!(sess, sp, E0004, "{}", &error_message)
37 }
38
39 struct MatchVisitor<'a, 'tcx> {
40 tcx: TyCtxt<'tcx>,
41 tables: &'a ty::TypeckTables<'tcx>,
42 param_env: ty::ParamEnv<'tcx>,
43 }
44
45 impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
46 type Map = Map<'tcx>;
47
48 fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
49 NestedVisitorMap::None
50 }
51
52 fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
53 intravisit::walk_expr(self, ex);
54
55 if let hir::ExprKind::Match(ref scrut, ref arms, source) = ex.kind {
56 self.check_match(scrut, arms, source);
57 }
58 }
59
60 fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) {
61 intravisit::walk_local(self, loc);
62
63 let (msg, sp) = match loc.source {
64 hir::LocalSource::Normal => ("local binding", Some(loc.span)),
65 hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
66 hir::LocalSource::AsyncFn => ("async fn binding", None),
67 hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
68 };
69 self.check_irrefutable(&loc.pat, msg, sp);
70 self.check_patterns(false, &loc.pat);
71 }
72
73 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
74 intravisit::walk_param(self, param);
75 self.check_irrefutable(&param.pat, "function argument", None);
76 self.check_patterns(false, &param.pat);
77 }
78 }
79
80 impl PatCtxt<'_, '_> {
81 fn report_inlining_errors(&self, pat_span: Span) {
82 for error in &self.errors {
83 match *error {
84 PatternError::StaticInPattern(span) => {
85 self.span_e0158(span, "statics cannot be referenced in patterns")
86 }
87 PatternError::AssocConstInPattern(span) => {
88 self.span_e0158(span, "associated consts cannot be referenced in patterns")
89 }
90 PatternError::FloatBug => {
91 // FIXME(#31407) this is only necessary because float parsing is buggy
92 ::rustc::mir::interpret::struct_error(
93 self.tcx.at(pat_span),
94 "could not evaluate float literal (see issue #31407)",
95 )
96 .emit();
97 }
98 PatternError::NonConstPath(span) => {
99 ::rustc::mir::interpret::struct_error(
100 self.tcx.at(span),
101 "runtime values cannot be referenced in patterns",
102 )
103 .emit();
104 }
105 }
106 }
107 }
108
109 fn span_e0158(&self, span: Span, text: &str) {
110 struct_span_err!(self.tcx.sess, span, E0158, "{}", text).emit();
111 }
112 }
113
114 impl<'tcx> MatchVisitor<'_, 'tcx> {
115 fn check_patterns(&mut self, has_guard: bool, pat: &Pat<'_>) {
116 if !self.tcx.features().move_ref_pattern {
117 check_legality_of_move_bindings(self, has_guard, pat);
118 }
119 pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
120 if !self.tcx.features().bindings_after_at {
121 check_legality_of_bindings_in_at_patterns(self, pat);
122 }
123 check_for_bindings_named_same_as_variants(self, pat);
124 }
125
126 fn lower_pattern<'p>(
127 &self,
128 cx: &mut MatchCheckCtxt<'p, 'tcx>,
129 pat: &'tcx hir::Pat<'tcx>,
130 have_errors: &mut bool,
131 ) -> (&'p super::Pat<'tcx>, Ty<'tcx>) {
132 let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.tables);
133 patcx.include_lint_checks();
134 let pattern = patcx.lower_pattern(pat);
135 let pattern_ty = pattern.ty;
136 let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
137 if !patcx.errors.is_empty() {
138 *have_errors = true;
139 patcx.report_inlining_errors(pat.span);
140 }
141 (pattern, pattern_ty)
142 }
143
144 fn check_in_cx(&self, hir_id: HirId, f: impl FnOnce(MatchCheckCtxt<'_, 'tcx>)) {
145 let module = self.tcx.parent_module(hir_id);
146 MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |cx| f(cx));
147 }
148
149 fn check_match(
150 &mut self,
151 scrut: &hir::Expr<'_>,
152 arms: &'tcx [hir::Arm<'tcx>],
153 source: hir::MatchSource,
154 ) {
155 for arm in arms {
156 // Check the arm for some things unrelated to exhaustiveness.
157 self.check_patterns(arm.guard.is_some(), &arm.pat);
158 }
159
160 self.check_in_cx(scrut.hir_id, |ref mut cx| {
161 let mut have_errors = false;
162
163 let inlined_arms: Vec<_> = arms
164 .iter()
165 .map(|hir::Arm { pat, guard, .. }| {
166 (self.lower_pattern(cx, pat, &mut have_errors).0, pat.hir_id, guard.is_some())
167 })
168 .collect();
169
170 // Bail out early if inlining failed.
171 if have_errors {
172 return;
173 }
174
175 // Fourth, check for unreachable arms.
176 let matrix = check_arms(cx, &inlined_arms, source);
177
178 // Fifth, check if the match is exhaustive.
179 let scrut_ty = self.tables.node_type(scrut.hir_id);
180 // Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
181 // since an empty matrix can occur when there are arms, if those arms all have guards.
182 let is_empty_match = inlined_arms.is_empty();
183 check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match);
184 })
185 }
186
187 fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
188 self.check_in_cx(pat.hir_id, |ref mut cx| {
189 let (pattern, pattern_ty) = self.lower_pattern(cx, pat, &mut false);
190 let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect();
191
192 let witnesses = match check_not_useful(cx, pattern_ty, &pats, pat.hir_id) {
193 Ok(_) => return,
194 Err(err) => err,
195 };
196
197 let joined_patterns = joined_uncovered_patterns(&witnesses);
198 let mut err = struct_span_err!(
199 self.tcx.sess,
200 pat.span,
201 E0005,
202 "refutable pattern in {}: {} not covered",
203 origin,
204 joined_patterns
205 );
206 let suggest_if_let = match &pat.kind {
207 hir::PatKind::Path(hir::QPath::Resolved(None, path))
208 if path.segments.len() == 1 && path.segments[0].args.is_none() =>
209 {
210 const_not_var(&mut err, cx.tcx, pat, path);
211 false
212 }
213 _ => {
214 err.span_label(
215 pat.span,
216 pattern_not_covered_label(&witnesses, &joined_patterns),
217 );
218 true
219 }
220 };
221
222 if let (Some(span), true) = (sp, suggest_if_let) {
223 err.note(
224 "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
225 an `enum` with only one variant",
226 );
227 if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
228 err.span_suggestion(
229 span,
230 "you might want to use `if let` to ignore the variant that isn't matched",
231 format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]),
232 Applicability::HasPlaceholders,
233 );
234 }
235 err.note(
236 "for more information, visit \
237 https://doc.rust-lang.org/book/ch18-02-refutability.html",
238 );
239 }
240
241 adt_defined_here(cx, &mut err, pattern_ty, &witnesses);
242 err.emit();
243 });
244 }
245 }
246
247 /// A path pattern was interpreted as a constant, not a new variable.
248 /// This caused an irrefutable match failure in e.g. `let`.
249 fn const_not_var(
250 err: &mut DiagnosticBuilder<'_>,
251 tcx: TyCtxt<'_>,
252 pat: &Pat<'_>,
253 path: &hir::Path<'_>,
254 ) {
255 let descr = path.res.descr();
256 err.span_label(
257 pat.span,
258 format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,),
259 );
260
261 err.span_suggestion(
262 pat.span,
263 "introduce a variable instead",
264 format!("{}_var", path.segments[0].ident).to_lowercase(),
265 // Cannot use `MachineApplicable` as it's not really *always* correct
266 // because there may be such an identifier in scope or the user maybe
267 // really wanted to match against the constant. This is quite unlikely however.
268 Applicability::MaybeIncorrect,
269 );
270
271 if let Some(span) = tcx.hir().res_span(path.res) {
272 err.span_label(span, format!("{} defined here", descr));
273 }
274 }
275
276 fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
277 pat.walk_always(|p| {
278 if let hir::PatKind::Binding(_, _, ident, None) = p.kind {
279 if let Some(ty::BindByValue(hir::Mutability::Not)) =
280 cx.tables.extract_binding_mode(cx.tcx.sess, p.hir_id, p.span)
281 {
282 let pat_ty = cx.tables.pat_ty(p).peel_refs();
283 if let ty::Adt(edef, _) = pat_ty.kind {
284 if edef.is_enum()
285 && edef.variants.iter().any(|variant| {
286 variant.ident == ident && variant.ctor_kind == CtorKind::Const
287 })
288 {
289 cx.tcx.struct_span_lint_hir(
290 BINDINGS_WITH_VARIANT_NAME,
291 p.hir_id,
292 p.span,
293 |lint| {
294 let ty_path = cx.tcx.def_path_str(edef.did);
295 lint.build(&format!(
296 "pattern binding `{}` is named the same as one \
297 of the variants of the type `{}`",
298 ident, ty_path
299 ))
300 .code(error_code!(E0170))
301 .span_suggestion(
302 p.span,
303 "to match on the variant, qualify the path",
304 format!("{}::{}", ty_path, ident),
305 Applicability::MachineApplicable,
306 )
307 .emit();
308 },
309 )
310 }
311 }
312 }
313 }
314 });
315 }
316
317 /// Checks for common cases of "catchall" patterns that may not be intended as such.
318 fn pat_is_catchall(pat: &super::Pat<'_>) -> bool {
319 use super::PatKind::*;
320 match &*pat.kind {
321 Binding { subpattern: None, .. } => true,
322 Binding { subpattern: Some(s), .. } | Deref { subpattern: s } => pat_is_catchall(s),
323 Leaf { subpatterns: s } => s.iter().all(|p| pat_is_catchall(&p.pattern)),
324 _ => false,
325 }
326 }
327
328 fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
329 tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, |lint| {
330 let mut err = lint.build("unreachable pattern");
331 if let Some(catchall) = catchall {
332 // We had a catchall pattern, hint at that.
333 err.span_label(span, "unreachable pattern");
334 err.span_label(catchall, "matches any value");
335 }
336 err.emit();
337 });
338 }
339
340 fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::MatchSource) {
341 tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| {
342 let msg = match source {
343 hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern",
344 hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern",
345 _ => bug!(),
346 };
347 lint.build(msg).emit()
348 });
349 }
350
351 /// Check for unreachable patterns.
352 fn check_arms<'p, 'tcx>(
353 cx: &mut MatchCheckCtxt<'p, 'tcx>,
354 arms: &[(&'p super::Pat<'tcx>, HirId, bool)],
355 source: hir::MatchSource,
356 ) -> Matrix<'p, 'tcx> {
357 let mut seen = Matrix::empty();
358 let mut catchall = None;
359 for (arm_index, (pat, id, has_guard)) in arms.iter().copied().enumerate() {
360 let v = PatStack::from_pattern(pat);
361 match is_useful(cx, &seen, &v, LeaveOutWitness, id, true) {
362 NotUseful => {
363 match source {
364 hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
365
366 hir::MatchSource::IfLetDesugar { .. } | hir::MatchSource::WhileLetDesugar => {
367 // Check which arm we're on.
368 match arm_index {
369 // The arm with the user-specified pattern.
370 0 => unreachable_pattern(cx.tcx, pat.span, id, None),
371 // The arm with the wildcard pattern.
372 1 => irrefutable_let_pattern(cx.tcx, pat.span, id, source),
373 _ => bug!(),
374 }
375 }
376
377 hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
378 unreachable_pattern(cx.tcx, pat.span, id, catchall);
379 }
380
381 // Unreachable patterns in try and await expressions occur when one of
382 // the arms are an uninhabited type. Which is OK.
383 hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
384 }
385 }
386 Useful(unreachable_subpatterns) => {
387 for pat in unreachable_subpatterns {
388 unreachable_pattern(cx.tcx, pat.span, id, None);
389 }
390 }
391 UsefulWithWitness(_) => bug!(),
392 }
393 if !has_guard {
394 seen.push(v);
395 if catchall.is_none() && pat_is_catchall(pat) {
396 catchall = Some(pat.span);
397 }
398 }
399 }
400 seen
401 }
402
403 fn check_not_useful<'p, 'tcx>(
404 cx: &mut MatchCheckCtxt<'p, 'tcx>,
405 ty: Ty<'tcx>,
406 matrix: &Matrix<'p, 'tcx>,
407 hir_id: HirId,
408 ) -> Result<(), Vec<super::Pat<'tcx>>> {
409 let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty));
410 let v = PatStack::from_pattern(wild_pattern);
411 match is_useful(cx, matrix, &v, ConstructWitness, hir_id, true) {
412 NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
413 UsefulWithWitness(pats) => Err(if pats.is_empty() {
414 bug!("Exhaustiveness check returned no witnesses")
415 } else {
416 pats.into_iter().map(|w| w.single_pattern()).collect()
417 }),
418 Useful(_) => bug!(),
419 }
420 }
421
422 fn check_exhaustive<'p, 'tcx>(
423 cx: &mut MatchCheckCtxt<'p, 'tcx>,
424 scrut_ty: Ty<'tcx>,
425 sp: Span,
426 matrix: &Matrix<'p, 'tcx>,
427 hir_id: HirId,
428 is_empty_match: bool,
429 ) {
430 // In the absence of the `exhaustive_patterns` feature, empty matches are not detected by
431 // `is_useful` to exhaustively match uninhabited types, so we manually check here.
432 if is_empty_match && !cx.tcx.features().exhaustive_patterns {
433 let scrutinee_is_visibly_uninhabited = match scrut_ty.kind {
434 ty::Never => true,
435 ty::Adt(def, _) => {
436 def.is_enum()
437 && def.variants.is_empty()
438 && !cx.is_foreign_non_exhaustive_enum(scrut_ty)
439 }
440 _ => false,
441 };
442 if scrutinee_is_visibly_uninhabited {
443 // If the type *is* uninhabited, an empty match is vacuously exhaustive.
444 return;
445 }
446 }
447
448 let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) {
449 Ok(_) => return,
450 Err(err) => err,
451 };
452
453 let non_empty_enum = match scrut_ty.kind {
454 ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(),
455 _ => false,
456 };
457 // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
458 // informative.
459 let mut err;
460 if is_empty_match && !non_empty_enum {
461 err = create_e0004(
462 cx.tcx.sess,
463 sp,
464 format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
465 );
466 } else {
467 let joined_patterns = joined_uncovered_patterns(&witnesses);
468 err = create_e0004(
469 cx.tcx.sess,
470 sp,
471 format!("non-exhaustive patterns: {} not covered", joined_patterns),
472 );
473 err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
474 };
475
476 adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
477 err.help(
478 "ensure that all possible cases are being handled, \
479 possibly by adding wildcards or more match arms",
480 );
481 err.emit();
482 }
483
484 fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
485 const LIMIT: usize = 3;
486 match witnesses {
487 [] => bug!(),
488 [witness] => format!("`{}`", witness),
489 [head @ .., tail] if head.len() < LIMIT => {
490 let head: Vec<_> = head.iter().map(<_>::to_string).collect();
491 format!("`{}` and `{}`", head.join("`, `"), tail)
492 }
493 _ => {
494 let (head, tail) = witnesses.split_at(LIMIT);
495 let head: Vec<_> = head.iter().map(<_>::to_string).collect();
496 format!("`{}` and {} more", head.join("`, `"), tail.len())
497 }
498 }
499 }
500
501 fn pattern_not_covered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String {
502 format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
503 }
504
505 /// Point at the definition of non-covered `enum` variants.
506 fn adt_defined_here(
507 cx: &MatchCheckCtxt<'_, '_>,
508 err: &mut DiagnosticBuilder<'_>,
509 ty: Ty<'_>,
510 witnesses: &[super::Pat<'_>],
511 ) {
512 let ty = ty.peel_refs();
513 if let ty::Adt(def, _) = ty.kind {
514 if let Some(sp) = cx.tcx.hir().span_if_local(def.did) {
515 err.span_label(sp, format!("`{}` defined here", ty));
516 }
517
518 if witnesses.len() < 4 {
519 for sp in maybe_point_at_variant(ty, &witnesses) {
520 err.span_label(sp, "not covered");
521 }
522 }
523 }
524 }
525
526 fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span> {
527 let mut covered = vec![];
528 if let ty::Adt(def, _) = ty.kind {
529 // Don't point at variants that have already been covered due to other patterns to avoid
530 // visual clutter.
531 for pattern in patterns {
532 use PatKind::{AscribeUserType, Deref, Leaf, Or, Variant};
533 match &*pattern.kind {
534 AscribeUserType { subpattern, .. } | Deref { subpattern } => {
535 covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern)));
536 }
537 Variant { adt_def, variant_index, subpatterns, .. } if adt_def.did == def.did => {
538 let sp = def.variants[*variant_index].ident.span;
539 if covered.contains(&sp) {
540 continue;
541 }
542 covered.push(sp);
543
544 let pats = subpatterns
545 .iter()
546 .map(|field_pattern| field_pattern.pattern.clone())
547 .collect::<Box<[_]>>();
548 covered.extend(maybe_point_at_variant(ty, &pats));
549 }
550 Leaf { subpatterns } => {
551 let pats = subpatterns
552 .iter()
553 .map(|field_pattern| field_pattern.pattern.clone())
554 .collect::<Box<[_]>>();
555 covered.extend(maybe_point_at_variant(ty, &pats));
556 }
557 Or { pats } => {
558 let pats = pats.iter().cloned().collect::<Box<[_]>>();
559 covered.extend(maybe_point_at_variant(ty, &pats));
560 }
561 _ => {}
562 }
563 }
564 }
565 covered
566 }
567
568 /// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
569 fn is_binding_by_move(cx: &MatchVisitor<'_, '_>, hir_id: HirId, span: Span) -> bool {
570 !cx.tables.node_type(hir_id).is_copy_modulo_regions(cx.tcx, cx.param_env, span)
571 }
572
573 /// Check the legality of legality of by-move bindings.
574 fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: bool, pat: &Pat<'_>) {
575 let sess = cx.tcx.sess;
576 let tables = cx.tables;
577
578 // Find all by-ref spans.
579 let mut by_ref_spans = Vec::new();
580 pat.each_binding(|_, hir_id, span, _| {
581 if let Some(ty::BindByReference(_)) = tables.extract_binding_mode(sess, hir_id, span) {
582 by_ref_spans.push(span);
583 }
584 });
585
586 // Find bad by-move spans:
587 let by_move_spans = &mut Vec::new();
588 let mut check_move = |p: &Pat<'_>, sub: Option<&Pat<'_>>| {
589 // Check legality of moving out of the enum.
590 //
591 // `x @ Foo(..)` is legal, but `x @ Foo(y)` isn't.
592 if sub.map_or(false, |p| p.contains_bindings()) {
593 struct_span_err!(sess, p.span, E0007, "cannot bind by-move with sub-bindings")
594 .span_label(p.span, "binds an already bound by-move value by moving it")
595 .emit();
596 } else if !has_guard && !by_ref_spans.is_empty() {
597 by_move_spans.push(p.span);
598 }
599 };
600 pat.walk_always(|p| {
601 if let hir::PatKind::Binding(.., sub) = &p.kind {
602 if let Some(ty::BindByValue(_)) = tables.extract_binding_mode(sess, p.hir_id, p.span) {
603 if is_binding_by_move(cx, p.hir_id, p.span) {
604 check_move(p, sub.as_deref());
605 }
606 }
607 }
608 });
609
610 // Found some bad by-move spans, error!
611 if !by_move_spans.is_empty() {
612 let mut err = feature_err(
613 &sess.parse_sess,
614 sym::move_ref_pattern,
615 by_move_spans.clone(),
616 "binding by-move and by-ref in the same pattern is unstable",
617 );
618 for span in by_ref_spans.iter() {
619 err.span_label(*span, "by-ref pattern here");
620 }
621 for span in by_move_spans.iter() {
622 err.span_label(*span, "by-move pattern here");
623 }
624 err.emit();
625 }
626 }
627
628 /// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
629 ///
630 /// For example, this would reject:
631 /// - `ref x @ Some(ref mut y)`,
632 /// - `ref mut x @ Some(ref y)`,
633 /// - `ref mut x @ Some(ref mut y)`,
634 /// - `ref mut? x @ Some(y)`, and
635 /// - `x @ Some(ref mut? y)`.
636 ///
637 /// This analysis is *not* subsumed by NLL.
638 fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
639 // Extract `sub` in `binding @ sub`.
640 let (name, sub) = match &pat.kind {
641 hir::PatKind::Binding(.., name, Some(sub)) => (*name, sub),
642 _ => return,
643 };
644 let binding_span = pat.span.with_hi(name.span.hi());
645
646 let tables = cx.tables;
647 let sess = cx.tcx.sess;
648
649 // Get the binding move, extract the mutability if by-ref.
650 let mut_outer = match tables.extract_binding_mode(sess, pat.hir_id, pat.span) {
651 Some(ty::BindByValue(_)) if is_binding_by_move(cx, pat.hir_id, pat.span) => {
652 // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
653 let mut conflicts_ref = Vec::new();
654 sub.each_binding(|_, hir_id, span, _| {
655 match tables.extract_binding_mode(sess, hir_id, span) {
656 Some(ty::BindByValue(_)) | None => {}
657 Some(ty::BindByReference(_)) => conflicts_ref.push(span),
658 }
659 });
660 if !conflicts_ref.is_empty() {
661 let occurs_because = format!(
662 "move occurs because `{}` has type `{}` which does not implement the `Copy` trait",
663 name,
664 tables.node_type(pat.hir_id),
665 );
666 sess.struct_span_err(pat.span, "borrow of moved value")
667 .span_label(binding_span, format!("value moved into `{}` here", name))
668 .span_label(binding_span, occurs_because)
669 .span_labels(conflicts_ref, "value borrowed here after move")
670 .emit();
671 }
672 return;
673 }
674 Some(ty::BindByValue(_)) | None => return,
675 Some(ty::BindByReference(m)) => m,
676 };
677
678 // We now have `ref $mut_outer binding @ sub` (semantically).
679 // Recurse into each binding in `sub` and find mutability or move conflicts.
680 let mut conflicts_move = Vec::new();
681 let mut conflicts_mut_mut = Vec::new();
682 let mut conflicts_mut_ref = Vec::new();
683 sub.each_binding(|_, hir_id, span, name| {
684 match tables.extract_binding_mode(sess, hir_id, span) {
685 Some(ty::BindByReference(mut_inner)) => match (mut_outer, mut_inner) {
686 (Mutability::Not, Mutability::Not) => {} // Both sides are `ref`.
687 (Mutability::Mut, Mutability::Mut) => conflicts_mut_mut.push((span, name)), // 2x `ref mut`.
688 _ => conflicts_mut_ref.push((span, name)), // `ref` + `ref mut` in either direction.
689 },
690 Some(ty::BindByValue(_)) if is_binding_by_move(cx, hir_id, span) => {
691 conflicts_move.push((span, name)) // `ref mut?` + by-move conflict.
692 }
693 Some(ty::BindByValue(_)) | None => {} // `ref mut?` + by-copy is fine.
694 }
695 });
696
697 // Report errors if any.
698 if !conflicts_mut_mut.is_empty() {
699 // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
700 let mut err = sess
701 .struct_span_err(pat.span, "cannot borrow value as mutable more than once at a time");
702 err.span_label(binding_span, format!("first mutable borrow, by `{}`, occurs here", name));
703 for (span, name) in conflicts_mut_mut {
704 err.span_label(span, format!("another mutable borrow, by `{}`, occurs here", name));
705 }
706 for (span, name) in conflicts_mut_ref {
707 err.span_label(span, format!("also borrowed as immutable, by `{}`, here", name));
708 }
709 for (span, name) in conflicts_move {
710 err.span_label(span, format!("also moved into `{}` here", name));
711 }
712 err.emit();
713 } else if !conflicts_mut_ref.is_empty() {
714 // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
715 let (primary, also) = match mut_outer {
716 Mutability::Mut => ("mutable", "immutable"),
717 Mutability::Not => ("immutable", "mutable"),
718 };
719 let msg =
720 format!("cannot borrow value as {} because it is also borrowed as {}", also, primary);
721 let mut err = sess.struct_span_err(pat.span, &msg);
722 err.span_label(binding_span, format!("{} borrow, by `{}`, occurs here", primary, name));
723 for (span, name) in conflicts_mut_ref {
724 err.span_label(span, format!("{} borrow, by `{}`, occurs here", also, name));
725 }
726 for (span, name) in conflicts_move {
727 err.span_label(span, format!("also moved into `{}` here", name));
728 }
729 err.emit();
730 } else if !conflicts_move.is_empty() {
731 // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
732 let mut err =
733 sess.struct_span_err(pat.span, "cannot move out of value because it is borrowed");
734 err.span_label(binding_span, format!("value borrowed, by `{}`, here", name));
735 for (span, name) in conflicts_move {
736 err.span_label(span, format!("value moved into `{}` here", name));
737 }
738 err.emit();
739 }
740 }
741
742 /// Forbids bindings in `@` patterns. This used to be is necessary for memory safety,
743 /// because of the way rvalues were handled in the borrow check. (See issue #14587.)
744 fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
745 AtBindingPatternVisitor { cx, bindings_allowed: true }.visit_pat(pat);
746
747 struct AtBindingPatternVisitor<'a, 'b, 'tcx> {
748 cx: &'a MatchVisitor<'b, 'tcx>,
749 bindings_allowed: bool,
750 }
751
752 impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> {
753 type Map = Map<'v>;
754
755 fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
756 NestedVisitorMap::None
757 }
758
759 fn visit_pat(&mut self, pat: &Pat<'_>) {
760 match pat.kind {
761 hir::PatKind::Binding(.., ref subpat) => {
762 if !self.bindings_allowed {
763 feature_err(
764 &self.cx.tcx.sess.parse_sess,
765 sym::bindings_after_at,
766 pat.span,
767 "pattern bindings after an `@` are unstable",
768 )
769 .emit();
770 }
771
772 if subpat.is_some() {
773 let bindings_were_allowed = self.bindings_allowed;
774 self.bindings_allowed = false;
775 intravisit::walk_pat(self, pat);
776 self.bindings_allowed = bindings_were_allowed;
777 }
778 }
779 _ => intravisit::walk_pat(self, pat),
780 }
781 }
782 }
783 }