]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_utils/src/ty.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / src / tools / clippy / clippy_utils / src / ty.rs
CommitLineData
cdc7bbd5
XL
1//! Util methods for [`rustc_middle::ty`]
2
3#![allow(clippy::module_name_repetitions)]
4
064997fb 5use core::ops::ControlFlow;
add651ee 6use itertools::Itertools;
cdc7bbd5 7use rustc_ast::ast::Mutability;
04454e1e 8use rustc_data_structures::fx::{FxHashMap, FxHashSet};
cdc7bbd5 9use rustc_hir as hir;
064997fb 10use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
cdc7bbd5 11use rustc_hir::def_id::DefId;
064997fb 12use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
add651ee
FG
13use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
14use rustc_infer::infer::TyCtxtInferExt;
cdc7bbd5 15use rustc_lint::LateContext;
781aab86
FG
16use rustc_middle::mir::interpret::Scalar;
17use rustc_middle::mir::ConstValue;
add651ee
FG
18use rustc_middle::traits::EvaluationResult;
19use rustc_middle::ty::layout::ValidityRequirement;
5099ac24 20use rustc_middle::ty::{
add651ee
FG
21 self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
22 GenericParamDefKind, IntTy, List, ParamEnv, Region, RegionKind, ToPredicate, TraitRef, Ty, TyCtxt,
23 TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
5099ac24 24};
a2a8927a 25use rustc_span::symbol::Ident;
487cf647 26use rustc_span::{sym, Span, Symbol, DUMMY_SP};
5e7ed085 27use rustc_target::abi::{Size, VariantIdx};
add651ee 28use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
487cf647 29use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
add651ee 30use rustc_trait_selection::traits::{Obligation, ObligationCause};
781aab86 31use std::assert_matches::debug_assert_matches;
a2a8927a 32use std::iter;
cdc7bbd5 33
04454e1e 34use crate::{match_def_path, path_res, paths};
cdc7bbd5 35
add651ee
FG
36mod type_certainty;
37pub use type_certainty::expr_type_is_certain;
38
9c376795 39/// Checks if the given type implements copy.
cdc7bbd5 40pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
2b03887a
FG
41 ty.is_copy_modulo_regions(cx.tcx, cx.param_env)
42}
43
44/// This checks whether a given type is known to implement Debug.
45pub fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
46 cx.tcx
47 .get_diagnostic_item(sym::Debug)
48 .map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
cdc7bbd5
XL
49}
50
51/// Checks whether a type can be partially moved.
5099ac24 52pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
cdc7bbd5
XL
53 if has_drop(cx, ty) || is_copy(cx, ty) {
54 return false;
55 }
56 match ty.kind() {
57 ty::Param(_) => false,
58 ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
59 _ => true,
60 }
61}
62
cdc7bbd5
XL
63/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
64/// constructor.
923072b8 65pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool {
5099ac24 66 ty.walk().any(|inner| match inner.unpack() {
cdc7bbd5
XL
67 GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt),
68 GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
69 })
70}
71
487cf647
FG
72/// Walks into `ty` and returns `true` if any inner type is an instance of the given type, or adt
73/// constructor of the same type.
74///
75/// This method also recurses into opaque type predicates, so call it with `impl Trait<U>` and `U`
76/// will also return `true`.
77pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, needle: Ty<'tcx>) -> bool {
78 fn contains_ty_adt_constructor_opaque_inner<'tcx>(
79 cx: &LateContext<'tcx>,
80 ty: Ty<'tcx>,
81 needle: Ty<'tcx>,
82 seen: &mut FxHashSet<DefId>,
83 ) -> bool {
84 ty.walk().any(|inner| match inner.unpack() {
85 GenericArgKind::Type(inner_ty) => {
86 if inner_ty == needle {
87 return true;
88 }
89
90 if inner_ty.ty_adt_def() == needle.ty_adt_def() {
91 return true;
92 }
93
9c376795 94 if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *inner_ty.kind() {
487cf647
FG
95 if !seen.insert(def_id) {
96 return false;
97 }
98
add651ee 99 for (predicate, _span) in cx.tcx.explicit_item_bounds(def_id).instantiate_identity_iter_copied() {
487cf647
FG
100 match predicate.kind().skip_binder() {
101 // For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
49aad941 102 // and check substitutions to find `U`.
fe692bf9 103 ty::ClauseKind::Trait(trait_predicate) => {
487cf647
FG
104 if trait_predicate
105 .trait_ref
add651ee 106 .args
487cf647
FG
107 .types()
108 .skip(1) // Skip the implicit `Self` generic parameter
109 .any(|ty| contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen))
110 {
111 return true;
112 }
113 },
114 // For `impl Trait<Assoc=U>`, it will register a predicate of `<T as Trait>::Assoc = U`,
115 // so we check the term for `U`.
fe692bf9 116 ty::ClauseKind::Projection(projection_predicate) => {
487cf647
FG
117 if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() {
118 if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) {
119 return true;
120 }
121 };
122 },
123 _ => (),
124 }
125 }
126 }
127
128 false
129 },
130 GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
131 })
132 }
133
134 // A hash set to ensure that the same opaque type (`impl Trait` in RPIT or TAIT) is not
135 // visited twice.
136 let mut seen = FxHashSet::default();
137 contains_ty_adt_constructor_opaque_inner(cx, ty, needle, &mut seen)
138}
139
cdc7bbd5
XL
140/// Resolves `<T as Iterator>::Item` for `T`
141/// Do not invoke without first verifying that the type implements `Iterator`
142pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
143 cx.tcx
144 .get_diagnostic_item(sym::Iterator)
487cf647 145 .and_then(|iter_did| cx.get_associated_type(ty, iter_did, "Item"))
cdc7bbd5
XL
146}
147
04454e1e
FG
148/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type
149/// implements a trait marked with a diagnostic item use [`implements_trait`].
150///
151/// For a further exploitation what diagnostic items are see [diagnostic items] in
152/// rustc-dev-guide.
153///
154/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
155pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
156 match ty.kind() {
157 ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()),
158 _ => None,
159 }
160}
161
cdc7bbd5
XL
162/// Returns true if ty has `iter` or `iter_mut` methods
163pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
164 // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
165 // exists and has the desired signature. Unfortunately FnCtxt is not exported
166 // so we can't use its `lookup_method` method.
167 let into_iter_collections: &[Symbol] = &[
c295e0f8
XL
168 sym::Vec,
169 sym::Option,
170 sym::Result,
cdc7bbd5
XL
171 sym::BTreeMap,
172 sym::BTreeSet,
c295e0f8 173 sym::VecDeque,
cdc7bbd5
XL
174 sym::LinkedList,
175 sym::BinaryHeap,
c295e0f8
XL
176 sym::HashSet,
177 sym::HashMap,
cdc7bbd5
XL
178 sym::PathBuf,
179 sym::Path,
180 sym::Receiver,
181 ];
182
183 let ty_to_check = match probably_ref_ty.kind() {
5099ac24 184 ty::Ref(_, ty_to_check, _) => *ty_to_check,
cdc7bbd5
XL
185 _ => probably_ref_ty,
186 };
187
188 let def_id = match ty_to_check.kind() {
189 ty::Array(..) => return Some(sym::array),
190 ty::Slice(..) => return Some(sym::slice),
5e7ed085 191 ty::Adt(adt, _) => adt.did(),
cdc7bbd5
XL
192 _ => return None,
193 };
194
195 for &name in into_iter_collections {
196 if cx.tcx.is_diagnostic_item(name, def_id) {
197 return Some(cx.tcx.item_name(def_id));
198 }
199 }
200 None
201}
202
203/// Checks whether a type implements a trait.
136023e0 204/// The function returns false in case the type contains an inference variable.
a2a8927a
XL
205///
206/// See:
207/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
208/// * [Common tools for writing lints] for an example how to use this function and other options.
209///
064997fb 210/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
cdc7bbd5
XL
211pub fn implements_trait<'tcx>(
212 cx: &LateContext<'tcx>,
213 ty: Ty<'tcx>,
214 trait_id: DefId,
add651ee 215 args: &[GenericArg<'tcx>],
923072b8 216) -> bool {
add651ee 217 implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, trait_id, args.iter().map(|&x| Some(x)))
923072b8
FG
218}
219
220/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
221pub fn implements_trait_with_env<'tcx>(
222 tcx: TyCtxt<'tcx>,
223 param_env: ParamEnv<'tcx>,
224 ty: Ty<'tcx>,
225 trait_id: DefId,
add651ee
FG
226 args: &[GenericArg<'tcx>],
227) -> bool {
228 implements_trait_with_env_from_iter(tcx, param_env, ty, trait_id, args.iter().map(|&x| Some(x)))
229}
230
231/// Same as `implements_trait_from_env` but takes the arguments as an iterator.
232pub fn implements_trait_with_env_from_iter<'tcx>(
233 tcx: TyCtxt<'tcx>,
234 param_env: ParamEnv<'tcx>,
235 ty: Ty<'tcx>,
236 trait_id: DefId,
237 args: impl IntoIterator<Item = impl Into<Option<GenericArg<'tcx>>>>,
cdc7bbd5 238) -> bool {
136023e0 239 // Clippy shouldn't have infer types
49aad941 240 assert!(!ty.has_infer());
136023e0 241
923072b8 242 let ty = tcx.erase_regions(ty);
cdc7bbd5
XL
243 if ty.has_escaping_bound_vars() {
244 return false;
245 }
add651ee 246
2b03887a 247 let infcx = tcx.infer_ctxt().build();
add651ee
FG
248 let trait_ref = TraitRef::new(
249 tcx,
250 trait_id,
251 Some(GenericArg::from(ty))
487cf647 252 .into_iter()
add651ee
FG
253 .chain(args.into_iter().map(|arg| {
254 arg.into().unwrap_or_else(|| {
255 let orig = TypeVariableOrigin {
256 kind: TypeVariableOriginKind::MiscVariable,
257 span: DUMMY_SP,
258 };
259 infcx.next_ty_var(orig).into()
260 })
261 })),
487cf647 262 );
add651ee 263
781aab86
FG
264 debug_assert_matches!(
265 tcx.def_kind(trait_id),
266 DefKind::Trait | DefKind::TraitAlias,
267 "`DefId` must belong to a trait or trait alias"
268 );
add651ee
FG
269 #[cfg(debug_assertions)]
270 assert_generic_args_match(tcx, trait_id, trait_ref.args);
271
272 let obligation = Obligation {
273 cause: ObligationCause::dummy(),
274 param_env,
275 recursion_depth: 0,
276 predicate: ty::Binder::dummy(trait_ref).to_predicate(tcx),
277 };
2b03887a 278 infcx
add651ee
FG
279 .evaluate_obligation(&obligation)
280 .is_ok_and(EvaluationResult::must_apply_modulo_regions)
cdc7bbd5
XL
281}
282
283/// Checks whether this type implements `Drop`.
284pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
285 match ty.ty_adt_def() {
286 Some(def) => def.has_dtor(cx.tcx),
287 None => false,
288 }
289}
290
291// Returns whether the type has #[must_use] attribute
292pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
293 match ty.kind() {
04454e1e
FG
294 ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use),
295 ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use),
17df50a5 296 ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
cdc7bbd5
XL
297 // for the Array case we don't need to care for the len == 0 case
298 // because we don't want to lint functions returning empty arrays
299 is_must_use_ty(cx, *ty)
300 },
add651ee 301 ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)),
9c376795 302 ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
49aad941 303 for (predicate, _) in cx.tcx.explicit_item_bounds(def_id).skip_binder() {
fe692bf9 304 if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
04454e1e 305 if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) {
cdc7bbd5
XL
306 return true;
307 }
308 }
309 }
310 false
311 },
f2b60f7d 312 ty::Dynamic(binder, _, _) => {
fe692bf9 313 for predicate in *binder {
cdc7bbd5 314 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
04454e1e 315 if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) {
cdc7bbd5
XL
316 return true;
317 }
318 }
319 }
320 false
321 },
322 _ => false,
323 }
324}
325
326// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
94222f64 327// this function can be removed once the `normalize` method does not panic when normalization does
cdc7bbd5
XL
328// not succeed
329/// Checks if `Ty` is normalizable. This function is useful
330/// to avoid crashes on `layout_of`.
331pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
17df50a5 332 is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
cdc7bbd5
XL
333}
334
335fn is_normalizable_helper<'tcx>(
336 cx: &LateContext<'tcx>,
337 param_env: ty::ParamEnv<'tcx>,
338 ty: Ty<'tcx>,
17df50a5 339 cache: &mut FxHashMap<Ty<'tcx>, bool>,
cdc7bbd5 340) -> bool {
5099ac24 341 if let Some(&cached_result) = cache.get(&ty) {
cdc7bbd5
XL
342 return cached_result;
343 }
344 // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
345 cache.insert(ty, false);
2b03887a
FG
346 let infcx = cx.tcx.infer_ctxt().build();
347 let cause = rustc_middle::traits::ObligationCause::dummy();
487cf647 348 let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() {
2b03887a 349 match ty.kind() {
add651ee 350 ty::Adt(def, args) => def.variants().iter().all(|variant| {
2b03887a
FG
351 variant
352 .fields
353 .iter()
add651ee 354 .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, args), cache))
2b03887a
FG
355 }),
356 _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
357 GenericArgKind::Type(inner_ty) if inner_ty != ty => {
358 is_normalizable_helper(cx, param_env, inner_ty, cache)
359 },
360 _ => true, // if inner_ty == ty, we've already checked it
361 }),
cdc7bbd5 362 }
2b03887a
FG
363 } else {
364 false
365 };
cdc7bbd5
XL
366 cache.insert(ty, result);
367 result
368}
369
c295e0f8
XL
370/// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any
371/// integer or floating-point number type). For checking aggregation of primitive types (e.g.
372/// tuples and slices of primitive type) see `is_recursively_primitive_type`
373pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool {
374 matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_))
375}
376
377/// Returns `true` if the given type is a primitive (a `bool` or `char`, any integer or
378/// floating-point number type, a `str`, or an array, slice, or tuple of those types).
cdc7bbd5 379pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
5e7ed085 380 match *ty.kind() {
cdc7bbd5 381 ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
9ffffee4 382 ty::Ref(_, inner, _) if inner.is_str() => true,
5e7ed085
FG
383 ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
384 ty::Tuple(inner_types) => inner_types.iter().all(is_recursively_primitive_type),
cdc7bbd5
XL
385 _ => false,
386 }
387}
388
136023e0
XL
389/// Checks if the type is a reference equals to a diagnostic item
390pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
391 match ty.kind() {
392 ty::Ref(_, ref_ty, _) => match ref_ty.kind() {
5e7ed085 393 ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
136023e0
XL
394 _ => false,
395 },
396 _ => false,
397 }
398}
399
a2a8927a
XL
400/// Checks if the type is equal to a diagnostic item. To check if a type implements a
401/// trait marked with a diagnostic item use [`implements_trait`].
402///
403/// For a further exploitation what diagnostic items are see [diagnostic items] in
404/// rustc-dev-guide.
405///
406/// ---
cdc7bbd5
XL
407///
408/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
a2a8927a
XL
409///
410/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
cdc7bbd5
XL
411pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
412 match ty.kind() {
5e7ed085 413 ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
cdc7bbd5
XL
414 _ => false,
415 }
416}
417
136023e0
XL
418/// Checks if the type is equal to a lang item.
419///
420/// Returns `false` if the `LangItem` is not defined.
cdc7bbd5
XL
421pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
422 match ty.kind() {
487cf647 423 ty::Adt(adt, _) => cx.tcx.lang_items().get(lang_item) == Some(adt.did()),
cdc7bbd5
XL
424 _ => false,
425 }
426}
427
add651ee
FG
428/// Gets the diagnostic name of the type, if it has one
429pub fn type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
430 ty.ty_adt_def().and_then(|adt| cx.tcx.get_diagnostic_name(adt.did()))
431}
432
cdc7bbd5
XL
433/// Return `true` if the passed `typ` is `isize` or `usize`.
434pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
435 matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
436}
437
438/// Checks if type is struct, enum or union type with the given def path.
439///
440/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
441/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
442pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
443 match ty.kind() {
5e7ed085 444 ty::Adt(adt, _) => match_def_path(cx, adt.did(), path),
cdc7bbd5
XL
445 _ => false,
446 }
447}
448
04454e1e
FG
449/// Checks if the drop order for a type matters. Some std types implement drop solely to
450/// deallocate memory. For these types, and composites containing them, changing the drop order
451/// won't result in any observable side effects.
452pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
453 fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
454 if !seen.insert(ty) {
455 return false;
456 }
457 if !ty.has_significant_drop(cx.tcx, cx.param_env) {
458 false
459 }
460 // Check for std types which implement drop, but only for memory allocation.
461 else if is_type_lang_item(cx, ty, LangItem::OwnedBox)
462 || matches!(
463 get_type_diagnostic_name(cx, ty),
464 Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type)
465 )
466 || match_type(cx, ty, &paths::WEAK_RC)
467 || match_type(cx, ty, &paths::WEAK_ARC)
468 {
469 // Check all of the generic arguments.
470 if let ty::Adt(_, subs) = ty.kind() {
471 subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen))
472 } else {
473 true
474 }
475 } else if !cx
476 .tcx
477 .lang_items()
478 .drop_trait()
479 .map_or(false, |id| implements_trait(cx, ty, id, &[]))
480 {
481 // This type doesn't implement drop, so no side effects here.
482 // Check if any component type has any.
483 match ty.kind() {
484 ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
485 ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen),
486 ty::Adt(adt, subs) => adt
487 .all_fields()
488 .map(|f| f.ty(cx.tcx, subs))
489 .any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
490 _ => true,
491 }
492 } else {
493 true
494 }
495 }
496
497 needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
498}
499
cdc7bbd5
XL
500/// Peels off all references on the type. Returns the underlying type and the number of references
501/// removed.
502pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
503 fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
504 if let ty::Ref(_, ty, _) = ty.kind() {
5099ac24 505 peel(*ty, count + 1)
cdc7bbd5
XL
506 } else {
507 (ty, count)
508 }
509 }
510 peel(ty, 0)
511}
512
f2b60f7d 513/// Peels off all references on the type. Returns the underlying type, the number of references
cdc7bbd5
XL
514/// removed, and whether the pointer is ultimately mutable or not.
515pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
516 fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
517 match ty.kind() {
5099ac24
FG
518 ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability),
519 ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not),
cdc7bbd5
XL
520 _ => (ty, count, mutability),
521 }
522 }
523 f(ty, 0, Mutability::Mut)
524}
525
526/// Returns `true` if the given type is an `unsafe` function.
527pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
528 match ty.kind() {
529 ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
530 _ => false,
531 }
532}
533
534/// Returns the base type for HIR references and pointers.
535pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
536 match ty.kind {
9c376795 537 TyKind::Ptr(ref mut_ty) | TyKind::Ref(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty),
cdc7bbd5
XL
538 _ => ty,
539 }
540}
541
542/// Returns the base type for references and raw pointers, and count reference
543/// depth.
544pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
545 fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
546 match ty.kind() {
5099ac24 547 ty::Ref(_, ty, _) => inner(*ty, depth + 1),
cdc7bbd5
XL
548 _ => (ty, depth),
549 }
550 }
551 inner(ty, 0)
552}
17df50a5
XL
553
554/// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
555/// otherwise returns `false`
5099ac24 556pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
17df50a5 557 match (&a.kind(), &b.kind()) {
add651ee 558 (&ty::Adt(did_a, args_a), &ty::Adt(did_b, args_b)) => {
17df50a5
XL
559 if did_a != did_b {
560 return false;
561 }
562
add651ee 563 args_a
17df50a5 564 .iter()
add651ee 565 .zip(args_b.iter())
17df50a5
XL
566 .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) {
567 (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
568 (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
569 same_type_and_consts(type_a, type_b)
570 },
571 _ => true,
572 })
573 },
574 _ => a == b,
575 }
576}
3c0e092e
XL
577
578/// Checks if a given type looks safe to be uninitialized.
353b0b11
FG
579pub fn is_uninit_value_valid_for_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
580 cx.tcx
581 .check_validity_requirement((ValidityRequirement::Uninit, cx.param_env.and(ty)))
582 .unwrap_or_else(|_| is_uninit_value_valid_for_ty_fallback(cx, ty))
583}
584
585/// A fallback for polymorphic types, which are not supported by `check_validity_requirement`.
586fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
5e7ed085 587 match *ty.kind() {
353b0b11 588 // The array length may be polymorphic, let's try the inner type.
5e7ed085 589 ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component),
353b0b11 590 // Peek through tuples and try their fallbacks.
5e7ed085 591 ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
353b0b11
FG
592 // Unions are always fine right now.
593 // This includes MaybeUninit, the main way people use uninitialized memory.
594 // For ADTs, we could look at all fields just like for tuples, but that's potentially
595 // exponential, so let's avoid doing that for now. Code doing that is sketchy enough to
596 // just use an `#[allow()]`.
597 ty::Adt(adt, _) => adt.is_union(),
598 // For the rest, conservatively assume that they cannot be uninit.
3c0e092e
XL
599 _ => false,
600 }
601}
a2a8927a
XL
602
603/// Gets an iterator over all predicates which apply to the given item.
fe692bf9 604pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(ty::Clause<'_>, Span)> {
a2a8927a
XL
605 let mut next_id = Some(id);
606 iter::from_fn(move || {
607 next_id.take().map(|id| {
608 let preds = tcx.predicates_of(id);
609 next_id = preds.parent;
610 preds.predicates.iter()
611 })
612 })
613 .flatten()
614}
5099ac24
FG
615
616/// A signature for a function like type.
617#[derive(Clone, Copy)]
618pub enum ExprFnSig<'tcx> {
064997fb
FG
619 Sig(Binder<'tcx, FnSig<'tcx>>, Option<DefId>),
620 Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>),
f2b60f7d 621 Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>, Option<DefId>),
5099ac24
FG
622}
623impl<'tcx> ExprFnSig<'tcx> {
064997fb
FG
624 /// Gets the argument type at the given offset. This will return `None` when the index is out of
625 /// bounds only for variadic functions, otherwise this will panic.
626 pub fn input(self, i: usize) -> Option<Binder<'tcx, Ty<'tcx>>> {
5099ac24 627 match self {
064997fb
FG
628 Self::Sig(sig, _) => {
629 if sig.c_variadic() {
630 sig.inputs().map_bound(|inputs| inputs.get(i).copied()).transpose()
631 } else {
632 Some(sig.input(i))
633 }
634 },
635 Self::Closure(_, sig) => Some(sig.input(0).map_bound(|ty| ty.tuple_fields()[i])),
f2b60f7d 636 Self::Trait(inputs, _, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])),
064997fb
FG
637 }
638 }
639
640 /// Gets the argument type at the given offset. For closures this will also get the type as
641 /// written. This will return `None` when the index is out of bounds only for variadic
642 /// functions, otherwise this will panic.
643 pub fn input_with_hir(self, i: usize) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Binder<'tcx, Ty<'tcx>>)> {
644 match self {
645 Self::Sig(sig, _) => {
646 if sig.c_variadic() {
647 sig.inputs()
648 .map_bound(|inputs| inputs.get(i).copied())
649 .transpose()
650 .map(|arg| (None, arg))
651 } else {
652 Some((None, sig.input(i)))
653 }
654 },
655 Self::Closure(decl, sig) => Some((
656 decl.and_then(|decl| decl.inputs.get(i)),
657 sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
658 )),
f2b60f7d 659 Self::Trait(inputs, _, _) => Some((None, inputs.map_bound(|ty| ty.tuple_fields()[i]))),
5099ac24
FG
660 }
661 }
662
663 /// Gets the result type, if one could be found. Note that the result type of a trait may not be
664 /// specified.
665 pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
666 match self {
064997fb 667 Self::Sig(sig, _) | Self::Closure(_, sig) => Some(sig.output()),
f2b60f7d 668 Self::Trait(_, output, _) => output,
5099ac24
FG
669 }
670 }
064997fb
FG
671
672 pub fn predicates_id(&self) -> Option<DefId> {
f2b60f7d
FG
673 if let ExprFnSig::Sig(_, id) | ExprFnSig::Trait(_, _, id) = *self {
674 id
675 } else {
676 None
677 }
064997fb 678 }
5099ac24
FG
679}
680
681/// If the expression is function like, get the signature for it.
682pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
683 if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
add651ee 684 Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).instantiate_identity(), Some(id)))
5099ac24 685 } else {
064997fb
FG
686 ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
687 }
688}
689
f2b60f7d
FG
690/// If the type is function like, get the signature for it.
691pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
064997fb
FG
692 if ty.is_box() {
693 return ty_sig(cx, ty.boxed_ty());
694 }
695 match *ty.kind() {
696 ty::Closure(id, subs) => {
697 let decl = id
698 .as_local()
699 .and_then(|id| cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.hir().local_def_id_to_hir_id(id)));
700 Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
701 },
add651ee
FG
702 ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).instantiate(cx.tcx, subs), Some(id))),
703 ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => sig_from_bounds(
9ffffee4
FG
704 cx,
705 ty,
add651ee 706 cx.tcx.item_bounds(def_id).iter_instantiated(cx.tcx, args),
9ffffee4
FG
707 cx.tcx.opt_parent(def_id),
708 ),
064997fb 709 ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
f2b60f7d 710 ty::Dynamic(bounds, _, _) => {
064997fb
FG
711 let lang_items = cx.tcx.lang_items();
712 match bounds.principal() {
713 Some(bound)
714 if Some(bound.def_id()) == lang_items.fn_trait()
715 || Some(bound.def_id()) == lang_items.fn_once_trait()
716 || Some(bound.def_id()) == lang_items.fn_mut_trait() =>
717 {
718 let output = bounds
719 .projection_bounds()
720 .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
721 .map(|p| p.map_bound(|p| p.term.ty().unwrap()));
add651ee 722 Some(ExprFnSig::Trait(bound.map_bound(|b| b.args.type_at(0)), output, None))
064997fb
FG
723 },
724 _ => None,
725 }
726 },
9c376795 727 ty::Alias(ty::Projection, proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) {
064997fb 728 Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
f2b60f7d 729 _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)),
064997fb 730 },
f2b60f7d 731 ty::Param(_) => sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None),
064997fb
FG
732 _ => None,
733 }
734}
735
f2b60f7d
FG
736fn sig_from_bounds<'tcx>(
737 cx: &LateContext<'tcx>,
738 ty: Ty<'tcx>,
fe692bf9 739 predicates: impl IntoIterator<Item = ty::Clause<'tcx>>,
f2b60f7d
FG
740 predicates_id: Option<DefId>,
741) -> Option<ExprFnSig<'tcx>> {
064997fb
FG
742 let mut inputs = None;
743 let mut output = None;
744 let lang_items = cx.tcx.lang_items();
745
f2b60f7d 746 for pred in predicates {
064997fb 747 match pred.kind().skip_binder() {
fe692bf9 748 ty::ClauseKind::Trait(p)
064997fb
FG
749 if (lang_items.fn_trait() == Some(p.def_id())
750 || lang_items.fn_mut_trait() == Some(p.def_id())
751 || lang_items.fn_once_trait() == Some(p.def_id()))
752 && p.self_ty() == ty =>
753 {
add651ee 754 let i = pred.kind().rebind(p.trait_ref.args.type_at(1));
f2b60f7d 755 if inputs.map_or(false, |inputs| i != inputs) {
064997fb
FG
756 // Multiple different fn trait impls. Is this even allowed?
757 return None;
5099ac24 758 }
f2b60f7d 759 inputs = Some(i);
5099ac24 760 },
fe692bf9 761 ty::ClauseKind::Projection(p)
9c376795 762 if Some(p.projection_ty.def_id) == lang_items.fn_once_output() && p.projection_ty.self_ty() == ty =>
064997fb
FG
763 {
764 if output.is_some() {
765 // Multiple different fn trait impls. Is this even allowed?
766 return None;
5099ac24 767 }
064997fb
FG
768 output = Some(pred.kind().rebind(p.term.ty().unwrap()));
769 },
770 _ => (),
771 }
772 }
5099ac24 773
f2b60f7d 774 inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id))
064997fb
FG
775}
776
9c376795 777fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option<ExprFnSig<'tcx>> {
064997fb
FG
778 let mut inputs = None;
779 let mut output = None;
780 let lang_items = cx.tcx.lang_items();
781
2b03887a 782 for (pred, _) in cx
064997fb 783 .tcx
49aad941 784 .explicit_item_bounds(ty.def_id)
add651ee 785 .iter_instantiated_copied(cx.tcx, ty.args)
064997fb 786 {
2b03887a 787 match pred.kind().skip_binder() {
fe692bf9 788 ty::ClauseKind::Trait(p)
064997fb
FG
789 if (lang_items.fn_trait() == Some(p.def_id())
790 || lang_items.fn_mut_trait() == Some(p.def_id())
791 || lang_items.fn_once_trait() == Some(p.def_id())) =>
792 {
add651ee 793 let i = pred.kind().rebind(p.trait_ref.args.type_at(1));
f2b60f7d
FG
794
795 if inputs.map_or(false, |inputs| inputs != i) {
064997fb
FG
796 // Multiple different fn trait impls. Is this even allowed?
797 return None;
798 }
f2b60f7d 799 inputs = Some(i);
5099ac24 800 },
fe692bf9 801 ty::ClauseKind::Projection(p) if Some(p.projection_ty.def_id) == lang_items.fn_once_output() => {
064997fb
FG
802 if output.is_some() {
803 // Multiple different fn trait impls. Is this even allowed?
804 return None;
805 }
2b03887a 806 output = pred.kind().rebind(p.term.ty()).transpose();
064997fb
FG
807 },
808 _ => (),
5099ac24
FG
809 }
810 }
064997fb 811
f2b60f7d 812 inputs.map(|ty| ExprFnSig::Trait(ty, output, None))
5099ac24 813}
5e7ed085
FG
814
815#[derive(Clone, Copy)]
816pub enum EnumValue {
817 Unsigned(u128),
818 Signed(i128),
819}
820impl core::ops::Add<u32> for EnumValue {
821 type Output = Self;
822 fn add(self, n: u32) -> Self::Output {
823 match self {
824 Self::Unsigned(x) => Self::Unsigned(x + u128::from(n)),
825 Self::Signed(x) => Self::Signed(x + i128::from(n)),
826 }
827 }
828}
829
487cf647 830/// Attempts to read the given constant as though it were an enum value.
923072b8 831#[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
5e7ed085
FG
832pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option<EnumValue> {
833 if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) {
add651ee 834 match tcx.type_of(id).instantiate_identity().kind() {
5e7ed085
FG
835 ty::Int(_) => Some(EnumValue::Signed(match value.size().bytes() {
836 1 => i128::from(value.assert_bits(Size::from_bytes(1)) as u8 as i8),
837 2 => i128::from(value.assert_bits(Size::from_bytes(2)) as u16 as i16),
838 4 => i128::from(value.assert_bits(Size::from_bytes(4)) as u32 as i32),
839 8 => i128::from(value.assert_bits(Size::from_bytes(8)) as u64 as i64),
840 16 => value.assert_bits(Size::from_bytes(16)) as i128,
841 _ => return None,
842 })),
843 ty::Uint(_) => Some(EnumValue::Unsigned(match value.size().bytes() {
844 1 => value.assert_bits(Size::from_bytes(1)),
845 2 => value.assert_bits(Size::from_bytes(2)),
846 4 => value.assert_bits(Size::from_bytes(4)),
847 8 => value.assert_bits(Size::from_bytes(8)),
848 16 => value.assert_bits(Size::from_bytes(16)),
849 _ => return None,
850 })),
851 _ => None,
852 }
853 } else {
854 None
855 }
856}
857
858/// Gets the value of the given variant.
859pub fn get_discriminant_value(tcx: TyCtxt<'_>, adt: AdtDef<'_>, i: VariantIdx) -> EnumValue {
860 let variant = &adt.variant(i);
861 match variant.discr {
862 VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap(),
863 VariantDiscr::Relative(x) => match adt.variant((i.as_usize() - x as usize).into()).discr {
864 VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap() + x,
865 VariantDiscr::Relative(_) => EnumValue::Unsigned(x.into()),
866 },
867 }
868}
869
870/// Check if the given type is either `core::ffi::c_void`, `std::os::raw::c_void`, or one of the
871/// platform specific `libc::<platform>::c_void` types in libc.
872pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
873 if let ty::Adt(adt, _) = ty.kind()
874 && let &[krate, .., name] = &*cx.get_def_path(adt.did())
875 && let sym::libc | sym::core | sym::std = krate
49aad941 876 && name == rustc_span::sym::c_void
5e7ed085
FG
877 {
878 true
879 } else {
880 false
881 }
882}
064997fb
FG
883
884pub fn for_each_top_level_late_bound_region<B>(
885 ty: Ty<'_>,
886 f: impl FnMut(BoundRegion) -> ControlFlow<B>,
887) -> ControlFlow<B> {
888 struct V<F> {
889 index: u32,
890 f: F,
891 }
9ffffee4 892 impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow<B>> TypeVisitor<TyCtxt<'tcx>> for V<F> {
064997fb
FG
893 type BreakTy = B;
894 fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
895 if let RegionKind::ReLateBound(idx, bound) = r.kind() && idx.as_u32() == self.index {
896 (self.f)(bound)
897 } else {
898 ControlFlow::Continue(())
899 }
900 }
9ffffee4 901 fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> ControlFlow<Self::BreakTy> {
064997fb
FG
902 self.index += 1;
903 let res = t.super_visit_with(self);
904 self.index -= 1;
905 res
906 }
907 }
908 ty.visit_with(&mut V { index: 0, f })
909}
910
487cf647
FG
911pub struct AdtVariantInfo {
912 pub ind: usize,
913 pub size: u64,
914
915 /// (ind, size)
916 pub fields_size: Vec<(usize, u64)>,
917}
918
919impl AdtVariantInfo {
920 /// Returns ADT variants ordered by size
921 pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: &'tcx List<GenericArg<'tcx>>) -> Vec<Self> {
922 let mut variants_size = adt
923 .variants()
924 .iter()
925 .enumerate()
926 .map(|(i, variant)| {
927 let mut fields_size = variant
928 .fields
929 .iter()
930 .enumerate()
931 .map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst))))
932 .collect::<Vec<_>>();
933 fields_size.sort_by(|(_, a_size), (_, b_size)| (a_size.cmp(b_size)));
934
935 Self {
936 ind: i,
937 size: fields_size.iter().map(|(_, size)| size).sum(),
938 fields_size,
939 }
940 })
941 .collect::<Vec<_>>();
942 variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
943 variants_size
944 }
945}
946
064997fb 947/// Gets the struct or enum variant from the given `Res`
9ffffee4 948pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<(AdtDef<'tcx>, &'tcx VariantDef)> {
064997fb 949 match res {
9ffffee4
FG
950 Res::Def(DefKind::Struct, id) => {
951 let adt = cx.tcx.adt_def(id);
952 Some((adt, adt.non_enum_variant()))
953 },
954 Res::Def(DefKind::Variant, id) => {
955 let adt = cx.tcx.adt_def(cx.tcx.parent(id));
956 Some((adt, adt.variant_with_id(id)))
957 },
958 Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => {
959 let adt = cx.tcx.adt_def(cx.tcx.parent(id));
960 Some((adt, adt.non_enum_variant()))
961 },
064997fb
FG
962 Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => {
963 let var_id = cx.tcx.parent(id);
9ffffee4
FG
964 let adt = cx.tcx.adt_def(cx.tcx.parent(var_id));
965 Some((adt, adt.variant_with_id(var_id)))
966 },
967 Res::SelfCtor(id) => {
add651ee 968 let adt = cx.tcx.type_of(id).instantiate_identity().ty_adt_def().unwrap();
9ffffee4 969 Some((adt, adt.non_enum_variant()))
064997fb 970 },
064997fb
FG
971 _ => None,
972 }
973}
974
975/// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`.
fe692bf9 976pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [ty::Clause<'_>]) -> bool {
064997fb
FG
977 let ty::Param(ty) = *ty.kind() else {
978 return false;
979 };
980 let lang = tcx.lang_items();
add651ee 981 let (Some(fn_once_id), Some(fn_mut_id), Some(fn_id)) = (lang.fn_once_trait(), lang.fn_mut_trait(), lang.fn_trait())
064997fb
FG
982 else {
983 return false;
984 };
985 predicates
986 .iter()
987 .try_fold(false, |found, p| {
fe692bf9 988 if let ty::ClauseKind::Trait(p) = p.kind().skip_binder()
064997fb
FG
989 && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
990 && ty.index == self_ty.index
991 {
992 // This should use `super_traits_of`, but that's a private function.
993 if p.trait_ref.def_id == fn_once_id {
994 return Some(true);
995 } else if p.trait_ref.def_id == fn_mut_id || p.trait_ref.def_id == fn_id {
996 return None;
997 }
998 }
999 Some(found)
1000 })
1001 .unwrap_or(false)
1002}
f2b60f7d
FG
1003
1004/// Comes up with an "at least" guesstimate for the type's size, not taking into
1005/// account the layout of type parameters.
1006pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
1007 use rustc_middle::ty::layout::LayoutOf;
1008 if !is_normalizable(cx, cx.param_env, ty) {
1009 return 0;
1010 }
1011 match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) {
1012 (Ok(size), _) => size,
49aad941 1013 (Err(_), ty::Tuple(list)) => list.iter().map(|t| approx_ty_size(cx, t)).sum(),
f2b60f7d 1014 (Err(_), ty::Array(t, n)) => {
9ffffee4 1015 n.try_eval_target_usize(cx.tcx, cx.param_env).unwrap_or_default() * approx_ty_size(cx, *t)
f2b60f7d
FG
1016 },
1017 (Err(_), ty::Adt(def, subst)) if def.is_struct() => def
1018 .variants()
1019 .iter()
1020 .map(|v| {
1021 v.fields
1022 .iter()
1023 .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
1024 .sum::<u64>()
1025 })
1026 .sum(),
1027 (Err(_), ty::Adt(def, subst)) if def.is_enum() => def
1028 .variants()
1029 .iter()
1030 .map(|v| {
1031 v.fields
1032 .iter()
1033 .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
1034 .sum::<u64>()
1035 })
1036 .max()
1037 .unwrap_or_default(),
1038 (Err(_), ty::Adt(def, subst)) if def.is_union() => def
1039 .variants()
1040 .iter()
1041 .map(|v| {
1042 v.fields
1043 .iter()
1044 .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
1045 .max()
1046 .unwrap_or_default()
1047 })
1048 .max()
1049 .unwrap_or_default(),
1050 (Err(_), _) => 0,
1051 }
1052}
487cf647 1053
add651ee
FG
1054/// Asserts that the given arguments match the generic parameters of the given item.
1055#[allow(dead_code)]
1056fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[GenericArg<'tcx>]) {
1057 let g = tcx.generics_of(did);
1058 let parent = g.parent.map(|did| tcx.generics_of(did));
1059 let count = g.parent_count + g.params.len();
1060 let params = parent
1061 .map_or([].as_slice(), |p| p.params.as_slice())
1062 .iter()
1063 .chain(&g.params)
1064 .map(|x| &x.kind);
1065
1066 assert!(
1067 count == args.len(),
1068 "wrong number of arguments for `{did:?}`: expected `{count}`, found {}\n\
1069 note: the expected arguments are: `[{}]`\n\
1070 the given arguments are: `{args:#?}`",
1071 args.len(),
1072 params.clone().map(GenericParamDefKind::descr).format(", "),
1073 );
1074
1075 if let Some((idx, (param, arg))) =
1076 params
1077 .clone()
1078 .zip(args.iter().map(|&x| x.unpack()))
1079 .enumerate()
1080 .find(|(_, (param, arg))| match (param, arg) {
1081 (GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_))
1082 | (GenericParamDefKind::Type { .. }, GenericArgKind::Type(_))
1083 | (GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) => false,
1084 (
1085 GenericParamDefKind::Lifetime
1086 | GenericParamDefKind::Type { .. }
1087 | GenericParamDefKind::Const { .. },
1088 _,
1089 ) => true,
1090 })
1091 {
1092 panic!(
1093 "incorrect argument for `{did:?}` at index `{idx}`: expected a {}, found `{arg:?}`\n\
1094 note: the expected arguments are `[{}]`\n\
1095 the given arguments are `{args:#?}`",
1096 param.descr(),
1097 params.clone().map(GenericParamDefKind::descr).format(", "),
1098 );
1099 }
1100}
1101
1102/// Returns whether `ty` is never-like; i.e., `!` (never) or an enum with zero variants.
1103pub fn is_never_like(ty: Ty<'_>) -> bool {
1104 ty.is_never() || (ty.is_enum() && ty.ty_adt_def().is_some_and(|def| def.variants().is_empty()))
1105}
1106
487cf647
FG
1107/// Makes the projection type for the named associated type in the given impl or trait impl.
1108///
1109/// This function is for associated types which are "known" to exist, and as such, will only return
1110/// `None` when debug assertions are disabled in order to prevent ICE's. With debug assertions
1111/// enabled this will check that the named associated type exists, the correct number of
add651ee 1112/// arguments are given, and that the correct kinds of arguments are given (lifetime,
487cf647
FG
1113/// constant or type). This will not check if type normalization would succeed.
1114pub fn make_projection<'tcx>(
1115 tcx: TyCtxt<'tcx>,
1116 container_id: DefId,
1117 assoc_ty: Symbol,
add651ee 1118 args: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
9c376795 1119) -> Option<AliasTy<'tcx>> {
487cf647
FG
1120 fn helper<'tcx>(
1121 tcx: TyCtxt<'tcx>,
1122 container_id: DefId,
1123 assoc_ty: Symbol,
add651ee 1124 args: GenericArgsRef<'tcx>,
9c376795 1125 ) -> Option<AliasTy<'tcx>> {
add651ee
FG
1126 let Some(assoc_item) = tcx.associated_items(container_id).find_by_name_and_kind(
1127 tcx,
1128 Ident::with_dummy_span(assoc_ty),
1129 AssocKind::Type,
1130 container_id,
1131 ) else {
487cf647
FG
1132 debug_assert!(false, "type `{assoc_ty}` not found in `{container_id:?}`");
1133 return None;
1134 };
1135 #[cfg(debug_assertions)]
add651ee 1136 assert_generic_args_match(tcx, assoc_item.def_id, args);
487cf647 1137
add651ee 1138 Some(tcx.mk_alias_ty(assoc_item.def_id, args))
487cf647
FG
1139 }
1140 helper(
1141 tcx,
1142 container_id,
1143 assoc_ty,
add651ee 1144 tcx.mk_args_from_iter(args.into_iter().map(Into::into)),
487cf647
FG
1145 )
1146}
1147
1148/// Normalizes the named associated type in the given impl or trait impl.
1149///
1150/// This function is for associated types which are "known" to be valid with the given
add651ee 1151/// arguments, and as such, will only return `None` when debug assertions are disabled in order
49aad941 1152/// to prevent ICE's. With debug assertions enabled this will check that type normalization
487cf647
FG
1153/// succeeds as well as everything checked by `make_projection`.
1154pub fn make_normalized_projection<'tcx>(
1155 tcx: TyCtxt<'tcx>,
1156 param_env: ParamEnv<'tcx>,
1157 container_id: DefId,
1158 assoc_ty: Symbol,
add651ee 1159 args: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
487cf647 1160) -> Option<Ty<'tcx>> {
9c376795 1161 fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
487cf647 1162 #[cfg(debug_assertions)]
add651ee 1163 if let Some((i, arg)) = ty.args.iter().enumerate().find(|(_, arg)| arg.has_late_bound_regions()) {
487cf647
FG
1164 debug_assert!(
1165 false,
add651ee 1166 "args contain late-bound region at index `{i}` which can't be normalized.\n\
487cf647 1167 use `TyCtxt::erase_late_bound_regions`\n\
add651ee 1168 note: arg is `{arg:#?}`",
487cf647
FG
1169 );
1170 return None;
1171 }
add651ee 1172 match tcx.try_normalize_erasing_regions(param_env, Ty::new_projection(tcx, ty.def_id, ty.args)) {
487cf647
FG
1173 Ok(ty) => Some(ty),
1174 Err(e) => {
1175 debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
1176 None
1177 },
1178 }
1179 }
add651ee 1180 helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, args)?)
487cf647 1181}
353b0b11
FG
1182
1183/// Check if given type has inner mutability such as [`std::cell::Cell`] or [`std::cell::RefCell`]
1184/// etc.
1185pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
1186 match *ty.kind() {
1187 ty::Ref(_, inner_ty, mutbl) => mutbl == Mutability::Mut || is_interior_mut_ty(cx, inner_ty),
1188 ty::Slice(inner_ty) => is_interior_mut_ty(cx, inner_ty),
1189 ty::Array(inner_ty, size) => {
1190 size.try_eval_target_usize(cx.tcx, cx.param_env)
1191 .map_or(true, |u| u != 0)
1192 && is_interior_mut_ty(cx, inner_ty)
1193 },
1194 ty::Tuple(fields) => fields.iter().any(|ty| is_interior_mut_ty(cx, ty)),
add651ee 1195 ty::Adt(def, args) => {
353b0b11
FG
1196 // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
1197 // that of their type parameters. Note: we don't include `HashSet` and `HashMap`
1198 // because they have no impl for `Hash` or `Ord`.
1199 let def_id = def.did();
1200 let is_std_collection = [
1201 sym::Option,
1202 sym::Result,
1203 sym::LinkedList,
1204 sym::Vec,
1205 sym::VecDeque,
1206 sym::BTreeMap,
1207 sym::BTreeSet,
1208 sym::Rc,
1209 sym::Arc,
1210 ]
1211 .iter()
1212 .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id));
1213 let is_box = Some(def_id) == cx.tcx.lang_items().owned_box();
1214 if is_std_collection || is_box {
1215 // The type is mutable if any of its type parameters are
add651ee 1216 args.types().any(|ty| is_interior_mut_ty(cx, ty))
353b0b11
FG
1217 } else {
1218 !ty.has_escaping_bound_vars()
1219 && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
1220 && !ty.is_freeze(cx.tcx, cx.param_env)
1221 }
1222 },
1223 _ => false,
1224 }
1225}
fe692bf9
FG
1226
1227pub fn make_normalized_projection_with_regions<'tcx>(
1228 tcx: TyCtxt<'tcx>,
1229 param_env: ParamEnv<'tcx>,
1230 container_id: DefId,
1231 assoc_ty: Symbol,
add651ee 1232 args: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
fe692bf9
FG
1233) -> Option<Ty<'tcx>> {
1234 fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
1235 #[cfg(debug_assertions)]
add651ee 1236 if let Some((i, arg)) = ty.args.iter().enumerate().find(|(_, arg)| arg.has_late_bound_regions()) {
fe692bf9
FG
1237 debug_assert!(
1238 false,
add651ee 1239 "args contain late-bound region at index `{i}` which can't be normalized.\n\
fe692bf9 1240 use `TyCtxt::erase_late_bound_regions`\n\
add651ee 1241 note: arg is `{arg:#?}`",
fe692bf9
FG
1242 );
1243 return None;
1244 }
1245 let cause = rustc_middle::traits::ObligationCause::dummy();
1246 match tcx
1247 .infer_ctxt()
1248 .build()
1249 .at(&cause, param_env)
add651ee 1250 .query_normalize(Ty::new_projection(tcx, ty.def_id, ty.args))
fe692bf9
FG
1251 {
1252 Ok(ty) => Some(ty.value),
1253 Err(e) => {
1254 debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
1255 None
1256 },
1257 }
1258 }
add651ee 1259 helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, args)?)
fe692bf9
FG
1260}
1261
1262pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
1263 let cause = rustc_middle::traits::ObligationCause::dummy();
1264 match tcx.infer_ctxt().build().at(&cause, param_env).query_normalize(ty) {
1265 Ok(ty) => ty.value,
1266 Err(_) => ty,
1267 }
1268}