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