@@ -15,6 +15,7 @@ use rustc::ty::subst::{InternalSubsts, SubstsRef};
1515use rustc:: ty:: { self , Ty , TyCtxt } ;
1616use rustc_error_codes:: * ;
1717use rustc_errors:: { Applicability , DiagnosticBuilder } ;
18+ use syntax:: ast:: Mutability ;
1819use syntax:: feature_gate:: feature_err;
1920use syntax_pos:: symbol:: sym;
2021use syntax_pos:: { MultiSpan , Span } ;
@@ -122,6 +123,7 @@ impl PatCtxt<'_, '_> {
122123impl < ' tcx > MatchVisitor < ' _ , ' tcx > {
123124 fn check_patterns ( & mut self , has_guard : bool , pat : & Pat ) {
124125 check_legality_of_move_bindings ( self , has_guard, pat) ;
126+ check_borrow_conflicts_in_at_patterns ( self , pat) ;
125127 if !self . tcx . features ( ) . bindings_after_at {
126128 check_legality_of_bindings_in_at_patterns ( self , pat) ;
127129 }
@@ -639,44 +641,116 @@ fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: boo
639641 }
640642}
641643
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+ }
648712
649- struct AtBindingPatternVisitor < ' a , ' b , ' tcx > {
650- cx : & ' a MatchVisitor < ' b , ' tcx > ,
651- bindings_allowed : bool ,
713+ true
714+ } ) ;
652715}
653716
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 ,
657725 }
658726
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+ }
671731
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+ }
677751 }
752+ _ => intravisit:: walk_pat ( self , pat) ,
678753 }
679- _ => intravisit:: walk_pat ( self , pat) ,
680754 }
681755 }
682756}
0 commit comments