@@ -110,7 +110,7 @@ impl LocationState {
110110
111111 // Helper to optimize the tree traversal.
112112 // The optimization here consists of observing thanks to the tests
113- // `foreign_read_is_noop_after_write ` and `all_transitions_idempotent`,
113+ // `foreign_read_is_noop_after_foreign_write ` and `all_transitions_idempotent`,
114114 // that there are actually just three possible sequences of events that can occur
115115 // in between two child accesses that produce different results.
116116 //
@@ -139,7 +139,7 @@ impl LocationState {
139139 let new_access_noop = match ( self . latest_foreign_access , access_kind) {
140140 // Previously applied transition makes the new one a guaranteed
141141 // noop in the two following cases:
142- // (1) justified by `foreign_read_is_noop_after_write `
142+ // (1) justified by `foreign_read_is_noop_after_foreign_write `
143143 ( Some ( AccessKind :: Write ) , AccessKind :: Read ) => true ,
144144 // (2) justified by `all_transitions_idempotent`
145145 ( Some ( old) , new) if old == new => true ,
@@ -670,7 +670,8 @@ impl AccessRelatedness {
670670mod commutation_tests {
671671 use super :: * ;
672672 impl LocationState {
673- pub fn all_without_access ( ) -> impl Iterator < Item = Self > {
673+ pub fn all ( ) -> impl Iterator < Item = Self > {
674+ // We keep `latest_foreign_access` at `None` as that's just a cache.
674675 Permission :: all ( ) . flat_map ( |permission| {
675676 [ false , true ] . into_iter ( ) . map ( move |initialized| {
676677 Self { permission, initialized, latest_foreign_access : None }
@@ -695,12 +696,12 @@ mod commutation_tests {
695696 // Any protector state works, but we can't move reads across function boundaries
696697 // so the two read accesses occur under the same protector.
697698 for & protected in & [ true , false ] {
698- for loc in LocationState :: all_without_access ( ) {
699+ for loc in LocationState :: all ( ) {
699700 // Apply 1 then 2. Failure here means that there is UB in the source
700701 // and we skip the check in the target.
701702 let mut loc12 = loc;
702- let Ok ( _) = loc12. perform_access ( kind, rel1, protected) else { continue ; } ;
703- let Ok ( _) = loc12. perform_access ( kind, rel2, protected) else { continue ; } ;
703+ let Ok ( _) = loc12. perform_access ( kind, rel1, protected) else { continue } ;
704+ let Ok ( _) = loc12. perform_access ( kind, rel2, protected) else { continue } ;
704705
705706 // If 1 followed by 2 succeeded, then 2 followed by 1 must also succeed...
706707 let mut loc21 = loc;
@@ -718,4 +719,33 @@ mod commutation_tests {
718719 }
719720 }
720721 }
722+
723+ #[ test]
724+ #[ rustfmt:: skip]
725+ // Ensure that of 2 accesses happen, one foreign and one a child, and we are protected, that we
726+ // get UB unless they are both reads.
727+ fn protected_enforces_noalias ( ) {
728+ for rel1 in AccessRelatedness :: all ( ) {
729+ for rel2 in AccessRelatedness :: all ( ) {
730+ if rel1. is_foreign ( ) == rel2. is_foreign ( ) {
731+ // We want to check pairs of accesses where one is foreign and one is not.
732+ continue ;
733+ }
734+ for kind1 in AccessKind :: all ( ) {
735+ for kind2 in AccessKind :: all ( ) {
736+ for mut state in LocationState :: all ( ) {
737+ let protected = true ;
738+ let Ok ( _) = state. perform_access ( kind1, rel1, protected) else { continue } ;
739+ let Ok ( _) = state. perform_access ( kind2, rel2, protected) else { continue } ;
740+ // If these were both allowed, it must have been two reads.
741+ assert ! (
742+ kind1 == AccessKind :: Read && kind2 == AccessKind :: Read ,
743+ "failed to enforce noalias between two accesses that are not both reads"
744+ ) ;
745+ }
746+ }
747+ }
748+ }
749+ }
750+ }
721751}
0 commit comments