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::{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::{ImplTrait, ImplTraitIdx},
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            ImplTrait::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            ImplTrait::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: &Arena<ImplTrait>,
85        mut make_impl_trait: impl FnMut(ImplTraitIdx) -> ImplTraitId,
86        result: &mut Vec<SolverDefId<'db>>,
87    ) {
88        for (opaque_idx, _) in opaques.iter() {
89            let opaque_id = InternedOpaqueTyId::new(db, make_impl_trait(opaque_idx));
90            result.push(opaque_id.into());
91        }
92    }
93}
94
95// These are firewall queries to prevent drawing dependencies between infers:
96
97#[salsa::tracked(returns(ref))]
98pub(crate) fn rpit_hidden_types(
99    db: &dyn HirDatabase,
100    function: FunctionId,
101) -> ArenaMap<ImplTraitIdx, StoredEarlyBinder<StoredTy>> {
102    let infer = InferenceResult::of(db, DefWithBodyId::from(function));
103    let mut result = ArenaMap::new();
104    for (opaque, hidden_type) in infer.return_position_impl_trait_types(db) {
105        result.insert(opaque, StoredEarlyBinder::bind(hidden_type.store()));
106    }
107    result.shrink_to_fit();
108    result
109}
110
111#[salsa::tracked(returns(ref))]
112pub(crate) fn tait_hidden_types(
113    db: &dyn HirDatabase,
114    type_alias: TypeAliasId,
115) -> ArenaMap<ImplTraitIdx, StoredEarlyBinder<StoredTy>> {
116    // Call this first, to not perform redundant work if there are no TAITs.
117    let taits_count = ImplTrait::type_alias_impl_traits(db, type_alias).len();
118
119    let loc = type_alias.loc(db);
120    let module = loc.module(db);
121    let interner = DbInterner::new_with(db, module.krate(db));
122    let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
123    let mut ocx = ObligationCtxt::new(&infcx);
124    let cause = ObligationCause::dummy();
125    let param_env = db.trait_environment(type_alias.into());
126
127    let defining_bodies = tait_defining_bodies(db, loc);
128
129    let mut result = ArenaMap::with_capacity(taits_count);
130    for defining_body in defining_bodies {
131        let infer = InferenceResult::of(db, defining_body);
132        for (&opaque, hidden_type) in &infer.type_of_opaque {
133            let ImplTraitId::TypeAliasImplTrait(opaque_owner, opaque_idx) = opaque.loc(db) else {
134                continue;
135            };
136            if opaque_owner != type_alias {
137                continue;
138            }
139            // In the presence of errors, we attempt to create a unified type from all
140            // types. rustc doesn't do that, but this should improve the experience.
141            let hidden_type = infcx.insert_type_vars(hidden_type.as_ref());
142            match result.entry(opaque_idx) {
143                la_arena::Entry::Vacant(entry) => {
144                    entry.insert(StoredEarlyBinder::bind(hidden_type.store()));
145                }
146                la_arena::Entry::Occupied(entry) => {
147                    _ = ocx.eq(
148                        &cause,
149                        param_env,
150                        entry.get().get().instantiate_identity().skip_norm_wip(),
151                        hidden_type,
152                    );
153                }
154            }
155        }
156    }
157
158    _ = ocx.try_evaluate_obligations();
159
160    // Fill missing entries.
161    for idx in 0..taits_count {
162        let idx = la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(idx as u32));
163        match result.entry(idx) {
164            la_arena::Entry::Vacant(entry) => {
165                entry.insert(StoredEarlyBinder::bind(
166                    Ty::new_error(interner, ErrorGuaranteed).store(),
167                ));
168            }
169            la_arena::Entry::Occupied(mut entry) => {
170                let hidden_type = entry.get().get().skip_binder();
171                let hidden_type =
172                    infcx.resolve_vars_if_possible(hidden_type).replace_infer_with_error(interner);
173                *entry.get_mut() = StoredEarlyBinder::bind(hidden_type.store());
174            }
175        }
176    }
177
178    result
179}
180
181fn tait_defining_bodies(
182    db: &dyn HirDatabase,
183    loc: &AssocItemLoc<ast::TypeAlias>,
184) -> Vec<DefWithBodyId> {
185    let from_assoc_items = |assoc_items: &[(Name, AssocItemId)]| {
186        // Associated Type Position Impl Trait.
187        assoc_items
188            .iter()
189            .filter_map(|&(_, assoc_id)| match assoc_id {
190                AssocItemId::FunctionId(it) => Some(it.into()),
191                AssocItemId::ConstId(it) => Some(it.into()),
192                AssocItemId::TypeAliasId(_) => None,
193            })
194            .collect()
195    };
196    match loc.container {
197        ItemContainerId::ImplId(impl_id)
198            if ImplSignature::of(db, impl_id).target_trait.is_some() =>
199        {
200            return from_assoc_items(&impl_id.impl_items(db).items);
201        }
202        ItemContainerId::TraitId(trait_id) => {
203            return from_assoc_items(&trait_id.trait_items(db).items);
204        }
205        _ => {}
206    }
207
208    // FIXME: Support general TAITs, or decisively decide not to.
209    Vec::new()
210}