@@ -110,7 +110,7 @@ impl LocationState {
110
110
111
111
// Helper to optimize the tree traversal.
112
112
// 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`,
114
114
// that there are actually just three possible sequences of events that can occur
115
115
// in between two child accesses that produce different results.
116
116
//
@@ -139,7 +139,7 @@ impl LocationState {
139
139
let new_access_noop = match ( self . latest_foreign_access , access_kind) {
140
140
// Previously applied transition makes the new one a guaranteed
141
141
// 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 `
143
143
( Some ( AccessKind :: Write ) , AccessKind :: Read ) => true ,
144
144
// (2) justified by `all_transitions_idempotent`
145
145
( Some ( old) , new) if old == new => true ,
@@ -670,7 +670,8 @@ impl AccessRelatedness {
670
670
mod commutation_tests {
671
671
use super :: * ;
672
672
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.
674
675
Permission :: all ( ) . flat_map ( |permission| {
675
676
[ false , true ] . into_iter ( ) . map ( move |initialized| {
676
677
Self { permission, initialized, latest_foreign_access : None }
@@ -695,12 +696,12 @@ mod commutation_tests {
695
696
// Any protector state works, but we can't move reads across function boundaries
696
697
// so the two read accesses occur under the same protector.
697
698
for & protected in & [ true , false ] {
698
- for loc in LocationState :: all_without_access ( ) {
699
+ for loc in LocationState :: all ( ) {
699
700
// Apply 1 then 2. Failure here means that there is UB in the source
700
701
// and we skip the check in the target.
701
702
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 } ;
704
705
705
706
// If 1 followed by 2 succeeded, then 2 followed by 1 must also succeed...
706
707
let mut loc21 = loc;
@@ -718,4 +719,33 @@ mod commutation_tests {
718
719
}
719
720
}
720
721
}
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
+ }
721
751
}
0 commit comments