@@ -16,8 +16,7 @@ use rustc_session::lint::builtin::BINDINGS_WITH_VARIANT_NAME;
1616use rustc_session:: lint:: builtin:: { IRREFUTABLE_LET_PATTERNS , UNREACHABLE_PATTERNS } ;
1717use rustc_session:: parse:: feature_err;
1818use rustc_session:: Session ;
19- use rustc_span:: symbol:: sym;
20- use rustc_span:: { MultiSpan , Span } ;
19+ use rustc_span:: { sym, Span } ;
2120use syntax:: ast:: Mutability ;
2221
2322use std:: slice;
@@ -114,8 +113,10 @@ impl PatCtxt<'_, '_> {
114113
115114impl < ' tcx > MatchVisitor < ' _ , ' tcx > {
116115 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) ) ;
119120 if !self . tcx . features ( ) . bindings_after_at {
120121 check_legality_of_bindings_in_at_patterns ( self , pat) ;
121122 }
@@ -559,6 +560,11 @@ fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span>
559560 covered
560561}
561562
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+
562568/// Check the legality of legality of by-move bindings.
563569fn check_legality_of_move_bindings ( cx : & mut MatchVisitor < ' _ , ' _ > , has_guard : bool , pat : & Pat < ' _ > ) {
564570 let sess = cx. tcx . sess ;
@@ -589,8 +595,7 @@ fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: boo
589595 pat. walk_always ( |p| {
590596 if let hir:: PatKind :: Binding ( .., sub) = & p. kind {
591597 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 ) {
594599 check_move ( p, sub. as_deref ( ) ) ;
595600 }
596601 }
@@ -599,11 +604,11 @@ fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: boo
599604
600605 // Found some bad by-move spans, error!
601606 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 " ,
607612 ) ;
608613 for span in by_ref_spans. iter ( ) {
609614 err. span_label ( * span, "by-ref pattern here" ) ;
@@ -615,81 +620,118 @@ fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: boo
615620 }
616621}
617622
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.
619624///
620625/// For example, this would reject:
621626/// - `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)`.
624631///
625632/// This analysis is *not* subsumed by NLL.
626633fn 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 ,
633638 } ;
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 ( ) ) ;
640640
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 ;
646643
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) ,
657653 }
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 ( ) ;
658666 }
659- } ) ;
667+ return ;
668+ }
669+ Some ( ty:: BindByValue ( _) ) | None => return ,
670+ Some ( ty:: BindByReference ( m) ) => m,
671+ } ;
660672
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.
689687 }
690- err . emit ( ) ;
688+ Some ( ty :: BindByValue ( _ ) ) | None => { } // `ref mut?` + by-copy is fine.
691689 }
692690 } ) ;
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+ }
693735}
694736
695737/// Forbids bindings in `@` patterns. This used to be is necessary for memory safety,
0 commit comments