rustc_infer/infer/opaque_types/mod.rs
1use hir::def_id::{DefId, LocalDefId};
2use rustc_data_structures::fx::FxIndexMap;
3use rustc_hir as hir;
4use rustc_middle::bug;
5use rustc_middle::traits::ObligationCause;
6use rustc_middle::traits::solve::Goal;
7use rustc_middle::ty::error::{ExpectedFound, TypeError};
8use rustc_middle::ty::{
9 self, BottomUpFolder, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
10 TypeVisitableExt,
11};
12use rustc_span::Span;
13use tracing::{debug, instrument};
14
15use super::DefineOpaqueTypes;
16use crate::errors::OpaqueHiddenTypeDiag;
17use crate::infer::{InferCtxt, InferOk};
18use crate::traits::{self, Obligation, PredicateObligations};
19
20mod table;
21
22pub(crate) type OpaqueTypeMap<'tcx> = FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>;
23pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable};
24
25impl<'tcx> InferCtxt<'tcx> {
26 /// This is a backwards compatibility hack to prevent breaking changes from
27 /// lazy TAIT around RPIT handling.
28 pub fn replace_opaque_types_with_inference_vars<T: TypeFoldable<TyCtxt<'tcx>>>(
29 &self,
30 value: T,
31 body_id: LocalDefId,
32 span: Span,
33 param_env: ty::ParamEnv<'tcx>,
34 ) -> InferOk<'tcx, T> {
35 // We handle opaque types differently in the new solver.
36 if self.next_trait_solver() {
37 return InferOk { value, obligations: PredicateObligations::new() };
38 }
39
40 if !value.has_opaque_types() {
41 return InferOk { value, obligations: PredicateObligations::new() };
42 }
43
44 let mut obligations = PredicateObligations::new();
45 let value = value.fold_with(&mut BottomUpFolder {
46 tcx: self.tcx,
47 lt_op: |lt| lt,
48 ct_op: |ct| ct,
49 ty_op: |ty| match *ty.kind() {
50 ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })
51 if self.can_define_opaque_ty(def_id) && !ty.has_escaping_bound_vars() =>
52 {
53 let def_span = self.tcx.def_span(def_id);
54 let span = if span.contains(def_span) { def_span } else { span };
55 let ty_var = self.next_ty_var(span);
56 obligations.extend(
57 self.handle_opaque_type(ty, ty_var, span, param_env)
58 .unwrap()
59 .into_iter()
60 .map(|goal| {
61 Obligation::new(
62 self.tcx,
63 ObligationCause::new(
64 span,
65 body_id,
66 traits::ObligationCauseCode::OpaqueReturnType(None),
67 ),
68 goal.param_env,
69 goal.predicate,
70 )
71 }),
72 );
73 ty_var
74 }
75 _ => ty,
76 },
77 });
78 InferOk { value, obligations }
79 }
80
81 pub fn handle_opaque_type(
82 &self,
83 a: Ty<'tcx>,
84 b: Ty<'tcx>,
85 span: Span,
86 param_env: ty::ParamEnv<'tcx>,
87 ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, TypeError<'tcx>> {
88 debug_assert!(!self.next_trait_solver());
89 let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() {
90 ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) if def_id.is_local() => {
91 let def_id = def_id.expect_local();
92 if let ty::TypingMode::Coherence = self.typing_mode() {
93 // See comment on `insert_hidden_type` for why this is sufficient in coherence
94 return Some(self.register_hidden_type(
95 OpaqueTypeKey { def_id, args },
96 span,
97 param_env,
98 b,
99 ));
100 }
101 // Check that this is `impl Trait` type is
102 // declared by `parent_def_id` -- i.e., one whose
103 // value we are inferring. At present, this is
104 // always true during the first phase of
105 // type-check, but not always true later on during
106 // NLL. Once we support named opaque types more fully,
107 // this same scenario will be able to arise during all phases.
108 //
109 // Here is an example using type alias `impl Trait`
110 // that indicates the distinction we are checking for:
111 //
112 // ```rust
113 // mod a {
114 // pub type Foo = impl Iterator;
115 // pub fn make_foo() -> Foo { .. }
116 // }
117 //
118 // mod b {
119 // fn foo() -> a::Foo { a::make_foo() }
120 // }
121 // ```
122 //
123 // Here, the return type of `foo` references an
124 // `Opaque` indeed, but not one whose value is
125 // presently being inferred. You can get into a
126 // similar situation with closure return types
127 // today:
128 //
129 // ```rust
130 // fn foo() -> impl Iterator { .. }
131 // fn bar() {
132 // let x = || foo(); // returns the Opaque assoc with `foo`
133 // }
134 // ```
135 if !self.can_define_opaque_ty(def_id) {
136 return None;
137 }
138
139 if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }) = *b.kind() {
140 // We could accept this, but there are various ways to handle this situation,
141 // and we don't want to make a decision on it right now. Likely this case is so
142 // super rare anyway, that no one encounters it in practice. It does occur
143 // however in `fn fut() -> impl Future<Output = i32> { async { 42 } }`, where
144 // it is of no concern, so we only check for TAITs.
145 if self.can_define_opaque_ty(b_def_id)
146 && matches!(
147 self.tcx.opaque_ty_origin(b_def_id),
148 hir::OpaqueTyOrigin::TyAlias { .. }
149 )
150 {
151 self.dcx().emit_err(OpaqueHiddenTypeDiag {
152 span,
153 hidden_type: self.tcx.def_span(b_def_id),
154 opaque_type: self.tcx.def_span(def_id),
155 });
156 }
157 }
158 Some(self.register_hidden_type(OpaqueTypeKey { def_id, args }, span, param_env, b))
159 }
160 _ => None,
161 };
162 if let Some(res) = process(a, b) {
163 res
164 } else if let Some(res) = process(b, a) {
165 res
166 } else {
167 let (a, b) = self.resolve_vars_if_possible((a, b));
168 Err(TypeError::Sorts(ExpectedFound::new(a, b)))
169 }
170 }
171}
172
173impl<'tcx> InferCtxt<'tcx> {
174 #[instrument(skip(self), level = "debug")]
175 fn register_hidden_type(
176 &self,
177 opaque_type_key: OpaqueTypeKey<'tcx>,
178 span: Span,
179 param_env: ty::ParamEnv<'tcx>,
180 hidden_ty: Ty<'tcx>,
181 ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, TypeError<'tcx>> {
182 let mut goals = Vec::new();
183
184 self.insert_hidden_type(opaque_type_key, span, param_env, hidden_ty, &mut goals)?;
185
186 self.add_item_bounds_for_hidden_type(
187 opaque_type_key.def_id.to_def_id(),
188 opaque_type_key.args,
189 param_env,
190 hidden_ty,
191 &mut goals,
192 );
193
194 Ok(goals)
195 }
196
197 /// Insert a hidden type into the opaque type storage, making sure
198 /// it hasn't previously been defined. This does not emit any
199 /// constraints and it's the responsibility of the caller to make
200 /// sure that the item bounds of the opaque are checked.
201 pub fn inject_new_hidden_type_unchecked(
202 &self,
203 opaque_type_key: OpaqueTypeKey<'tcx>,
204 hidden_ty: OpaqueHiddenType<'tcx>,
205 ) {
206 let prev = self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty);
207 assert_eq!(prev, None);
208 }
209
210 /// Insert a hidden type into the opaque type storage, equating it
211 /// with any previous entries if necessary.
212 ///
213 /// This **does not** add the item bounds of the opaque as nested
214 /// obligations. That is only necessary when normalizing the opaque
215 /// itself, not when getting the opaque type constraints from
216 /// somewhere else.
217 pub fn insert_hidden_type(
218 &self,
219 opaque_type_key: OpaqueTypeKey<'tcx>,
220 span: Span,
221 param_env: ty::ParamEnv<'tcx>,
222 hidden_ty: Ty<'tcx>,
223 goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
224 ) -> Result<(), TypeError<'tcx>> {
225 // Ideally, we'd get the span where *this specific `ty` came
226 // from*, but right now we just use the span from the overall
227 // value being folded. In simple cases like `-> impl Foo`,
228 // these are the same span, but not in cases like `-> (impl
229 // Foo, impl Bar)`.
230 match self.typing_mode() {
231 ty::TypingMode::Coherence => {
232 // During intercrate we do not define opaque types but instead always
233 // force ambiguity unless the hidden type is known to not implement
234 // our trait.
235 goals.push(Goal::new(self.tcx, param_env, ty::PredicateKind::Ambiguous));
236 }
237 ty::TypingMode::Analysis { .. } => {
238 let prev = self
239 .inner
240 .borrow_mut()
241 .opaque_types()
242 .register(opaque_type_key, OpaqueHiddenType { ty: hidden_ty, span });
243 if let Some(prev) = prev {
244 goals.extend(
245 self.at(&ObligationCause::dummy_with_span(span), param_env)
246 .eq(DefineOpaqueTypes::Yes, prev, hidden_ty)?
247 .obligations
248 .into_iter()
249 .map(|obligation| obligation.as_goal()),
250 );
251 }
252 }
253 mode @ (ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => {
254 bug!("insert hidden type in {mode:?}")
255 }
256 }
257
258 Ok(())
259 }
260
261 pub fn add_item_bounds_for_hidden_type(
262 &self,
263 def_id: DefId,
264 args: ty::GenericArgsRef<'tcx>,
265 param_env: ty::ParamEnv<'tcx>,
266 hidden_ty: Ty<'tcx>,
267 goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
268 ) {
269 let tcx = self.tcx;
270 // Require that the hidden type is well-formed. We have to
271 // make sure we wf-check the hidden type to fix #114728.
272 //
273 // However, we don't check that all types are well-formed.
274 // We only do so for types provided by the user or if they are
275 // "used", e.g. for method selection.
276 //
277 // This means we never check the wf requirements of the hidden
278 // type during MIR borrowck, causing us to infer the wrong
279 // lifetime for its member constraints which then results in
280 // unexpected region errors.
281 goals.push(Goal::new(tcx, param_env, ty::ClauseKind::WellFormed(hidden_ty.into())));
282
283 let replace_opaques_in = |clause: ty::Clause<'tcx>, goals: &mut Vec<_>| {
284 clause.fold_with(&mut BottomUpFolder {
285 tcx,
286 ty_op: |ty| match *ty.kind() {
287 // We can't normalize associated types from `rustc_infer`,
288 // but we can eagerly register inference variables for them.
289 // FIXME(RPITIT): Don't replace RPITITs with inference vars.
290 // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
291 ty::Alias(ty::Projection, projection_ty)
292 if !projection_ty.has_escaping_bound_vars()
293 && !tcx.is_impl_trait_in_trait(projection_ty.def_id)
294 && !self.next_trait_solver() =>
295 {
296 let ty_var = self.next_ty_var(self.tcx.def_span(projection_ty.def_id));
297 goals.push(Goal::new(
298 self.tcx,
299 param_env,
300 ty::PredicateKind::Clause(ty::ClauseKind::Projection(
301 ty::ProjectionPredicate {
302 projection_term: projection_ty.into(),
303 term: ty_var.into(),
304 },
305 )),
306 ));
307 ty_var
308 }
309 // Replace all other mentions of the same opaque type with the hidden type,
310 // as the bounds must hold on the hidden type after all.
311 ty::Alias(ty::Opaque, ty::AliasTy { def_id: def_id2, args: args2, .. })
312 if def_id == def_id2 && args == args2 =>
313 {
314 hidden_ty
315 }
316 _ => ty,
317 },
318 lt_op: |lt| lt,
319 ct_op: |ct| ct,
320 })
321 };
322
323 let item_bounds = tcx.explicit_item_bounds(def_id);
324 for (predicate, _) in item_bounds.iter_instantiated_copied(tcx, args) {
325 let predicate = replace_opaques_in(predicate, goals);
326
327 // Require that the predicate holds for the concrete type.
328 debug!(?predicate);
329 goals.push(Goal::new(self.tcx, param_env, predicate));
330 }
331
332 // If this opaque is being defined and it's conditionally const,
333 if self.tcx.is_conditionally_const(def_id) {
334 let item_bounds = tcx.explicit_implied_const_bounds(def_id);
335 for (predicate, _) in item_bounds.iter_instantiated_copied(tcx, args) {
336 let predicate = replace_opaques_in(
337 predicate.to_host_effect_clause(self.tcx, ty::BoundConstness::Maybe),
338 goals,
339 );
340
341 // Require that the predicate holds for the concrete type.
342 debug!(?predicate);
343 goals.push(Goal::new(self.tcx, param_env, predicate));
344 }
345 }
346 }
347}