rustc_borrowck/region_infer/opaque_types.rs
1use rustc_data_structures::fx::FxIndexMap;
2use rustc_errors::ErrorGuaranteed;
3use rustc_hir::OpaqueTyOrigin;
4use rustc_hir::def_id::LocalDefId;
5use rustc_infer::infer::outlives::env::OutlivesEnvironment;
6use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _};
7use rustc_macros::extension;
8use rustc_middle::ty::fold::fold_regions;
9use rustc_middle::ty::visit::TypeVisitableExt;
10use rustc_middle::ty::{
11 self, GenericArgKind, GenericArgs, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
12 TypingMode,
13};
14use rustc_span::Span;
15use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt;
16use rustc_trait_selection::traits::ObligationCtxt;
17use tracing::{debug, instrument};
18
19use super::RegionInferenceContext;
20use crate::session_diagnostics::{LifetimeMismatchOpaqueParam, NonGenericOpaqueTypeParam};
21use crate::universal_regions::RegionClassification;
22
23impl<'tcx> RegionInferenceContext<'tcx> {
24 /// Resolve any opaque types that were encountered while borrow checking
25 /// this item. This is then used to get the type in the `type_of` query.
26 ///
27 /// For example consider `fn f<'a>(x: &'a i32) -> impl Sized + 'a { x }`.
28 /// This is lowered to give HIR something like
29 ///
30 /// type f<'a>::_Return<'_x> = impl Sized + '_x;
31 /// fn f<'a>(x: &'a i32) -> f<'a>::_Return<'a> { x }
32 ///
33 /// When checking the return type record the type from the return and the
34 /// type used in the return value. In this case they might be `_Return<'1>`
35 /// and `&'2 i32` respectively.
36 ///
37 /// Once we to this method, we have completed region inference and want to
38 /// call `infer_opaque_definition_from_instantiation` to get the inferred
39 /// type of `_Return<'_x>`. `infer_opaque_definition_from_instantiation`
40 /// compares lifetimes directly, so we need to map the inference variables
41 /// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`.
42 ///
43 /// First we map the regions in the generic parameters `_Return<'1>` to
44 /// their `external_name` giving `_Return<'a>`. This step is a bit involved.
45 /// See the [rustc-dev-guide chapter] for more info.
46 ///
47 /// Then we map all the lifetimes in the concrete type to an equal
48 /// universal region that occurs in the opaque type's args, in this case
49 /// this would result in `&'a i32`. We only consider regions in the args
50 /// in case there is an equal region that does not. For example, this should
51 /// be allowed:
52 /// `fn f<'a: 'b, 'b: 'a>(x: *mut &'b i32) -> impl Sized + 'a { x }`
53 ///
54 /// This will then allow `infer_opaque_definition_from_instantiation` to
55 /// determine that `_Return<'_x> = &'_x i32`.
56 ///
57 /// There's a slight complication around closures. Given
58 /// `fn f<'a: 'a>() { || {} }` the closure's type is something like
59 /// `f::<'a>::{{closure}}`. The region parameter from f is essentially
60 /// ignored by type checking so ends up being inferred to an empty region.
61 /// Calling `universal_upper_bound` for such a region gives `fr_fn_body`,
62 /// which has no `external_name` in which case we use `'{erased}` as the
63 /// region to pass to `infer_opaque_definition_from_instantiation`.
64 ///
65 /// [rustc-dev-guide chapter]:
66 /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
67 #[instrument(level = "debug", skip(self, infcx), ret)]
68 pub(crate) fn infer_opaque_types(
69 &self,
70 infcx: &InferCtxt<'tcx>,
71 opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
72 ) -> FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> {
73 let mut result: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> = FxIndexMap::default();
74 let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
75 FxIndexMap::default();
76
77 for (opaque_type_key, concrete_type) in opaque_ty_decls {
78 debug!(?opaque_type_key, ?concrete_type);
79
80 let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> =
81 vec![(self.universal_regions().fr_static, infcx.tcx.lifetimes.re_static)];
82
83 let opaque_type_key =
84 opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| {
85 // Use the SCC representative instead of directly using `region`.
86 // See [rustc-dev-guide chapter] § "Strict lifetime equality".
87 let scc = self.constraint_sccs.scc(region.as_var());
88 let vid = self.scc_representative(scc);
89 let named = match self.definitions[vid].origin {
90 // Iterate over all universal regions in a consistent order and find the
91 // *first* equal region. This makes sure that equal lifetimes will have
92 // the same name and simplifies subsequent handling.
93 // See [rustc-dev-guide chapter] § "Semantic lifetime equality".
94 NllRegionVariableOrigin::FreeRegion => self
95 .universal_regions()
96 .universal_regions_iter()
97 .filter(|&ur| {
98 // See [rustc-dev-guide chapter] § "Closure restrictions".
99 !matches!(
100 self.universal_regions().region_classification(ur),
101 Some(RegionClassification::External)
102 )
103 })
104 .find(|&ur| self.universal_region_relations.equal(vid, ur))
105 .map(|ur| self.definitions[ur].external_name.unwrap()),
106 NllRegionVariableOrigin::Placeholder(placeholder) => {
107 Some(ty::Region::new_placeholder(infcx.tcx, placeholder))
108 }
109 NllRegionVariableOrigin::Existential { .. } => None,
110 }
111 .unwrap_or_else(|| {
112 ty::Region::new_error_with_message(
113 infcx.tcx,
114 concrete_type.span,
115 "opaque type with non-universal region args",
116 )
117 });
118
119 arg_regions.push((vid, named));
120 named
121 });
122 debug!(?opaque_type_key, ?arg_regions);
123
124 let concrete_type = fold_regions(infcx.tcx, concrete_type, |region, _| {
125 arg_regions
126 .iter()
127 .find(|&&(arg_vid, _)| self.eval_equal(region.as_var(), arg_vid))
128 .map(|&(_, arg_named)| arg_named)
129 .unwrap_or(infcx.tcx.lifetimes.re_erased)
130 });
131 debug!(?concrete_type);
132
133 let ty =
134 infcx.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type);
135
136 // Sometimes, when the hidden type is an inference variable, it can happen that
137 // the hidden type becomes the opaque type itself. In this case, this was an opaque
138 // usage of the opaque type and we can ignore it. This check is mirrored in typeck's
139 // writeback.
140 if !infcx.next_trait_solver() {
141 if let ty::Alias(ty::Opaque, alias_ty) = ty.kind()
142 && alias_ty.def_id == opaque_type_key.def_id.to_def_id()
143 && alias_ty.args == opaque_type_key.args
144 {
145 continue;
146 }
147 }
148 // Sometimes two opaque types are the same only after we remap the generic parameters
149 // back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
150 // `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we
151 // only know that once we convert the generic parameters to those of the opaque type.
152 if let Some(prev) = result.get_mut(&opaque_type_key.def_id) {
153 if prev.ty != ty {
154 let guar = ty.error_reported().err().unwrap_or_else(|| {
155 let (Ok(e) | Err(e)) = prev
156 .build_mismatch_error(
157 &OpaqueHiddenType { ty, span: concrete_type.span },
158 infcx.tcx,
159 )
160 .map(|d| d.emit());
161 e
162 });
163 prev.ty = Ty::new_error(infcx.tcx, guar);
164 }
165 // Pick a better span if there is one.
166 // FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
167 prev.span = prev.span.substitute_dummy(concrete_type.span);
168 } else {
169 result.insert(
170 opaque_type_key.def_id,
171 OpaqueHiddenType { ty, span: concrete_type.span },
172 );
173 }
174
175 // Check that all opaque types have the same region parameters if they have the same
176 // non-region parameters. This is necessary because within the new solver we perform
177 // various query operations modulo regions, and thus could unsoundly select some impls
178 // that don't hold.
179 if !ty.references_error()
180 && let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert(
181 infcx.tcx.erase_regions(opaque_type_key),
182 (opaque_type_key, concrete_type.span),
183 )
184 && let Some((arg1, arg2)) = std::iter::zip(
185 prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
186 opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
187 )
188 .find(|(arg1, arg2)| arg1 != arg2)
189 {
190 infcx.dcx().emit_err(LifetimeMismatchOpaqueParam {
191 arg: arg1,
192 prev: arg2,
193 span: prev_span,
194 prev_span: concrete_type.span,
195 });
196 }
197 }
198 result
199 }
200
201 /// Map the regions in the type to named regions. This is similar to what
202 /// `infer_opaque_types` does, but can infer any universal region, not only
203 /// ones from the args for the opaque type. It also doesn't double check
204 /// that the regions produced are in fact equal to the named region they are
205 /// replaced with. This is fine because this function is only to improve the
206 /// region names in error messages.
207 ///
208 /// This differs from `MirBorrowckCtxt::name_regions` since it is particularly
209 /// lax with mapping region vids that are *shorter* than a universal region to
210 /// that universal region. This is useful for member region constraints since
211 /// we want to suggest a universal region name to capture even if it's technically
212 /// not equal to the error region.
213 pub(crate) fn name_regions_for_member_constraint<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
214 where
215 T: TypeFoldable<TyCtxt<'tcx>>,
216 {
217 fold_regions(tcx, ty, |region, _| match *region {
218 ty::ReVar(vid) => {
219 let scc = self.constraint_sccs.scc(vid);
220
221 // Special handling of higher-ranked regions.
222 if !self.scc_universe(scc).is_root() {
223 match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
224 // If the region contains a single placeholder then they're equal.
225 Some((0, placeholder)) => {
226 return ty::Region::new_placeholder(tcx, placeholder);
227 }
228
229 // Fallback: this will produce a cryptic error message.
230 _ => return region,
231 }
232 }
233
234 // Find something that we can name
235 let upper_bound = self.approx_universal_upper_bound(vid);
236 if let Some(universal_region) = self.definitions[upper_bound].external_name {
237 return universal_region;
238 }
239
240 // Nothing exact found, so we pick a named upper bound, if there's only one.
241 // If there's >1 universal region, then we probably are dealing w/ an intersection
242 // region which cannot be mapped back to a universal.
243 // FIXME: We could probably compute the LUB if there is one.
244 let scc = self.constraint_sccs.scc(vid);
245 let upper_bounds: Vec<_> = self
246 .rev_scc_graph
247 .as_ref()
248 .unwrap()
249 .upper_bounds(scc)
250 .filter_map(|vid| self.definitions[vid].external_name)
251 .filter(|r| !r.is_static())
252 .collect();
253 match &upper_bounds[..] {
254 [universal_region] => *universal_region,
255 _ => region,
256 }
257 }
258 _ => region,
259 })
260 }
261}
262
263#[extension(pub trait InferCtxtExt<'tcx>)]
264impl<'tcx> InferCtxt<'tcx> {
265 /// Given the fully resolved, instantiated type for an opaque
266 /// type, i.e., the value of an inference variable like C1 or C2
267 /// (*), computes the "definition type" for an opaque type
268 /// definition -- that is, the inferred value of `Foo1<'x>` or
269 /// `Foo2<'x>` that we would conceptually use in its definition:
270 /// ```ignore (illustrative)
271 /// type Foo1<'x> = impl Bar<'x> = AAA; // <-- this type AAA
272 /// type Foo2<'x> = impl Bar<'x> = BBB; // <-- or this type BBB
273 /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
274 /// ```
275 /// Note that these values are defined in terms of a distinct set of
276 /// generic parameters (`'x` instead of `'a`) from C1 or C2. The main
277 /// purpose of this function is to do that translation.
278 ///
279 /// (*) C1 and C2 were introduced in the comments on
280 /// `register_member_constraints`. Read that comment for more context.
281 ///
282 /// # Parameters
283 ///
284 /// - `def_id`, the `impl Trait` type
285 /// - `args`, the args used to instantiate this opaque type
286 /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
287 /// `opaque_defn.concrete_ty`
288 #[instrument(level = "debug", skip(self))]
289 fn infer_opaque_definition_from_instantiation(
290 &self,
291 opaque_type_key: OpaqueTypeKey<'tcx>,
292 instantiated_ty: OpaqueHiddenType<'tcx>,
293 ) -> Ty<'tcx> {
294 if let Some(e) = self.tainted_by_errors() {
295 return Ty::new_error(self.tcx, e);
296 }
297
298 if let Err(guar) =
299 check_opaque_type_parameter_valid(self, opaque_type_key, instantiated_ty.span)
300 {
301 return Ty::new_error(self.tcx, guar);
302 }
303
304 let definition_ty = instantiated_ty
305 .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false)
306 .ty;
307
308 if let Err(e) = definition_ty.error_reported() {
309 return Ty::new_error(self.tcx, e);
310 }
311
312 definition_ty
313 }
314}
315
316/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter].
317///
318/// [rustc-dev-guide chapter]:
319/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
320fn check_opaque_type_parameter_valid<'tcx>(
321 infcx: &InferCtxt<'tcx>,
322 opaque_type_key: OpaqueTypeKey<'tcx>,
323 span: Span,
324) -> Result<(), ErrorGuaranteed> {
325 let tcx = infcx.tcx;
326 let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
327 let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
328 let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
329
330 for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
331 let arg_is_param = match arg.unpack() {
332 GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
333 GenericArgKind::Lifetime(lt) => {
334 matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_))
335 || (lt.is_static() && opaque_env.param_equal_static(i))
336 }
337 GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)),
338 };
339
340 if arg_is_param {
341 // Register if the same lifetime appears multiple times in the generic args.
342 // There is an exception when the opaque type *requires* the lifetimes to be equal.
343 // See [rustc-dev-guide chapter] § "An exception to uniqueness rule".
344 let seen_where = seen_params.entry(arg).or_default();
345 if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) {
346 seen_where.push(i);
347 }
348 } else {
349 // Prevent `fn foo() -> Foo<u32>` from being defining.
350 let opaque_param = opaque_generics.param_at(i, tcx);
351 let kind = opaque_param.kind.descr();
352
353 opaque_env.param_is_error(i)?;
354
355 return Err(infcx.dcx().emit_err(NonGenericOpaqueTypeParam {
356 ty: arg,
357 kind,
358 span,
359 param_span: tcx.def_span(opaque_param.def_id),
360 }));
361 }
362 }
363
364 for (_, indices) in seen_params {
365 if indices.len() > 1 {
366 let descr = opaque_generics.param_at(indices[0], tcx).kind.descr();
367 let spans: Vec<_> = indices
368 .into_iter()
369 .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
370 .collect();
371 #[allow(rustc::diagnostic_outside_of_impl)]
372 #[allow(rustc::untranslatable_diagnostic)]
373 return Err(infcx
374 .dcx()
375 .struct_span_err(span, "non-defining opaque type use in defining scope")
376 .with_span_note(spans, format!("{descr} used multiple times"))
377 .emit());
378 }
379 }
380
381 Ok(())
382}
383
384/// Computes if an opaque type requires a lifetime parameter to be equal to
385/// another one or to the `'static` lifetime.
386/// These requirements are derived from the explicit and implied bounds.
387struct LazyOpaqueTyEnv<'tcx> {
388 tcx: TyCtxt<'tcx>,
389 def_id: LocalDefId,
390
391 /// Equal parameters will have the same name. Computed Lazily.
392 /// Example:
393 /// `type Opaque<'a: 'static, 'b: 'c, 'c: 'b> = impl Sized;`
394 /// Identity args: `['a, 'b, 'c]`
395 /// Canonical args: `['static, 'b, 'b]`
396 canonical_args: std::cell::OnceCell<ty::GenericArgsRef<'tcx>>,
397}
398
399impl<'tcx> LazyOpaqueTyEnv<'tcx> {
400 fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
401 Self { tcx, def_id, canonical_args: std::cell::OnceCell::new() }
402 }
403
404 fn param_equal_static(&self, param_index: usize) -> bool {
405 self.get_canonical_args()[param_index].expect_region().is_static()
406 }
407
408 fn params_equal(&self, param1: usize, param2: usize) -> bool {
409 let canonical_args = self.get_canonical_args();
410 canonical_args[param1] == canonical_args[param2]
411 }
412
413 fn param_is_error(&self, param_index: usize) -> Result<(), ErrorGuaranteed> {
414 self.get_canonical_args()[param_index].error_reported()
415 }
416
417 fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> {
418 if let Some(&canonical_args) = self.canonical_args.get() {
419 return canonical_args;
420 }
421
422 let &Self { tcx, def_id, .. } = self;
423 let origin = tcx.local_opaque_ty_origin(def_id);
424 let parent = match origin {
425 OpaqueTyOrigin::FnReturn { parent, .. }
426 | OpaqueTyOrigin::AsyncFn { parent, .. }
427 | OpaqueTyOrigin::TyAlias { parent, .. } => parent,
428 };
429 let param_env = tcx.param_env(parent);
430 let args = GenericArgs::identity_for_item(tcx, parent).extend_to(
431 tcx,
432 def_id.to_def_id(),
433 |param, _| {
434 tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into()
435 },
436 );
437
438 // FIXME(#132279): It feels wrong to use `non_body_analysis` here given that we're
439 // in a body here.
440 let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
441 let ocx = ObligationCtxt::new(&infcx);
442
443 let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| {
444 tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds");
445 Default::default()
446 });
447 let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys);
448
449 let mut seen = vec![tcx.lifetimes.re_static];
450 let canonical_args = fold_regions(tcx, args, |r1, _| {
451 if r1.is_error() {
452 r1
453 } else if let Some(&r2) = seen.iter().find(|&&r2| {
454 let free_regions = outlives_env.free_region_map();
455 free_regions.sub_free_regions(tcx, r1, r2)
456 && free_regions.sub_free_regions(tcx, r2, r1)
457 }) {
458 r2
459 } else {
460 seen.push(r1);
461 r1
462 }
463 });
464 self.canonical_args.set(canonical_args).unwrap();
465 canonical_args
466 }
467}