@@ -271,6 +271,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
271271 self . access_lvalue ( context,
272272 ( output, span) ,
273273 ( Deep , Read ( ReadKind :: Copy ) ) ,
274+ LocalMutationIsAllowed :: No ,
274275 flow_state) ;
275276 self . check_if_path_is_moved ( context, InitializationRequiringAction :: Use ,
276277 ( output, span) , flow_state) ;
@@ -300,7 +301,9 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
300301 StatementKind :: StorageDead ( local) => {
301302 self . access_lvalue ( ContextKind :: StorageDead . new ( location) ,
302303 ( & Lvalue :: Local ( local) , span) ,
303- ( Shallow ( None ) , Write ( WriteKind :: StorageDeadOrDrop ) ) , flow_state) ;
304+ ( Shallow ( None ) , Write ( WriteKind :: StorageDeadOrDrop ) ) ,
305+ LocalMutationIsAllowed :: Yes ,
306+ flow_state) ;
304307 }
305308 }
306309 }
@@ -322,6 +325,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
322325 self . access_lvalue ( ContextKind :: Drop . new ( loc) ,
323326 ( drop_lvalue, span) ,
324327 ( Deep , Write ( WriteKind :: StorageDeadOrDrop ) ) ,
328+ LocalMutationIsAllowed :: Yes ,
325329 flow_state) ;
326330 }
327331 TerminatorKind :: DropAndReplace { location : ref drop_lvalue,
@@ -391,6 +395,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
391395 ContextKind :: StorageDead . new ( loc) ,
392396 ( & root_lvalue, self . mir . source_info ( borrow. location ) . span ) ,
393397 ( Deep , Write ( WriteKind :: StorageDeadOrDrop ) ) ,
398+ LocalMutationIsAllowed :: Yes ,
394399 flow_state
395400 ) ;
396401 }
@@ -399,6 +404,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
399404 ContextKind :: StorageDead . new ( loc) ,
400405 ( & root_lvalue, self . mir . source_info ( borrow. location ) . span ) ,
401406 ( Shallow ( None ) , Write ( WriteKind :: StorageDeadOrDrop ) ) ,
407+ LocalMutationIsAllowed :: Yes ,
402408 flow_state
403409 ) ;
404410 }
@@ -445,6 +451,8 @@ enum ShallowOrDeep {
445451 Deep ,
446452}
447453
454+ /// Kind of access to a value: read or write
455+ /// (For informational purposes only)
448456#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
449457enum ReadOrWrite {
450458 /// From the RFC: "A *read* means that the existing data may be
@@ -457,12 +465,16 @@ enum ReadOrWrite {
457465 Write ( WriteKind ) ,
458466}
459467
468+ /// Kind of read access to a value
469+ /// (For informational purposes only)
460470#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
461471enum ReadKind {
462472 Borrow ( BorrowKind ) ,
463473 Copy ,
464474}
465475
476+ /// Kind of write access to a value
477+ /// (For informational purposes only)
466478#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
467479enum WriteKind {
468480 StorageDeadOrDrop ,
@@ -471,6 +483,20 @@ enum WriteKind {
471483 Move ,
472484}
473485
486+ /// When checking permissions for an lvalue access, this flag is used to indicate that an immutable
487+ /// local lvalue can be mutated.
488+ ///
489+ /// FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications:
490+ /// - Merge `check_access_permissions()` and `check_if_reassignment_to_immutable_state()`
491+ /// - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and
492+ /// `is_declared_mutable()`
493+ /// - Take flow state into consideration in `is_assignable()` for local variables
494+ #[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
495+ enum LocalMutationIsAllowed {
496+ Yes ,
497+ No
498+ }
499+
474500#[ derive( Copy , Clone ) ]
475501enum InitializationRequiringAction {
476502 Update ,
@@ -510,6 +536,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
510536 context : Context ,
511537 lvalue_span : ( & Lvalue < ' tcx > , Span ) ,
512538 kind : ( ShallowOrDeep , ReadOrWrite ) ,
539+ is_local_mutation_allowed : LocalMutationIsAllowed ,
513540 flow_state : & InProgress < ' cx , ' gcx , ' tcx > ) {
514541 let ( sd, rw) = kind;
515542
@@ -526,9 +553,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
526553 }
527554
528555 // Check permissions
529- self . check_access_permissions ( lvalue_span, rw) ;
556+ let mut error_reported = self . check_access_permissions ( lvalue_span,
557+ rw,
558+ is_local_mutation_allowed) ;
530559
531- let mut error_reported = false ;
532560 self . each_borrow_involving_path (
533561 context, ( sd, lvalue_span. 0 ) , flow_state, |this, _index, borrow, common_prefix| {
534562 match ( rw, borrow. kind ) {
@@ -614,7 +642,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
614642 }
615643 }
616644
617- self . access_lvalue ( context, lvalue_span, ( kind, Write ( WriteKind :: Mutate ) ) , flow_state) ;
645+ self . access_lvalue ( context,
646+ lvalue_span,
647+ ( kind, Write ( WriteKind :: Mutate ) ) ,
648+ LocalMutationIsAllowed :: Yes ,
649+ flow_state) ;
618650
619651 // check for reassignments to immutable local variables
620652 self . check_if_reassignment_to_immutable_state ( context, lvalue_span, flow_state) ;
@@ -632,7 +664,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
632664 BorrowKind :: Unique |
633665 BorrowKind :: Mut => ( Deep , Write ( WriteKind :: MutableBorrow ( bk) ) ) ,
634666 } ;
635- self . access_lvalue ( context, ( lvalue, span) , access_kind, flow_state) ;
667+ self . access_lvalue ( context,
668+ ( lvalue, span) ,
669+ access_kind,
670+ LocalMutationIsAllowed :: No ,
671+ flow_state) ;
636672 self . check_if_path_is_moved ( context, InitializationRequiringAction :: Borrow ,
637673 ( lvalue, span) , flow_state) ;
638674 }
@@ -651,8 +687,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
651687 Rvalue :: Discriminant ( ..) => ArtificialField :: Discriminant ,
652688 _ => unreachable ! ( ) ,
653689 } ;
654- self . access_lvalue (
655- context, ( lvalue, span) , ( Shallow ( Some ( af) ) , Read ( ReadKind :: Copy ) ) , flow_state) ;
690+ self . access_lvalue ( context,
691+ ( lvalue, span) ,
692+ ( Shallow ( Some ( af) ) , Read ( ReadKind :: Copy ) ) ,
693+ LocalMutationIsAllowed :: No ,
694+ flow_state) ;
656695 self . check_if_path_is_moved ( context, InitializationRequiringAction :: Use ,
657696 ( lvalue, span) , flow_state) ;
658697 }
@@ -690,6 +729,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
690729 self . access_lvalue ( context,
691730 ( lvalue, span) ,
692731 ( Deep , Read ( ReadKind :: Copy ) ) ,
732+ LocalMutationIsAllowed :: No ,
693733 flow_state) ;
694734
695735 // Finally, check if path was already moved.
@@ -701,6 +741,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
701741 self . access_lvalue ( context,
702742 ( lvalue, span) ,
703743 ( Deep , Write ( WriteKind :: Move ) ) ,
744+ LocalMutationIsAllowed :: Yes ,
704745 flow_state) ;
705746
706747 // Finally, check if path was already moved.
@@ -735,9 +776,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
735776 }
736777 Lvalue :: Static ( ref static_) => {
737778 // mutation of non-mut static is always illegal,
738- // independent of dataflow.
779+ // independent of dataflow. However it will be catched by
780+ // `check_access_permissions()`, we call delay_span_bug here
781+ // to be sure that no case has been missed
739782 if !self . tcx . is_static_mut ( static_. def_id ) {
740- self . report_assignment_to_static ( context, ( lvalue, span) ) ;
783+ let item_msg = match self . describe_lvalue ( lvalue) {
784+ Some ( name) => format ! ( "immutable static item `{}`" , name) ,
785+ None => "immutable static item" . to_owned ( )
786+ } ;
787+ self . tcx . sess . delay_span_bug ( span,
788+ & format ! ( "cannot assign to {}, should have been caught by \
789+ `check_access_permissions()`", item_msg) ) ;
741790 }
742791 return ;
743792 }
@@ -949,41 +998,101 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
949998 }
950999
9511000 /// Check the permissions for the given lvalue and read or write kind
952- fn check_access_permissions ( & self , ( lvalue, span) : ( & Lvalue < ' tcx > , Span ) , kind : ReadOrWrite ) {
1001+ ///
1002+ /// Returns true if an error is reported, false otherwise.
1003+ fn check_access_permissions ( & self ,
1004+ ( lvalue, span) : ( & Lvalue < ' tcx > , Span ) ,
1005+ kind : ReadOrWrite ,
1006+ is_local_mutation_allowed : LocalMutationIsAllowed )
1007+ -> bool {
1008+ debug ! ( "check_access_permissions({:?}, {:?}, {:?})" ,
1009+ lvalue, kind, is_local_mutation_allowed) ;
1010+ let mut error_reported = false ;
9531011 match kind {
9541012 Write ( WriteKind :: MutableBorrow ( BorrowKind :: Unique ) ) => {
9551013 if let Err ( _lvalue_err) = self . is_unique ( lvalue) {
956- span_bug ! ( span, "&unique borrow for `{}` should not fail" ,
957- self . describe_lvalue( lvalue) ) ;
1014+ span_bug ! ( span, "&unique borrow for {:?} should not fail" , lvalue) ;
9581015 }
9591016 } ,
9601017 Write ( WriteKind :: MutableBorrow ( BorrowKind :: Mut ) ) => {
961- if let Err ( lvalue_err) = self . is_mutable ( lvalue) {
1018+ if let Err ( lvalue_err) = self . is_mutable ( lvalue, is_local_mutation_allowed) {
1019+ error_reported = true ;
1020+
1021+ let item_msg = match self . describe_lvalue ( lvalue) {
1022+ Some ( name) => format ! ( "immutable item `{}`" , name) ,
1023+ None => "immutable item" . to_owned ( )
1024+ } ;
1025+
9621026 let mut err = self . tcx . cannot_borrow_path_as_mutable ( span,
963- & format ! ( "immutable item `{}`" ,
964- self . describe_lvalue( lvalue) ) ,
1027+ & item_msg,
9651028 Origin :: Mir ) ;
9661029 err. span_label ( span, "cannot borrow as mutable" ) ;
9671030
9681031 if lvalue != lvalue_err {
969- err. note ( & format ! ( "Value not mutable causing this error: `{}`" ,
970- self . describe_lvalue( lvalue_err) ) ) ;
1032+ if let Some ( name) = self . describe_lvalue ( lvalue_err) {
1033+ err. note ( & format ! ( "Value not mutable causing this error: `{}`" , name) ) ;
1034+ }
1035+ }
1036+
1037+ err. emit ( ) ;
1038+ }
1039+ } ,
1040+ Write ( WriteKind :: Mutate ) => {
1041+ if let Err ( lvalue_err) = self . is_mutable ( lvalue, is_local_mutation_allowed) {
1042+ error_reported = true ;
1043+
1044+ let item_msg = match self . describe_lvalue ( lvalue) {
1045+ Some ( name) => format ! ( "immutable item `{}`" , name) ,
1046+ None => "immutable item" . to_owned ( )
1047+ } ;
1048+
1049+ let mut err = self . tcx . cannot_assign ( span,
1050+ & item_msg,
1051+ Origin :: Mir ) ;
1052+ err. span_label ( span, "cannot mutate" ) ;
1053+
1054+ if lvalue != lvalue_err {
1055+ if let Some ( name) = self . describe_lvalue ( lvalue_err) {
1056+ err. note ( & format ! ( "Value not mutable causing this error: `{}`" , name) ) ;
1057+ }
9711058 }
9721059
9731060 err. emit ( ) ;
9741061 }
9751062 } ,
976- _ => { } // Access authorized
1063+ Write ( WriteKind :: Move ) |
1064+ Write ( WriteKind :: StorageDeadOrDrop ) |
1065+ Write ( WriteKind :: MutableBorrow ( BorrowKind :: Shared ) ) => {
1066+ if let Err ( _lvalue_err) = self . is_mutable ( lvalue, is_local_mutation_allowed) {
1067+ self . tcx . sess . delay_span_bug ( span,
1068+ & format ! ( "Accessing `{:?}` with the kind `{:?}` shouldn't be possible" ,
1069+ lvalue,
1070+ kind) ) ;
1071+ }
1072+ } ,
1073+ Read ( ReadKind :: Borrow ( BorrowKind :: Unique ) ) |
1074+ Read ( ReadKind :: Borrow ( BorrowKind :: Mut ) ) |
1075+ Read ( ReadKind :: Borrow ( BorrowKind :: Shared ) ) |
1076+ Read ( ReadKind :: Copy ) => { } // Access authorized
9771077 }
1078+
1079+ error_reported
9781080 }
9791081
9801082 /// Can this value be written or borrowed mutably
981- fn is_mutable < ' d > ( & self , lvalue : & ' d Lvalue < ' tcx > ) -> Result < ( ) , & ' d Lvalue < ' tcx > > {
1083+ fn is_mutable < ' d > ( & self ,
1084+ lvalue : & ' d Lvalue < ' tcx > ,
1085+ is_local_mutation_allowed : LocalMutationIsAllowed )
1086+ -> Result < ( ) , & ' d Lvalue < ' tcx > > {
9821087 match * lvalue {
9831088 Lvalue :: Local ( local) => {
9841089 let local = & self . mir . local_decls [ local] ;
9851090 match local. mutability {
986- Mutability :: Not => Err ( lvalue) ,
1091+ Mutability :: Not =>
1092+ match is_local_mutation_allowed {
1093+ LocalMutationIsAllowed :: Yes => Ok ( ( ) ) ,
1094+ LocalMutationIsAllowed :: No => Err ( lvalue) ,
1095+ } ,
9871096 Mutability :: Mut => Ok ( ( ) )
9881097 }
9891098 } ,
@@ -1001,7 +1110,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10011110
10021111 // `Box<T>` owns its content, so mutable if its location is mutable
10031112 if base_ty. is_box ( ) {
1004- return self . is_mutable ( & proj. base ) ;
1113+ return self . is_mutable ( & proj. base , LocalMutationIsAllowed :: No ) ;
10051114 }
10061115
10071116 // Otherwise we check the kind of deref to decide
@@ -1035,7 +1144,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10351144 ProjectionElem :: ConstantIndex { ..} |
10361145 ProjectionElem :: Subslice { ..} |
10371146 ProjectionElem :: Downcast ( ..) =>
1038- self . is_mutable ( & proj. base )
1147+ self . is_mutable ( & proj. base , LocalMutationIsAllowed :: No )
10391148 }
10401149 }
10411150 }
@@ -1604,14 +1713,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
16041713 }
16051714 err. emit ( ) ;
16061715 }
1607-
1608- fn report_assignment_to_static ( & mut self ,
1609- _context : Context ,
1610- ( lvalue, span) : ( & Lvalue < ' tcx > , Span ) ) {
1611- let mut err = self . tcx . cannot_assign_static (
1612- span, & self . describe_lvalue ( lvalue) , Origin :: Mir ) ;
1613- err. emit ( ) ;
1614- }
16151716}
16161717
16171718impl < ' cx , ' gcx , ' tcx > MirBorrowckCtxt < ' cx , ' gcx , ' tcx > {
0 commit comments