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