@@ -893,6 +893,98 @@ test_verify_one_file! {
893893 } => Ok ( ( ) )
894894}
895895
896+ // verus-lang/verus#2674: a fresh range's end bound is still Included, but an
897+ // exhausted one is not - both directions in one file (one expected failure).
898+ test_verify_one_file ! {
899+ #[ test] test_range_inclusive_end_bound_exhausted verus_code! {
900+ use vstd:: prelude:: * ;
901+ use std:: ops:: { Bound , RangeBounds } ;
902+
903+ fn fresh( ) {
904+ let r = 1u8 ..=5u8 ;
905+ let end_is_included = match r. end_bound( ) {
906+ Bound :: Included ( _) => true ,
907+ _ => false ,
908+ } ;
909+ assert( end_is_included) ;
910+ }
911+
912+ fn exhausted( ) {
913+ let mut r = 1u8 ..=1u8 ;
914+ let _ = r. next( ) ;
915+ let end_is_included = match r. end_bound( ) {
916+ Bound :: Included ( _) => true ,
917+ _ => false ,
918+ } ;
919+ assert( end_is_included) ; // FAILS
920+ }
921+ } => Err ( err) => assert_one_fails( err)
922+ }
923+
924+ // Same fact one level down: `slice_range_end` (used by `copy_within`) treats
925+ // an exhausted range's end as excluded, not included.
926+ test_verify_one_file ! {
927+ #[ test] test_slice_range_end_treats_exhausted_range_as_excluded verus_code! {
928+ use std:: ops:: RangeInclusive ;
929+ use vstd:: prelude:: * ;
930+ use vstd:: std_specs:: range:: { slice_range_end, RangeInclusiveView } ;
931+
932+ proof fn correct( r: RangeInclusive <usize >)
933+ requires
934+ r@ == ( RangeInclusiveView { start: 2 , end: 2 , exhausted: true } ) ,
935+ {
936+ assert( slice_range_end( & r, 5 ) == 2 ) ;
937+ }
938+
939+ proof fn rejects_included( r: RangeInclusive <usize >)
940+ requires
941+ r@ == ( RangeInclusiveView { start: 2 , end: 2 , exhausted: true } ) ,
942+ {
943+ assert( slice_range_end( & r, 5 ) == 3 ) ; // FAILS
944+ }
945+ } => Err ( err) => assert_one_fails( err)
946+ }
947+
948+ // `is_empty()`: not empty fresh, empty once exhausted - even though
949+ // `start <= end` alone (1 <= 1) would wrongly say otherwise.
950+ test_verify_one_file ! {
951+ #[ test] test_range_inclusive_is_empty verus_code! {
952+ use vstd:: prelude:: * ;
953+
954+ fn fresh_is_not_empty( ) {
955+ let r = 1u8 ..=5u8 ;
956+ let empty = r. is_empty( ) ;
957+ assert( !empty) ;
958+ }
959+
960+ fn exhausted_is_empty( ) {
961+ let mut r = 1u8 ..=1u8 ;
962+ let _ = r. next( ) ;
963+ let empty = r. is_empty( ) ;
964+ assert( empty) ;
965+ assert( !empty) ; // FAILS: exhausted despite start <= end
966+ }
967+ } => Err ( err) => assert_one_fails( err)
968+ }
969+
970+ test_verify_one_file ! {
971+ #[ test] test_spec_range_inclusive_is_empty_inverted_range verus_code! {
972+ use std:: ops:: RangeInclusive ;
973+ use vstd:: prelude:: * ;
974+ use vstd:: std_specs:: range:: { spec_range_inclusive_is_empty, RangeInclusiveView } ;
975+
976+ // start > end (never valid, never exhausted) is empty too - not
977+ // exercised by test_range_inclusive_is_empty above, which only
978+ // reaches emptiness via a real .next() call.
979+ proof fn test_inverted( r: RangeInclusive <u8 >)
980+ requires
981+ r@ == ( RangeInclusiveView { start: 5u8 , end: 1u8 , exhausted: false } ) ,
982+ {
983+ assert( spec_range_inclusive_is_empty( & r) ) ;
984+ }
985+ } => Ok ( ( ) )
986+ }
987+
896988test_verify_one_file ! {
897989 #[ test] test_split_at_checked verus_code! {
898990 use vstd:: prelude:: * ;
0 commit comments