rustc_hir_analysis/collect/type_of/
opaque.rs1use rustc_hir::def::DefKind;
2use rustc_hir::def_id::LocalDefId;
3use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem, def, intravisit};
4use rustc_middle::bug;
5use rustc_middle::hir::nested_filter;
6use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
7use rustc_span::DUMMY_SP;
8use tracing::{debug, instrument, trace};
9
10use crate::errors::{TaitForwardCompat2, UnconstrainedOpaqueType};
11
12#[instrument(skip(tcx), level = "debug")]
15pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
16 tcx: TyCtxt<'_>,
17 def_id: LocalDefId,
18) -> Ty<'_> {
19 let mut parent_def_id = def_id;
20 while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy {
21 parent_def_id = tcx.local_parent(parent_def_id);
23 }
24 let impl_def_id = tcx.local_parent(parent_def_id);
25 match tcx.def_kind(impl_def_id) {
26 DefKind::Impl { .. } => {}
27 other => bug!("invalid impl trait in assoc type parent: {other:?}"),
28 }
29
30 let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };
31
32 for &assoc_id in tcx.associated_item_def_ids(impl_def_id) {
33 let assoc = tcx.associated_item(assoc_id);
34 match assoc.kind {
35 ty::AssocKind::Const | ty::AssocKind::Fn => locator.check(assoc_id.expect_local()),
36 ty::AssocKind::Type => {}
38 }
39 }
40
41 if let Some(hidden) = locator.found {
42 if !hidden.ty.references_error() {
44 for concrete_type in locator.typeck_types {
45 if concrete_type.ty != tcx.erase_regions(hidden.ty) {
46 if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) {
47 d.emit();
48 }
49 }
50 }
51 }
52
53 hidden.ty
54 } else {
55 let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
56 span: tcx.def_span(def_id),
57 name: tcx.item_ident(parent_def_id.to_def_id()),
58 what: "impl",
59 });
60 Ty::new_error(tcx, reported)
61 }
62}
63
64#[instrument(skip(tcx), level = "debug")]
83pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
84 let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };
85
86 tcx.hir_walk_toplevel_module(&mut locator);
87
88 if let Some(hidden) = locator.found {
89 if !hidden.ty.references_error() {
91 for concrete_type in locator.typeck_types {
92 if concrete_type.ty != tcx.erase_regions(hidden.ty) {
93 if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) {
94 d.emit();
95 }
96 }
97 }
98 }
99
100 hidden.ty
101 } else {
102 let mut parent_def_id = def_id;
103 while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy {
104 parent_def_id = tcx.local_parent(parent_def_id);
106 }
107 let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
108 span: tcx.def_span(def_id),
109 name: tcx.item_ident(parent_def_id.to_def_id()),
110 what: "crate",
111 });
112 Ty::new_error(tcx, reported)
113 }
114}
115
116struct TaitConstraintLocator<'tcx> {
117 tcx: TyCtxt<'tcx>,
118
119 def_id: LocalDefId,
121
122 found: Option<ty::OpaqueHiddenType<'tcx>>,
128
129 typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>,
133}
134
135impl TaitConstraintLocator<'_> {
136 #[instrument(skip(self), level = "debug")]
137 fn check(&mut self, item_def_id: LocalDefId) {
138 if !self.tcx.has_typeck_results(item_def_id) {
140 debug!("no constraint: no typeck results");
141 return;
142 }
143
144 let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id);
145 if !opaque_types_defined_by.contains(&self.def_id) {
147 debug!("no constraint: no opaque types defined");
148 return;
149 }
150
151 let hir_node = self.tcx.hir_node_by_def_id(item_def_id);
156 debug_assert!(
157 !matches!(hir_node, Node::ForeignItem(..)),
158 "foreign items cannot constrain opaque types",
159 );
160 if let Some(hir_sig) = hir_node.fn_sig()
161 && hir_sig.decl.output.is_suggestable_infer_ty().is_some()
162 {
163 let guar = self.tcx.dcx().span_delayed_bug(
164 hir_sig.decl.output.span(),
165 "inferring return types and opaque types do not mix well",
166 );
167 self.found =
168 Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
169 return;
170 }
171
172 let tables = self.tcx.typeck(item_def_id);
184 if let Some(guar) = tables.tainted_by_errors {
185 self.found =
186 Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
187 return;
188 }
189
190 let mut constrained = false;
191 for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
192 if opaque_type_key.def_id != self.def_id {
193 continue;
194 }
195 constrained = true;
196
197 let concrete_type =
198 self.tcx.erase_regions(hidden_type.remap_generic_params_to_declaration_params(
199 opaque_type_key,
200 self.tcx,
201 true,
202 ));
203 if self.typeck_types.iter().all(|prev| prev.ty != concrete_type.ty) {
204 self.typeck_types.push(concrete_type);
205 }
206 }
207
208 if !constrained {
209 debug!("no constraints in typeck results");
210 if opaque_types_defined_by.contains(&self.def_id) {
211 let guar = self.tcx.dcx().emit_err(TaitForwardCompat2 {
212 span: self
213 .tcx
214 .def_ident_span(item_def_id)
215 .unwrap_or_else(|| self.tcx.def_span(item_def_id)),
216 opaque_type_span: self.tcx.def_span(self.def_id),
217 opaque_type: self.tcx.def_path_str(self.def_id),
218 });
219 self.found = Some(ty::OpaqueHiddenType {
221 span: DUMMY_SP,
222 ty: Ty::new_error(self.tcx, guar),
223 });
224 }
225 return;
226 };
227
228 let borrowck_results = &self.tcx.mir_borrowck(item_def_id);
230
231 if let Some(guar) = borrowck_results.tainted_by_errors {
233 self.found =
234 Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
235 return;
236 }
237
238 debug!(?borrowck_results.concrete_opaque_types);
239 if let Some(&concrete_type) = borrowck_results.concrete_opaque_types.get(&self.def_id) {
240 debug!(?concrete_type, "found constraint");
241 if let Some(prev) = &mut self.found {
242 if concrete_type.ty != prev.ty {
243 let (Ok(guar) | Err(guar)) =
244 prev.build_mismatch_error(&concrete_type, self.tcx).map(|d| d.emit());
245 prev.ty = Ty::new_error(self.tcx, guar);
246 }
247 } else {
248 self.found = Some(concrete_type);
249 }
250 }
251 }
252}
253
254impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
255 type NestedFilter = nested_filter::All;
256
257 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
258 self.tcx
259 }
260 fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
261 intravisit::walk_expr(self, ex);
262 }
263 fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
264 trace!(?it.owner_id);
265 self.check(it.owner_id.def_id);
266 intravisit::walk_item(self, it);
267 }
268 fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
269 trace!(?it.owner_id);
270 self.check(it.owner_id.def_id);
271 intravisit::walk_impl_item(self, it);
272 }
273 fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
274 trace!(?it.owner_id);
275 self.check(it.owner_id.def_id);
276 intravisit::walk_trait_item(self, it);
277 }
278 fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
279 trace!(?it.owner_id);
280 assert_ne!(it.owner_id.def_id, self.def_id);
281 intravisit::walk_foreign_item(self, it);
283 }
284}
285
286pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
287 tcx: TyCtxt<'tcx>,
288 def_id: LocalDefId,
289 owner_def_id: LocalDefId,
290) -> Ty<'tcx> {
291 let tables = tcx.typeck(owner_def_id);
292
293 let mut hir_opaque_ty: Option<ty::OpaqueHiddenType<'tcx>> = None;
299 if tables.tainted_by_errors.is_none() {
300 for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
301 if opaque_type_key.def_id != def_id {
302 continue;
303 }
304 let concrete_type = tcx.erase_regions(
305 hidden_type.remap_generic_params_to_declaration_params(opaque_type_key, tcx, true),
306 );
307 if let Some(prev) = &mut hir_opaque_ty {
308 if concrete_type.ty != prev.ty {
309 if let Ok(d) = prev.build_mismatch_error(&concrete_type, tcx) {
310 d.emit();
311 }
312 }
313 } else {
314 hir_opaque_ty = Some(concrete_type);
315 }
316 }
317 }
318
319 let mir_opaque_ty = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied();
320 if let Some(mir_opaque_ty) = mir_opaque_ty {
321 if mir_opaque_ty.references_error() {
322 return mir_opaque_ty.ty;
323 }
324
325 debug!(?owner_def_id);
326 let mut locator = RpitConstraintChecker { def_id, tcx, found: mir_opaque_ty };
327
328 match tcx.hir_node_by_def_id(owner_def_id) {
329 Node::Item(it) => intravisit::walk_item(&mut locator, it),
330 Node::ImplItem(it) => intravisit::walk_impl_item(&mut locator, it),
331 Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it),
332 other => bug!("{:?} is not a valid scope for an opaque type item", other),
333 }
334
335 mir_opaque_ty.ty
336 } else if let Some(guar) = tables.tainted_by_errors {
337 Ty::new_error(tcx, guar)
340 } else {
341 if let Some(hir_opaque_ty) = hir_opaque_ty {
343 hir_opaque_ty.ty
344 } else {
345 Ty::new_diverging_default(tcx)
352 }
353 }
354}
355
356struct RpitConstraintChecker<'tcx> {
357 tcx: TyCtxt<'tcx>,
358
359 def_id: LocalDefId,
361
362 found: ty::OpaqueHiddenType<'tcx>,
363}
364
365impl RpitConstraintChecker<'_> {
366 #[instrument(skip(self), level = "debug")]
367 fn check(&self, def_id: LocalDefId) {
368 let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types;
370 debug!(?concrete_opaque_types);
371 if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) {
372 debug!(?concrete_type, "found constraint");
373 if concrete_type.ty != self.found.ty {
374 if let Ok(d) = self.found.build_mismatch_error(&concrete_type, self.tcx) {
375 d.emit();
376 }
377 }
378 }
379 }
380}
381
382impl<'tcx> intravisit::Visitor<'tcx> for RpitConstraintChecker<'tcx> {
383 type NestedFilter = nested_filter::OnlyBodies;
384
385 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
386 self.tcx
387 }
388 fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
389 intravisit::walk_expr(self, ex);
390 }
391 fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
392 trace!(?it.owner_id);
393 if it.owner_id.def_id != self.def_id {
395 self.check(it.owner_id.def_id);
396 intravisit::walk_item(self, it);
397 }
398 }
399 fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
400 trace!(?it.owner_id);
401 if it.owner_id.def_id != self.def_id {
403 self.check(it.owner_id.def_id);
404 intravisit::walk_impl_item(self, it);
405 }
406 }
407 fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
408 trace!(?it.owner_id);
409 self.check(it.owner_id.def_id);
410 intravisit::walk_trait_item(self, it);
411 }
412}