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};
1213use crate::errors::{DuplicateArg, NotParam};
1415struct OpaqueTypeCollector<'tcx> {
16 tcx: TyCtxt<'tcx>,
17 opaques: Vec<LocalDefId>,
18/// The `DefId` of the item which we are collecting opaque types for.
19item: LocalDefId,
2021/// Avoid infinite recursion due to recursive declarations.
22seen: FxHashSet<LocalDefId>,
2324 span: Option<Span>,
2526 mode: CollectionMode,
27}
2829enum CollectionMode {
30/// For impl trait in assoc types we only permit collecting them from
31 /// associated types of the same impl block.
32ImplTraitInAssocTypes,
33 TypeAliasImplTraitTransition,
34}
3536impl<'tcx> OpaqueTypeCollector<'tcx> {
37fn new(tcx: TyCtxt<'tcx>, item: LocalDefId) -> Self {
38let mode = match tcx.def_kind(tcx.local_parent(item)) {
39DefKind::Impl { of_trait: true } => CollectionMode::ImplTraitInAssocTypes,
40_ => CollectionMode::TypeAliasImplTraitTransition,
41 };
42Self { tcx, opaques: Vec::new(), item, seen: Default::default(), span: None, mode }
43 }
4445fn span(&self) -> Span {
46self.span.unwrap_or_else(|| {
47self.tcx.def_ident_span(self.item).unwrap_or_else(|| self.tcx.def_span(self.item))
48 })
49 }
5051fn visit_spanned(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) {
52let old = self.span;
53self.span = Some(span);
54value.visit_with(self);
55self.span = old;
56 }
5758fn parent_impl_trait_ref(&self) -> Option<ty::TraitRef<'tcx>> {
59let parent = self.parent()?;
60if matches!(self.tcx.def_kind(parent), DefKind::Impl { .. }) {
61Some(self.tcx.impl_trait_ref(parent)?.instantiate_identity())
62 } else {
63None64 }
65 }
6667fn parent(&self) -> Option<LocalDefId> {
68match self.tcx.def_kind(self.item) {
69DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
70Some(self.tcx.local_parent(self.item))
71 }
72_ => None,
73 }
74 }
7576/// 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)]
96fn check_tait_defining_scope(&self, opaque_def_id: LocalDefId) -> bool {
97let mut hir_id = self.tcx.local_def_id_to_hir_id(self.item);
98let opaque_hir_id = self.tcx.local_def_id_to_hir_id(opaque_def_id);
99100// Named opaque types can be defined by any siblings or children of siblings.
101let 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.
103while 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:
107hir_id == scope
108 }
109110#[instrument(level = "trace", skip(self))]
111fn collect_taits_declared_in_body(&mut self) {
112let body = self.tcx.hir().body_owned_by(self.item).value;
113struct TaitInBodyFinder<'a, 'tcx> {
114 collector: &'a mut OpaqueTypeCollector<'tcx>,
115 }
116impl<'v> intravisit::Visitor<'v> for TaitInBodyFinder<'_, '_> {
117#[instrument(level = "trace", skip(self))]
118fn visit_nested_item(&mut self, id: rustc_hir::ItemId) {
119let id = id.owner_id.def_id;
120if let DefKind::TyAlias = self.collector.tcx.def_kind(id) {
121let items = self.collector.tcx.opaque_types_defined_by(id);
122self.collector.opaques.extend(items);
123 }
124 }
125#[instrument(level = "trace", skip(self))]
126// Recurse into these, as they are type checked with their parent
127fn visit_nested_body(&mut self, id: rustc_hir::BodyId) {
128let body = self.collector.tcx.hir().body(id);
129self.visit_body(body);
130 }
131 }
132 TaitInBodyFinder { collector: self }.visit_expr(body);
133 }
134135#[instrument(level = "debug", skip(self))]
136fn visit_opaque_ty(&mut self, alias_ty: ty::AliasTy<'tcx>) {
137if !self.seen.insert(alias_ty.def_id.expect_local()) {
138return;
139 }
140141// TAITs outside their defining scopes are ignored.
142let origin = self.tcx.local_opaque_ty_origin(alias_ty.def_id.expect_local());
143trace!(?origin);
144match origin {
145 rustc_hir::OpaqueTyOrigin::FnReturn { .. }
146 | rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {}
147 rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => {
148if !in_assoc_ty && !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
149return;
150 }
151 }
152 }
153154self.opaques.push(alias_ty.def_id.expect_local());
155156let 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.
160match self
161.tcx
162 .uses_unique_generic_params(&alias_ty.args[..parent_count], CheckRegions::FromFunction)
163 {
164Ok(()) => {
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.
168169 // 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.
172for (pred, span) in
173self.tcx.explicit_item_bounds(alias_ty.def_id).iter_identity_copied()
174 {
175trace!(?pred);
176self.visit_spanned(span, pred);
177 }
178 }
179Err(NotUniqueParam::NotParam(arg)) => {
180self.tcx.dcx().emit_err(NotParam {
181 arg,
182 span: self.span(),
183 opaque_span: self.tcx.def_span(alias_ty.def_id),
184 });
185 }
186Err(NotUniqueParam::DuplicateParam(arg)) => {
187self.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}
196197impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> {
198#[instrument(skip(self), ret, level = "trace")]
199fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) {
200self.visit_spanned(span, value);
201 }
202}
203204impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
205#[instrument(skip(self), ret, level = "trace")]
206fn visit_ty(&mut self, t: Ty<'tcx>) {
207 t.super_visit_with(self);
208match *t.kind() {
209 ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
210self.visit_opaque_ty(alias_ty);
211 }
212// Skips type aliases, as they are meant to be transparent.
213ty::Alias(ty::Weak, alias_ty) if alias_ty.def_id.is_local() => {
214self.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.
223if 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.
227if alias_ty.trait_ref(self.tcx) == impl_trait_ref {
228let parent = self.parent().expect("we should have a parent here");
229230for &assoc in self.tcx.associated_items(parent).in_definition_order() {
231trace!(?assoc);
232if assoc.trait_item_def_id != Some(alias_ty.def_id) {
233continue;
234 }
235236// If the type is further specializable, then the type_of
237 // is not actually correct below.
238if !assoc.defaultness(self.tcx).is_final() {
239continue;
240 }
241242if !self.seen.insert(assoc.def_id.expect_local()) {
243return;
244 }
245246let impl_args = alias_ty.args.rebase_onto(
247self.tcx,
248 impl_trait_ref.def_id,
249 ty::GenericArgs::identity_for_item(self.tcx, parent),
250 );
251252if self.tcx.check_args_compatible(assoc.def_id, impl_args) {
253self.tcx
254 .type_of(assoc.def_id)
255 .instantiate(self.tcx, impl_args)
256 .visit_with(self);
257return;
258 } else {
259self.tcx.dcx().span_delayed_bug(
260self.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, .. }) =
267self.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.
281let ty = self.tcx.type_of(alias_ty.def_id).instantiate(self.tcx, alias_ty.args);
282let ty::Alias(ty::Opaque, alias_ty) = *ty.kind() else { bug!("{ty:?}") };
283self.visit_opaque_ty(alias_ty);
284 }
285 }
286 ty::Adt(def, _) if def.did().is_local() => {
287if let CollectionMode::ImplTraitInAssocTypes = self.mode {
288return;
289 }
290if !self.seen.insert(def.did().expect_local()) {
291return;
292 }
293for variant in def.variants().iter() {
294for 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.
303let ty = self.tcx.type_of(field.did).instantiate_identity();
304self.visit_spanned(self.tcx.def_span(field.did), ty);
305 }
306 }
307 }
308_ => trace!(kind=?t.kind()),
309 }
310 }
311}
312313fn opaque_types_defined_by<'tcx>(
314 tcx: TyCtxt<'tcx>,
315 item: LocalDefId,
316) -> &'tcx ty::List<LocalDefId> {
317let kind = tcx.def_kind(item);
318trace!(?kind);
319let mut collector = OpaqueTypeCollector::new(tcx, item);
320super::sig_types::walk_types(tcx, item, &mut collector);
321match kind {
322DefKind::AssocFn323 | DefKind::Fn324 | DefKind::Static { .. }
325 | DefKind::Const326 | DefKind::AssocConst327 | DefKind::AnonConst => {
328collector.collect_taits_declared_in_body();
329 }
330DefKind::OpaqueTy331 | DefKind::TyAlias332 | DefKind::AssocTy333 | DefKind::Mod334 | DefKind::Struct335 | DefKind::Union336 | DefKind::Enum337 | DefKind::Variant338 | DefKind::Trait339 | DefKind::ForeignTy340 | DefKind::TraitAlias341 | DefKind::TyParam342 | DefKind::ConstParam343 | DefKind::Ctor(_, _)
344 | DefKind::Macro(_)
345 | DefKind::ExternCrate346 | DefKind::Use347 | DefKind::ForeignMod348 | DefKind::Field349 | DefKind::LifetimeParam350 | DefKind::GlobalAsm351 | 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.
355DefKind::Closure | DefKind::InlineConst => {
356collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item)));
357 }
358 }
359tcx.mk_local_def_ids(&collector.opaques)
360}
361362pub(super) fn provide(providers: &mut Providers) {
363*providers = Providers { opaque_types_defined_by, ..*providers };
364}