@@ -15,6 +15,7 @@ use rustc::ty::subst::{InternalSubsts, SubstsRef};
15
15
use rustc:: ty:: { self , Ty , TyCtxt } ;
16
16
use rustc_error_codes:: * ;
17
17
use rustc_errors:: { Applicability , DiagnosticBuilder } ;
18
+ use syntax:: ast:: Mutability ;
18
19
use syntax:: feature_gate:: feature_err;
19
20
use syntax_pos:: symbol:: sym;
20
21
use syntax_pos:: { MultiSpan , Span } ;
@@ -122,6 +123,7 @@ impl PatCtxt<'_, '_> {
122
123
impl < ' tcx > MatchVisitor < ' _ , ' tcx > {
123
124
fn check_patterns ( & mut self , has_guard : bool , pat : & Pat ) {
124
125
check_legality_of_move_bindings ( self , has_guard, pat) ;
126
+ check_borrow_conflicts_in_at_patterns ( self , pat) ;
125
127
if !self . tcx . features ( ) . bindings_after_at {
126
128
check_legality_of_bindings_in_at_patterns ( self , pat) ;
127
129
}
@@ -639,44 +641,116 @@ fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: boo
639
641
}
640
642
}
641
643
642
- /// Forbids bindings in `@` patterns. This is necessary for memory safety,
643
- /// because of the way rvalues are handled in the borrow check. (See issue
644
- /// #14587.)
645
- fn check_legality_of_bindings_in_at_patterns ( cx : & MatchVisitor < ' _ , ' _ > , pat : & Pat ) {
646
- AtBindingPatternVisitor { cx, bindings_allowed : true } . visit_pat ( pat) ;
647
- }
644
+ fn check_borrow_conflicts_in_at_patterns ( cx : & MatchVisitor < ' _ , ' _ > , pat : & Pat ) {
645
+ // Get the mutability of `p` if it's by-ref.
646
+ let extract_binding_mut = |hir_id, span| match cx. tables . pat_binding_modes ( ) . get ( hir_id) {
647
+ None => {
648
+ cx. tcx . sess . delay_span_bug ( span, "missing binding mode" ) ;
649
+ None
650
+ }
651
+ Some ( ty:: BindByValue ( ..) ) => None ,
652
+ Some ( ty:: BindByReference ( m) ) => Some ( * m) ,
653
+ } ;
654
+ pat. walk ( |pat| {
655
+ // Extract `sub` in `binding @ sub`.
656
+ let ( name, sub) = match & pat. kind {
657
+ hir:: PatKind :: Binding ( .., name, Some ( sub) ) => ( * name, sub) ,
658
+ _ => return true ,
659
+ } ;
660
+
661
+ // Extract the mutability.
662
+ let mut_outer = match extract_binding_mut ( pat. hir_id , pat. span ) {
663
+ None => return true ,
664
+ Some ( m) => m,
665
+ } ;
666
+
667
+ // We now have `ref $mut_outer binding @ sub` (semantically).
668
+ // Recurse into each binding in `sub` and find mutability conflicts.
669
+ let mut conflicts_mut_mut = Vec :: new ( ) ;
670
+ let mut conflicts_mut_ref = Vec :: new ( ) ;
671
+ sub. each_binding ( |_, hir_id, span, _| {
672
+ if let Some ( mut_inner) = extract_binding_mut ( hir_id, span) {
673
+ match ( mut_outer, mut_inner) {
674
+ ( Mutability :: Immutable , Mutability :: Immutable ) => { }
675
+ ( Mutability :: Mutable , Mutability :: Mutable ) => conflicts_mut_mut. push ( span) ,
676
+ _ => conflicts_mut_ref. push ( span) ,
677
+ }
678
+ }
679
+ } ) ;
680
+
681
+ // Report errors if any.
682
+ let binding_span = pat. span . with_hi ( name. span . hi ( ) ) ;
683
+ if !conflicts_mut_mut. is_empty ( ) {
684
+ // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
685
+ let msg = & format ! ( "cannot borrow `{}` as mutable more than once at a time" , name) ;
686
+ let mut err = cx. tcx . sess . struct_span_err ( pat. span , msg) ;
687
+ err. span_label ( binding_span, "first mutable borrow occurs here" ) ;
688
+ for sp in conflicts_mut_mut {
689
+ err. span_label ( sp, "another mutable borrow occurs here" ) ;
690
+ }
691
+ for sp in conflicts_mut_ref {
692
+ err. span_label ( sp, "also borrowed as immutable here" ) ;
693
+ }
694
+ err. emit ( ) ;
695
+ } else if !conflicts_mut_ref. is_empty ( ) {
696
+ // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
697
+ let ( primary, also) = match mut_outer {
698
+ Mutability :: Mutable => ( "mutable" , "immutable" ) ,
699
+ Mutability :: Immutable => ( "immutable" , "mutable" ) ,
700
+ } ;
701
+ let msg = & format ! (
702
+ "cannot borrow `{}` as {} because it is also borrowed as {}" ,
703
+ name, primary, also,
704
+ ) ;
705
+ let mut err = cx. tcx . sess . struct_span_err ( pat. span , msg) ;
706
+ err. span_label ( binding_span, & format ! ( "{} borrow occurs here" , primary) ) ;
707
+ for sp in conflicts_mut_ref {
708
+ err. span_label ( sp, & format ! ( "{} borrow occurs here" , also) ) ;
709
+ }
710
+ err. emit ( ) ;
711
+ }
648
712
649
- struct AtBindingPatternVisitor < ' a , ' b , ' tcx > {
650
- cx : & ' a MatchVisitor < ' b , ' tcx > ,
651
- bindings_allowed : bool ,
713
+ true
714
+ } ) ;
652
715
}
653
716
654
- impl < ' v > Visitor < ' v > for AtBindingPatternVisitor < ' _ , ' _ , ' _ > {
655
- fn nested_visit_map < ' this > ( & ' this mut self ) -> NestedVisitorMap < ' this , ' v > {
656
- NestedVisitorMap :: None
717
+ /// Forbids bindings in `@` patterns. This used to be is necessary for memory safety,
718
+ /// because of the way rvalues were handled in the borrow check. (See issue #14587.)
719
+ fn check_legality_of_bindings_in_at_patterns ( cx : & MatchVisitor < ' _ , ' _ > , pat : & Pat ) {
720
+ AtBindingPatternVisitor { cx, bindings_allowed : true } . visit_pat ( pat) ;
721
+
722
+ struct AtBindingPatternVisitor < ' a , ' b , ' tcx > {
723
+ cx : & ' a MatchVisitor < ' b , ' tcx > ,
724
+ bindings_allowed : bool ,
657
725
}
658
726
659
- fn visit_pat ( & mut self , pat : & Pat ) {
660
- match pat. kind {
661
- hir:: PatKind :: Binding ( .., ref subpat) => {
662
- if !self . bindings_allowed {
663
- feature_err (
664
- & self . cx . tcx . sess . parse_sess ,
665
- sym:: bindings_after_at,
666
- pat. span ,
667
- "pattern bindings after an `@` are unstable" ,
668
- )
669
- . emit ( ) ;
670
- }
727
+ impl < ' v > Visitor < ' v > for AtBindingPatternVisitor < ' _ , ' _ , ' _ > {
728
+ fn nested_visit_map < ' this > ( & ' this mut self ) -> NestedVisitorMap < ' this , ' v > {
729
+ NestedVisitorMap :: None
730
+ }
671
731
672
- if subpat. is_some ( ) {
673
- let bindings_were_allowed = self . bindings_allowed ;
674
- self . bindings_allowed = false ;
675
- intravisit:: walk_pat ( self , pat) ;
676
- self . bindings_allowed = bindings_were_allowed;
732
+ fn visit_pat ( & mut self , pat : & Pat ) {
733
+ match pat. kind {
734
+ hir:: PatKind :: Binding ( .., ref subpat) => {
735
+ if !self . bindings_allowed {
736
+ feature_err (
737
+ & self . cx . tcx . sess . parse_sess ,
738
+ sym:: bindings_after_at,
739
+ pat. span ,
740
+ "pattern bindings after an `@` are unstable" ,
741
+ )
742
+ . emit ( ) ;
743
+ }
744
+
745
+ if subpat. is_some ( ) {
746
+ let bindings_were_allowed = self . bindings_allowed ;
747
+ self . bindings_allowed = false ;
748
+ intravisit:: walk_pat ( self , pat) ;
749
+ self . bindings_allowed = bindings_were_allowed;
750
+ }
677
751
}
752
+ _ => intravisit:: walk_pat ( self , pat) ,
678
753
}
679
- _ => intravisit:: walk_pat ( self , pat) ,
680
754
}
681
755
}
682
756
}
0 commit comments