Skip to content

Commit 107e072

Browse files
committed
Remove is_normalizable. Add sizedness_of to avoid layout_of query on type aliases.
1 parent 95c62ff commit 107e072

File tree

4 files changed

+135
-64
lines changed

4 files changed

+135
-64
lines changed

clippy_lints/src/transmute/eager_transmute.rs

-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
2-
use clippy_utils::ty::is_normalizable;
32
use clippy_utils::{eq_expr_value, path_to_local};
43
use rustc_abi::WrappingRange;
54
use rustc_errors::Applicability;
@@ -84,8 +83,6 @@ pub(super) fn check<'tcx>(
8483
&& path.ident.name == sym!(then_some)
8584
&& is_local_with_projections(transmutable)
8685
&& binops_with_local(cx, transmutable, receiver)
87-
&& is_normalizable(cx, cx.param_env, from_ty)
88-
&& is_normalizable(cx, cx.param_env, to_ty)
8986
// we only want to lint if the target type has a niche that is larger than the one of the source type
9087
// e.g. `u8` to `NonZeroU8` should lint, but `NonZeroU8` to `u8` should not
9188
&& let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from_ty))

clippy_lints/src/zero_sized_map_values.rs

+4-11
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
use clippy_utils::diagnostics::span_lint_and_help;
2-
use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item};
2+
use clippy_utils::ty::{is_type_diagnostic_item, sizedness_of};
33
use rustc_hir::{self as hir, HirId, ItemKind, Node};
44
use rustc_hir_analysis::hir_ty_to_ty;
55
use rustc_lint::{LateContext, LateLintPass};
6-
use rustc_middle::ty::layout::LayoutOf as _;
7-
use rustc_middle::ty::{Adt, Ty, TypeVisitableExt};
6+
use rustc_middle::ty::{self, Ty};
87
use rustc_session::declare_lint_pass;
98
use rustc_span::sym;
109

