]>
Commit | Line | Data |
---|---|---|
1 | //! Util methods for [`rustc_middle::ty`] | |
2 | ||
3 | #![allow(clippy::module_name_repetitions)] | |
4 | ||
5 | use core::ops::ControlFlow; | |
6 | use itertools::Itertools; | |
7 | use rustc_ast::ast::Mutability; | |
8 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; | |
9 | use rustc_hir as hir; | |
10 | use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; | |
11 | use rustc_hir::def_id::DefId; | |
12 | use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety}; | |
13 | use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; | |
14 | use rustc_infer::infer::TyCtxtInferExt; | |
15 | use rustc_lint::LateContext; | |
16 | use rustc_middle::mir::interpret::Scalar; | |
17 | use rustc_middle::mir::ConstValue; | |
18 | use rustc_middle::traits::EvaluationResult; | |
19 | use rustc_middle::ty::layout::ValidityRequirement; | |
20 | use rustc_middle::ty::{ | |
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, | |
24 | }; | |
25 | use rustc_span::symbol::Ident; | |
26 | use rustc_span::{sym, Span, Symbol, DUMMY_SP}; | |
27 | use rustc_target::abi::{Size, VariantIdx}; | |
28 | use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; | |
29 | use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; | |
30 | use rustc_trait_selection::traits::{Obligation, ObligationCause}; | |
31 | use std::assert_matches::debug_assert_matches; | |
32 | use std::iter; | |
33 | ||
34 | use crate::{match_def_path, path_res, paths}; | |
35 | ||
36 | mod type_certainty; | |
37 | pub use type_certainty::expr_type_is_certain; | |
38 | ||
39 | /// Checks if the given type implements copy. | |
40 | pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { | |
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. | |
45 | pub 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, &[])) | |
49 | } | |
50 | ||
51 | /// Checks whether a type can be partially moved. | |
52 | pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { | |
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 | ||
63 | /// Walks into `ty` and returns `true` if any inner type is an instance of the given adt | |
64 | /// constructor. | |
65 | pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool { | |
66 | ty.walk().any(|inner| match inner.unpack() { | |
67 | GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt), | |
68 | GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, | |
69 | }) | |
70 | } | |
71 | ||
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`. | |
77 | pub 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 | ||
94 | if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *inner_ty.kind() { | |
95 | if !seen.insert(def_id) { | |
96 | return false; | |
97 | } | |
98 | ||
99 | for (predicate, _span) in cx.tcx.explicit_item_bounds(def_id).instantiate_identity_iter_copied() { | |
100 | match predicate.kind().skip_binder() { | |
101 | // For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through | |
102 | // and check substitutions to find `U`. | |
103 | ty::ClauseKind::Trait(trait_predicate) => { | |
104 | if trait_predicate | |
105 | .trait_ref | |
106 | .args | |
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`. | |
116 | ty::ClauseKind::Projection(projection_predicate) => { | |
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 | ||
140 | /// Resolves `<T as Iterator>::Item` for `T` | |
141 | /// Do not invoke without first verifying that the type implements `Iterator` | |
142 | pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { | |
143 | cx.tcx | |
144 | .get_diagnostic_item(sym::Iterator) | |
145 | .and_then(|iter_did| cx.get_associated_type(ty, iter_did, "Item")) | |
146 | } | |
147 | ||
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 | |
155 | pub 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 | ||
162 | /// Returns true if ty has `iter` or `iter_mut` methods | |
163 | pub 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] = &[ | |
168 | sym::Vec, | |
169 | sym::Option, | |
170 | sym::Result, | |
171 | sym::BTreeMap, | |
172 | sym::BTreeSet, | |
173 | sym::VecDeque, | |
174 | sym::LinkedList, | |
175 | sym::BinaryHeap, | |
176 | sym::HashSet, | |
177 | sym::HashMap, | |
178 | sym::PathBuf, | |
179 | sym::Path, | |
180 | sym::Receiver, | |
181 | ]; | |
182 | ||
183 | let ty_to_check = match probably_ref_ty.kind() { | |
184 | ty::Ref(_, ty_to_check, _) => *ty_to_check, | |
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), | |
191 | ty::Adt(adt, _) => adt.did(), | |
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. | |
204 | /// The function returns false in case the type contains an inference variable. | |
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 | /// | |
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 | |
211 | pub fn implements_trait<'tcx>( | |
212 | cx: &LateContext<'tcx>, | |
213 | ty: Ty<'tcx>, | |
214 | trait_id: DefId, | |
215 | args: &[GenericArg<'tcx>], | |
216 | ) -> bool { | |
217 | implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, trait_id, args.iter().map(|&x| Some(x))) | |
218 | } | |
219 | ||
220 | /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. | |
221 | pub fn implements_trait_with_env<'tcx>( | |
222 | tcx: TyCtxt<'tcx>, | |
223 | param_env: ParamEnv<'tcx>, | |
224 | ty: Ty<'tcx>, | |
225 | trait_id: DefId, | |
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. | |
232 | pub 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>>>>, | |
238 | ) -> bool { | |
239 | // Clippy shouldn't have infer types | |
240 | assert!(!ty.has_infer()); | |
241 | ||
242 | let ty = tcx.erase_regions(ty); | |
243 | if ty.has_escaping_bound_vars() { | |
244 | return false; | |
245 | } | |
246 | ||
247 | let infcx = tcx.infer_ctxt().build(); | |
248 | let trait_ref = TraitRef::new( | |
249 | tcx, | |
250 | trait_id, | |
251 | Some(GenericArg::from(ty)) | |
252 | .into_iter() | |
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 | })), | |
262 | ); | |
263 | ||
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 | ); | |
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 | }; | |
278 | infcx | |
279 | .evaluate_obligation(&obligation) | |
280 | .is_ok_and(EvaluationResult::must_apply_modulo_regions) | |
281 | } | |
282 | ||
283 | /// Checks whether this type implements `Drop`. | |
284 | pub 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 | |
292 | pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { | |
293 | match ty.kind() { | |
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), | |
296 | ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => { | |
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 | }, | |
301 | ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)), | |
302 | ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { | |
303 | for (predicate, _) in cx.tcx.explicit_item_bounds(def_id).skip_binder() { | |
304 | if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() { | |
305 | if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { | |
306 | return true; | |
307 | } | |
308 | } | |
309 | } | |
310 | false | |
311 | }, | |
312 | ty::Dynamic(binder, _, _) => { | |
313 | for predicate in *binder { | |
314 | if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { | |
315 | if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) { | |
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 | |
327 | // this function can be removed once the `normalize` method does not panic when normalization does | |
328 | // not succeed | |
329 | /// Checks if `Ty` is normalizable. This function is useful | |
330 | /// to avoid crashes on `layout_of`. | |
331 | pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { | |
332 | is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default()) | |
333 | } | |
334 | ||
335 | fn is_normalizable_helper<'tcx>( | |
336 | cx: &LateContext<'tcx>, | |
337 | param_env: ty::ParamEnv<'tcx>, | |
338 | ty: Ty<'tcx>, | |
339 | cache: &mut FxHashMap<Ty<'tcx>, bool>, | |
340 | ) -> bool { | |
341 | if let Some(&cached_result) = cache.get(&ty) { | |
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); | |
346 | let infcx = cx.tcx.infer_ctxt().build(); | |
347 | let cause = rustc_middle::traits::ObligationCause::dummy(); | |
348 | let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() { | |
349 | match ty.kind() { | |
350 | ty::Adt(def, args) => def.variants().iter().all(|variant| { | |
351 | variant | |
352 | .fields | |
353 | .iter() | |
354 | .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, args), cache)) | |
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 | }), | |
362 | } | |
363 | } else { | |
364 | false | |
365 | }; | |
366 | cache.insert(ty, result); | |
367 | result | |
368 | } | |
369 | ||
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` | |
373 | pub 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). | |
379 | pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { | |
380 | match *ty.kind() { | |
381 | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, | |
382 | ty::Ref(_, inner, _) if inner.is_str() => true, | |
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), | |
385 | _ => false, | |
386 | } | |
387 | } | |
388 | ||
389 | /// Checks if the type is a reference equals to a diagnostic item | |
390 | pub 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() { | |
393 | ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()), | |
394 | _ => false, | |
395 | }, | |
396 | _ => false, | |
397 | } | |
398 | } | |
399 | ||
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 | /// --- | |
407 | /// | |
408 | /// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` | |
409 | /// | |
410 | /// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html | |
411 | pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { | |
412 | match ty.kind() { | |
413 | ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()), | |
414 | _ => false, | |
415 | } | |
416 | } | |
417 | ||
418 | /// Checks if the type is equal to a lang item. | |
419 | /// | |
420 | /// Returns `false` if the `LangItem` is not defined. | |
421 | pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool { | |
422 | match ty.kind() { | |
423 | ty::Adt(adt, _) => cx.tcx.lang_items().get(lang_item) == Some(adt.did()), | |
424 | _ => false, | |
425 | } | |
426 | } | |
427 | ||
428 | /// Gets the diagnostic name of the type, if it has one | |
429 | pub 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 | ||
433 | /// Return `true` if the passed `typ` is `isize` or `usize`. | |
434 | pub 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` | |
442 | pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { | |
443 | match ty.kind() { | |
444 | ty::Adt(adt, _) => match_def_path(cx, adt.did(), path), | |
445 | _ => false, | |
446 | } | |
447 | } | |
448 | ||
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. | |
452 | pub 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 | ||
500 | /// Peels off all references on the type. Returns the underlying type and the number of references | |
501 | /// removed. | |
502 | pub 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() { | |
505 | peel(*ty, count + 1) | |
506 | } else { | |
507 | (ty, count) | |
508 | } | |
509 | } | |
510 | peel(ty, 0) | |
511 | } | |
512 | ||
513 | /// Peels off all references on the type. Returns the underlying type, the number of references | |
514 | /// removed, and whether the pointer is ultimately mutable or not. | |
515 | pub 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() { | |
518 | ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability), | |
519 | ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not), | |
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. | |
527 | pub 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. | |
535 | pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { | |
536 | match ty.kind { | |
537 | TyKind::Ptr(ref mut_ty) | TyKind::Ref(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), | |
538 | _ => ty, | |
539 | } | |
540 | } | |
541 | ||
542 | /// Returns the base type for references and raw pointers, and count reference | |
543 | /// depth. | |
544 | pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { | |
545 | fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) { | |
546 | match ty.kind() { | |
547 | ty::Ref(_, ty, _) => inner(*ty, depth + 1), | |
548 | _ => (ty, depth), | |
549 | } | |
550 | } | |
551 | inner(ty, 0) | |
552 | } | |
553 | ||
554 | /// Returns `true` if types `a` and `b` are same types having same `Const` generic args, | |
555 | /// otherwise returns `false` | |
556 | pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { | |
557 | match (&a.kind(), &b.kind()) { | |
558 | (&ty::Adt(did_a, args_a), &ty::Adt(did_b, args_b)) => { | |
559 | if did_a != did_b { | |
560 | return false; | |
561 | } | |
562 | ||
563 | args_a | |
564 | .iter() | |
565 | .zip(args_b.iter()) | |
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 | } | |
577 | ||
578 | /// Checks if a given type looks safe to be uninitialized. | |
579 | pub 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`. | |
586 | fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { | |
587 | match *ty.kind() { | |
588 | // The array length may be polymorphic, let's try the inner type. | |
589 | ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component), | |
590 | // Peek through tuples and try their fallbacks. | |
591 | ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)), | |
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. | |
599 | _ => false, | |
600 | } | |
601 | } | |
602 | ||
603 | /// Gets an iterator over all predicates which apply to the given item. | |
604 | pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(ty::Clause<'_>, Span)> { | |
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 | } | |
615 | ||
616 | /// A signature for a function like type. | |
617 | #[derive(Clone, Copy)] | |
618 | pub enum ExprFnSig<'tcx> { | |
619 | Sig(Binder<'tcx, FnSig<'tcx>>, Option<DefId>), | |
620 | Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>), | |
621 | Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>, Option<DefId>), | |
622 | } | |
623 | impl<'tcx> ExprFnSig<'tcx> { | |
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>>> { | |
627 | match self { | |
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])), | |
636 | Self::Trait(inputs, _, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])), | |
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 | )), | |
659 | Self::Trait(inputs, _, _) => Some((None, inputs.map_bound(|ty| ty.tuple_fields()[i]))), | |
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 { | |
667 | Self::Sig(sig, _) | Self::Closure(_, sig) => Some(sig.output()), | |
668 | Self::Trait(_, output, _) => output, | |
669 | } | |
670 | } | |
671 | ||
672 | pub fn predicates_id(&self) -> Option<DefId> { | |
673 | if let ExprFnSig::Sig(_, id) | ExprFnSig::Trait(_, _, id) = *self { | |
674 | id | |
675 | } else { | |
676 | None | |
677 | } | |
678 | } | |
679 | } | |
680 | ||
681 | /// If the expression is function like, get the signature for it. | |
682 | pub 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) { | |
684 | Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).instantiate_identity(), Some(id))) | |
685 | } else { | |
686 | ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs()) | |
687 | } | |
688 | } | |
689 | ||
690 | /// If the type is function like, get the signature for it. | |
691 | pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> { | |
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 | }, | |
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( | |
704 | cx, | |
705 | ty, | |
706 | cx.tcx.item_bounds(def_id).iter_instantiated(cx.tcx, args), | |
707 | cx.tcx.opt_parent(def_id), | |
708 | ), | |
709 | ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), | |
710 | ty::Dynamic(bounds, _, _) => { | |
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())); | |
722 | Some(ExprFnSig::Trait(bound.map_bound(|b| b.args.type_at(0)), output, None)) | |
723 | }, | |
724 | _ => None, | |
725 | } | |
726 | }, | |
727 | ty::Alias(ty::Projection, proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) { | |
728 | Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty), | |
729 | _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)), | |
730 | }, | |
731 | ty::Param(_) => sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None), | |
732 | _ => None, | |
733 | } | |
734 | } | |
735 | ||
736 | fn sig_from_bounds<'tcx>( | |
737 | cx: &LateContext<'tcx>, | |
738 | ty: Ty<'tcx>, | |
739 | predicates: impl IntoIterator<Item = ty::Clause<'tcx>>, | |
740 | predicates_id: Option<DefId>, | |
741 | ) -> Option<ExprFnSig<'tcx>> { | |
742 | let mut inputs = None; | |
743 | let mut output = None; | |
744 | let lang_items = cx.tcx.lang_items(); | |
745 | ||
746 | for pred in predicates { | |
747 | match pred.kind().skip_binder() { | |
748 | ty::ClauseKind::Trait(p) | |
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 | { | |
754 | let i = pred.kind().rebind(p.trait_ref.args.type_at(1)); | |
755 | if inputs.map_or(false, |inputs| i != inputs) { | |
756 | // Multiple different fn trait impls. Is this even allowed? | |
757 | return None; | |
758 | } | |
759 | inputs = Some(i); | |
760 | }, | |
761 | ty::ClauseKind::Projection(p) | |
762 | if Some(p.projection_ty.def_id) == lang_items.fn_once_output() && p.projection_ty.self_ty() == ty => | |
763 | { | |
764 | if output.is_some() { | |
765 | // Multiple different fn trait impls. Is this even allowed? | |
766 | return None; | |
767 | } | |
768 | output = Some(pred.kind().rebind(p.term.ty().unwrap())); | |
769 | }, | |
770 | _ => (), | |
771 | } | |
772 | } | |
773 | ||
774 | inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id)) | |
775 | } | |
776 | ||
777 | fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option<ExprFnSig<'tcx>> { | |
778 | let mut inputs = None; | |
779 | let mut output = None; | |
780 | let lang_items = cx.tcx.lang_items(); | |
781 | ||
782 | for (pred, _) in cx | |
783 | .tcx | |
784 | .explicit_item_bounds(ty.def_id) | |
785 | .iter_instantiated_copied(cx.tcx, ty.args) | |
786 | { | |
787 | match pred.kind().skip_binder() { | |
788 | ty::ClauseKind::Trait(p) | |
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 | { | |
793 | let i = pred.kind().rebind(p.trait_ref.args.type_at(1)); | |
794 | ||
795 | if inputs.map_or(false, |inputs| inputs != i) { | |
796 | // Multiple different fn trait impls. Is this even allowed? | |
797 | return None; | |
798 | } | |
799 | inputs = Some(i); | |
800 | }, | |
801 | ty::ClauseKind::Projection(p) if Some(p.projection_ty.def_id) == lang_items.fn_once_output() => { | |
802 | if output.is_some() { | |
803 | // Multiple different fn trait impls. Is this even allowed? | |
804 | return None; | |
805 | } | |
806 | output = pred.kind().rebind(p.term.ty()).transpose(); | |
807 | }, | |
808 | _ => (), | |
809 | } | |
810 | } | |
811 | ||
812 | inputs.map(|ty| ExprFnSig::Trait(ty, output, None)) | |
813 | } | |
814 | ||
815 | #[derive(Clone, Copy)] | |
816 | pub enum EnumValue { | |
817 | Unsigned(u128), | |
818 | Signed(i128), | |
819 | } | |
820 | impl 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 | ||
830 | /// Attempts to read the given constant as though it were an enum value. | |
831 | #[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] | |
832 | pub 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) { | |
834 | match tcx.type_of(id).instantiate_identity().kind() { | |
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. | |
859 | pub 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. | |
872 | pub 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 | |
876 | && name == rustc_span::sym::c_void | |
877 | { | |
878 | true | |
879 | } else { | |
880 | false | |
881 | } | |
882 | } | |
883 | ||
884 | pub 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 | } | |
892 | impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow<B>> TypeVisitor<TyCtxt<'tcx>> for V<F> { | |
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 | } | |
901 | fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> ControlFlow<Self::BreakTy> { | |
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 | ||
911 | pub struct AdtVariantInfo { | |
912 | pub ind: usize, | |
913 | pub size: u64, | |
914 | ||
915 | /// (ind, size) | |
916 | pub fields_size: Vec<(usize, u64)>, | |
917 | } | |
918 | ||
919 | impl 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 | ||
947 | /// Gets the struct or enum variant from the given `Res` | |
948 | pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<(AdtDef<'tcx>, &'tcx VariantDef)> { | |
949 | match res { | |
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 | }, | |
962 | Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => { | |
963 | let var_id = cx.tcx.parent(id); | |
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) => { | |
968 | let adt = cx.tcx.type_of(id).instantiate_identity().ty_adt_def().unwrap(); | |
969 | Some((adt, adt.non_enum_variant())) | |
970 | }, | |
971 | _ => None, | |
972 | } | |
973 | } | |
974 | ||
975 | /// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`. | |
976 | pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [ty::Clause<'_>]) -> bool { | |
977 | let ty::Param(ty) = *ty.kind() else { | |
978 | return false; | |
979 | }; | |
980 | let lang = tcx.lang_items(); | |
981 | let (Some(fn_once_id), Some(fn_mut_id), Some(fn_id)) = (lang.fn_once_trait(), lang.fn_mut_trait(), lang.fn_trait()) | |
982 | else { | |
983 | return false; | |
984 | }; | |
985 | predicates | |
986 | .iter() | |
987 | .try_fold(false, |found, p| { | |
988 | if let ty::ClauseKind::Trait(p) = p.kind().skip_binder() | |
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 | } | |
1003 | ||
1004 | /// Comes up with an "at least" guesstimate for the type's size, not taking into | |
1005 | /// account the layout of type parameters. | |
1006 | pub 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, | |
1013 | (Err(_), ty::Tuple(list)) => list.iter().map(|t| approx_ty_size(cx, t)).sum(), | |
1014 | (Err(_), ty::Array(t, n)) => { | |
1015 | n.try_eval_target_usize(cx.tcx, cx.param_env).unwrap_or_default() * approx_ty_size(cx, *t) | |
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 | } | |
1053 | ||
1054 | /// Asserts that the given arguments match the generic parameters of the given item. | |
1055 | #[allow(dead_code)] | |
1056 | fn 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. | |
1103 | pub 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 | ||
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 | |
1112 | /// arguments are given, and that the correct kinds of arguments are given (lifetime, | |
1113 | /// constant or type). This will not check if type normalization would succeed. | |
1114 | pub fn make_projection<'tcx>( | |
1115 | tcx: TyCtxt<'tcx>, | |
1116 | container_id: DefId, | |
1117 | assoc_ty: Symbol, | |
1118 | args: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>, | |
1119 | ) -> Option<AliasTy<'tcx>> { | |
1120 | fn helper<'tcx>( | |
1121 | tcx: TyCtxt<'tcx>, | |
1122 | container_id: DefId, | |
1123 | assoc_ty: Symbol, | |
1124 | args: GenericArgsRef<'tcx>, | |
1125 | ) -> Option<AliasTy<'tcx>> { | |
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 { | |
1132 | debug_assert!(false, "type `{assoc_ty}` not found in `{container_id:?}`"); | |
1133 | return None; | |
1134 | }; | |
1135 | #[cfg(debug_assertions)] | |
1136 | assert_generic_args_match(tcx, assoc_item.def_id, args); | |
1137 | ||
1138 | Some(tcx.mk_alias_ty(assoc_item.def_id, args)) | |
1139 | } | |
1140 | helper( | |
1141 | tcx, | |
1142 | container_id, | |
1143 | assoc_ty, | |
1144 | tcx.mk_args_from_iter(args.into_iter().map(Into::into)), | |
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 | |
1151 | /// arguments, and as such, will only return `None` when debug assertions are disabled in order | |
1152 | /// to prevent ICE's. With debug assertions enabled this will check that type normalization | |
1153 | /// succeeds as well as everything checked by `make_projection`. | |
1154 | pub fn make_normalized_projection<'tcx>( | |
1155 | tcx: TyCtxt<'tcx>, | |
1156 | param_env: ParamEnv<'tcx>, | |
1157 | container_id: DefId, | |
1158 | assoc_ty: Symbol, | |
1159 | args: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>, | |
1160 | ) -> Option<Ty<'tcx>> { | |
1161 | fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> { | |
1162 | #[cfg(debug_assertions)] | |
1163 | if let Some((i, arg)) = ty.args.iter().enumerate().find(|(_, arg)| arg.has_late_bound_regions()) { | |
1164 | debug_assert!( | |
1165 | false, | |
1166 | "args contain late-bound region at index `{i}` which can't be normalized.\n\ | |
1167 | use `TyCtxt::erase_late_bound_regions`\n\ | |
1168 | note: arg is `{arg:#?}`", | |
1169 | ); | |
1170 | return None; | |
1171 | } | |
1172 | match tcx.try_normalize_erasing_regions(param_env, Ty::new_projection(tcx, ty.def_id, ty.args)) { | |
1173 | Ok(ty) => Some(ty), | |
1174 | Err(e) => { | |
1175 | debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}"); | |
1176 | None | |
1177 | }, | |
1178 | } | |
1179 | } | |
1180 | helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, args)?) | |
1181 | } | |
1182 | ||
1183 | /// Check if given type has inner mutability such as [`std::cell::Cell`] or [`std::cell::RefCell`] | |
1184 | /// etc. | |
1185 | pub 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)), | |
1195 | ty::Adt(def, args) => { | |
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 | |
1216 | args.types().any(|ty| is_interior_mut_ty(cx, ty)) | |
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 | } | |
1226 | ||
1227 | pub fn make_normalized_projection_with_regions<'tcx>( | |
1228 | tcx: TyCtxt<'tcx>, | |
1229 | param_env: ParamEnv<'tcx>, | |
1230 | container_id: DefId, | |
1231 | assoc_ty: Symbol, | |
1232 | args: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>, | |
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)] | |
1236 | if let Some((i, arg)) = ty.args.iter().enumerate().find(|(_, arg)| arg.has_late_bound_regions()) { | |
1237 | debug_assert!( | |
1238 | false, | |
1239 | "args contain late-bound region at index `{i}` which can't be normalized.\n\ | |
1240 | use `TyCtxt::erase_late_bound_regions`\n\ | |
1241 | note: arg is `{arg:#?}`", | |
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) | |
1250 | .query_normalize(Ty::new_projection(tcx, ty.def_id, ty.args)) | |
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 | } | |
1259 | helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, args)?) | |
1260 | } | |
1261 | ||
1262 | pub 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 | } |