@@ -1377,33 +1377,48 @@ pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
1377
1377
}
1378
1378
}
1379
1379
1380
- /// Check if `ty` is an `Iterator` and has side effects when iterated over. Currently, this only
1381
- /// checks if the `ty` contains mutable captures, and thus may be imcomplete.
1382
- pub fn is_iter_with_side_effects < ' tcx > ( cx : & LateContext < ' tcx > , iter_ty : Ty < ' tcx > ) -> bool {
1383
- let Some ( iter_trait) = cx. tcx . lang_items ( ) . iterator_trait ( ) else {
1384
- return false ;
1385
- } ;
1386
-
1387
- is_iter_with_side_effects_impl ( cx, iter_ty, iter_trait)
1388
- }
1380
+ /// Check if a Ty<'_> of `Iterator` contains any mutable access to non-owning types by checking if
1381
+ /// it contains fields of mutable references or pointers, or references/pointers to non-`Freeze`
1382
+ /// types, or `PhantomData` types containing any of the previous. This can be used to check whether
1383
+ /// skipping iterating over an iterator will change its behavior.
1384
+ pub fn has_non_owning_mutable_access < ' tcx > ( cx : & LateContext < ' tcx > , iter_ty : Ty < ' tcx > ) -> bool {
1385
+ fn normalize_ty < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> Ty < ' tcx > {
1386
+ cx. tcx . try_normalize_erasing_regions ( cx. typing_env ( ) , ty) . unwrap_or ( ty)
1387
+ }
1389
1388
1390
- fn is_iter_with_side_effects_impl < ' tcx > ( cx : & LateContext < ' tcx > , iter_ty : Ty < ' tcx > , iter_trait : DefId ) -> bool {
1391
- if implements_trait ( cx, iter_ty, iter_trait, & [ ] )
1392
- && let ty:: Adt ( _, args) = iter_ty. kind ( )
1393
- {
1394
- return args. types ( ) . any ( |arg_ty| {
1395
- if let ty:: Closure ( _, closure_args) = arg_ty. kind ( )
1396
- && let Some ( captures) = closure_args. types ( ) . next_back ( )
1397
- {
1398
- captures
1399
- . tuple_fields ( )
1400
- . iter ( )
1401
- . any ( |capture_ty| matches ! ( capture_ty. ref_mutability( ) , Some ( Mutability :: Mut ) ) )
1402
- } else {
1403
- is_iter_with_side_effects_impl ( cx, arg_ty, iter_trait)
1404
- }
1405
- } ) ;
1389
+ /// Check if `ty` contains mutable references or equivalent, which includes:
1390
+ /// - A mutable reference/pointer.
1391
+ /// - A reference/pointer to a non-`Freeze` type.
1392
+ /// - A `PhantomData` type containing any of the previous.
1393
+ fn has_non_owning_mutable_access_inner < ' tcx > (
1394
+ cx : & LateContext < ' tcx > ,
1395
+ phantoms : & mut FxHashSet < Ty < ' tcx > > ,
1396
+ ty : Ty < ' tcx > ,
1397
+ ) -> bool {
1398
+ match ty. kind ( ) {
1399
+ ty:: Adt ( adt_def, args) if adt_def. is_phantom_data ( ) => {
1400
+ phantoms. insert ( ty)
1401
+ && args
1402
+ . types ( )
1403
+ . any ( |arg_ty| has_non_owning_mutable_access_inner ( cx, phantoms, arg_ty) )
1404
+ } ,
1405
+ ty:: Adt ( adt_def, args) => adt_def. all_fields ( ) . any ( |field| {
1406
+ has_non_owning_mutable_access_inner ( cx, phantoms, normalize_ty ( cx, field. ty ( cx. tcx , args) ) )
1407
+ } ) ,
1408
+ ty:: Array ( elem_ty, _) | ty:: Slice ( elem_ty) => has_non_owning_mutable_access_inner ( cx, phantoms, * elem_ty) ,
1409
+ ty:: RawPtr ( pointee_ty, mutability) | ty:: Ref ( _, pointee_ty, mutability) => {
1410
+ mutability. is_mut ( ) || !pointee_ty. is_freeze ( cx. tcx , cx. typing_env ( ) )
1411
+ } ,
1412
+ ty:: Closure ( _, closure_args) => {
1413
+ matches ! ( closure_args. types( ) . next_back( ) , Some ( captures) if has_non_owning_mutable_access_inner( cx, phantoms, captures) )
1414
+ } ,
1415
+ ty:: Tuple ( tuple_args) => tuple_args
1416
+ . iter ( )
1417
+ . any ( |arg_ty| has_non_owning_mutable_access_inner ( cx, phantoms, arg_ty) ) ,
1418
+ _ => false ,
1419
+ }
1406
1420
}
1407
1421
1408
- false
1422
+ let mut phantoms = FxHashSet :: default ( ) ;
1423
+ has_non_owning_mutable_access_inner ( cx, & mut phantoms, iter_ty)
1409
1424
}
0 commit comments