@@ -16,8 +16,7 @@ use rustc_session::lint::builtin::BINDINGS_WITH_VARIANT_NAME;
16
16
use rustc_session:: lint:: builtin:: { IRREFUTABLE_LET_PATTERNS , UNREACHABLE_PATTERNS } ;
17
17
use rustc_session:: parse:: feature_err;
18
18
use rustc_session:: Session ;
19
- use rustc_span:: symbol:: sym;
20
- use rustc_span:: { MultiSpan , Span } ;
19
+ use rustc_span:: { sym, Span } ;
21
20
use syntax:: ast:: Mutability ;
22
21
23
22
use std:: slice;
@@ -114,8 +113,10 @@ impl PatCtxt<'_, '_> {
114
113
115
114
impl < ' tcx > MatchVisitor < ' _ , ' tcx > {
116
115
fn check_patterns ( & mut self , has_guard : bool , pat : & Pat < ' _ > ) {
117
- check_legality_of_move_bindings ( self , has_guard, pat) ;
118
- check_borrow_conflicts_in_at_patterns ( self , 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) ) ;
119
120
if !self . tcx . features ( ) . bindings_after_at {
120
121
check_legality_of_bindings_in_at_patterns ( self , pat) ;
121
122
}
@@ -559,6 +560,11 @@ fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span>
559
560
covered
560
561
}
561
562
563
+ /// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
564
+ fn is_binding_by_move ( cx : & MatchVisitor < ' _ , ' _ > , hir_id : HirId , span : Span ) -> bool {
565
+ !cx. tables . node_type ( hir_id) . is_copy_modulo_regions ( cx. tcx , cx. param_env , span)
566
+ }
567
+
562
568
/// Check the legality of legality of by-move bindings.
563
569
fn check_legality_of_move_bindings ( cx : & mut MatchVisitor < ' _ , ' _ > , has_guard : bool , pat : & Pat < ' _ > ) {
564
570
let sess = cx. tcx . sess ;
@@ -589,8 +595,7 @@ fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: boo
589
595
pat. walk_always ( |p| {
590
596
if let hir:: PatKind :: Binding ( .., sub) = & p. kind {
591
597
if let Some ( ty:: BindByValue ( _) ) = tables. extract_binding_mode ( sess, p. hir_id , p. span ) {
592
- let pat_ty = tables. node_type ( p. hir_id ) ;
593
- if !pat_ty. is_copy_modulo_regions ( cx. tcx , cx. param_env , pat. span ) {
598
+ if is_binding_by_move ( cx, p. hir_id , p. span ) {
594
599
check_move ( p, sub. as_deref ( ) ) ;
595
600
}
596
601
}
@@ -599,11 +604,11 @@ fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: boo
599
604
600
605
// Found some bad by-move spans, error!
601
606
if !by_move_spans. is_empty ( ) {
602
- let mut err = struct_span_err ! (
603
- sess,
604
- MultiSpan :: from_spans ( by_move_spans . clone ( ) ) ,
605
- E0009 ,
606
- "cannot bind by-move and by-ref in the same pattern" ,
607
+ let mut err = feature_err (
608
+ & sess. parse_sess ,
609
+ sym :: move_ref_pattern ,
610
+ by_move_spans . clone ( ) ,
611
+ "binding by-move and by-ref in the same pattern is unstable " ,
607
612
) ;
608
613
for span in by_ref_spans. iter ( ) {
609
614
err. span_label ( * span, "by-ref pattern here" ) ;
@@ -615,81 +620,118 @@ fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: boo
615
620
}
616
621
}
617
622
618
- /// Check that there are no borrow conflicts in `binding @ subpat` patterns.
623
+ /// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
619
624
///
620
625
/// For example, this would reject:
621
626
/// - `ref x @ Some(ref mut y)`,
622
- /// - `ref mut x @ Some(ref y)`
623
- /// - `ref mut x @ Some(ref mut y)`.
627
+ /// - `ref mut x @ Some(ref y)`,
628
+ /// - `ref mut x @ Some(ref mut y)`,
629
+ /// - `ref mut? x @ Some(y)`, and
630
+ /// - `x @ Some(ref mut? y)`.
624
631
///
625
632
/// This analysis is *not* subsumed by NLL.
626
633
fn check_borrow_conflicts_in_at_patterns ( cx : & MatchVisitor < ' _ , ' _ > , pat : & Pat < ' _ > ) {
627
- let tab = cx. tables ;
628
- let sess = cx. tcx . sess ;
629
- // Get the mutability of `p` if it's by-ref.
630
- let extract_binding_mut = |hir_id, span| match tab. extract_binding_mode ( sess, hir_id, span) ? {
631
- ty:: BindByValue ( _) => None ,
632
- ty:: BindByReference ( m) => Some ( m) ,
634
+ // Extract `sub` in `binding @ sub`.
635
+ let ( name, sub) = match & pat. kind {
636
+ hir:: PatKind :: Binding ( .., name, Some ( sub) ) => ( * name, sub) ,
637
+ _ => return ,
633
638
} ;
634
- pat. walk_always ( |pat| {
635
- // Extract `sub` in `binding @ sub`.
636
- let ( name, sub) = match & pat. kind {
637
- hir:: PatKind :: Binding ( .., name, Some ( sub) ) => ( * name, sub) ,
638
- _ => return ,
639
- } ;
639
+ let binding_span = pat. span . with_hi ( name. span . hi ( ) ) ;
640
640
641
- // Extract the mutability.
642
- let mut_outer = match extract_binding_mut ( pat. hir_id , pat. span ) {
643
- None => return ,
644
- Some ( m) => m,
645
- } ;
641
+ let tables = cx. tables ;
642
+ let sess = cx. tcx . sess ;
646
643
647
- // We now have `ref $mut_outer binding @ sub` (semantically).
648
- // Recurse into each binding in `sub` and find mutability conflicts.
649
- let mut conflicts_mut_mut = Vec :: new ( ) ;
650
- let mut conflicts_mut_ref = Vec :: new ( ) ;
651
- sub. each_binding ( |_, hir_id, span, _| {
652
- if let Some ( mut_inner) = extract_binding_mut ( hir_id, span) {
653
- match ( mut_outer, mut_inner) {
654
- ( Mutability :: Not , Mutability :: Not ) => { }
655
- ( Mutability :: Mut , Mutability :: Mut ) => conflicts_mut_mut. push ( span) ,
656
- _ => conflicts_mut_ref. push ( span) ,
644
+ // Get the binding move, extract the mutability if by-ref.
645
+ let mut_outer = match tables. extract_binding_mode ( sess, pat. hir_id , pat. span ) {
646
+ Some ( ty:: BindByValue ( _) ) if is_binding_by_move ( cx, pat. hir_id , pat. span ) => {
647
+ // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
648
+ let mut conflicts_ref = Vec :: new ( ) ;
649
+ sub. each_binding ( |_, hir_id, span, _| {
650
+ match tables. extract_binding_mode ( sess, hir_id, span) {
651
+ Some ( ty:: BindByValue ( _) ) | None => { }
652
+ Some ( ty:: BindByReference ( _) ) => conflicts_ref. push ( span) ,
657
653
}
654
+ } ) ;
655
+ if !conflicts_ref. is_empty ( ) {
656
+ let occurs_because = format ! (
657
+ "move occurs because `{}` has type `{}` which does implement the `Copy` trait" ,
658
+ name,
659
+ tables. node_type( pat. hir_id) ,
660
+ ) ;
661
+ sess. struct_span_err ( pat. span , "borrow of moved value" )
662
+ . span_label ( binding_span, format ! ( "value moved into `{}` here" , name) )
663
+ . span_label ( binding_span, occurs_because)
664
+ . span_labels ( conflicts_ref, "value borrowed here after move" )
665
+ . emit ( ) ;
658
666
}
659
- } ) ;
667
+ return ;
668
+ }
669
+ Some ( ty:: BindByValue ( _) ) | None => return ,
670
+ Some ( ty:: BindByReference ( m) ) => m,
671
+ } ;
660
672
661
- // Report errors if any.
662
- let binding_span = pat. span . with_hi ( name. span . hi ( ) ) ;
663
- if !conflicts_mut_mut. is_empty ( ) {
664
- // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
665
- let msg = & format ! ( "cannot borrow `{}` as mutable more than once at a time" , name) ;
666
- let mut err = sess. struct_span_err ( pat. span , msg) ;
667
- err. span_label ( binding_span, "first mutable borrow occurs here" ) ;
668
- for sp in conflicts_mut_mut {
669
- err. span_label ( sp, "another mutable borrow occurs here" ) ;
670
- }
671
- for sp in conflicts_mut_ref {
672
- err. span_label ( sp, "also borrowed as immutable here" ) ;
673
- }
674
- err. emit ( ) ;
675
- } else if !conflicts_mut_ref. is_empty ( ) {
676
- // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
677
- let ( primary, also) = match mut_outer {
678
- Mutability :: Mut => ( "mutable" , "immutable" ) ,
679
- Mutability :: Not => ( "immutable" , "mutable" ) ,
680
- } ;
681
- let msg = & format ! (
682
- "cannot borrow `{}` as {} because it is also borrowed as {}" ,
683
- name, also, primary,
684
- ) ;
685
- let mut err = sess. struct_span_err ( pat. span , msg) ;
686
- err. span_label ( binding_span, & format ! ( "{} borrow occurs here" , primary) ) ;
687
- for sp in conflicts_mut_ref {
688
- err. span_label ( sp, & format ! ( "{} borrow occurs here" , also) ) ;
673
+ // We now have `ref $mut_outer binding @ sub` (semantically).
674
+ // Recurse into each binding in `sub` and find mutability or move conflicts.
675
+ let mut conflicts_move = Vec :: new ( ) ;
676
+ let mut conflicts_mut_mut = Vec :: new ( ) ;
677
+ let mut conflicts_mut_ref = Vec :: new ( ) ;
678
+ sub. each_binding ( |_, hir_id, span, name| {
679
+ match tables. extract_binding_mode ( sess, hir_id, span) {
680
+ Some ( ty:: BindByReference ( mut_inner) ) => match ( mut_outer, mut_inner) {
681
+ ( Mutability :: Not , Mutability :: Not ) => { } // Both sides are `ref`.
682
+ ( Mutability :: Mut , Mutability :: Mut ) => conflicts_mut_mut. push ( ( span, name) ) , // 2x `ref mut`.
683
+ _ => conflicts_mut_ref. push ( ( span, name) ) , // `ref` + `ref mut` in either direction.
684
+ } ,
685
+ Some ( ty:: BindByValue ( _) ) if is_binding_by_move ( cx, hir_id, span) => {
686
+ conflicts_move. push ( ( span, name) ) // `ref mut?` + by-move conflict.
689
687
}
690
- err . emit ( ) ;
688
+ Some ( ty :: BindByValue ( _ ) ) | None => { } // `ref mut?` + by-copy is fine.
691
689
}
692
690
} ) ;
691
+
692
+ // Report errors if any.
693
+ if !conflicts_mut_mut. is_empty ( ) {
694
+ // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
695
+ let mut err = sess
696
+ . struct_span_err ( pat. span , "cannot borrow value as mutable more than once at a time" ) ;
697
+ err. span_label ( binding_span, format ! ( "first mutable borrow, by `{}`, occurs here" , name) ) ;
698
+ for ( span, name) in conflicts_mut_mut {
699
+ err. span_label ( span, format ! ( "another mutable borrow, by `{}`, occurs here" , name) ) ;
700
+ }
701
+ for ( span, name) in conflicts_mut_ref {
702
+ err. span_label ( span, format ! ( "also borrowed as immutable, by `{}`, here" , name) ) ;
703
+ }
704
+ for ( span, name) in conflicts_move {
705
+ err. span_label ( span, format ! ( "also moved into `{}` here" , name) ) ;
706
+ }
707
+ err. emit ( ) ;
708
+ } else if !conflicts_mut_ref. is_empty ( ) {
709
+ // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
710
+ let ( primary, also) = match mut_outer {
711
+ Mutability :: Mut => ( "mutable" , "immutable" ) ,
712
+ Mutability :: Not => ( "immutable" , "mutable" ) ,
713
+ } ;
714
+ let msg =
715
+ format ! ( "cannot borrow value as {} because it is also borrowed as {}" , also, primary) ;
716
+ let mut err = sess. struct_span_err ( pat. span , & msg) ;
717
+ err. span_label ( binding_span, format ! ( "{} borrow, by `{}`, occurs here" , primary, name) ) ;
718
+ for ( span, name) in conflicts_mut_ref {
719
+ err. span_label ( span, format ! ( "{} borrow, by `{}`, occurs here" , also, name) ) ;
720
+ }
721
+ for ( span, name) in conflicts_move {
722
+ err. span_label ( span, format ! ( "also moved into `{}` here" , name) ) ;
723
+ }
724
+ err. emit ( ) ;
725
+ } else if !conflicts_move. is_empty ( ) {
726
+ // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
727
+ let mut err =
728
+ sess. struct_span_err ( pat. span , "cannot move out of value because it is borrowed" ) ;
729
+ err. span_label ( binding_span, format ! ( "value borrowed, by `{}`, here" , name) ) ;
730
+ for ( span, name) in conflicts_move {
731
+ err. span_label ( span, format ! ( "value moved into `{}` here" , name) ) ;
732
+ }
733
+ err. emit ( ) ;
734
+ }
693
735
}
694
736
695
737
/// Forbids bindings in `@` patterns. This used to be is necessary for memory safety,
0 commit comments