@@ -49,15 +48,9 @@ impl LateLintPass<'_> for ZeroSizedMapValues {
4948
&& !in_trait_impl(cx, hir_ty.hir_id)
5049
&& let ty = ty_from_hir_ty(cx, hir_ty)
5150
&& (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap))
52-
&& let Adt(_, args) = ty.kind()
51+
&& let ty::Adt(_, args) = ty.kind()
5352
&& let ty = args.type_at(1)
54-
// Fixes https://github.com/rust-lang/rust-clippy/issues/7447 because of
55-
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/sty.rs#L968
56-
&& !ty.has_escaping_bound_vars()
57-
// Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`.
58-
&& is_normalizable(cx, cx.param_env, ty)
59-
&& let Ok(layout) = cx.layout_of(ty)
60-
&& layout.is_zst()
53+
&& sizedness_of(cx.tcx, cx.param_env, ty).is_zero()
6154
{
6255
span_lint_and_help(
6356
cx,

clippy_utils/src/ty.rs

+112-50
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use core::ops::ControlFlow;
66
use itertools::Itertools;
77
use rustc_ast::ast::Mutability;
8-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
8+
use rustc_data_structures::fx::FxHashSet;
99
use rustc_hir as hir;
1010
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
1111
use rustc_hir::def_id::DefId;
@@ -16,7 +16,7 @@ use rustc_lint::LateContext;
1616
use rustc_middle::mir::interpret::Scalar;
1717
use rustc_middle::mir::ConstValue;
1818
use rustc_middle::traits::EvaluationResult;
19-
use rustc_middle::ty::layout::ValidityRequirement;
19+
use rustc_middle::ty::layout::{LayoutOf, ValidityRequirement};
2020
use rustc_middle::ty::{
2121
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
2222
GenericParamDefKind, IntTy, List, ParamEnv, Region, RegionKind, ToPredicate, TraitRef, Ty, TyCtxt,
@@ -361,50 +361,6 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
361361
}
362362
}
363363

364-
// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
365-
// this function can be removed once the `normalize` method does not panic when normalization does
366-
// not succeed
367-
/// Checks if `Ty` is normalizable. This function is useful
368-
/// to avoid crashes on `layout_of`.
369-
pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
370-
is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
371-
}
372-
373-
fn is_normalizable_helper<'tcx>(
374-
cx: &LateContext<'tcx>,
375-
param_env: ParamEnv<'tcx>,
376-
ty: Ty<'tcx>,
377-
cache: &mut FxHashMap<Ty<'tcx>, bool>,
378-
) -> bool {
379-
if let Some(&cached_result) = cache.get(&ty) {
380-
return cached_result;
381-
}
382-
// prevent recursive loops, false-negative is better than endless loop leading to stack overflow
383-
cache.insert(ty, false);
384-
let infcx = cx.tcx.infer_ctxt().build();
385-
let cause = ObligationCause::dummy();
386-
let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() {
387-
match ty.kind() {
388-
ty::Adt(def, args) => def.variants().iter().all(|variant| {
389-
variant
390-
.fields
391-
.iter()
392-
.all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, args), cache))
393-
}),
394-
_ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
395-
GenericArgKind::Type(inner_ty) if inner_ty != ty => {
396-
is_normalizable_helper(cx, param_env, inner_ty, cache)
397-
},
398-
_ => true, // if inner_ty == ty, we've already checked it
399-
}),
400-
}
401-
} else {
402-
false
403-
};
404-
cache.insert(ty, result);
405-
result
406-
}
407-
408364
/// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any
409365
/// integer or floating-point number type). For checking aggregation of primitive types (e.g.
410366
/// tuples and slices of primitive type) see `is_recursively_primitive_type`
@@ -1013,10 +969,6 @@ pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<
1013969
/// Comes up with an "at least" guesstimate for the type's size, not taking into
1014970
/// account the layout of type parameters.
1015971
pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
1016-
use rustc_middle::ty::layout::LayoutOf;
1017-
if !is_normalizable(cx, cx.param_env, ty) {
1018-
return 0;
1019-
}
1020972
match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) {
1021973
(Ok(size), _) => size,
1022974
(Err(_), ty::Tuple(list)) => list.iter().map(|t| approx_ty_size(cx, t)).sum(),
@@ -1290,3 +1242,113 @@ pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>
12901242
pub fn is_manually_drop(ty: Ty<'_>) -> bool {
12911243
ty.ty_adt_def().map_or(false, AdtDef::is_manually_drop)
12921244
}
1245+
1246+
#[derive(Clone, Copy)]
1247+
pub enum Sizedness {
1248+
/// The type is uninhabited. (e.g. `!`)
1249+
Uninhabited,
1250+
/// The type is zero-sized.
1251+
Zero,
1252+
/// The type has some other size or an unknown size.
1253+
Other,
1254+
}
1255+
impl Sizedness {
1256+
pub fn is_zero(self) -> bool {
1257+
matches!(self, Self::Zero)
1258+
}
1259+
1260+
pub fn is_uninhabited(self) -> bool {
1261+
matches!(self, Self::Uninhabited)
1262+
}
1263+
}
1264+
1265+
/// Calculates the sizedness of a type.
1266+
pub fn sizedness_of<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Sizedness {
1267+
fn for_list<'tcx>(
1268+
tcx: TyCtxt<'tcx>,
1269+
param_env: ParamEnv<'tcx>,
1270+
tys: impl IntoIterator<Item = Ty<'tcx>>,
1271+
) -> Sizedness {
1272+
let mut res = Sizedness::Zero;
1273+
for ty in tys {
1274+
match sizedness_of(tcx, param_env, ty) {
1275+
Sizedness::Uninhabited => return Sizedness::Uninhabited,
1276+
Sizedness::Other => res = Sizedness::Other,
1277+
Sizedness::Zero => {},
1278+
}
1279+
}
1280+
res
1281+
}
1282+
1283+
match *ty.kind() {
1284+
ty::FnDef(..) => Sizedness::Zero,
1285+
ty::Tuple(tys) => for_list(tcx, param_env, tys),
1286+
ty::Array(_, len) if len.try_eval_target_usize(tcx, param_env).is_some_and(|x| x == 0) => Sizedness::Zero,
1287+
ty::Array(ty, _) => sizedness_of(tcx, param_env, ty),
1288+
ty::Adt(adt, args) => {
1289+
let mut iter = adt
1290+
.variants()
1291+
.iter()
1292+
.map(|v| for_list(tcx, param_env, v.fields.iter().map(|f| f.ty(tcx, args))))
1293+
.filter(|x| !x.is_uninhabited());
1294+
match iter.next() {
1295+
None => Sizedness::Uninhabited,
1296+
Some(Sizedness::Other) => Sizedness::Other,
1297+
Some(_) => {
1298+
if iter.next().is_some() {
1299+
Sizedness::Other
1300+
} else {
1301+
Sizedness::Zero
1302+
}
1303+
},
1304+
}
1305+
},
1306+
ty::Closure(_, args) => for_list(tcx, param_env, args.as_closure().upvar_tys().iter()),
1307+
ty::Coroutine(id, args) => {
1308+
if for_list(tcx, param_env, args.as_coroutine().upvar_tys().iter()).is_uninhabited() {
1309+
Sizedness::Uninhabited
1310+
} else {
1311+
Sizedness::Other
1312+
}
1313+
},
1314+
ty::CoroutineClosure(_, args) => {
1315+
if for_list(tcx, param_env, args.as_coroutine_closure().upvar_tys().iter()).is_uninhabited() {
1316+
Sizedness::Uninhabited
1317+
} else {
1318+
Sizedness::Other
1319+
}
1320+
},
1321+
ty::CoroutineWitness(_, args) => for_list(tcx, param_env, args.iter().filter_map(GenericArg::as_type)),
1322+
ty::Alias(..) => {
1323+
if let Ok(normalized) = tcx
1324+
.infer_ctxt()
1325+
.build()
1326+
.at(&ObligationCause::dummy(), param_env)
1327+
.query_normalize(ty)
1328+
&& normalized.value != ty
1329+
{
1330+
sizedness_of(tcx, param_env, normalized.value)
1331+
} else {
1332+
Sizedness::Other
1333+
}
1334+
},
1335+
ty::Never => Sizedness::Uninhabited,
1336+
ty::Bool
1337+
| ty::Char
1338+
| ty::Int(_)
1339+
| ty::Uint(_)
1340+
| ty::Float(_)
1341+
| ty::RawPtr(..)
1342+
| ty::Ref(..)
1343+
| ty::FnPtr(..)
1344+
| ty::Param(_)
1345+
| ty::Bound(..)
1346+
| ty::Placeholder(_)
1347+
| ty::Infer(_)
1348+
| ty::Error(_)
1349+
| ty::Dynamic(..)
1350+
| ty::Slice(..)
1351+
| ty::Str
1352+
| ty::Foreign(_) => Sizedness::Other,
1353+
}
1354+
}

tests/ui/crashes/ice-10508.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Used to overflow in `is_normalizable`
2+
3+
use std::marker::PhantomData;
4+
5+
struct Node<T: 'static> {
6+
m: PhantomData<&'static T>,
7+
}
8+
9+
struct Digit<T> {
10+
elem: T,
11+
}
12+
13+
enum FingerTree<T: 'static> {
14+
Single(T),
15+
16+
Deep(Digit<T>, Box<FingerTree<Node<T>>>),
17+
}
18+
19+
fn main() {}

0 commit comments

Comments
 (0)