1 //! Util methods for [`rustc_middle::ty`]
3 #![allow(clippy::module_name_repetitions)]
5 use core
::ops
::ControlFlow
;
6 use rustc_ast
::ast
::Mutability
;
7 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
9 use rustc_hir
::def
::{CtorKind, CtorOf, DefKind, Res}
;
10 use rustc_hir
::def_id
::DefId
;
11 use rustc_hir
::{Expr, FnDecl, LangItem, TyKind, Unsafety}
;
12 use rustc_infer
::infer
::{
13 type_variable
::{TypeVariableOrigin, TypeVariableOriginKind}
,
16 use rustc_lint
::LateContext
;
17 use rustc_middle
::mir
::interpret
::{ConstValue, Scalar}
;
18 use rustc_middle
::ty
::{
19 self, layout
::ValidityRequirement
, AdtDef
, AliasTy
, AssocKind
, Binder
, BoundRegion
, FnSig
, IntTy
, List
, ParamEnv
,
20 Predicate
, PredicateKind
, Region
, RegionKind
, SubstsRef
, Ty
, TyCtxt
, TypeSuperVisitable
, TypeVisitable
,
21 TypeVisitableExt
, TypeVisitor
, UintTy
, VariantDef
, VariantDiscr
,
23 use rustc_middle
::ty
::{GenericArg, GenericArgKind}
;
24 use rustc_span
::symbol
::Ident
;
25 use rustc_span
::{sym, Span, Symbol, DUMMY_SP}
;
26 use rustc_target
::abi
::{Size, VariantIdx}
;
27 use rustc_trait_selection
::infer
::InferCtxtExt
;
28 use rustc_trait_selection
::traits
::query
::normalize
::QueryNormalizeExt
;
31 use crate::{match_def_path, path_res, paths}
;
33 /// Checks if the given type implements copy.
34 pub fn is_copy
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> bool
{
35 ty
.is_copy_modulo_regions(cx
.tcx
, cx
.param_env
)
38 /// This checks whether a given type is known to implement Debug.
39 pub fn has_debug_impl
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> bool
{
41 .get_diagnostic_item(sym
::Debug
)
42 .map_or(false, |debug
| implements_trait(cx
, ty
, debug
, &[]))
45 /// Checks whether a type can be partially moved.
46 pub fn can_partially_move_ty
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> bool
{
47 if has_drop(cx
, ty
) || is_copy(cx
, ty
) {
51 ty
::Param(_
) => false,
52 ty
::Adt(def
, subs
) => def
.all_fields().any(|f
| !is_copy(cx
, f
.ty(cx
.tcx
, subs
))),
57 /// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
59 pub fn contains_adt_constructor
<'tcx
>(ty
: Ty
<'tcx
>, adt
: AdtDef
<'tcx
>) -> bool
{
60 ty
.walk().any(|inner
| match inner
.unpack() {
61 GenericArgKind
::Type(inner_ty
) => inner_ty
.ty_adt_def() == Some(adt
),
62 GenericArgKind
::Lifetime(_
) | GenericArgKind
::Const(_
) => false,
66 /// Walks into `ty` and returns `true` if any inner type is an instance of the given type, or adt
67 /// constructor of the same type.
69 /// This method also recurses into opaque type predicates, so call it with `impl Trait<U>` and `U`
70 /// will also return `true`.
71 pub fn contains_ty_adt_constructor_opaque
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>, needle
: Ty
<'tcx
>) -> bool
{
72 fn contains_ty_adt_constructor_opaque_inner
<'tcx
>(
73 cx
: &LateContext
<'tcx
>,
76 seen
: &mut FxHashSet
<DefId
>,
78 ty
.walk().any(|inner
| match inner
.unpack() {
79 GenericArgKind
::Type(inner_ty
) => {
80 if inner_ty
== needle
{
84 if inner_ty
.ty_adt_def() == needle
.ty_adt_def() {
88 if let ty
::Alias(ty
::Opaque
, ty
::AliasTy { def_id, .. }
) = *inner_ty
.kind() {
89 if !seen
.insert(def_id
) {
93 for &(predicate
, _span
) in cx
.tcx
.explicit_item_bounds(def_id
) {
94 match predicate
.kind().skip_binder() {
95 // For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
96 // and check substituions to find `U`.
97 ty
::PredicateKind
::Clause(ty
::Clause
::Trait(trait_predicate
)) => {
102 .skip(1) // Skip the implicit `Self` generic parameter
103 .any(|ty
| contains_ty_adt_constructor_opaque_inner(cx
, ty
, needle
, seen
))
108 // For `impl Trait<Assoc=U>`, it will register a predicate of `<T as Trait>::Assoc = U`,
109 // so we check the term for `U`.
110 ty
::PredicateKind
::Clause(ty
::Clause
::Projection(projection_predicate
)) => {
111 if let ty
::TermKind
::Ty(ty
) = projection_predicate
.term
.unpack() {
112 if contains_ty_adt_constructor_opaque_inner(cx
, ty
, needle
, seen
) {
124 GenericArgKind
::Lifetime(_
) | GenericArgKind
::Const(_
) => false,
128 // A hash set to ensure that the same opaque type (`impl Trait` in RPIT or TAIT) is not
130 let mut seen
= FxHashSet
::default();
131 contains_ty_adt_constructor_opaque_inner(cx
, ty
, needle
, &mut seen
)
134 /// Resolves `<T as Iterator>::Item` for `T`
135 /// Do not invoke without first verifying that the type implements `Iterator`
136 pub fn get_iterator_item_ty
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> Option
<Ty
<'tcx
>> {
138 .get_diagnostic_item(sym
::Iterator
)
139 .and_then(|iter_did
| cx
.get_associated_type(ty
, iter_did
, "Item"))
142 /// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type
143 /// implements a trait marked with a diagnostic item use [`implements_trait`].
145 /// For a further exploitation what diagnostic items are see [diagnostic items] in
148 /// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
149 pub fn get_type_diagnostic_name(cx
: &LateContext
<'_
>, ty
: Ty
<'_
>) -> Option
<Symbol
> {
151 ty
::Adt(adt
, _
) => cx
.tcx
.get_diagnostic_name(adt
.did()),
156 /// Returns true if ty has `iter` or `iter_mut` methods
157 pub fn has_iter_method(cx
: &LateContext
<'_
>, probably_ref_ty
: Ty
<'_
>) -> Option
<Symbol
> {
158 // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
159 // exists and has the desired signature. Unfortunately FnCtxt is not exported
160 // so we can't use its `lookup_method` method.
161 let into_iter_collections
: &[Symbol
] = &[
177 let ty_to_check
= match probably_ref_ty
.kind() {
178 ty
::Ref(_
, ty_to_check
, _
) => *ty_to_check
,
179 _
=> probably_ref_ty
,
182 let def_id
= match ty_to_check
.kind() {
183 ty
::Array(..) => return Some(sym
::array
),
184 ty
::Slice(..) => return Some(sym
::slice
),
185 ty
::Adt(adt
, _
) => adt
.did(),
189 for &name
in into_iter_collections
{
190 if cx
.tcx
.is_diagnostic_item(name
, def_id
) {
191 return Some(cx
.tcx
.item_name(def_id
));
197 /// Checks whether a type implements a trait.
198 /// The function returns false in case the type contains an inference variable.
201 /// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
202 /// * [Common tools for writing lints] for an example how to use this function and other options.
204 /// [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
205 pub fn implements_trait
<'tcx
>(
206 cx
: &LateContext
<'tcx
>,
209 ty_params
: &[GenericArg
<'tcx
>],
211 implements_trait_with_env(
216 ty_params
.iter().map(|&arg
| Some(arg
)),
220 /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
221 pub fn implements_trait_with_env
<'tcx
>(
223 param_env
: ParamEnv
<'tcx
>,
226 ty_params
: impl IntoIterator
<Item
= Option
<GenericArg
<'tcx
>>>,
228 // Clippy shouldn't have infer types
229 assert
!(!ty
.needs_infer());
231 let ty
= tcx
.erase_regions(ty
);
232 if ty
.has_escaping_bound_vars() {
235 let infcx
= tcx
.infer_ctxt().build();
236 let orig
= TypeVariableOrigin
{
237 kind
: TypeVariableOriginKind
::MiscVariable
,
240 let ty_params
= tcx
.mk_substs_from_iter(
243 .map(|arg
| arg
.unwrap_or_else(|| infcx
.next_ty_var(orig
).into())),
246 .type_implements_trait(trait_id
, [ty
.into()].into_iter().chain(ty_params
), param_env
)
247 .must_apply_modulo_regions()
250 /// Checks whether this type implements `Drop`.
251 pub fn has_drop
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> bool
{
252 match ty
.ty_adt_def() {
253 Some(def
) => def
.has_dtor(cx
.tcx
),
258 // Returns whether the type has #[must_use] attribute
259 pub fn is_must_use_ty
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> bool
{
261 ty
::Adt(adt
, _
) => cx
.tcx
.has_attr(adt
.did(), sym
::must_use
),
262 ty
::Foreign(did
) => cx
.tcx
.has_attr(*did
, sym
::must_use
),
263 ty
::Slice(ty
) | ty
::Array(ty
, _
) | ty
::RawPtr(ty
::TypeAndMut { ty, .. }
) | ty
::Ref(_
, ty
, _
) => {
264 // for the Array case we don't need to care for the len == 0 case
265 // because we don't want to lint functions returning empty arrays
266 is_must_use_ty(cx
, *ty
)
268 ty
::Tuple(substs
) => substs
.iter().any(|ty
| is_must_use_ty(cx
, ty
)),
269 ty
::Alias(ty
::Opaque
, ty
::AliasTy { def_id, .. }
) => {
270 for (predicate
, _
) in cx
.tcx
.explicit_item_bounds(*def_id
) {
271 if let ty
::PredicateKind
::Clause(ty
::Clause
::Trait(trait_predicate
)) = predicate
.kind().skip_binder() {
272 if cx
.tcx
.has_attr(trait_predicate
.trait_ref
.def_id
, sym
::must_use
) {
279 ty
::Dynamic(binder
, _
, _
) => {
280 for predicate
in binder
.iter() {
281 if let ty
::ExistentialPredicate
::Trait(ref trait_ref
) = predicate
.skip_binder() {
282 if cx
.tcx
.has_attr(trait_ref
.def_id
, sym
::must_use
) {
293 // FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
294 // this function can be removed once the `normalize` method does not panic when normalization does
296 /// Checks if `Ty` is normalizable. This function is useful
297 /// to avoid crashes on `layout_of`.
298 pub fn is_normalizable
<'tcx
>(cx
: &LateContext
<'tcx
>, param_env
: ty
::ParamEnv
<'tcx
>, ty
: Ty
<'tcx
>) -> bool
{
299 is_normalizable_helper(cx
, param_env
, ty
, &mut FxHashMap
::default())
302 fn is_normalizable_helper
<'tcx
>(
303 cx
: &LateContext
<'tcx
>,
304 param_env
: ty
::ParamEnv
<'tcx
>,
306 cache
: &mut FxHashMap
<Ty
<'tcx
>, bool
>,
308 if let Some(&cached_result
) = cache
.get(&ty
) {
309 return cached_result
;
311 // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
312 cache
.insert(ty
, false);
313 let infcx
= cx
.tcx
.infer_ctxt().build();
314 let cause
= rustc_middle
::traits
::ObligationCause
::dummy();
315 let result
= if infcx
.at(&cause
, param_env
).query_normalize(ty
).is_ok() {
317 ty
::Adt(def
, substs
) => def
.variants().iter().all(|variant
| {
321 .all(|field
| is_normalizable_helper(cx
, param_env
, field
.ty(cx
.tcx
, substs
), cache
))
323 _
=> ty
.walk().all(|generic_arg
| match generic_arg
.unpack() {
324 GenericArgKind
::Type(inner_ty
) if inner_ty
!= ty
=> {
325 is_normalizable_helper(cx
, param_env
, inner_ty
, cache
)
327 _
=> true, // if inner_ty == ty, we've already checked it
333 cache
.insert(ty
, result
);
337 /// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any
338 /// integer or floating-point number type). For checking aggregation of primitive types (e.g.
339 /// tuples and slices of primitive type) see `is_recursively_primitive_type`
340 pub fn is_non_aggregate_primitive_type(ty
: Ty
<'_
>) -> bool
{
341 matches
!(ty
.kind(), ty
::Bool
| ty
::Char
| ty
::Int(_
) | ty
::Uint(_
) | ty
::Float(_
))
344 /// Returns `true` if the given type is a primitive (a `bool` or `char`, any integer or
345 /// floating-point number type, a `str`, or an array, slice, or tuple of those types).
346 pub fn is_recursively_primitive_type(ty
: Ty
<'_
>) -> bool
{
348 ty
::Bool
| ty
::Char
| ty
::Int(_
) | ty
::Uint(_
) | ty
::Float(_
) | ty
::Str
=> true,
349 ty
::Ref(_
, inner
, _
) if inner
.is_str() => true,
350 ty
::Array(inner_type
, _
) | ty
::Slice(inner_type
) => is_recursively_primitive_type(inner_type
),
351 ty
::Tuple(inner_types
) => inner_types
.iter().all(is_recursively_primitive_type
),
356 /// Checks if the type is a reference equals to a diagnostic item
357 pub fn is_type_ref_to_diagnostic_item(cx
: &LateContext
<'_
>, ty
: Ty
<'_
>, diag_item
: Symbol
) -> bool
{
359 ty
::Ref(_
, ref_ty
, _
) => match ref_ty
.kind() {
360 ty
::Adt(adt
, _
) => cx
.tcx
.is_diagnostic_item(diag_item
, adt
.did()),
367 /// Checks if the type is equal to a diagnostic item. To check if a type implements a
368 /// trait marked with a diagnostic item use [`implements_trait`].
370 /// For a further exploitation what diagnostic items are see [diagnostic items] in
375 /// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
377 /// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
378 pub fn is_type_diagnostic_item(cx
: &LateContext
<'_
>, ty
: Ty
<'_
>, diag_item
: Symbol
) -> bool
{
380 ty
::Adt(adt
, _
) => cx
.tcx
.is_diagnostic_item(diag_item
, adt
.did()),
385 /// Checks if the type is equal to a lang item.
387 /// Returns `false` if the `LangItem` is not defined.
388 pub fn is_type_lang_item(cx
: &LateContext
<'_
>, ty
: Ty
<'_
>, lang_item
: hir
::LangItem
) -> bool
{
390 ty
::Adt(adt
, _
) => cx
.tcx
.lang_items().get(lang_item
) == Some(adt
.did()),
395 /// Return `true` if the passed `typ` is `isize` or `usize`.
396 pub fn is_isize_or_usize(typ
: Ty
<'_
>) -> bool
{
397 matches
!(typ
.kind(), ty
::Int(IntTy
::Isize
) | ty
::Uint(UintTy
::Usize
))
400 /// Checks if type is struct, enum or union type with the given def path.
402 /// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
403 /// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
404 pub fn match_type(cx
: &LateContext
<'_
>, ty
: Ty
<'_
>, path
: &[&str]) -> bool
{
406 ty
::Adt(adt
, _
) => match_def_path(cx
, adt
.did(), path
),
411 /// Checks if the drop order for a type matters. Some std types implement drop solely to
412 /// deallocate memory. For these types, and composites containing them, changing the drop order
413 /// won't result in any observable side effects.
414 pub fn needs_ordered_drop
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> bool
{
415 fn needs_ordered_drop_inner
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>, seen
: &mut FxHashSet
<Ty
<'tcx
>>) -> bool
{
416 if !seen
.insert(ty
) {
419 if !ty
.has_significant_drop(cx
.tcx
, cx
.param_env
) {
422 // Check for std types which implement drop, but only for memory allocation.
423 else if is_type_lang_item(cx
, ty
, LangItem
::OwnedBox
)
425 get_type_diagnostic_name(cx
, ty
),
426 Some(sym
::HashSet
| sym
::Rc
| sym
::Arc
| sym
::cstring_type
)
428 || match_type(cx
, ty
, &paths
::WEAK_RC
)
429 || match_type(cx
, ty
, &paths
::WEAK_ARC
)
431 // Check all of the generic arguments.
432 if let ty
::Adt(_
, subs
) = ty
.kind() {
433 subs
.types().any(|ty
| needs_ordered_drop_inner(cx
, ty
, seen
))
441 .map_or(false, |id
| implements_trait(cx
, ty
, id
, &[]))
443 // This type doesn't implement drop, so no side effects here.
444 // Check if any component type has any.
446 ty
::Tuple(fields
) => fields
.iter().any(|ty
| needs_ordered_drop_inner(cx
, ty
, seen
)),
447 ty
::Array(ty
, _
) => needs_ordered_drop_inner(cx
, *ty
, seen
),
448 ty
::Adt(adt
, subs
) => adt
450 .map(|f
| f
.ty(cx
.tcx
, subs
))
451 .any(|ty
| needs_ordered_drop_inner(cx
, ty
, seen
)),
459 needs_ordered_drop_inner(cx
, ty
, &mut FxHashSet
::default())
462 /// Peels off all references on the type. Returns the underlying type and the number of references
464 pub fn peel_mid_ty_refs(ty
: Ty
<'_
>) -> (Ty
<'_
>, usize) {
465 fn peel(ty
: Ty
<'_
>, count
: usize) -> (Ty
<'_
>, usize) {
466 if let ty
::Ref(_
, ty
, _
) = ty
.kind() {
475 /// Peels off all references on the type. Returns the underlying type, the number of references
476 /// removed, and whether the pointer is ultimately mutable or not.
477 pub fn peel_mid_ty_refs_is_mutable(ty
: Ty
<'_
>) -> (Ty
<'_
>, usize, Mutability
) {
478 fn f(ty
: Ty
<'_
>, count
: usize, mutability
: Mutability
) -> (Ty
<'_
>, usize, Mutability
) {
480 ty
::Ref(_
, ty
, Mutability
::Mut
) => f(*ty
, count
+ 1, mutability
),
481 ty
::Ref(_
, ty
, Mutability
::Not
) => f(*ty
, count
+ 1, Mutability
::Not
),
482 _
=> (ty
, count
, mutability
),
485 f(ty
, 0, Mutability
::Mut
)
488 /// Returns `true` if the given type is an `unsafe` function.
489 pub fn type_is_unsafe_function
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> bool
{
491 ty
::FnDef(..) | ty
::FnPtr(_
) => ty
.fn_sig(cx
.tcx
).unsafety() == Unsafety
::Unsafe
,
496 /// Returns the base type for HIR references and pointers.
497 pub fn walk_ptrs_hir_ty
<'tcx
>(ty
: &'tcx hir
::Ty
<'tcx
>) -> &'tcx hir
::Ty
<'tcx
> {
499 TyKind
::Ptr(ref mut_ty
) | TyKind
::Ref(_
, ref mut_ty
) => walk_ptrs_hir_ty(mut_ty
.ty
),
504 /// Returns the base type for references and raw pointers, and count reference
506 pub fn walk_ptrs_ty_depth(ty
: Ty
<'_
>) -> (Ty
<'_
>, usize) {
507 fn inner(ty
: Ty
<'_
>, depth
: usize) -> (Ty
<'_
>, usize) {
509 ty
::Ref(_
, ty
, _
) => inner(*ty
, depth
+ 1),
516 /// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
517 /// otherwise returns `false`
518 pub fn same_type_and_consts
<'tcx
>(a
: Ty
<'tcx
>, b
: Ty
<'tcx
>) -> bool
{
519 match (&a
.kind(), &b
.kind()) {
520 (&ty
::Adt(did_a
, substs_a
), &ty
::Adt(did_b
, substs_b
)) => {
527 .zip(substs_b
.iter())
528 .all(|(arg_a
, arg_b
)| match (arg_a
.unpack(), arg_b
.unpack()) {
529 (GenericArgKind
::Const(inner_a
), GenericArgKind
::Const(inner_b
)) => inner_a
== inner_b
,
530 (GenericArgKind
::Type(type_a
), GenericArgKind
::Type(type_b
)) => {
531 same_type_and_consts(type_a
, type_b
)
540 /// Checks if a given type looks safe to be uninitialized.
541 pub fn is_uninit_value_valid_for_ty
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> bool
{
543 .check_validity_requirement((ValidityRequirement
::Uninit
, cx
.param_env
.and(ty
)))
544 .unwrap_or_else(|_
| is_uninit_value_valid_for_ty_fallback(cx
, ty
))
547 /// A fallback for polymorphic types, which are not supported by `check_validity_requirement`.
548 fn is_uninit_value_valid_for_ty_fallback
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> bool
{
550 // The array length may be polymorphic, let's try the inner type.
551 ty
::Array(component
, _
) => is_uninit_value_valid_for_ty(cx
, component
),
552 // Peek through tuples and try their fallbacks.
553 ty
::Tuple(types
) => types
.iter().all(|ty
| is_uninit_value_valid_for_ty(cx
, ty
)),
554 // Unions are always fine right now.
555 // This includes MaybeUninit, the main way people use uninitialized memory.
556 // For ADTs, we could look at all fields just like for tuples, but that's potentially
557 // exponential, so let's avoid doing that for now. Code doing that is sketchy enough to
558 // just use an `#[allow()]`.
559 ty
::Adt(adt
, _
) => adt
.is_union(),
560 // For the rest, conservatively assume that they cannot be uninit.
565 /// Gets an iterator over all predicates which apply to the given item.
566 pub fn all_predicates_of(tcx
: TyCtxt
<'_
>, id
: DefId
) -> impl Iterator
<Item
= &(Predicate
<'_
>, Span
)> {
567 let mut next_id
= Some(id
);
568 iter
::from_fn(move || {
569 next_id
.take().map(|id
| {
570 let preds
= tcx
.predicates_of(id
);
571 next_id
= preds
.parent
;
572 preds
.predicates
.iter()
578 /// A signature for a function like type.
579 #[derive(Clone, Copy)]
580 pub enum ExprFnSig
<'tcx
> {
581 Sig(Binder
<'tcx
, FnSig
<'tcx
>>, Option
<DefId
>),
582 Closure(Option
<&'tcx FnDecl
<'tcx
>>, Binder
<'tcx
, FnSig
<'tcx
>>),
583 Trait(Binder
<'tcx
, Ty
<'tcx
>>, Option
<Binder
<'tcx
, Ty
<'tcx
>>>, Option
<DefId
>),
585 impl<'tcx
> ExprFnSig
<'tcx
> {
586 /// Gets the argument type at the given offset. This will return `None` when the index is out of
587 /// bounds only for variadic functions, otherwise this will panic.
588 pub fn input(self, i
: usize) -> Option
<Binder
<'tcx
, Ty
<'tcx
>>> {
590 Self::Sig(sig
, _
) => {
591 if sig
.c_variadic() {
592 sig
.inputs().map_bound(|inputs
| inputs
.get(i
).copied()).transpose()
597 Self::Closure(_
, sig
) => Some(sig
.input(0).map_bound(|ty
| ty
.tuple_fields()[i
])),
598 Self::Trait(inputs
, _
, _
) => Some(inputs
.map_bound(|ty
| ty
.tuple_fields()[i
])),
602 /// Gets the argument type at the given offset. For closures this will also get the type as
603 /// written. This will return `None` when the index is out of bounds only for variadic
604 /// functions, otherwise this will panic.
605 pub fn input_with_hir(self, i
: usize) -> Option
<(Option
<&'tcx hir
::Ty
<'tcx
>>, Binder
<'tcx
, Ty
<'tcx
>>)> {
607 Self::Sig(sig
, _
) => {
608 if sig
.c_variadic() {
610 .map_bound(|inputs
| inputs
.get(i
).copied())
612 .map(|arg
| (None
, arg
))
614 Some((None
, sig
.input(i
)))
617 Self::Closure(decl
, sig
) => Some((
618 decl
.and_then(|decl
| decl
.inputs
.get(i
)),
619 sig
.input(0).map_bound(|ty
| ty
.tuple_fields()[i
]),
621 Self::Trait(inputs
, _
, _
) => Some((None
, inputs
.map_bound(|ty
| ty
.tuple_fields()[i
]))),
625 /// Gets the result type, if one could be found. Note that the result type of a trait may not be
627 pub fn output(self) -> Option
<Binder
<'tcx
, Ty
<'tcx
>>> {
629 Self::Sig(sig
, _
) | Self::Closure(_
, sig
) => Some(sig
.output()),
630 Self::Trait(_
, output
, _
) => output
,
634 pub fn predicates_id(&self) -> Option
<DefId
> {
635 if let ExprFnSig
::Sig(_
, id
) | ExprFnSig
::Trait(_
, _
, id
) = *self {
643 /// If the expression is function like, get the signature for it.
644 pub fn expr_sig
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &Expr
<'_
>) -> Option
<ExprFnSig
<'tcx
>> {
645 if let Res
::Def(DefKind
::Fn
| DefKind
::Ctor(_
, CtorKind
::Fn
) | DefKind
::AssocFn
, id
) = path_res(cx
, expr
) {
646 Some(ExprFnSig
::Sig(cx
.tcx
.fn_sig(id
).subst_identity(), Some(id
)))
648 ty_sig(cx
, cx
.typeck_results().expr_ty_adjusted(expr
).peel_refs())
652 /// If the type is function like, get the signature for it.
653 pub fn ty_sig
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> Option
<ExprFnSig
<'tcx
>> {
655 return ty_sig(cx
, ty
.boxed_ty());
658 ty
::Closure(id
, subs
) => {
661 .and_then(|id
| cx
.tcx
.hir().fn_decl_by_hir_id(cx
.tcx
.hir().local_def_id_to_hir_id(id
)));
662 Some(ExprFnSig
::Closure(decl
, subs
.as_closure().sig()))
664 ty
::FnDef(id
, subs
) => Some(ExprFnSig
::Sig(cx
.tcx
.fn_sig(id
).subst(cx
.tcx
, subs
), Some(id
))),
665 ty
::Alias(ty
::Opaque
, ty
::AliasTy { def_id, substs, .. }
) => sig_from_bounds(
668 cx
.tcx
.item_bounds(def_id
).subst(cx
.tcx
, substs
),
669 cx
.tcx
.opt_parent(def_id
),
671 ty
::FnPtr(sig
) => Some(ExprFnSig
::Sig(sig
, None
)),
672 ty
::Dynamic(bounds
, _
, _
) => {
673 let lang_items
= cx
.tcx
.lang_items();
674 match bounds
.principal() {
676 if Some(bound
.def_id()) == lang_items
.fn_trait()
677 || Some(bound
.def_id()) == lang_items
.fn_once_trait()
678 || Some(bound
.def_id()) == lang_items
.fn_mut_trait() =>
682 .find(|p
| lang_items
.fn_once_output().map_or(false, |id
| id
== p
.item_def_id()))
683 .map(|p
| p
.map_bound(|p
| p
.term
.ty().unwrap()));
684 Some(ExprFnSig
::Trait(bound
.map_bound(|b
| b
.substs
.type_at(0)), output
, None
))
689 ty
::Alias(ty
::Projection
, proj
) => match cx
.tcx
.try_normalize_erasing_regions(cx
.param_env
, ty
) {
690 Ok(normalized_ty
) if normalized_ty
!= ty
=> ty_sig(cx
, normalized_ty
),
691 _
=> sig_for_projection(cx
, proj
).or_else(|| sig_from_bounds(cx
, ty
, cx
.param_env
.caller_bounds(), None
)),
693 ty
::Param(_
) => sig_from_bounds(cx
, ty
, cx
.param_env
.caller_bounds(), None
),
698 fn sig_from_bounds
<'tcx
>(
699 cx
: &LateContext
<'tcx
>,
701 predicates
: &'tcx
[Predicate
<'tcx
>],
702 predicates_id
: Option
<DefId
>,
703 ) -> Option
<ExprFnSig
<'tcx
>> {
704 let mut inputs
= None
;
705 let mut output
= None
;
706 let lang_items
= cx
.tcx
.lang_items();
708 for pred
in predicates
{
709 match pred
.kind().skip_binder() {
710 PredicateKind
::Clause(ty
::Clause
::Trait(p
))
711 if (lang_items
.fn_trait() == Some(p
.def_id())
712 || lang_items
.fn_mut_trait() == Some(p
.def_id())
713 || lang_items
.fn_once_trait() == Some(p
.def_id()))
714 && p
.self_ty() == ty
=>
716 let i
= pred
.kind().rebind(p
.trait_ref
.substs
.type_at(1));
717 if inputs
.map_or(false, |inputs
| i
!= inputs
) {
718 // Multiple different fn trait impls. Is this even allowed?
723 PredicateKind
::Clause(ty
::Clause
::Projection(p
))
724 if Some(p
.projection_ty
.def_id
) == lang_items
.fn_once_output() && p
.projection_ty
.self_ty() == ty
=>
726 if output
.is_some() {
727 // Multiple different fn trait impls. Is this even allowed?
730 output
= Some(pred
.kind().rebind(p
.term
.ty().unwrap()));
736 inputs
.map(|ty
| ExprFnSig
::Trait(ty
, output
, predicates_id
))
739 fn sig_for_projection
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: AliasTy
<'tcx
>) -> Option
<ExprFnSig
<'tcx
>> {
740 let mut inputs
= None
;
741 let mut output
= None
;
742 let lang_items
= cx
.tcx
.lang_items();
746 .bound_explicit_item_bounds(ty
.def_id
)
747 .subst_iter_copied(cx
.tcx
, ty
.substs
)
749 match pred
.kind().skip_binder() {
750 PredicateKind
::Clause(ty
::Clause
::Trait(p
))
751 if (lang_items
.fn_trait() == Some(p
.def_id())
752 || lang_items
.fn_mut_trait() == Some(p
.def_id())
753 || lang_items
.fn_once_trait() == Some(p
.def_id())) =>
755 let i
= pred
.kind().rebind(p
.trait_ref
.substs
.type_at(1));
757 if inputs
.map_or(false, |inputs
| inputs
!= i
) {
758 // Multiple different fn trait impls. Is this even allowed?
763 PredicateKind
::Clause(ty
::Clause
::Projection(p
))
764 if Some(p
.projection_ty
.def_id
) == lang_items
.fn_once_output() =>
766 if output
.is_some() {
767 // Multiple different fn trait impls. Is this even allowed?
770 output
= pred
.kind().rebind(p
.term
.ty()).transpose();
776 inputs
.map(|ty
| ExprFnSig
::Trait(ty
, output
, None
))
779 #[derive(Clone, Copy)]
784 impl core
::ops
::Add
<u32> for EnumValue
{
786 fn add(self, n
: u32) -> Self::Output
{
788 Self::Unsigned(x
) => Self::Unsigned(x
+ u128
::from(n
)),
789 Self::Signed(x
) => Self::Signed(x
+ i128
::from(n
)),
794 /// Attempts to read the given constant as though it were an enum value.
795 #[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
796 pub fn read_explicit_enum_value(tcx
: TyCtxt
<'_
>, id
: DefId
) -> Option
<EnumValue
> {
797 if let Ok(ConstValue
::Scalar(Scalar
::Int(value
))) = tcx
.const_eval_poly(id
) {
798 match tcx
.type_of(id
).subst_identity().kind() {
799 ty
::Int(_
) => Some(EnumValue
::Signed(match value
.size().bytes() {
800 1 => i128
::from(value
.assert_bits(Size
::from_bytes(1)) as u8 as i8),
801 2 => i128
::from(value
.assert_bits(Size
::from_bytes(2)) as u16 as i16),
802 4 => i128
::from(value
.assert_bits(Size
::from_bytes(4)) as u32 as i32),
803 8 => i128
::from(value
.assert_bits(Size
::from_bytes(8)) as u64 as i64),
804 16 => value
.assert_bits(Size
::from_bytes(16)) as i128
,
807 ty
::Uint(_
) => Some(EnumValue
::Unsigned(match value
.size().bytes() {
808 1 => value
.assert_bits(Size
::from_bytes(1)),
809 2 => value
.assert_bits(Size
::from_bytes(2)),
810 4 => value
.assert_bits(Size
::from_bytes(4)),
811 8 => value
.assert_bits(Size
::from_bytes(8)),
812 16 => value
.assert_bits(Size
::from_bytes(16)),
822 /// Gets the value of the given variant.
823 pub fn get_discriminant_value(tcx
: TyCtxt
<'_
>, adt
: AdtDef
<'_
>, i
: VariantIdx
) -> EnumValue
{
824 let variant
= &adt
.variant(i
);
825 match variant
.discr
{
826 VariantDiscr
::Explicit(id
) => read_explicit_enum_value(tcx
, id
).unwrap(),
827 VariantDiscr
::Relative(x
) => match adt
.variant((i
.as_usize() - x
as usize).into()).discr
{
828 VariantDiscr
::Explicit(id
) => read_explicit_enum_value(tcx
, id
).unwrap() + x
,
829 VariantDiscr
::Relative(_
) => EnumValue
::Unsigned(x
.into()),
834 /// Check if the given type is either `core::ffi::c_void`, `std::os::raw::c_void`, or one of the
835 /// platform specific `libc::<platform>::c_void` types in libc.
836 pub fn is_c_void(cx
: &LateContext
<'_
>, ty
: Ty
<'_
>) -> bool
{
837 if let ty
::Adt(adt
, _
) = ty
.kind()
838 && let &[krate
, .., name
] = &*cx
.get_def_path(adt
.did())
839 && let sym
::libc
| sym
::core
| sym
::std
= krate
840 && name
.as_str() == "c_void"
848 pub fn for_each_top_level_late_bound_region
<B
>(
850 f
: impl FnMut(BoundRegion
) -> ControlFlow
<B
>,
851 ) -> ControlFlow
<B
> {
856 impl<'tcx
, B
, F
: FnMut(BoundRegion
) -> ControlFlow
<B
>> TypeVisitor
<TyCtxt
<'tcx
>> for V
<F
> {
858 fn visit_region(&mut self, r
: Region
<'tcx
>) -> ControlFlow
<Self::BreakTy
> {
859 if let RegionKind
::ReLateBound(idx
, bound
) = r
.kind() && idx
.as_u32() == self.index
{
862 ControlFlow
::Continue(())
865 fn visit_binder
<T
: TypeVisitable
<TyCtxt
<'tcx
>>>(&mut self, t
: &Binder
<'tcx
, T
>) -> ControlFlow
<Self::BreakTy
> {
867 let res
= t
.super_visit_with(self);
872 ty
.visit_with(&mut V { index: 0, f }
)
875 pub struct AdtVariantInfo
{
880 pub fields_size
: Vec
<(usize, u64)>,
883 impl AdtVariantInfo
{
884 /// Returns ADT variants ordered by size
885 pub fn new
<'tcx
>(cx
: &LateContext
<'tcx
>, adt
: AdtDef
<'tcx
>, subst
: &'tcx List
<GenericArg
<'tcx
>>) -> Vec
<Self> {
886 let mut variants_size
= adt
890 .map(|(i
, variant
)| {
891 let mut fields_size
= variant
895 .map(|(i
, f
)| (i
, approx_ty_size(cx
, f
.ty(cx
.tcx
, subst
))))
896 .collect
::<Vec
<_
>>();
897 fields_size
.sort_by(|(_
, a_size
), (_
, b_size
)| (a_size
.cmp(b_size
)));
901 size
: fields_size
.iter().map(|(_
, size
)| size
).sum(),
905 .collect
::<Vec
<_
>>();
906 variants_size
.sort_by(|a
, b
| (b
.size
.cmp(&a
.size
)));
911 /// Gets the struct or enum variant from the given `Res`
912 pub fn adt_and_variant_of_res
<'tcx
>(cx
: &LateContext
<'tcx
>, res
: Res
) -> Option
<(AdtDef
<'tcx
>, &'tcx VariantDef
)> {
914 Res
::Def(DefKind
::Struct
, id
) => {
915 let adt
= cx
.tcx
.adt_def(id
);
916 Some((adt
, adt
.non_enum_variant()))
918 Res
::Def(DefKind
::Variant
, id
) => {
919 let adt
= cx
.tcx
.adt_def(cx
.tcx
.parent(id
));
920 Some((adt
, adt
.variant_with_id(id
)))
922 Res
::Def(DefKind
::Ctor(CtorOf
::Struct
, _
), id
) => {
923 let adt
= cx
.tcx
.adt_def(cx
.tcx
.parent(id
));
924 Some((adt
, adt
.non_enum_variant()))
926 Res
::Def(DefKind
::Ctor(CtorOf
::Variant
, _
), id
) => {
927 let var_id
= cx
.tcx
.parent(id
);
928 let adt
= cx
.tcx
.adt_def(cx
.tcx
.parent(var_id
));
929 Some((adt
, adt
.variant_with_id(var_id
)))
931 Res
::SelfCtor(id
) => {
932 let adt
= cx
.tcx
.type_of(id
).subst_identity().ty_adt_def().unwrap();
933 Some((adt
, adt
.non_enum_variant()))
939 /// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`.
940 pub fn ty_is_fn_once_param
<'tcx
>(tcx
: TyCtxt
<'_
>, ty
: Ty
<'tcx
>, predicates
: &'tcx
[Predicate
<'_
>]) -> bool
{
941 let ty
::Param(ty
) = *ty
.kind() else {
944 let lang
= tcx
.lang_items();
945 let (Some(fn_once_id
), Some(fn_mut_id
), Some(fn_id
))
946 = (lang
.fn_once_trait(), lang
.fn_mut_trait(), lang
.fn_trait())
952 .try_fold(false, |found
, p
| {
953 if let PredicateKind
::Clause(ty
::Clause
::Trait(p
)) = p
.kind().skip_binder()
954 && let ty
::Param(self_ty
) = p
.trait_ref
.self_ty().kind()
955 && ty
.index
== self_ty
.index
957 // This should use `super_traits_of`, but that's a private function.
958 if p
.trait_ref
.def_id
== fn_once_id
{
960 } else if p
.trait_ref
.def_id
== fn_mut_id
|| p
.trait_ref
.def_id
== fn_id
{
969 /// Comes up with an "at least" guesstimate for the type's size, not taking into
970 /// account the layout of type parameters.
971 pub fn approx_ty_size
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> u64 {
972 use rustc_middle
::ty
::layout
::LayoutOf
;
973 if !is_normalizable(cx
, cx
.param_env
, ty
) {
976 match (cx
.layout_of(ty
).map(|layout
| layout
.size
.bytes()), ty
.kind()) {
977 (Ok(size
), _
) => size
,
978 (Err(_
), ty
::Tuple(list
)) => list
.as_substs().types().map(|t
| approx_ty_size(cx
, t
)).sum(),
979 (Err(_
), ty
::Array(t
, n
)) => {
980 n
.try_eval_target_usize(cx
.tcx
, cx
.param_env
).unwrap_or_default() * approx_ty_size(cx
, *t
)
982 (Err(_
), ty
::Adt(def
, subst
)) if def
.is_struct() => def
988 .map(|field
| approx_ty_size(cx
, field
.ty(cx
.tcx
, subst
)))
992 (Err(_
), ty
::Adt(def
, subst
)) if def
.is_enum() => def
998 .map(|field
| approx_ty_size(cx
, field
.ty(cx
.tcx
, subst
)))
1002 .unwrap_or_default(),
1003 (Err(_
), ty
::Adt(def
, subst
)) if def
.is_union() => def
1009 .map(|field
| approx_ty_size(cx
, field
.ty(cx
.tcx
, subst
)))
1011 .unwrap_or_default()
1014 .unwrap_or_default(),
1019 /// Makes the projection type for the named associated type in the given impl or trait impl.
1021 /// This function is for associated types which are "known" to exist, and as such, will only return
1022 /// `None` when debug assertions are disabled in order to prevent ICE's. With debug assertions
1023 /// enabled this will check that the named associated type exists, the correct number of
1024 /// substitutions are given, and that the correct kinds of substitutions are given (lifetime,
1025 /// constant or type). This will not check if type normalization would succeed.
1026 pub fn make_projection
<'tcx
>(
1028 container_id
: DefId
,
1030 substs
: impl IntoIterator
<Item
= impl Into
<GenericArg
<'tcx
>>>,
1031 ) -> Option
<AliasTy
<'tcx
>> {
1034 container_id
: DefId
,
1036 substs
: SubstsRef
<'tcx
>,
1037 ) -> Option
<AliasTy
<'tcx
>> {
1038 let Some(assoc_item
) = tcx
1039 .associated_items(container_id
)
1040 .find_by_name_and_kind(tcx
, Ident
::with_dummy_span(assoc_ty
), AssocKind
::Type
, container_id
)
1042 debug_assert
!(false, "type `{assoc_ty}` not found in `{container_id:?}`");
1045 #[cfg(debug_assertions)]
1047 let generics
= tcx
.generics_of(assoc_item
.def_id
);
1048 let generic_count
= generics
.parent_count
+ generics
.params
.len();
1049 let params
= generics
1051 .map_or([].as_slice(), |id
| &*tcx
.generics_of(id
).params
)
1053 .chain(&generics
.params
)
1057 generic_count
== substs
.len(),
1058 "wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\
1059 note: the expected parameters are: {:#?}\n\
1060 the given arguments are: `{substs:#?}`",
1063 params
.map(ty
::GenericParamDefKind
::descr
).collect
::<Vec
<_
>>(),
1066 if let Some((idx
, (param
, arg
))) = params
1068 .zip(substs
.iter().map(GenericArg
::unpack
))
1070 .find(|(_
, (param
, arg
))| {
1073 (ty
::GenericParamDefKind
::Lifetime
, GenericArgKind
::Lifetime(_
))
1074 | (ty
::GenericParamDefKind
::Type { .. }
, GenericArgKind
::Type(_
))
1075 | (ty
::GenericParamDefKind
::Const { .. }
, GenericArgKind
::Const(_
))
1081 "mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\
1082 note: the expected parameters are {:#?}\n\
1083 the given arguments are {substs:#?}",
1085 params
.map(ty
::GenericParamDefKind
::descr
).collect
::<Vec
<_
>>()
1090 Some(tcx
.mk_alias_ty(assoc_item
.def_id
, substs
))
1096 tcx
.mk_substs_from_iter(substs
.into_iter().map(Into
::into
)),
1100 /// Normalizes the named associated type in the given impl or trait impl.
1102 /// This function is for associated types which are "known" to be valid with the given
1103 /// substitutions, and as such, will only return `None` when debug assertions are disabled in order
1104 /// to prevent ICE's. With debug assertions enabled this will check that that type normalization
1105 /// succeeds as well as everything checked by `make_projection`.
1106 pub fn make_normalized_projection
<'tcx
>(
1108 param_env
: ParamEnv
<'tcx
>,
1109 container_id
: DefId
,
1111 substs
: impl IntoIterator
<Item
= impl Into
<GenericArg
<'tcx
>>>,
1112 ) -> Option
<Ty
<'tcx
>> {
1113 fn helper
<'tcx
>(tcx
: TyCtxt
<'tcx
>, param_env
: ParamEnv
<'tcx
>, ty
: AliasTy
<'tcx
>) -> Option
<Ty
<'tcx
>> {
1114 #[cfg(debug_assertions)]
1115 if let Some((i
, subst
)) = ty
1119 .find(|(_
, subst
)| subst
.has_late_bound_regions())
1123 "substs contain late-bound region at index `{i}` which can't be normalized.\n\
1124 use `TyCtxt::erase_late_bound_regions`\n\
1125 note: subst is `{subst:#?}`",
1129 match tcx
.try_normalize_erasing_regions(param_env
, tcx
.mk_projection(ty
.def_id
, ty
.substs
)) {
1132 debug_assert
!(false, "failed to normalize type `{ty}`: {e:#?}");
1137 helper(tcx
, param_env
, make_projection(tcx
, container_id
, assoc_ty
, substs
)?
)
1140 /// Check if given type has inner mutability such as [`std::cell::Cell`] or [`std::cell::RefCell`]
1142 pub fn is_interior_mut_ty
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> bool
{
1144 ty
::Ref(_
, inner_ty
, mutbl
) => mutbl
== Mutability
::Mut
|| is_interior_mut_ty(cx
, inner_ty
),
1145 ty
::Slice(inner_ty
) => is_interior_mut_ty(cx
, inner_ty
),
1146 ty
::Array(inner_ty
, size
) => {
1147 size
.try_eval_target_usize(cx
.tcx
, cx
.param_env
)
1148 .map_or(true, |u
| u
!= 0)
1149 && is_interior_mut_ty(cx
, inner_ty
)
1151 ty
::Tuple(fields
) => fields
.iter().any(|ty
| is_interior_mut_ty(cx
, ty
)),
1152 ty
::Adt(def
, substs
) => {
1153 // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
1154 // that of their type parameters. Note: we don't include `HashSet` and `HashMap`
1155 // because they have no impl for `Hash` or `Ord`.
1156 let def_id
= def
.did();
1157 let is_std_collection
= [
1169 .any(|diag_item
| cx
.tcx
.is_diagnostic_item(*diag_item
, def_id
));
1170 let is_box
= Some(def_id
) == cx
.tcx
.lang_items().owned_box();
1171 if is_std_collection
|| is_box
{
1172 // The type is mutable if any of its type parameters are
1173 substs
.types().any(|ty
| is_interior_mut_ty(cx
, ty
))
1175 !ty
.has_escaping_bound_vars()
1176 && cx
.tcx
.layout_of(cx
.param_env
.and(ty
)).is_ok()
1177 && !ty
.is_freeze(cx
.tcx
, cx
.param_env
)