Skip to main content

hir_ty/
opaques.rs

1//! Handling of opaque types, detection of defining scope and hidden type.
2
3use hir_def::{
4    AssocItemId, AssocItemLoc, DefWithBodyId, FunctionId, HasModule, ItemContainerId, TypeAliasId,
5    signatures::ImplSignature,
6};
7use hir_expand::name::Name;
8use la_arena::ArenaMap;
9use rustc_type_ir::inherent::Ty as _;
10use syntax::ast;
11
12use crate::{
13    ImplTraitId, InferBodyId, InferenceResult,
14    db::{HirDatabase, InternedOpaqueTyId},
15    lower::{ImplTraitIdx, SelfAndAssocBounds},
16    next_solver::{
17        DbInterner, ErrorGuaranteed, SolverDefId, StoredEarlyBinder, StoredTy, Ty, TypingMode,
18        infer::{DbInternerInferExt, traits::ObligationCause},
19        obligation_ctxt::ObligationCtxt,
20    },
21};
22
23pub(crate) fn opaque_types_defined_by<'db>(
24    db: &'db dyn HirDatabase,
25    def_id: InferBodyId<'_>,
26    result: &mut Vec<SolverDefId<'db>>,
27) {
28    if let Some(func) = def_id.as_function() {
29        // A function may define its own RPITs.
30        extend_with_opaques(
31            db,
32            SelfAndAssocBounds::return_type_impl_traits(db, func),
33            |opaque_idx| ImplTraitId::ReturnTypeImplTrait(func, opaque_idx),
34            result,
35        );
36    }
37
38    let extend_with_taits = |type_alias| {
39        extend_with_opaques(
40            db,
41            SelfAndAssocBounds::type_alias_impl_traits(db, type_alias),
42            |opaque_idx| ImplTraitId::TypeAliasImplTrait(type_alias, opaque_idx),
43            result,
44        );
45    };
46
47    // Collect opaques from assoc items.
48    let extend_with_atpit_from_assoc_items = |assoc_items: &[(Name, AssocItemId)]| {
49        assoc_items
50            .iter()
51            .filter_map(|&(_, assoc_id)| match assoc_id {
52                AssocItemId::TypeAliasId(it) => Some(it),
53                AssocItemId::FunctionId(_) | AssocItemId::ConstId(_) => None,
54            })
55            .for_each(extend_with_taits);
56    };
57    let extend_with_atpit_from_container = |container| match container {
58        ItemContainerId::ImplId(impl_id)
59            if ImplSignature::of(db, impl_id).target_trait.is_some() =>
60        {
61            extend_with_atpit_from_assoc_items(&impl_id.impl_items(db).items);
62        }
63        ItemContainerId::TraitId(trait_id) => {
64            extend_with_atpit_from_assoc_items(&trait_id.trait_items(db).items);
65        }
66        _ => {}
67    };
68    match def_id {
69        InferBodyId::DefWithBodyId(DefWithBodyId::ConstId(id)) => {
70            extend_with_atpit_from_container(id.loc(db).container)
71        }
72        InferBodyId::DefWithBodyId(DefWithBodyId::FunctionId(id)) => {
73            extend_with_atpit_from_container(id.loc(db).container)
74        }
75        InferBodyId::DefWithBodyId(DefWithBodyId::StaticId(_))
76        | InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(_))
77        | InferBodyId::AnonConstId(_) => {}
78    }
79
80    // FIXME: Collect opaques from `#[define_opaque]`.
81
82    fn extend_with_opaques<'db>(
83        db: &'db dyn HirDatabase,
84        opaques: &[SelfAndAssocBounds],
85        mut make_impl_trait: impl FnMut(ImplTraitIdx) -> ImplTraitId,
86        result: &mut Vec<SolverDefId<'db>>,
87    ) {
88        for (opaque_idx, _) in opaques.iter().enumerate() {
89            let opaque_id = InternedOpaqueTyId::new(
90                db,
91                make_impl_trait(ImplTraitIdx::from_raw(la_arena::RawIdx::from_u32(
92                    opaque_idx as u32,
93                ))),
94            );
95            result.push(opaque_id.into());
96        }
97    }
98}
99
100// These are firewall queries to prevent drawing dependencies between infers:
101
102#[salsa::tracked(returns(ref))]
103pub(crate) fn rpit_hidden_types(
104    db: &dyn HirDatabase,
105    function: FunctionId,
106) -> ArenaMap<ImplTraitIdx, StoredEarlyBinder<StoredTy>> {
107    let infer = InferenceResult::of(db, DefWithBodyId::from(function));
108    let mut result = ArenaMap::new();
109    for (opaque, hidden_type) in infer.return_position_impl_trait_types(db) {
110        result.insert(opaque, StoredEarlyBinder::bind(hidden_type.store()));
111    }
112    result.shrink_to_fit();
113    result
114}
115
116#[salsa::tracked(returns(ref))]
117pub(crate) fn tait_hidden_types(
118    db: &dyn HirDatabase,
119    type_alias: TypeAliasId,
120) -> ArenaMap<ImplTraitIdx, StoredEarlyBinder<StoredTy>> {
121    // Call this first, to not perform redundant work if there are no TAITs.
122    let taits_count = SelfAndAssocBounds::type_alias_impl_traits(db, type_alias).len();
123
124    let loc = type_alias.loc(db);
125    let module = loc.module(db);
126    let interner = DbInterner::new_with(db, module.krate(db));
127    let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
128    let mut ocx = ObligationCtxt::new(&infcx);
129    let cause = ObligationCause::dummy();
130    let param_env = db.trait_environment(type_alias.into());
131
132    let defining_bodies = tait_defining_bodies(db, loc);
133
134    let mut result = ArenaMap::with_capacity(taits_count);
135    for defining_body in defining_bodies {
136        let infer = InferenceResult::of(db, defining_body);
137        for (&opaque, hidden_type) in &infer.type_of_opaque {
138            let ImplTraitId::TypeAliasImplTrait(opaque_owner, opaque_idx) = opaque.loc(db) else {
139                continue;
140            };
141            if opaque_owner != type_alias {
142                continue;
143            }
144            // In the presence of errors, we attempt to create a unified type from all
145            // types. rustc doesn't do that, but this should improve the experience.
146            let hidden_type = infcx.insert_type_vars(hidden_type.as_ref());
147            match result.entry(opaque_idx) {
148                la_arena::Entry::Vacant(entry) => {
149                    entry.insert(StoredEarlyBinder::bind(hidden_type.store()));
150                }
151                la_arena::Entry::Occupied(entry) => {
152                    _ = ocx.eq(
153                        &cause,
154                        param_env,
155                        entry.get().get().instantiate_identity().skip_norm_wip(),
156                        hidden_type,
157                    );
158                }
159            }
160        }
161    }
162
163    _ = ocx.try_evaluate_obligations();
164
165    // Fill missing entries.
166    for idx in 0..taits_count {
167        let idx = la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(idx as u32));
168        match result.entry(idx) {
169            la_arena::Entry::Vacant(entry) => {
170                entry.insert(StoredEarlyBinder::bind(
171                    Ty::new_error(interner, ErrorGuaranteed).store(),
172                ));
173            }
174            la_arena::Entry::Occupied(mut entry) => {
175                let hidden_type = entry.get().get().skip_binder();
176                let hidden_type =
177                    infcx.resolve_vars_if_possible(hidden_type).replace_infer_with_error(interner);
178                *entry.get_mut() = StoredEarlyBinder::bind(hidden_type.store());
179            }
180        }
181    }
182
183    result
184}
185
186fn tait_defining_bodies(
187    db: &dyn HirDatabase,
188    loc: &AssocItemLoc<ast::TypeAlias>,
189) -> Vec<DefWithBodyId> {
190    let from_assoc_items = |assoc_items: &[(Name, AssocItemId)]| {
191        // Associated Type Position Impl Trait.
192        assoc_items
193            .iter()
194            .filter_map(|&(_, assoc_id)| match assoc_id {
195                AssocItemId::FunctionId(it) => Some(it.into()),
196                AssocItemId::ConstId(it) => Some(it.into()),
197                AssocItemId::TypeAliasId(_) => None,
198            })
199            .collect()
200    };
201    match loc.container {
202        ItemContainerId::ImplId(impl_id)
203            if ImplSignature::of(db, impl_id).target_trait.is_some() =>
204        {
205            return from_assoc_items(&impl_id.impl_items(db).items);
206        }
207        ItemContainerId::TraitId(trait_id) => {
208            return from_assoc_items(&trait_id.trait_items(db).items);
209        }
210        _ => {}
211    }
212
213    // FIXME: Support general TAITs, or decisively decide not to.
214    Vec::new()
215}