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 Some(body) = self.tcx.hir_maybe_body_owned_by(self.item) else {
68 return;
69 };
70 let body = body.value;
71 struct TaitInBodyFinder<'a, 'tcx> {
72 collector: &'a mut OpaqueTypeCollector<'tcx>,
73 }
74 impl<'v> intravisit::Visitor<'v> for TaitInBodyFinder<'_, '_> {
75 #[instrument(level = "trace", skip(self))]
76 fn visit_nested_item(&mut self, id: rustc_hir::ItemId) {
77 let id = id.owner_id.def_id;
78 if let DefKind::TyAlias = self.collector.tcx.def_kind(id) {
79 let items = self.collector.tcx.opaque_types_defined_by(id);
80 self.collector.opaques.extend(items);
81 }
82 }
83 #[instrument(level = "trace", skip(self))]
84 fn visit_nested_body(&mut self, id: rustc_hir::BodyId) {
86 let body = self.collector.tcx.hir_body(id);
87 self.visit_body(body);
88 }
89 }
90 TaitInBodyFinder { collector: self }.visit_expr(body);
91 }
92
93 #[instrument(level = "debug", skip(self))]
94 fn visit_opaque_ty(&mut self, alias_ty: ty::AliasTy<'tcx>) {
95 if !self.seen.insert(alias_ty.def_id.expect_local()) {
96 return;
97 }
98
99 match self.tcx.local_opaque_ty_origin(alias_ty.def_id.expect_local()) {
101 rustc_hir::OpaqueTyOrigin::FnReturn { .. }
102 | rustc_hir::OpaqueTyOrigin::AsyncFn { .. } => {}
103 rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => match self.mode {
104 CollectionMode::ImplTraitInAssocTypes => {
108 if !in_assoc_ty {
109 return;
110 }
111 }
112 CollectionMode::Taits => {
116 if in_assoc_ty {
117 return;
118 }
119 }
120 CollectionMode::RpitAndAsyncFnOnly => return,
121 },
122 }
123
124 trace!(?alias_ty, "adding");
125 self.opaques.push(alias_ty.def_id.expect_local());
126
127 let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
128 match self
132 .tcx
133 .uses_unique_generic_params(&alias_ty.args[..parent_count], CheckRegions::FromFunction)
134 {
135 Ok(()) => {
136 for (pred, span) in
144 self.tcx.explicit_item_bounds(alias_ty.def_id).iter_identity_copied()
145 {
146 trace!(?pred);
147 self.visit_spanned(span, pred);
148 }
149 }
150 Err(NotUniqueParam::NotParam(arg)) => {
151 self.tcx.dcx().emit_err(NotParam {
152 arg,
153 span: self.span(),
154 opaque_span: self.tcx.def_span(alias_ty.def_id),
155 });
156 }
157 Err(NotUniqueParam::DuplicateParam(arg)) => {
158 self.tcx.dcx().emit_err(DuplicateArg {
159 arg,
160 span: self.span(),
161 opaque_span: self.tcx.def_span(alias_ty.def_id),
162 });
163 }
164 }
165 }
166
167 #[instrument(level = "trace", skip(self))]
170 fn collect_taits_from_defines_attr(&mut self) {
171 let hir_id = self.tcx.local_def_id_to_hir_id(self.item);
172 if !hir_id.is_owner() {
173 return;
174 }
175 let Some(defines) = self.tcx.hir_attr_map(hir_id.owner).define_opaque else {
176 return;
177 };
178 for &(span, define) in defines {
179 trace!(?define);
180 let mode = std::mem::replace(&mut self.mode, CollectionMode::Taits);
181 let n = self.opaques.len();
182 super::sig_types::walk_types(self.tcx, define, self);
183 if n == self.opaques.len() {
184 self.tcx.dcx().span_err(span, "item does not contain any opaque types");
185 }
186 self.mode = mode;
187 }
188 self.mode = CollectionMode::RpitAndAsyncFnOnly;
191 }
192}
193
194impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> {
195 #[instrument(skip(self), ret, level = "trace")]
196 fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) {
197 self.visit_spanned(span, value);
198 }
199}
200
201impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
202 #[instrument(skip(self), ret, level = "trace")]
203 fn visit_ty(&mut self, t: Ty<'tcx>) {
204 t.super_visit_with(self);
205 match *t.kind() {
206 ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
207 self.visit_opaque_ty(alias_ty);
208 }
209 ty::Alias(ty::Free, alias_ty) if let Some(def_id) = alias_ty.def_id.as_local() => {
212 if !self.seen.insert(def_id) {
213 return;
214 }
215 self.tcx
216 .type_of(alias_ty.def_id)
217 .instantiate(self.tcx, alias_ty.args)
218 .visit_with(self);
219 }
220 ty::Alias(ty::Projection, alias_ty) => {
221 if let Some(parent) = self.tcx.trait_impl_of_assoc(self.item.to_def_id()) {
225 let impl_trait_ref = self.tcx.impl_trait_ref(parent).instantiate_identity();
226 if alias_ty.trait_ref(self.tcx) == impl_trait_ref {
230 for &assoc in self.tcx.associated_items(parent).in_definition_order() {
231 trace!(?assoc);
232 if assoc.expect_trait_impl() != Ok(alias_ty.def_id) {
233 continue;
234 }
235
236 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 alias_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, alias_args) {
253 self.tcx
254 .type_of(assoc.def_id)
255 .instantiate(self.tcx, alias_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 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 _ => trace!(kind=?t.kind()),
287 }
288 }
289}
290
291fn opaque_types_defined_by<'tcx>(
292 tcx: TyCtxt<'tcx>,
293 item: LocalDefId,
294) -> &'tcx ty::List<LocalDefId> {
295 let kind = tcx.def_kind(item);
296 trace!(?kind);
297 let mut collector = OpaqueTypeCollector::new(tcx, item);
298 collector.collect_taits_from_defines_attr();
299 super::sig_types::walk_types(tcx, item, &mut collector);
300
301 match kind {
302 DefKind::AssocFn
303 | DefKind::Fn
304 | DefKind::Static { .. }
305 | DefKind::Const
306 | DefKind::AssocConst
307 | DefKind::AnonConst => {
308 collector.collect_taits_declared_in_body();
309 }
310 DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => {
315 collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item)));
316 }
317 DefKind::AssocTy | DefKind::TyAlias | DefKind::GlobalAsm => {}
318 DefKind::OpaqueTy
319 | DefKind::Mod
320 | DefKind::Struct
321 | DefKind::Union
322 | DefKind::Enum
323 | DefKind::Variant
324 | DefKind::Trait
325 | DefKind::ForeignTy
326 | DefKind::TraitAlias
327 | DefKind::TyParam
328 | DefKind::ConstParam
329 | DefKind::Ctor(_, _)
330 | DefKind::Macro(_)
331 | DefKind::ExternCrate
332 | DefKind::Use
333 | DefKind::ForeignMod
334 | DefKind::Field
335 | DefKind::LifetimeParam
336 | DefKind::Impl { .. } => {
337 span_bug!(
338 tcx.def_span(item),
339 "`opaque_types_defined_by` not defined for {} `{item:?}`",
340 kind.descr(item.to_def_id())
341 );
342 }
343 }
344 tcx.mk_local_def_ids(&collector.opaques)
345}
346
347pub(super) fn provide(providers: &mut Providers) {
348 *providers = Providers { opaque_types_defined_by, ..*providers };
349}