rustc_ty_utils/
opaque_types.rs

1use rustc_data_structures::fx::FxHashSet;
2use rustc_hir::def::DefKind;
3use rustc_hir::def_id::LocalDefId;
4use rustc_hir::intravisit::Visitor;
5use rustc_hir::{CRATE_HIR_ID, intravisit};
6use rustc_middle::bug;
7use rustc_middle::query::Providers;
8use rustc_middle::ty::util::{CheckRegions, NotUniqueParam};
9use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
10use rustc_span::Span;
11use tracing::{instrument, trace};
12
13use crate::errors::{DuplicateArg, NotParam};
14
15struct OpaqueTypeCollector<'tcx> {
16    tcx: TyCtxt<'tcx>,
17    opaques: Vec<LocalDefId>,
18    /// The `DefId` of the item which we are collecting opaque types for.
19    item: LocalDefId,
20
21    /// Avoid infinite recursion due to recursive declarations.
22    seen: FxHashSet<LocalDefId>,
23
24    span: Option<Span>,
25
26    mode: CollectionMode,
27}
28
29enum CollectionMode {
30    /// For impl trait in assoc types we only permit collecting them from
31    /// associated types of the same impl block.
32    ImplTraitInAssocTypes,
33    TypeAliasImplTraitTransition,
34}
35
36impl<'tcx> OpaqueTypeCollector<'tcx> {
37    fn new(tcx: TyCtxt<'tcx>, item: LocalDefId) -> Self {
38        let mode = match tcx.def_kind(tcx.local_parent(item)) {
39            DefKind::Impl { of_trait: true } => CollectionMode::ImplTraitInAssocTypes,
40            _ => CollectionMode::TypeAliasImplTraitTransition,
41        };
42        Self { tcx, opaques: Vec::new(), item, seen: Default::default(), span: None, mode }
43    }
44
45    fn span(&self) -> Span {
46        self.span.unwrap_or_else(|| {
47            self.tcx.def_ident_span(self.item).unwrap_or_else(|| self.tcx.def_span(self.item))
48        })
49    }
50
51    fn visit_spanned(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) {
52        let old = self.span;
53        self.span = Some(span);
54        value.visit_with(self);
55        self.span = old;
56    }
57
58    fn parent_impl_trait_ref(&self) -> Option<ty::TraitRef<'tcx>> {
59        let parent = self.parent()?;
60        if matches!(self.tcx.def_kind(parent), DefKind::Impl { .. }) {
61            Some(self.tcx.impl_trait_ref(parent)?.instantiate_identity())
62        } else {
63            None
64        }
65    }
66
67    fn parent(&self) -> Option<LocalDefId> {
68        match self.tcx.def_kind(self.item) {
69            DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
70                Some(self.tcx.local_parent(self.item))
71            }
72            _ => None,
73        }
74    }
75
76    /// Returns `true` if `opaque_hir_id` is a sibling or a child of a sibling of `self.item`.
77    ///
78    /// Example:
79    /// ```ignore UNSOLVED (is this a bug?)
80    /// # #![feature(type_alias_impl_trait)]
81    /// pub mod foo {
82    ///     pub mod bar {
83    ///         pub trait Bar { /* ... */ }
84    ///         pub type Baz = impl Bar;
85    ///
86    ///         # impl Bar for () {}
87    ///         fn f1() -> Baz { /* ... */ }
88    ///     }
89    ///     fn f2() -> bar::Baz { /* ... */ }
90    /// }
91    /// ```
92    ///
93    /// and `opaque_def_id` is the `DefId` of the definition of the opaque type `Baz`.
94    /// For the above example, this function returns `true` for `f1` and `false` for `f2`.
95    #[instrument(level = "trace", skip(self), ret)]
96    fn check_tait_defining_scope(&self, opaque_def_id: LocalDefId) -> bool {
97        let mut hir_id = self.tcx.local_def_id_to_hir_id(self.item);
98        let opaque_hir_id = self.tcx.local_def_id_to_hir_id(opaque_def_id);
99
100        // Named opaque types can be defined by any siblings or children of siblings.
101        let scope = self.tcx.hir().get_defining_scope(opaque_hir_id);
102        // We walk up the node tree until we hit the root or the scope of the opaque type.
103        while hir_id != scope && hir_id != CRATE_HIR_ID {
104            hir_id = self.tcx.hir().get_parent_item(hir_id).into();
105        }
106        // Syntactically, we are allowed to define the concrete type if:
107        hir_id == scope
108    }
109
110    #[instrument(level = "trace", skip(self))]
111    fn collect_taits_declared_in_body(&mut self) {
112        let body = self.tcx.hir().body_owned_by(self.item).value;
113        struct TaitInBodyFinder<'a, 'tcx> {
114            collector: &'a mut OpaqueTypeCollector<'tcx>,
115        }
116        impl<'v> intravisit::Visitor<'v> for TaitInBodyFinder<'_, '_> {
117            #[instrument(level = "trace", skip(self))]
118            fn visit_nested_item(&mut self, id: rustc_hir::ItemId) {
119                let id = id.owner_id.def_id;
120                if let DefKind::TyAlias = self.collector.tcx.def_kind(id) {
121                    let items = self.collector.tcx.opaque_types_defined_by(id);
122                    self.collector.opaques.extend(items);
123                }
124            }
125            #[instrument(level = "trace", skip(self))]
126            // Recurse into these, as they are type checked with their parent
127            fn visit_nested_body(&mut self, id: rustc_hir::BodyId) {
128                let body = self.collector.tcx.hir().body(id);
129                self.visit_body(body);
130            }
131        }
132        TaitInBodyFinder { collector: self }.visit_expr(body);
133    }
134
135    #[instrument(level = "debug", skip(self))]
136    fn visit_opaque_ty(&mut self, alias_ty: ty::AliasTy<'tcx>) {
137        if !self.seen.insert(alias_ty.def_id.expect_local()) {
138            return;
139        }
140
141        // TAITs outside their defining scopes are ignored.
142        let origin = self.tcx.local_opaque_ty_origin(alias_ty.def_id.expect_local());
143        trace!(?origin);
144        match origin {
145            rustc_hir::OpaqueTyOrigin::FnReturn { .. }
146            | rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {}
147            rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => {
148                if !in_assoc_ty && !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
149                    return;
150                }
151            }
152        }
153
154        self.opaques.push(alias_ty.def_id.expect_local());
155
156        let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
157        // Only check that the parent generics of the TAIT/RPIT are unique.
158        // the args owned by the opaque are going to always be duplicate
159        // lifetime params for RPITs, and empty for TAITs.
160        match self
161            .tcx
162            .uses_unique_generic_params(&alias_ty.args[..parent_count], CheckRegions::FromFunction)
163        {
164            Ok(()) => {
165                // FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not
166                // supported at all, so this is sound to do, but once we want to support them, you'll
167                // start seeing the error below.
168
169                // Collect opaque types nested within the associated type bounds of this opaque type.
170                // We use identity args here, because we already know that the opaque type uses
171                // only generic parameters, and thus instantiating would not give us more information.
172                for (pred, span) in
173                    self.tcx.explicit_item_bounds(alias_ty.def_id).iter_identity_copied()
174                {
175                    trace!(?pred);
176                    self.visit_spanned(span, pred);
177                }
178            }
179            Err(NotUniqueParam::NotParam(arg)) => {
180                self.tcx.dcx().emit_err(NotParam {
181                    arg,
182                    span: self.span(),
183                    opaque_span: self.tcx.def_span(alias_ty.def_id),
184                });
185            }
186            Err(NotUniqueParam::DuplicateParam(arg)) => {
187                self.tcx.dcx().emit_err(DuplicateArg {
188                    arg,
189                    span: self.span(),
190                    opaque_span: self.tcx.def_span(alias_ty.def_id),
191                });
192            }
193        }
194    }
195}
196
197impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> {
198    #[instrument(skip(self), ret, level = "trace")]
199    fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) {
200        self.visit_spanned(span, value);
201    }
202}
203
204impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
205    #[instrument(skip(self), ret, level = "trace")]
206    fn visit_ty(&mut self, t: Ty<'tcx>) {
207        t.super_visit_with(self);
208        match *t.kind() {
209            ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
210                self.visit_opaque_ty(alias_ty);
211            }
212            // Skips type aliases, as they are meant to be transparent.
213            ty::Alias(ty::Weak, alias_ty) if alias_ty.def_id.is_local() => {
214                self.tcx
215                    .type_of(alias_ty.def_id)
216                    .instantiate(self.tcx, alias_ty.args)
217                    .visit_with(self);
218            }
219            ty::Alias(ty::Projection, alias_ty) => {
220                // This avoids having to do normalization of `Self::AssocTy` by only
221                // supporting the case of a method defining opaque types from assoc types
222                // in the same impl block.
223                if let Some(impl_trait_ref) = self.parent_impl_trait_ref() {
224                    // If the trait ref of the associated item and the impl differs,
225                    // then we can't use the impl's identity args below, so
226                    // just skip.
227                    if alias_ty.trait_ref(self.tcx) == impl_trait_ref {
228                        let parent = self.parent().expect("we should have a parent here");
229
230                        for &assoc in self.tcx.associated_items(parent).in_definition_order() {
231                            trace!(?assoc);
232                            if assoc.trait_item_def_id != Some(alias_ty.def_id) {
233                                continue;
234                            }
235
236                            // If the type is further specializable, then the type_of
237                            // is not actually correct below.
238                            if !assoc.defaultness(self.tcx).is_final() {
239                                continue;
240                            }
241
242                            if !self.seen.insert(assoc.def_id.expect_local()) {
243                                return;
244                            }
245
246                            let impl_args = alias_ty.args.rebase_onto(
247                                self.tcx,
248                                impl_trait_ref.def_id,
249                                ty::GenericArgs::identity_for_item(self.tcx, parent),
250                            );
251
252                            if self.tcx.check_args_compatible(assoc.def_id, impl_args) {
253                                self.tcx
254                                    .type_of(assoc.def_id)
255                                    .instantiate(self.tcx, impl_args)
256                                    .visit_with(self);
257                                return;
258                            } else {
259                                self.tcx.dcx().span_delayed_bug(
260                                    self.tcx.def_span(assoc.def_id),
261                                    "item had incorrect args",
262                                );
263                            }
264                        }
265                    }
266                } else if let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) =
267                    self.tcx.opt_rpitit_info(alias_ty.def_id)
268                    && fn_def_id == self.item.into()
269                {
270                    // RPITIT in trait definitions get desugared to an associated type. For
271                    // default methods we also create an opaque type this associated type
272                    // normalizes to. The associated type is only known to normalize to the
273                    // opaque if it is fully concrete. There could otherwise be an impl
274                    // overwriting the default method.
275                    //
276                    // However, we have to be able to normalize the associated type while inside
277                    // of the default method. This is normally handled by adding an unchecked
278                    // `Projection(<Self as Trait>::synthetic_assoc_ty, trait_def::opaque)`
279                    // assumption to the `param_env` of the default method. We also separately
280                    // rely on that assumption here.
281                    let ty = self.tcx.type_of(alias_ty.def_id).instantiate(self.tcx, alias_ty.args);
282                    let ty::Alias(ty::Opaque, alias_ty) = *ty.kind() else { bug!("{ty:?}") };
283                    self.visit_opaque_ty(alias_ty);
284                }
285            }
286            ty::Adt(def, _) if def.did().is_local() => {
287                if let CollectionMode::ImplTraitInAssocTypes = self.mode {
288                    return;
289                }
290                if !self.seen.insert(def.did().expect_local()) {
291                    return;
292                }
293                for variant in def.variants().iter() {
294                    for field in variant.fields.iter() {
295                        // Don't use the `ty::Adt` args, we either
296                        // * found the opaque in the args
297                        // * will find the opaque in the uninstantiated fields
298                        // The only other situation that can occur is that after instantiating,
299                        // some projection resolves to an opaque that we would have otherwise
300                        // not found. While we could instantiate and walk those, that would mean we
301                        // would have to walk all generic parameters of an Adt, which can quickly
302                        // degenerate into looking at an exponential number of types.
303                        let ty = self.tcx.type_of(field.did).instantiate_identity();
304                        self.visit_spanned(self.tcx.def_span(field.did), ty);
305                    }
306                }
307            }
308            _ => trace!(kind=?t.kind()),
309        }
310    }
311}
312
313fn opaque_types_defined_by<'tcx>(
314    tcx: TyCtxt<'tcx>,
315    item: LocalDefId,
316) -> &'tcx ty::List<LocalDefId> {
317    let kind = tcx.def_kind(item);
318    trace!(?kind);
319    let mut collector = OpaqueTypeCollector::new(tcx, item);
320    super::sig_types::walk_types(tcx, item, &mut collector);
321    match kind {
322        DefKind::AssocFn
323        | DefKind::Fn
324        | DefKind::Static { .. }
325        | DefKind::Const
326        | DefKind::AssocConst
327        | DefKind::AnonConst => {
328            collector.collect_taits_declared_in_body();
329        }
330        DefKind::OpaqueTy
331        | DefKind::TyAlias
332        | DefKind::AssocTy
333        | DefKind::Mod
334        | DefKind::Struct
335        | DefKind::Union
336        | DefKind::Enum
337        | DefKind::Variant
338        | DefKind::Trait
339        | DefKind::ForeignTy
340        | DefKind::TraitAlias
341        | DefKind::TyParam
342        | DefKind::ConstParam
343        | DefKind::Ctor(_, _)
344        | DefKind::Macro(_)
345        | DefKind::ExternCrate
346        | DefKind::Use
347        | DefKind::ForeignMod
348        | DefKind::Field
349        | DefKind::LifetimeParam
350        | DefKind::GlobalAsm
351        | DefKind::Impl { .. }
352        | DefKind::SyntheticCoroutineBody => {}
353        // Closures and coroutines are type checked with their parent, so we need to allow all
354        // opaques from the closure signature *and* from the parent body.
355        DefKind::Closure | DefKind::InlineConst => {
356            collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item)));
357        }
358    }
359    tcx.mk_local_def_ids(&collector.opaques)
360}
361
362pub(super) fn provide(providers: &mut Providers) {
363    *providers = Providers { opaque_types_defined_by, ..*providers };
364}