Skip to content

Commit da93448

Browse files
authored
Do not recurse indefinitely while checking for inner mutability (#14965)
`clippy_utils::ty::InteriorMut::interior_mut_ty_chain` must stop recursing forever when types are chained indefinitely due to the use of associated types in generics. A false negative is acceptable, and documented here. Should this situation be later identified specifically, a conversion of `Option` to `Result` would allow separating the infinitely recursive case from a negative one. changelog: [`mutable_key_type`]: fix ICE when infinitely associated generic types are used Fixes rust-lang/rust-clippy#14935
2 parents b379d54 + 1ae37ad commit da93448

File tree

2 files changed

+55
-9
lines changed

2 files changed

+55
-9
lines changed

clippy_utils/src/ty/mod.rs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,21 +1137,38 @@ impl<'tcx> InteriorMut<'tcx> {
11371137

11381138
/// Check if given type has interior mutability such as [`std::cell::Cell`] or
11391139
/// [`std::cell::RefCell`] etc. and if it does, returns a chain of types that causes
1140-
/// this type to be interior mutable
1140+
/// this type to be interior mutable. False negatives may be expected for infinitely recursive
1141+
/// types, and `None` will be returned there.
11411142
pub fn interior_mut_ty_chain(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx ty::List<Ty<'tcx>>> {
1143+
self.interior_mut_ty_chain_inner(cx, ty, 0)
1144+
}
1145+
1146+
fn interior_mut_ty_chain_inner(
1147+
&mut self,
1148+
cx: &LateContext<'tcx>,
1149+
ty: Ty<'tcx>,
1150+
depth: usize,
1151+
) -> Option<&'tcx ty::List<Ty<'tcx>>> {
1152+
if !cx.tcx.recursion_limit().value_within_limit(depth) {
1153+
return None;
1154+
}
1155+
11421156
match self.tys.entry(ty) {
11431157
Entry::Occupied(o) => return *o.get(),
11441158
// Temporarily insert a `None` to break cycles
11451159
Entry::Vacant(v) => v.insert(None),
11461160
};
1161+
let depth = depth + 1;
11471162

11481163
let chain = match *ty.kind() {
1149-
ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.interior_mut_ty_chain(cx, inner_ty),
1150-
ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.interior_mut_ty_chain(cx, inner_ty),
1164+
ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.interior_mut_ty_chain_inner(cx, inner_ty, depth),
1165+
ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.interior_mut_ty_chain_inner(cx, inner_ty, depth),
11511166
ty::Array(inner_ty, size) if size.try_to_target_usize(cx.tcx) != Some(0) => {
1152-
self.interior_mut_ty_chain(cx, inner_ty)
1167+
self.interior_mut_ty_chain_inner(cx, inner_ty, depth)
11531168
},
1154-
ty::Tuple(fields) => fields.iter().find_map(|ty| self.interior_mut_ty_chain(cx, ty)),
1169+
ty::Tuple(fields) => fields
1170+
.iter()
1171+
.find_map(|ty| self.interior_mut_ty_chain_inner(cx, ty, depth)),
11551172
ty::Adt(def, _) if def.is_unsafe_cell() => Some(ty::List::empty()),
11561173
ty::Adt(def, args) => {
11571174
let is_std_collection = matches!(
@@ -1171,16 +1188,17 @@ impl<'tcx> InteriorMut<'tcx> {
11711188

11721189
if is_std_collection || def.is_box() {
11731190
// Include the types from std collections that are behind pointers internally
1174-
args.types().find_map(|ty| self.interior_mut_ty_chain(cx, ty))
1191+
args.types()
1192+
.find_map(|ty| self.interior_mut_ty_chain_inner(cx, ty, depth))
11751193
} else if self.ignored_def_ids.contains(&def.did()) || def.is_phantom_data() {
11761194
None
11771195
} else {
11781196
def.all_fields()
1179-
.find_map(|f| self.interior_mut_ty_chain(cx, f.ty(cx.tcx, args)))
1197+
.find_map(|f| self.interior_mut_ty_chain_inner(cx, f.ty(cx.tcx, args), depth))
11801198
}
11811199
},
11821200
ty::Alias(ty::Projection, _) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) {
1183-
Ok(normalized_ty) if ty != normalized_ty => self.interior_mut_ty_chain(cx, normalized_ty),
1201+
Ok(normalized_ty) if ty != normalized_ty => self.interior_mut_ty_chain_inner(cx, normalized_ty, depth),
11841202
_ => None,
11851203
},
11861204
_ => None,
@@ -1342,7 +1360,8 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'
13421360
mutability.is_mut() || !pointee_ty.is_freeze(cx.tcx, cx.typing_env())
13431361
},
13441362
ty::Closure(_, closure_args) => {
1345-
matches!(closure_args.types().next_back(), Some(captures) if has_non_owning_mutable_access_inner(cx, phantoms, captures))
1363+
matches!(closure_args.types().next_back(),
1364+
Some(captures) if has_non_owning_mutable_access_inner(cx, phantoms, captures))
13461365
},
13471366
ty::Tuple(tuple_args) => tuple_args
13481367
.iter()

tests/ui/crashes/ice-14935.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//@check-pass
2+
#![warn(clippy::mutable_key_type)]
3+
4+
use std::marker::PhantomData;
5+
6+
trait Group {
7+
type ExposantSet: Group;
8+
}
9+
10+
struct Pow<T: Group> {
11+
exposant: Box<Pow<T::ExposantSet>>,
12+
_p: PhantomData<T>,
13+
}
14+
15+
impl<T: Group> Pow<T> {
16+
fn is_zero(&self) -> bool {
17+
false
18+
}
19+
fn normalize(&self) {
20+
#[expect(clippy::if_same_then_else)]
21+
if self.is_zero() {
22+
} else if false {
23+
}
24+
}
25+
}
26+
27+
fn main() {}

0 commit comments

Comments
 (0)