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::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 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 fn parent_impl_trait_ref(&self) -> Option<ty::TraitRef<'tcx>> {
66 let parent = self.parent()?;
67 if matches!(self.tcx.def_kind(parent), DefKind::Impl { .. }) {
68 Some(self.tcx.impl_trait_ref(parent)?.instantiate_identity())
69 } else {
70 None
71 }
72 }
73
74 fn parent(&self) -> Option<LocalDefId> {
75 match self.tcx.def_kind(self.item) {
76 DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
77 Some(self.tcx.local_parent(self.item))
78 }
79 _ => None,
80 }
81 }
82
83 #[instrument(level = "trace", skip(self))]
84 fn collect_taits_declared_in_body(&mut self) {
85 let body = self.tcx.hir_body_owned_by(self.item).value;
86 struct TaitInBodyFinder<'a, 'tcx> {
87 collector: &'a mut OpaqueTypeCollector<'tcx>,
88 }
89 impl<'v> intravisit::Visitor<'v> for TaitInBodyFinder<'_, '_> {
90 #[instrument(level = "trace", skip(self))]
91 fn visit_nested_item(&mut self, id: rustc_hir::ItemId) {
92 let id = id.owner_id.def_id;
93 if let DefKind::TyAlias = self.collector.tcx.def_kind(id) {
94 let items = self.collector.tcx.opaque_types_defined_by(id);
95 self.collector.opaques.extend(items);
96 }
97 }
98 #[instrument(level = "trace", skip(self))]
99 fn visit_nested_body(&mut self, id: rustc_hir::BodyId) {
101 let body = self.collector.tcx.hir_body(id);
102 self.visit_body(body);
103 }
104 }
105 TaitInBodyFinder { collector: self }.visit_expr(body);
106 }
107
108 #[instrument(level = "debug", skip(self))]
109 fn visit_opaque_ty(&mut self, alias_ty: ty::AliasTy<'tcx>) {
110 if !self.seen.insert(alias_ty.def_id.expect_local()) {
111 return;
112 }
113
114 match self.tcx.local_opaque_ty_origin(alias_ty.def_id.expect_local()) {
116 rustc_hir::OpaqueTyOrigin::FnReturn { .. }
117 | rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {}
118 rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => match self.mode {
119 CollectionMode::ImplTraitInAssocTypes => {
123 if !in_assoc_ty {
124 return;
125 }
126 }
127 CollectionMode::Taits => {
131 if in_assoc_ty {
132 return;
133 }
134 }
135 CollectionMode::RpitAndAsyncFnOnly => return,
136 },
137 }
138
139 trace!(?alias_ty, "adding");
140 self.opaques.push(alias_ty.def_id.expect_local());
141
142 let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
143 match self
147 .tcx
148 .uses_unique_generic_params(&alias_ty.args[..parent_count], CheckRegions::FromFunction)
149 {
150 Ok(()) => {
151 for (pred, span) in
159 self.tcx.explicit_item_bounds(alias_ty.def_id).iter_identity_copied()
160 {
161 trace!(?pred);
162 self.visit_spanned(span, pred);
163 }
164 }
165 Err(NotUniqueParam::NotParam(arg)) => {
166 self.tcx.dcx().emit_err(NotParam {
167 arg,
168 span: self.span(),
169 opaque_span: self.tcx.def_span(alias_ty.def_id),
170 });
171 }
172 Err(NotUniqueParam::DuplicateParam(arg)) => {
173 self.tcx.dcx().emit_err(DuplicateArg {
174 arg,
175 span: self.span(),
176 opaque_span: self.tcx.def_span(alias_ty.def_id),
177 });
178 }
179 }
180 }
181
182 #[instrument(level = "trace", skip(self))]
185 fn collect_taits_from_defines_attr(&mut self) {
186 let hir_id = self.tcx.local_def_id_to_hir_id(self.item);
187 if !hir_id.is_owner() {
188 return;
189 }
190 let Some(defines) = self.tcx.hir_attr_map(hir_id.owner).define_opaque else {
191 return;
192 };
193 for &(span, define) in defines {
194 trace!(?define);
195 let mode = std::mem::replace(&mut self.mode, CollectionMode::Taits);
196 let n = self.opaques.len();
197 super::sig_types::walk_types(self.tcx, define, self);
198 if n == self.opaques.len() {
199 self.tcx.dcx().span_err(span, "item does not contain any opaque types");
200 }
201 self.mode = mode;
202 }
203 self.mode = CollectionMode::RpitAndAsyncFnOnly;
206 }
207}
208
209impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> {
210 #[instrument(skip(self), ret, level = "trace")]
211 fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) {
212 self.visit_spanned(span, value);
213 }
214}
215
216impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
217 #[instrument(skip(self), ret, level = "trace")]
218 fn visit_ty(&mut self, t: Ty<'tcx>) {
219 t.super_visit_with(self);
220 match *t.kind() {
221 ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
222 self.visit_opaque_ty(alias_ty);
223 }
224 ty::Alias(ty::Weak, alias_ty) if alias_ty.def_id.is_local() => {
227 self.tcx
228 .type_of(alias_ty.def_id)
229 .instantiate(self.tcx, alias_ty.args)
230 .visit_with(self);
231 }
232 ty::Alias(ty::Projection, alias_ty) => {
233 if let Some(impl_trait_ref) = self.parent_impl_trait_ref() {
237 if alias_ty.trait_ref(self.tcx) == impl_trait_ref {
241 let parent = self.parent().expect("we should have a parent here");
242
243 for &assoc in self.tcx.associated_items(parent).in_definition_order() {
244 trace!(?assoc);
245 if assoc.trait_item_def_id != Some(alias_ty.def_id) {
246 continue;
247 }
248
249 if !assoc.defaultness(self.tcx).is_final() {
252 continue;
253 }
254
255 if !self.seen.insert(assoc.def_id.expect_local()) {
256 return;
257 }
258
259 let impl_args = alias_ty.args.rebase_onto(
260 self.tcx,
261 impl_trait_ref.def_id,
262 ty::GenericArgs::identity_for_item(self.tcx, parent),
263 );
264
265 if self.tcx.check_args_compatible(assoc.def_id, impl_args) {
266 self.tcx
267 .type_of(assoc.def_id)
268 .instantiate(self.tcx, impl_args)
269 .visit_with(self);
270 return;
271 } else {
272 self.tcx.dcx().span_delayed_bug(
273 self.tcx.def_span(assoc.def_id),
274 "item had incorrect args",
275 );
276 }
277 }
278 }
279 } else if let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) =
280 self.tcx.opt_rpitit_info(alias_ty.def_id)
281 && fn_def_id == self.item.into()
282 {
283 let ty = self.tcx.type_of(alias_ty.def_id).instantiate(self.tcx, alias_ty.args);
295 let ty::Alias(ty::Opaque, alias_ty) = *ty.kind() else { bug!("{ty:?}") };
296 self.visit_opaque_ty(alias_ty);
297 }
298 }
299 _ => trace!(kind=?t.kind()),
300 }
301 }
302}
303
304fn opaque_types_defined_by<'tcx>(
305 tcx: TyCtxt<'tcx>,
306 item: LocalDefId,
307) -> &'tcx ty::List<LocalDefId> {
308 let kind = tcx.def_kind(item);
309 trace!(?kind);
310 let mut collector = OpaqueTypeCollector::new(tcx, item);
311 collector.collect_taits_from_defines_attr();
312 super::sig_types::walk_types(tcx, item, &mut collector);
313
314 match kind {
315 DefKind::AssocFn
316 | DefKind::Fn
317 | DefKind::Static { .. }
318 | DefKind::Const
319 | DefKind::AssocConst
320 | DefKind::AnonConst => {
321 collector.collect_taits_declared_in_body();
322 }
323 DefKind::OpaqueTy
324 | DefKind::TyAlias
325 | DefKind::AssocTy
326 | DefKind::Mod
327 | DefKind::Struct
328 | DefKind::Union
329 | DefKind::Enum
330 | DefKind::Variant
331 | DefKind::Trait
332 | DefKind::ForeignTy
333 | DefKind::TraitAlias
334 | DefKind::TyParam
335 | DefKind::ConstParam
336 | DefKind::Ctor(_, _)
337 | DefKind::Macro(_)
338 | DefKind::ExternCrate
339 | DefKind::Use
340 | DefKind::ForeignMod
341 | DefKind::Field
342 | DefKind::LifetimeParam
343 | DefKind::GlobalAsm
344 | DefKind::Impl { .. }
345 | DefKind::SyntheticCoroutineBody => {}
346 DefKind::Closure | DefKind::InlineConst => {
348 collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item)));
349 }
350 }
351 tcx.mk_local_def_ids(&collector.opaques)
352}
353
354pub(super) fn provide(providers: &mut Providers) {
355 *providers = Providers { opaque_types_defined_by, ..*providers };
356}