1use rustc_data_structures::fx::FxHashSet;
2use rustc_hir::def::DefKind;
3use rustc_hir::def_id::LocalDefId;
4use rustc_hir::intravisit;
5use rustc_hir::intravisit::Visitor;
6use rustc_middle::query::Providers;
7use rustc_middle::ty::util::{CheckRegions, NotUniqueParam};
8use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
9use rustc_middle::{bug, span_bug};
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 item: LocalDefId,
20
21 seen: FxHashSet<LocalDefId>,
23
24 span: Option<Span>,
25
26 mode: CollectionMode,
27}
28
29enum CollectionMode {
30 ImplTraitInAssocTypes,
33 Taits,
35 RpitAndAsyncFnOnly,
38}
39
40impl<'tcx> OpaqueTypeCollector<'tcx> {
41 fn new(tcx: TyCtxt<'tcx>, item: LocalDefId) -> Self {
42 let mode = match tcx.def_kind(item) {
43 DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy => {
44 CollectionMode::ImplTraitInAssocTypes
45 }
46 DefKind::TyAlias => CollectionMode::Taits,
47 _ => CollectionMode::RpitAndAsyncFnOnly,
48 };
49 Self { tcx, opaques: Vec::new(), item, seen: Default::default(), span: None, mode }
50 }
51
52 fn span(&self) -> Span {
53 self.span.unwrap_or_else(|| {
54 self.tcx.def_ident_span(self.item).unwrap_or_else(|| self.tcx.def_span(self.item))
55 })
56 }
57
58 fn visit_spanned(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) {
59 let old = self.span;
60 self.span = Some(span);
61 value.visit_with(self);
62 self.span = old;
63 }
64
65 #[instrument(level = "trace", skip(self))]
66 fn collect_taits_declared_in_body(&mut self) {
67 let body = self.tcx.hir_body_owned_by(self.item).value;
68 struct TaitInBodyFinder<'a, 'tcx> {
69 collector: &'a mut OpaqueTypeCollector<'tcx>,
70 }
71 impl<'v> intravisit::Visitor<'v> for TaitInBodyFinder<'_, '_> {
72 #[instrument(level = "trace", skip(self))]
73 fn visit_nested_item(&mut self, id: rustc_hir::ItemId) {
74 let id = id.owner_id.def_id;
75 if let DefKind::TyAlias = self.collector.tcx.def_kind(id) {
76 let items = self.collector.tcx.opaque_types_defined_by(id);
77 self.collector.opaques.extend(items);
78 }
79 }
80 #[instrument(level = "trace", skip(self))]
81 fn visit_nested_body(&mut self, id: rustc_hir::BodyId) {
83 let body = self.collector.tcx.hir_body(id);
84 self.visit_body(body);
85 }
86 }
87 TaitInBodyFinder { collector: self }.visit_expr(body);
88 }
89
90 #[instrument(level = "debug", skip(self))]
91 fn visit_opaque_ty(&mut self, alias_ty: ty::AliasTy<'tcx>) {
92 if !self.seen.insert(alias_ty.def_id.expect_local()) {
93 return;
94 }
95
96 match self.tcx.local_opaque_ty_origin(alias_ty.def_id.expect_local()) {
98 rustc_hir::OpaqueTyOrigin::FnReturn { .. }
99 | rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {}
100 rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => match self.mode {
101 CollectionMode::ImplTraitInAssocTypes => {
105 if !in_assoc_ty {
106 return;
107 }
108 }
109 CollectionMode::Taits => {
113 if in_assoc_ty {
114 return;
115 }
116 }
117 CollectionMode::RpitAndAsyncFnOnly => return,
118 },
119 }
120
121 trace!(?alias_ty, "adding");
122 self.opaques.push(alias_ty.def_id.expect_local());
123
124 let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
125 match self
129 .tcx
130 .uses_unique_generic_params(&alias_ty.args[..parent_count], CheckRegions::FromFunction)
131 {
132 Ok(()) => {
133 for (pred, span) in
141 self.tcx.explicit_item_bounds(alias_ty.def_id).iter_identity_copied()
142 {
143 trace!(?pred);
144 self.visit_spanned(span, pred);
145 }
146 }
147 Err(NotUniqueParam::NotParam(arg)) => {
148 self.tcx.dcx().emit_err(NotParam {
149 arg,
150 span: self.span(),
151 opaque_span: self.tcx.def_span(alias_ty.def_id),
152 });
153 }
154 Err(NotUniqueParam::DuplicateParam(arg)) => {
155 self.tcx.dcx().emit_err(DuplicateArg {
156 arg,
157 span: self.span(),
158 opaque_span: self.tcx.def_span(alias_ty.def_id),
159 });
160 }
161 }
162 }
163
164 #[instrument(level = "trace", skip(self))]
167 fn collect_taits_from_defines_attr(&mut self) {
168 let hir_id = self.tcx.local_def_id_to_hir_id(self.item);
169 if !hir_id.is_owner() {
170 return;
171 }
172 let Some(defines) = self.tcx.hir_attr_map(hir_id.owner).define_opaque else {
173 return;
174 };
175 for &(span, define) in defines {
176 trace!(?define);
177 let mode = std::mem::replace(&mut self.mode, CollectionMode::Taits);
178 let n = self.opaques.len();
179 super::sig_types::walk_types(self.tcx, define, self);
180 if n == self.opaques.len() {
181 self.tcx.dcx().span_err(span, "item does not contain any opaque types");
182 }
183 self.mode = mode;
184 }
185 self.mode = CollectionMode::RpitAndAsyncFnOnly;
188 }
189}
190
191impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> {
192 #[instrument(skip(self), ret, level = "trace")]
193 fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) {
194 self.visit_spanned(span, value);
195 }
196}
197
198impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
199 #[instrument(skip(self), ret, level = "trace")]
200 fn visit_ty(&mut self, t: Ty<'tcx>) {
201 t.super_visit_with(self);
202 match *t.kind() {
203 ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
204 self.visit_opaque_ty(alias_ty);
205 }
206 ty::Alias(ty::Free, alias_ty) if let Some(def_id) = alias_ty.def_id.as_local() => {
209 if !self.seen.insert(def_id) {
210 return;
211 }
212 self.tcx
213 .type_of(alias_ty.def_id)
214 .instantiate(self.tcx, alias_ty.args)
215 .visit_with(self);
216 }
217 ty::Alias(ty::Projection, alias_ty) => {
218 if let Some(parent) = self.tcx.trait_impl_of_assoc(self.item.to_def_id()) {
222 let impl_trait_ref = self.tcx.impl_trait_ref(parent).instantiate_identity();
223 if alias_ty.trait_ref(self.tcx) == impl_trait_ref {
227 for &assoc in self.tcx.associated_items(parent).in_definition_order() {
228 trace!(?assoc);
229 if assoc.expect_trait_impl() != Ok(alias_ty.def_id) {
230 continue;
231 }
232
233 if !assoc.defaultness(self.tcx).is_final() {
236 continue;
237 }
238
239 if !self.seen.insert(assoc.def_id.expect_local()) {
240 return;
241 }
242
243 let alias_args = alias_ty.args.rebase_onto(
244 self.tcx,
245 impl_trait_ref.def_id,
246 ty::GenericArgs::identity_for_item(self.tcx, parent),
247 );
248
249 if self.tcx.check_args_compatible(assoc.def_id, alias_args) {
250 self.tcx
251 .type_of(assoc.def_id)
252 .instantiate(self.tcx, alias_args)
253 .visit_with(self);
254 return;
255 } else {
256 self.tcx.dcx().span_delayed_bug(
257 self.tcx.def_span(assoc.def_id),
258 "item had incorrect args",
259 );
260 }
261 }
262 }
263 } else if let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) =
264 self.tcx.opt_rpitit_info(alias_ty.def_id)
265 && fn_def_id == self.item.into()
266 {
267 let ty = self.tcx.type_of(alias_ty.def_id).instantiate(self.tcx, alias_ty.args);
279 let ty::Alias(ty::Opaque, alias_ty) = *ty.kind() else { bug!("{ty:?}") };
280 self.visit_opaque_ty(alias_ty);
281 }
282 }
283 _ => trace!(kind=?t.kind()),
284 }
285 }
286}
287
288fn opaque_types_defined_by<'tcx>(
289 tcx: TyCtxt<'tcx>,
290 item: LocalDefId,
291) -> &'tcx ty::List<LocalDefId> {
292 let kind = tcx.def_kind(item);
293 trace!(?kind);
294 let mut collector = OpaqueTypeCollector::new(tcx, item);
295 collector.collect_taits_from_defines_attr();
296 super::sig_types::walk_types(tcx, item, &mut collector);
297
298 match kind {
299 DefKind::AssocFn
300 | DefKind::Fn
301 | DefKind::Static { .. }
302 | DefKind::Const
303 | DefKind::AssocConst
304 | DefKind::AnonConst => {
305 collector.collect_taits_declared_in_body();
306 }
307 DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => {
312 collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item)));
313 }
314 DefKind::AssocTy | DefKind::TyAlias | DefKind::GlobalAsm => {}
315 DefKind::OpaqueTy
316 | DefKind::Mod
317 | DefKind::Struct
318 | DefKind::Union
319 | DefKind::Enum
320 | DefKind::Variant
321 | DefKind::Trait
322 | DefKind::ForeignTy
323 | DefKind::TraitAlias
324 | DefKind::TyParam
325 | DefKind::ConstParam
326 | DefKind::Ctor(_, _)
327 | DefKind::Macro(_)
328 | DefKind::ExternCrate
329 | DefKind::Use
330 | DefKind::ForeignMod
331 | DefKind::Field
332 | DefKind::LifetimeParam
333 | DefKind::Impl { .. } => {
334 span_bug!(
335 tcx.def_span(item),
336 "`opaque_types_defined_by` not defined for {} `{item:?}`",
337 kind.descr(item.to_def_id())
338 );
339 }
340 }
341 tcx.mk_local_def_ids(&collector.opaques)
342}
343
344pub(super) fn provide(providers: &mut Providers) {
345 *providers = Providers { opaque_types_defined_by, ..*providers };
346}