@@ -19,38 +19,61 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, name_s
1919 let index_mut_trait = cx. tcx . lang_items ( ) . index_mut_trait ( ) ;
2020 let impls_deref_mut = |ty| deref_mut_trait. is_some_and ( |trait_id| implements_trait ( cx, ty, trait_id, & [ ] ) ) ;
2121 let impls_index_mut = |ty, idx| index_mut_trait. is_some_and ( |trait_id| implements_trait ( cx, ty, trait_id, & [ idx] ) ) ;
22+
23+ // The mutex was accessed either directly (`mutex.lock()`), or through a series of
24+ // deref/field/indexing projections. Since the final `.lock()` call only requires `&Mutex`,
25+ // those might be immutable, and so we need to manually check whether mutable projections
26+ // would've been possible. For that, we'll repeatedly peel off projections and check each
27+ // intermediary receiver.
2228 let mut r = recv;
2329 loop {
30+ // Check that the (deref) adjustments could be made mutable
2431 if ( typeck. expr_adjustments ( r) )
2532 . iter ( )
2633 . map_while ( |a| match a. kind {
2734 Adjust :: Deref ( x) => Some ( ( a. target , x) ) ,
2835 _ => None ,
2936 } )
3037 . try_fold ( typeck. expr_ty ( r) , |ty, ( target, deref) | match deref {
38+ // There has been an overloaded deref, most likely an immutable one, as `.lock()` didn't require a
39+ // mutable one -- we need to check if a mutable deref would've been possible, i.e. if
40+ // `ty: DerefMut<Target = target>` (we don't need to check the `Target` part, as `Deref` and `DerefMut`
41+ // impls necessarily have the same one)
3142 Some ( _) => impls_deref_mut ( ty) . then_some ( target) ,
43+ // There has been a simple deref; if it happened on a `&T`, then we know it will can't be changed to
44+ // provide mutable access
3245 None => ( ty. ref_mutability ( ) != Some ( Mutability :: Not ) ) . then_some ( target) ,
3346 } )
3447 . is_none ( )
3548 {
3649 return ;
3750 }
51+
52+ // Peel off one projection
3853 match r. kind {
39- ExprKind :: Field ( base , _ ) => r = base ,
40- ExprKind :: Index ( base , idx , _ ) => {
41- if impls_index_mut ( typeck. expr_ty_adjusted ( base) , typeck . expr_ty_adjusted ( idx ) . into ( ) ) {
54+ // In order to be able to make this `*` mean `.deref_mut()`, `base: DerefMut` needs to hold
55+ ExprKind :: Unary ( UnOp :: Deref , base ) => {
56+ if impls_deref_mut ( typeck. expr_ty_adjusted ( base) ) {
4257 r = base;
4358 } else {
4459 return ;
4560 }
4661 } ,
47- ExprKind :: Unary ( UnOp :: Deref , base) => {
48- if impls_deref_mut ( typeck. expr_ty_adjusted ( base) ) {
62+ // In order to be able to make this `[idx]` mean `.index_mut(idx)`, `base: IndexMut<idx>` needs to hold
63+ ExprKind :: Index ( base, idx, _) => {
64+ // NOTE: the reason we do need to take into account `idx` here is that it's a _generic_ of
65+ // `IndexMut`, not an associated type of the impl
66+ if impls_index_mut ( typeck. expr_ty_adjusted ( base) , typeck. expr_ty_adjusted ( idx) . into ( ) ) {
4967 r = base;
5068 } else {
5169 return ;
5270 }
5371 } ,
72+ // A field projection by itself can't be mutable/immutable -- we'll only need to check
73+ // that the field type is not a `&T`, and we'll do that in the next iteration of the
74+ // loop, during adjustment checking
75+ ExprKind :: Field ( base, _) => r = base,
76+ // No more projections to check
5477 _ => break ,
5578 }
5679 }
0 commit comments