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, DefiningScopeKind, Ty, TyCtxt, TypeVisitableExt};
7use tracing::{debug, instrument, trace};
8
9use crate::errors::{TaitForwardCompat2, UnconstrainedOpaqueType};
10
11#[instrument(skip(tcx), level = "debug")]
14pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
15 tcx: TyCtxt<'_>,
16 def_id: LocalDefId,
17 opaque_types_from: DefiningScopeKind,
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, opaque_types_from };
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 { .. } => {
36 locator.check(assoc_id.expect_local())
37 }
38 ty::AssocKind::Type { .. } => {}
40 }
41 }
42
43 if let Some(hidden) = locator.found {
44 hidden.ty
45 } else {
46 let guar = tcx.dcx().emit_err(UnconstrainedOpaqueType {
47 span: tcx.def_span(def_id),
48 name: tcx.item_ident(parent_def_id.to_def_id()),
49 what: "impl",
50 });
51 Ty::new_error(tcx, guar)
52 }
53}
54
55#[instrument(skip(tcx), level = "debug")]
74pub(super) fn find_opaque_ty_constraints_for_tait(
75 tcx: TyCtxt<'_>,
76 def_id: LocalDefId,
77 opaque_types_from: DefiningScopeKind,
78) -> Ty<'_> {
79 let mut locator = TaitConstraintLocator { def_id, tcx, found: None, opaque_types_from };
80
81 tcx.hir_walk_toplevel_module(&mut locator);
82
83 if let Some(hidden) = locator.found {
84 hidden.ty
85 } else {
86 let mut parent_def_id = def_id;
87 while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy {
88 parent_def_id = tcx.local_parent(parent_def_id);
90 }
91 let guar = tcx.dcx().emit_err(UnconstrainedOpaqueType {
92 span: tcx.def_span(def_id),
93 name: tcx.item_ident(parent_def_id.to_def_id()),
94 what: "crate",
95 });
96 Ty::new_error(tcx, guar)
97 }
98}
99
100struct TaitConstraintLocator<'tcx> {
101 tcx: TyCtxt<'tcx>,
102
103 def_id: LocalDefId,
105
106 found: Option<ty::OpaqueHiddenType<'tcx>>,
112
113 opaque_types_from: DefiningScopeKind,
114}
115
116impl<'tcx> TaitConstraintLocator<'tcx> {
117 fn insert_found(&mut self, hidden_ty: ty::OpaqueHiddenType<'tcx>) {
118 if let Some(prev) = &mut self.found {
119 if hidden_ty.ty != prev.ty {
120 let (Ok(guar) | Err(guar)) =
121 prev.build_mismatch_error(&hidden_ty, self.tcx).map(|d| d.emit());
122 prev.ty = Ty::new_error(self.tcx, guar);
123 }
124 } else {
125 self.found = Some(hidden_ty);
126 }
127 }
128
129 fn non_defining_use_in_defining_scope(&mut self, item_def_id: LocalDefId) {
130 let guar = self.tcx.dcx().emit_err(TaitForwardCompat2 {
131 span: self
132 .tcx
133 .def_ident_span(item_def_id)
134 .unwrap_or_else(|| self.tcx.def_span(item_def_id)),
135 opaque_type_span: self.tcx.def_span(self.def_id),
136 opaque_type: self.tcx.def_path_str(self.def_id),
137 });
138 self.insert_found(ty::OpaqueHiddenType::new_error(self.tcx, guar));
139 }
140
141 #[instrument(skip(self), level = "debug")]
142 fn check(&mut self, item_def_id: LocalDefId) {
143 let tcx = self.tcx;
145 if !tcx.has_typeck_results(item_def_id) {
146 debug!("no constraint: no typeck results");
147 return;
148 }
149
150 let opaque_types_defined_by = tcx.opaque_types_defined_by(item_def_id);
151 if !opaque_types_defined_by.contains(&self.def_id) {
153 debug!("no constraint: no opaque types defined");
154 return;
155 }
156
157 let hir_node = tcx.hir_node_by_def_id(item_def_id);
162 debug_assert!(
163 !matches!(hir_node, Node::ForeignItem(..)),
164 "foreign items cannot constrain opaque types",
165 );
166 if let Some(hir_sig) = hir_node.fn_sig()
167 && hir_sig.decl.output.is_suggestable_infer_ty().is_some()
168 {
169 let guar = self.tcx.dcx().span_delayed_bug(
170 hir_sig.decl.output.span(),
171 "inferring return types and opaque types do not mix well",
172 );
173 self.found = Some(ty::OpaqueHiddenType::new_error(tcx, guar));
174 return;
175 }
176
177 match self.opaque_types_from {
178 DefiningScopeKind::HirTypeck => {
179 let tables = tcx.typeck(item_def_id);
180 if let Some(guar) = tables.tainted_by_errors {
181 self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
182 } else if let Some(&hidden_type) = tables.concrete_opaque_types.get(&self.def_id) {
183 self.insert_found(hidden_type);
184 } else {
185 self.non_defining_use_in_defining_scope(item_def_id);
186 }
187 }
188 DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(item_def_id) {
189 Err(guar) => self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar)),
190 Ok(concrete_opaque_types) => {
191 if let Some(&hidden_type) = concrete_opaque_types.0.get(&self.def_id) {
192 debug!(?hidden_type, "found constraint");
193 self.insert_found(hidden_type);
194 } else if let Err(guar) = tcx
195 .type_of_opaque_hir_typeck(self.def_id)
196 .instantiate_identity()
197 .error_reported()
198 {
199 self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
200 } else {
201 self.non_defining_use_in_defining_scope(item_def_id);
202 }
203 }
204 },
205 }
206 }
207}
208
209impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
210 type NestedFilter = nested_filter::All;
211
212 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
213 self.tcx
214 }
215 fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
216 intravisit::walk_expr(self, ex);
217 }
218 fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
219 trace!(?it.owner_id);
220 self.check(it.owner_id.def_id);
221 intravisit::walk_item(self, it);
222 }
223 fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
224 trace!(?it.owner_id);
225 self.check(it.owner_id.def_id);
226 intravisit::walk_impl_item(self, it);
227 }
228 fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
229 trace!(?it.owner_id);
230 self.check(it.owner_id.def_id);
231 intravisit::walk_trait_item(self, it);
232 }
233 fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
234 trace!(?it.owner_id);
235 assert_ne!(it.owner_id.def_id, self.def_id);
236 intravisit::walk_foreign_item(self, it);
238 }
239}
240
241pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
242 tcx: TyCtxt<'tcx>,
243 def_id: LocalDefId,
244 owner_def_id: LocalDefId,
245 opaque_types_from: DefiningScopeKind,
246) -> Ty<'tcx> {
247 match opaque_types_from {
248 DefiningScopeKind::HirTypeck => {
249 let tables = tcx.typeck(owner_def_id);
250 if let Some(guar) = tables.tainted_by_errors {
251 Ty::new_error(tcx, guar)
252 } else if let Some(hidden_ty) = tables.concrete_opaque_types.get(&def_id) {
253 hidden_ty.ty
254 } else {
255 Ty::new_diverging_default(tcx)
265 }
266 }
267 DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(owner_def_id) {
268 Ok(concrete_opaque_types) => {
269 if let Some(hidden_ty) = concrete_opaque_types.0.get(&def_id) {
270 hidden_ty.ty
271 } else {
272 let hir_ty = tcx.type_of_opaque_hir_typeck(def_id).instantiate_identity();
273 if let Err(guar) = hir_ty.error_reported() {
274 Ty::new_error(tcx, guar)
275 } else {
276 hir_ty
277 }
278 }
279 }
280 Err(guar) => Ty::new_error(tcx, guar),
281 },
282 }
283}