hir_ty/
lib.rs

1//! The type system. We currently use this to infer types for completion, hover
2//! information and various assists.
3
4#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
5// It's useful to refer to code that is private in doc comments.
6#![allow(rustdoc::private_intra_doc_links)]
7
8// FIXME: We used to import `rustc_*` deps from `rustc_private` with `feature = "in-rust-tree" but
9// temporarily switched to crates.io versions due to hardships that working on them from rustc
10// demands corresponding changes on rust-analyzer at the same time.
11// For details, see the zulip discussion below:
12// https://rust-lang.zulipchat.com/#narrow/channel/185405-t-compiler.2Frust-analyzer/topic/relying.20on.20in-tree.20.60rustc_type_ir.60.2F.60rustc_next_trait_solver.60/with/541055689
13
14extern crate ra_ap_rustc_index as rustc_index;
15
16extern crate ra_ap_rustc_abi as rustc_abi;
17
18extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
19
20extern crate ra_ap_rustc_ast_ir as rustc_ast_ir;
21
22extern crate ra_ap_rustc_type_ir as rustc_type_ir;
23
24extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver;
25
26extern crate self as hir_ty;
27
28mod infer;
29mod inhabitedness;
30mod lower;
31pub mod next_solver;
32mod opaques;
33mod specialization;
34mod target_feature;
35mod utils;
36mod variance;
37
38pub mod autoderef;
39pub mod consteval;
40pub mod db;
41pub mod diagnostics;
42pub mod display;
43pub mod drop;
44pub mod dyn_compatibility;
45pub mod generics;
46pub mod lang_items;
47pub mod layout;
48pub mod method_resolution;
49pub mod mir;
50pub mod primitive;
51pub mod traits;
52
53#[cfg(test)]
54mod test_db;
55#[cfg(test)]
56mod tests;
57
58use std::hash::Hash;
59
60use hir_def::{CallableDefId, TypeOrConstParamId, type_ref::Rawness};
61use hir_expand::name::Name;
62use indexmap::{IndexMap, map::Entry};
63use intern::{Symbol, sym};
64use mir::{MirEvalError, VTableMap};
65use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
66use rustc_type_ir::{
67    BoundVarIndexKind, TypeSuperVisitable, TypeVisitableExt, UpcastFrom,
68    inherent::{IntoKind, SliceLike, Ty as _},
69};
70use syntax::ast::{ConstArg, make};
71use traits::FnTrait;
72
73use crate::{
74    db::HirDatabase,
75    display::{DisplayTarget, HirDisplay},
76    infer::unify::InferenceTable,
77    next_solver::{
78        AliasTy, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, Canonical,
79        CanonicalVarKind, CanonicalVars, Const, ConstKind, DbInterner, FnSig, PolyFnSig, Predicate,
80        Region, RegionKind, TraitRef, Ty, TyKind, Tys, abi,
81    },
82};
83
84pub use autoderef::autoderef;
85pub use infer::{
86    Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult,
87    InferenceTyDiagnosticSource, OverloadedDeref, PointerCast,
88    cast::CastError,
89    closure::analysis::{CaptureKind, CapturedItem},
90    could_coerce, could_unify, could_unify_deeply,
91};
92pub use lower::{
93    GenericPredicates, ImplTraits, LifetimeElisionKind, TyDefId, TyLoweringContext, ValueTyDefId,
94    associated_type_shorthand_candidates, diagnostics::*,
95};
96pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db};
97pub use target_feature::TargetFeatures;
98pub use traits::{ParamEnvAndCrate, check_orphan_rules};
99pub use utils::{
100    TargetFeatureIsSafeInTarget, Unsafety, all_super_traits, direct_super_traits,
101    is_fn_unsafe_to_call, target_feature_is_safe_in_target,
102};
103
104/// A constant can have reference to other things. Memory map job is holding
105/// the necessary bits of memory of the const eval session to keep the constant
106/// meaningful.
107#[derive(Debug, Default, Clone, PartialEq, Eq)]
108pub enum MemoryMap<'db> {
109    #[default]
110    Empty,
111    Simple(Box<[u8]>),
112    Complex(Box<ComplexMemoryMap<'db>>),
113}
114
115#[derive(Debug, Default, Clone, PartialEq, Eq)]
116pub struct ComplexMemoryMap<'db> {
117    memory: IndexMap<usize, Box<[u8]>, FxBuildHasher>,
118    vtable: VTableMap<'db>,
119}
120
121impl ComplexMemoryMap<'_> {
122    fn insert(&mut self, addr: usize, val: Box<[u8]>) {
123        match self.memory.entry(addr) {
124            Entry::Occupied(mut e) => {
125                if e.get().len() < val.len() {
126                    e.insert(val);
127                }
128            }
129            Entry::Vacant(e) => {
130                e.insert(val);
131            }
132        }
133    }
134}
135
136impl<'db> MemoryMap<'db> {
137    pub fn vtable_ty(&self, id: usize) -> Result<Ty<'db>, MirEvalError<'db>> {
138        match self {
139            MemoryMap::Empty | MemoryMap::Simple(_) => Err(MirEvalError::InvalidVTableId(id)),
140            MemoryMap::Complex(cm) => cm.vtable.ty(id),
141        }
142    }
143
144    fn simple(v: Box<[u8]>) -> Self {
145        MemoryMap::Simple(v)
146    }
147
148    /// This functions convert each address by a function `f` which gets the byte intervals and assign an address
149    /// to them. It is useful when you want to load a constant with a memory map in a new memory. You can pass an
150    /// allocator function as `f` and it will return a mapping of old addresses to new addresses.
151    fn transform_addresses(
152        &self,
153        mut f: impl FnMut(&[u8], usize) -> Result<usize, MirEvalError<'db>>,
154    ) -> Result<FxHashMap<usize, usize>, MirEvalError<'db>> {
155        let mut transform = |(addr, val): (&usize, &[u8])| {
156            let addr = *addr;
157            let align = if addr == 0 { 64 } else { (addr - (addr & (addr - 1))).min(64) };
158            f(val, align).map(|it| (addr, it))
159        };
160        match self {
161            MemoryMap::Empty => Ok(Default::default()),
162            MemoryMap::Simple(m) => transform((&0, m)).map(|(addr, val)| {
163                let mut map = FxHashMap::with_capacity_and_hasher(1, rustc_hash::FxBuildHasher);
164                map.insert(addr, val);
165                map
166            }),
167            MemoryMap::Complex(cm) => {
168                cm.memory.iter().map(|(addr, val)| transform((addr, val))).collect()
169            }
170        }
171    }
172
173    fn get(&self, addr: usize, size: usize) -> Option<&[u8]> {
174        if size == 0 {
175            Some(&[])
176        } else {
177            match self {
178                MemoryMap::Empty => Some(&[]),
179                MemoryMap::Simple(m) if addr == 0 => m.get(0..size),
180                MemoryMap::Simple(_) => None,
181                MemoryMap::Complex(cm) => cm.memory.get(&addr)?.get(0..size),
182            }
183        }
184    }
185}
186
187/// Return an index of a parameter in the generic type parameter list by it's id.
188pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<usize> {
189    generics::generics(db, id.parent).type_or_const_param_idx(id)
190}
191
192#[derive(Debug, Copy, Clone, Eq)]
193pub enum FnAbi {
194    Aapcs,
195    AapcsUnwind,
196    AvrInterrupt,
197    AvrNonBlockingInterrupt,
198    C,
199    CCmseNonsecureCall,
200    CCmseNonsecureEntry,
201    CDecl,
202    CDeclUnwind,
203    CUnwind,
204    Efiapi,
205    Fastcall,
206    FastcallUnwind,
207    Msp430Interrupt,
208    PtxKernel,
209    RiscvInterruptM,
210    RiscvInterruptS,
211    Rust,
212    RustCall,
213    RustCold,
214    RustIntrinsic,
215    Stdcall,
216    StdcallUnwind,
217    System,
218    SystemUnwind,
219    Sysv64,
220    Sysv64Unwind,
221    Thiscall,
222    ThiscallUnwind,
223    Unadjusted,
224    Vectorcall,
225    VectorcallUnwind,
226    Wasm,
227    Win64,
228    Win64Unwind,
229    X86Interrupt,
230    Unknown,
231}
232
233impl PartialEq for FnAbi {
234    fn eq(&self, _other: &Self) -> bool {
235        // FIXME: Proper equality breaks `coercion::two_closures_lub` test
236        true
237    }
238}
239
240impl Hash for FnAbi {
241    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
242        // Required because of the FIXME above and due to us implementing `Eq`, without this
243        // we would break the `Hash` + `Eq` contract
244        core::mem::discriminant(&Self::Unknown).hash(state);
245    }
246}
247
248impl FnAbi {
249    #[rustfmt::skip]
250    pub fn from_symbol(s: &Symbol) -> FnAbi {
251        match s {
252            s if *s == sym::aapcs_dash_unwind => FnAbi::AapcsUnwind,
253            s if *s == sym::aapcs => FnAbi::Aapcs,
254            s if *s == sym::avr_dash_interrupt => FnAbi::AvrInterrupt,
255            s if *s == sym::avr_dash_non_dash_blocking_dash_interrupt => FnAbi::AvrNonBlockingInterrupt,
256            s if *s == sym::C_dash_cmse_dash_nonsecure_dash_call => FnAbi::CCmseNonsecureCall,
257            s if *s == sym::C_dash_cmse_dash_nonsecure_dash_entry => FnAbi::CCmseNonsecureEntry,
258            s if *s == sym::C_dash_unwind => FnAbi::CUnwind,
259            s if *s == sym::C => FnAbi::C,
260            s if *s == sym::cdecl_dash_unwind => FnAbi::CDeclUnwind,
261            s if *s == sym::cdecl => FnAbi::CDecl,
262            s if *s == sym::efiapi => FnAbi::Efiapi,
263            s if *s == sym::fastcall_dash_unwind => FnAbi::FastcallUnwind,
264            s if *s == sym::fastcall => FnAbi::Fastcall,
265            s if *s == sym::msp430_dash_interrupt => FnAbi::Msp430Interrupt,
266            s if *s == sym::ptx_dash_kernel => FnAbi::PtxKernel,
267            s if *s == sym::riscv_dash_interrupt_dash_m => FnAbi::RiscvInterruptM,
268            s if *s == sym::riscv_dash_interrupt_dash_s => FnAbi::RiscvInterruptS,
269            s if *s == sym::rust_dash_call => FnAbi::RustCall,
270            s if *s == sym::rust_dash_cold => FnAbi::RustCold,
271            s if *s == sym::rust_dash_intrinsic => FnAbi::RustIntrinsic,
272            s if *s == sym::Rust => FnAbi::Rust,
273            s if *s == sym::stdcall_dash_unwind => FnAbi::StdcallUnwind,
274            s if *s == sym::stdcall => FnAbi::Stdcall,
275            s if *s == sym::system_dash_unwind => FnAbi::SystemUnwind,
276            s if *s == sym::system => FnAbi::System,
277            s if *s == sym::sysv64_dash_unwind => FnAbi::Sysv64Unwind,
278            s if *s == sym::sysv64 => FnAbi::Sysv64,
279            s if *s == sym::thiscall_dash_unwind => FnAbi::ThiscallUnwind,
280            s if *s == sym::thiscall => FnAbi::Thiscall,
281            s if *s == sym::unadjusted => FnAbi::Unadjusted,
282            s if *s == sym::vectorcall_dash_unwind => FnAbi::VectorcallUnwind,
283            s if *s == sym::vectorcall => FnAbi::Vectorcall,
284            s if *s == sym::wasm => FnAbi::Wasm,
285            s if *s == sym::win64_dash_unwind => FnAbi::Win64Unwind,
286            s if *s == sym::win64 => FnAbi::Win64,
287            s if *s == sym::x86_dash_interrupt => FnAbi::X86Interrupt,
288            _ => FnAbi::Unknown,
289        }
290    }
291
292    pub fn as_str(self) -> &'static str {
293        match self {
294            FnAbi::Aapcs => "aapcs",
295            FnAbi::AapcsUnwind => "aapcs-unwind",
296            FnAbi::AvrInterrupt => "avr-interrupt",
297            FnAbi::AvrNonBlockingInterrupt => "avr-non-blocking-interrupt",
298            FnAbi::C => "C",
299            FnAbi::CCmseNonsecureCall => "C-cmse-nonsecure-call",
300            FnAbi::CCmseNonsecureEntry => "C-cmse-nonsecure-entry",
301            FnAbi::CDecl => "C-decl",
302            FnAbi::CDeclUnwind => "cdecl-unwind",
303            FnAbi::CUnwind => "C-unwind",
304            FnAbi::Efiapi => "efiapi",
305            FnAbi::Fastcall => "fastcall",
306            FnAbi::FastcallUnwind => "fastcall-unwind",
307            FnAbi::Msp430Interrupt => "msp430-interrupt",
308            FnAbi::PtxKernel => "ptx-kernel",
309            FnAbi::RiscvInterruptM => "riscv-interrupt-m",
310            FnAbi::RiscvInterruptS => "riscv-interrupt-s",
311            FnAbi::Rust => "Rust",
312            FnAbi::RustCall => "rust-call",
313            FnAbi::RustCold => "rust-cold",
314            FnAbi::RustIntrinsic => "rust-intrinsic",
315            FnAbi::Stdcall => "stdcall",
316            FnAbi::StdcallUnwind => "stdcall-unwind",
317            FnAbi::System => "system",
318            FnAbi::SystemUnwind => "system-unwind",
319            FnAbi::Sysv64 => "sysv64",
320            FnAbi::Sysv64Unwind => "sysv64-unwind",
321            FnAbi::Thiscall => "thiscall",
322            FnAbi::ThiscallUnwind => "thiscall-unwind",
323            FnAbi::Unadjusted => "unadjusted",
324            FnAbi::Vectorcall => "vectorcall",
325            FnAbi::VectorcallUnwind => "vectorcall-unwind",
326            FnAbi::Wasm => "wasm",
327            FnAbi::Win64 => "win64",
328            FnAbi::Win64Unwind => "win64-unwind",
329            FnAbi::X86Interrupt => "x86-interrupt",
330            FnAbi::Unknown => "unknown-abi",
331        }
332    }
333}
334
335#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
336pub enum ImplTraitId<'db> {
337    ReturnTypeImplTrait(hir_def::FunctionId, next_solver::ImplTraitIdx<'db>),
338    TypeAliasImplTrait(hir_def::TypeAliasId, next_solver::ImplTraitIdx<'db>),
339}
340
341/// 'Canonicalizes' the `t` by replacing any errors with new variables. Also
342/// ensures there are no unbound variables or inference variables anywhere in
343/// the `t`.
344pub fn replace_errors_with_variables<'db, T>(interner: DbInterner<'db>, t: &T) -> Canonical<'db, T>
345where
346    T: rustc_type_ir::TypeFoldable<DbInterner<'db>> + Clone,
347{
348    use rustc_type_ir::{FallibleTypeFolder, TypeSuperFoldable};
349    struct ErrorReplacer<'db> {
350        interner: DbInterner<'db>,
351        vars: Vec<CanonicalVarKind<'db>>,
352        binder: rustc_type_ir::DebruijnIndex,
353    }
354    impl<'db> FallibleTypeFolder<DbInterner<'db>> for ErrorReplacer<'db> {
355        #[cfg(debug_assertions)]
356        type Error = ();
357        #[cfg(not(debug_assertions))]
358        type Error = std::convert::Infallible;
359
360        fn cx(&self) -> DbInterner<'db> {
361            self.interner
362        }
363
364        fn try_fold_binder<T>(&mut self, t: Binder<'db, T>) -> Result<Binder<'db, T>, Self::Error>
365        where
366            T: rustc_type_ir::TypeFoldable<DbInterner<'db>>,
367        {
368            self.binder.shift_in(1);
369            let result = t.try_super_fold_with(self);
370            self.binder.shift_out(1);
371            result
372        }
373
374        fn try_fold_ty(&mut self, t: Ty<'db>) -> Result<Ty<'db>, Self::Error> {
375            if !t.has_type_flags(
376                rustc_type_ir::TypeFlags::HAS_ERROR
377                    | rustc_type_ir::TypeFlags::HAS_TY_INFER
378                    | rustc_type_ir::TypeFlags::HAS_CT_INFER
379                    | rustc_type_ir::TypeFlags::HAS_RE_INFER,
380            ) {
381                return Ok(t);
382            }
383
384            #[cfg(debug_assertions)]
385            let error = || Err(());
386            #[cfg(not(debug_assertions))]
387            let error = || Ok(Ty::new_error(self.interner, crate::next_solver::ErrorGuaranteed));
388
389            match t.kind() {
390                TyKind::Error(_) => {
391                    let var = rustc_type_ir::BoundVar::from_usize(self.vars.len());
392                    self.vars.push(CanonicalVarKind::Ty {
393                        ui: rustc_type_ir::UniverseIndex::ZERO,
394                        sub_root: var,
395                    });
396                    Ok(Ty::new_bound(
397                        self.interner,
398                        self.binder,
399                        BoundTy { var, kind: BoundTyKind::Anon },
400                    ))
401                }
402                TyKind::Infer(_) => error(),
403                TyKind::Bound(BoundVarIndexKind::Bound(index), _) if index > self.binder => error(),
404                _ => t.try_super_fold_with(self),
405            }
406        }
407
408        fn try_fold_const(&mut self, ct: Const<'db>) -> Result<Const<'db>, Self::Error> {
409            if !ct.has_type_flags(
410                rustc_type_ir::TypeFlags::HAS_ERROR
411                    | rustc_type_ir::TypeFlags::HAS_TY_INFER
412                    | rustc_type_ir::TypeFlags::HAS_CT_INFER
413                    | rustc_type_ir::TypeFlags::HAS_RE_INFER,
414            ) {
415                return Ok(ct);
416            }
417
418            #[cfg(debug_assertions)]
419            let error = || Err(());
420            #[cfg(not(debug_assertions))]
421            let error = || Ok(Const::error(self.interner));
422
423            match ct.kind() {
424                ConstKind::Error(_) => {
425                    let var = rustc_type_ir::BoundVar::from_usize(self.vars.len());
426                    self.vars.push(CanonicalVarKind::Const(rustc_type_ir::UniverseIndex::ZERO));
427                    Ok(Const::new_bound(self.interner, self.binder, BoundConst { var }))
428                }
429                ConstKind::Infer(_) => error(),
430                ConstKind::Bound(BoundVarIndexKind::Bound(index), _) if index > self.binder => {
431                    error()
432                }
433                _ => ct.try_super_fold_with(self),
434            }
435        }
436
437        fn try_fold_region(&mut self, region: Region<'db>) -> Result<Region<'db>, Self::Error> {
438            #[cfg(debug_assertions)]
439            let error = || Err(());
440            #[cfg(not(debug_assertions))]
441            let error = || Ok(Region::error(self.interner));
442
443            match region.kind() {
444                RegionKind::ReError(_) => {
445                    let var = rustc_type_ir::BoundVar::from_usize(self.vars.len());
446                    self.vars.push(CanonicalVarKind::Region(rustc_type_ir::UniverseIndex::ZERO));
447                    Ok(Region::new_bound(
448                        self.interner,
449                        self.binder,
450                        BoundRegion { var, kind: BoundRegionKind::Anon },
451                    ))
452                }
453                RegionKind::ReVar(_) => error(),
454                RegionKind::ReBound(BoundVarIndexKind::Bound(index), _) if index > self.binder => {
455                    error()
456                }
457                _ => Ok(region),
458            }
459        }
460    }
461
462    let mut error_replacer =
463        ErrorReplacer { vars: Vec::new(), binder: rustc_type_ir::DebruijnIndex::ZERO, interner };
464    let value = match t.clone().try_fold_with(&mut error_replacer) {
465        Ok(t) => t,
466        Err(_) => panic!("Encountered unbound or inference vars in {t:?}"),
467    };
468    Canonical {
469        value,
470        max_universe: rustc_type_ir::UniverseIndex::ZERO,
471        variables: CanonicalVars::new_from_iter(interner, error_replacer.vars),
472    }
473}
474
475/// To be used from `hir` only.
476pub fn callable_sig_from_fn_trait<'db>(
477    self_ty: Ty<'db>,
478    trait_env: ParamEnvAndCrate<'db>,
479    db: &'db dyn HirDatabase,
480) -> Option<(FnTrait, PolyFnSig<'db>)> {
481    let mut table = InferenceTable::new(db, trait_env.param_env, trait_env.krate, None);
482    let lang_items = table.interner().lang_items();
483
484    let fn_once_trait = FnTrait::FnOnce.get_id(lang_items)?;
485    let output_assoc_type = fn_once_trait
486        .trait_items(db)
487        .associated_type_by_name(&Name::new_symbol_root(sym::Output))?;
488
489    // Register two obligations:
490    // - Self: FnOnce<?args_ty>
491    // - <Self as FnOnce<?args_ty>>::Output == ?ret_ty
492    let args_ty = table.next_ty_var();
493    let args = [self_ty, args_ty];
494    let trait_ref = TraitRef::new(table.interner(), fn_once_trait.into(), args);
495    let projection = Ty::new_alias(
496        table.interner(),
497        rustc_type_ir::AliasTyKind::Projection,
498        AliasTy::new(table.interner(), output_assoc_type.into(), args),
499    );
500
501    let pred = Predicate::upcast_from(trait_ref, table.interner());
502    if !table.try_obligation(pred).no_solution() {
503        table.register_obligation(pred);
504        let return_ty = table.normalize_alias_ty(projection);
505        for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
506            let fn_x_trait = fn_x.get_id(lang_items)?;
507            let trait_ref = TraitRef::new(table.interner(), fn_x_trait.into(), args);
508            if !table
509                .try_obligation(Predicate::upcast_from(trait_ref, table.interner()))
510                .no_solution()
511            {
512                let ret_ty = table.resolve_completely(return_ty);
513                let args_ty = table.resolve_completely(args_ty);
514                let TyKind::Tuple(params) = args_ty.kind() else {
515                    return None;
516                };
517                let inputs_and_output = Tys::new_from_iter(
518                    table.interner(),
519                    params.iter().chain(std::iter::once(ret_ty)),
520                );
521
522                return Some((
523                    fn_x,
524                    Binder::dummy(FnSig {
525                        inputs_and_output,
526                        c_variadic: false,
527                        safety: abi::Safety::Safe,
528                        abi: FnAbi::RustCall,
529                    }),
530                ));
531            }
532        }
533        unreachable!("It should at least implement FnOnce at this point");
534    } else {
535        None
536    }
537}
538
539struct ParamCollector {
540    params: FxHashSet<TypeOrConstParamId>,
541}
542
543impl<'db> rustc_type_ir::TypeVisitor<DbInterner<'db>> for ParamCollector {
544    type Result = ();
545
546    fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result {
547        if let TyKind::Param(param) = ty.kind() {
548            self.params.insert(param.id.into());
549        }
550
551        ty.super_visit_with(self);
552    }
553
554    fn visit_const(&mut self, konst: Const<'db>) -> Self::Result {
555        if let ConstKind::Param(param) = konst.kind() {
556            self.params.insert(param.id.into());
557        }
558
559        konst.super_visit_with(self);
560    }
561}
562
563/// Returns unique params for types and consts contained in `value`.
564pub fn collect_params<'db, T>(value: &T) -> Vec<TypeOrConstParamId>
565where
566    T: ?Sized + rustc_type_ir::TypeVisitable<DbInterner<'db>>,
567{
568    let mut collector = ParamCollector { params: FxHashSet::default() };
569    value.visit_with(&mut collector);
570    Vec::from_iter(collector.params)
571}
572
573struct TypeInferenceVarCollector<'db> {
574    type_inference_vars: Vec<Ty<'db>>,
575}
576
577impl<'db> rustc_type_ir::TypeVisitor<DbInterner<'db>> for TypeInferenceVarCollector<'db> {
578    type Result = ();
579
580    fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result {
581        use crate::rustc_type_ir::Flags;
582        if ty.is_ty_var() {
583            self.type_inference_vars.push(ty);
584        } else if ty.flags().intersects(rustc_type_ir::TypeFlags::HAS_TY_INFER) {
585            ty.super_visit_with(self);
586        } else {
587            // Fast path: don't visit inner types (e.g. generic arguments) when `flags` indicate
588            // that there are no placeholders.
589        }
590    }
591}
592
593pub fn collect_type_inference_vars<'db, T>(value: &T) -> Vec<Ty<'db>>
594where
595    T: ?Sized + rustc_type_ir::TypeVisitable<DbInterner<'db>>,
596{
597    let mut collector = TypeInferenceVarCollector { type_inference_vars: vec![] };
598    value.visit_with(&mut collector);
599    collector.type_inference_vars
600}
601
602pub fn known_const_to_ast<'db>(
603    konst: Const<'db>,
604    db: &'db dyn HirDatabase,
605    display_target: DisplayTarget,
606) -> Option<ConstArg> {
607    Some(make::expr_const_value(konst.display(db, display_target).to_string().as_str()))
608}
609
610#[derive(Debug, Copy, Clone)]
611pub(crate) enum DeclOrigin {
612    LetExpr,
613    /// from `let x = ..`
614    LocalDecl {
615        has_else: bool,
616    },
617}
618
619/// Provides context for checking patterns in declarations. More specifically this
620/// allows us to infer array types if the pattern is irrefutable and allows us to infer
621/// the size of the array. See issue rust-lang/rust#76342.
622#[derive(Debug, Copy, Clone)]
623pub(crate) struct DeclContext {
624    pub(crate) origin: DeclOrigin,
625}
626
627pub fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
628    use std::env;
629    use std::sync::LazyLock;
630    use tracing_subscriber::{Registry, layer::SubscriberExt};
631    use tracing_tree::HierarchicalLayer;
632
633    static ENABLE: LazyLock<bool> = LazyLock::new(|| env::var("CHALK_DEBUG").is_ok());
634    if !*ENABLE {
635        return None;
636    }
637
638    let filter: tracing_subscriber::filter::Targets =
639        env::var("CHALK_DEBUG").ok().and_then(|it| it.parse().ok()).unwrap_or_default();
640    let layer = HierarchicalLayer::default()
641        .with_indent_lines(true)
642        .with_ansi(false)
643        .with_indent_amount(2)
644        .with_writer(std::io::stderr);
645    let subscriber = Registry::default().with(filter).with(layer);
646    Some(tracing::subscriber::set_default(subscriber))
647}