Skip to content

Commit b27cbda

Browse files
committed
make is_normalizable more strict
1 parent d02ca3b commit b27cbda

File tree

2 files changed

+62
-3
lines changed

2 files changed

+62
-3
lines changed

clippy_utils/src/lib.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ use rustc_trait_selection::traits::query::normalize::AtExt;
8585
use smallvec::SmallVec;
8686

8787
use crate::consts::{constant, Constant};
88+
use std::collections::HashMap;
8889

8990
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
9091
if let Ok(version) = RustcVersion::parse(msrv) {
@@ -1488,10 +1489,45 @@ pub fn match_function_call<'tcx>(
14881489
/// Checks if `Ty` is normalizable. This function is useful
14891490
/// to avoid crashes on `layout_of`.
14901491
pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
1491-
cx.tcx.infer_ctxt().enter(|infcx| {
1492+
is_normalizable_helper(cx, param_env, ty, &mut HashMap::new())
1493+
}
1494+
1495+
fn is_normalizable_helper<'tcx>(
1496+
cx: &LateContext<'tcx>,
1497+
param_env: ty::ParamEnv<'tcx>,
1498+
ty: Ty<'tcx>,
1499+
cache: &mut HashMap<Ty<'tcx>, bool>,
1500+
) -> bool {
1501+
if let Some(&cached_result) = cache.get(ty) {
1502+
return cached_result;
1503+
}
1504+
cache.insert(ty, false); // prevent recursive loops
1505+
let result = cx.tcx.infer_ctxt().enter(|infcx| {
14921506
let cause = rustc_middle::traits::ObligationCause::dummy();
1493-
infcx.at(&cause, param_env).normalize(ty).is_ok()
1494-
})
1507+
if infcx.at(&cause, param_env).normalize(ty).is_err() {
1508+
false
1509+
} else {
1510+
match ty.kind() {
1511+
ty::Adt(def, substs) => !def.variants.iter().any(|variant| {
1512+
variant
1513+
.fields
1514+
.iter()
1515+
.any(|field| !is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
1516+
}),
1517+
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
1518+
is_normalizable_helper(cx, param_env, pointee, cache)
1519+
},
1520+
ty::Array(inner_ty, _) | ty::Slice(inner_ty) => is_normalizable_helper(cx, param_env, inner_ty, cache),
1521+
ty::Tuple(tys) => !tys.iter().any(|inner| match inner.unpack() {
1522+
GenericArgKind::Type(inner_ty) => !is_normalizable_helper(cx, param_env, inner_ty, cache),
1523+
_ => false,
1524+
}),
1525+
_ => true,
1526+
}
1527+
}
1528+
});
1529+
cache.insert(ty, result);
1530+
result
14951531
}
14961532

14971533
pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {

tests/ui/crashes/ice-6840.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//! This is a reproducer for the ICE 6840: https://github.com/rust-lang/rust-clippy/issues/6840.
2+
//! The ICE is caused by `TyCtxt::layout_of` and `is_normalizable` not being strict enough
3+
#![allow(dead_code)]
4+
use std::collections::HashMap;
5+
6+
pub trait Rule {
7+
type DependencyKey;
8+
}
9+
10+
pub struct RuleEdges<R: Rule> {
11+
dependencies: R::DependencyKey,
12+
}
13+
14+
type RuleDependencyEdges<R> = HashMap<u32, RuleEdges<R>>;
15+
16+
// and additional potential variants
17+
type RuleDependencyEdgesArray<R> = HashMap<u32, [RuleEdges<R>; 8]>;
18+
type RuleDependencyEdgesSlice<R> = HashMap<u32, &'static [RuleEdges<R>]>;
19+
type RuleDependencyEdgesRef<R> = HashMap<u32, &'static RuleEdges<R>>;
20+
type RuleDependencyEdgesRaw<R> = HashMap<u32, *const RuleEdges<R>>;
21+
type RuleDependencyEdgesTuple<R> = HashMap<u32, (RuleEdges<R>, RuleEdges<R>)>;
22+
23+
fn main() {}

0 commit comments

Comments
 (0)