rustc_borrowck/region_infer/
opaque_types.rs

1use rustc_data_structures::fx::FxIndexMap;
2use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
3use rustc_macros::extension;
4use rustc_middle::ty::{
5    self, DefiningScopeKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
6    TypeVisitableExt, fold_regions,
7};
8use rustc_span::Span;
9use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid;
10use tracing::{debug, instrument};
11
12use super::RegionInferenceContext;
13use crate::BorrowCheckRootCtxt;
14use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
15use crate::universal_regions::RegionClassification;
16
17impl<'tcx> RegionInferenceContext<'tcx> {
18    /// Resolve any opaque types that were encountered while borrow checking
19    /// this item. This is then used to get the type in the `type_of` query.
20    ///
21    /// For example consider `fn f<'a>(x: &'a i32) -> impl Sized + 'a { x }`.
22    /// This is lowered to give HIR something like
23    ///
24    /// type f<'a>::_Return<'_x> = impl Sized + '_x;
25    /// fn f<'a>(x: &'a i32) -> f<'a>::_Return<'a> { x }
26    ///
27    /// When checking the return type record the type from the return and the
28    /// type used in the return value. In this case they might be `_Return<'1>`
29    /// and `&'2 i32` respectively.
30    ///
31    /// Once we to this method, we have completed region inference and want to
32    /// call `infer_opaque_definition_from_instantiation` to get the inferred
33    /// type of `_Return<'_x>`. `infer_opaque_definition_from_instantiation`
34    /// compares lifetimes directly, so we need to map the inference variables
35    /// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`.
36    ///
37    /// First we map the regions in the generic parameters `_Return<'1>` to
38    /// their `external_name` giving `_Return<'a>`. This step is a bit involved.
39    /// See the [rustc-dev-guide chapter] for more info.
40    ///
41    /// Then we map all the lifetimes in the concrete type to an equal
42    /// universal region that occurs in the opaque type's args, in this case
43    /// this would result in `&'a i32`. We only consider regions in the args
44    /// in case there is an equal region that does not. For example, this should
45    /// be allowed:
46    /// `fn f<'a: 'b, 'b: 'a>(x: *mut &'b i32) -> impl Sized + 'a { x }`
47    ///
48    /// This will then allow `infer_opaque_definition_from_instantiation` to
49    /// determine that `_Return<'_x> = &'_x i32`.
50    ///
51    /// There's a slight complication around closures. Given
52    /// `fn f<'a: 'a>() { || {} }` the closure's type is something like
53    /// `f::<'a>::{{closure}}`. The region parameter from f is essentially
54    /// ignored by type checking so ends up being inferred to an empty region.
55    /// Calling `universal_upper_bound` for such a region gives `fr_fn_body`,
56    /// which has no `external_name` in which case we use `'{erased}` as the
57    /// region to pass to `infer_opaque_definition_from_instantiation`.
58    ///
59    /// [rustc-dev-guide chapter]:
60    /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
61    #[instrument(level = "debug", skip(self, root_cx, infcx), ret)]
62    pub(crate) fn infer_opaque_types(
63        &self,
64        root_cx: &mut BorrowCheckRootCtxt<'tcx>,
65        infcx: &InferCtxt<'tcx>,
66        opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
67    ) {
68        let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
69            FxIndexMap::default();
70
71        for (opaque_type_key, concrete_type) in opaque_ty_decls {
72            debug!(?opaque_type_key, ?concrete_type);
73
74            let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> =
75                vec![(self.universal_regions().fr_static, infcx.tcx.lifetimes.re_static)];
76
77            let opaque_type_key =
78                opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| {
79                    // Use the SCC representative instead of directly using `region`.
80                    // See [rustc-dev-guide chapter] § "Strict lifetime equality".
81                    let scc = self.constraint_sccs.scc(region.as_var());
82                    let vid = self.scc_representative(scc);
83                    let named = match self.definitions[vid].origin {
84                        // Iterate over all universal regions in a consistent order and find the
85                        // *first* equal region. This makes sure that equal lifetimes will have
86                        // the same name and simplifies subsequent handling.
87                        // See [rustc-dev-guide chapter] § "Semantic lifetime equality".
88                        NllRegionVariableOrigin::FreeRegion => self
89                            .universal_regions()
90                            .universal_regions_iter()
91                            .filter(|&ur| {
92                                // See [rustc-dev-guide chapter] § "Closure restrictions".
93                                !matches!(
94                                    self.universal_regions().region_classification(ur),
95                                    Some(RegionClassification::External)
96                                )
97                            })
98                            .find(|&ur| self.universal_region_relations.equal(vid, ur))
99                            .map(|ur| self.definitions[ur].external_name.unwrap()),
100                        NllRegionVariableOrigin::Placeholder(placeholder) => {
101                            Some(ty::Region::new_placeholder(infcx.tcx, placeholder))
102                        }
103                        NllRegionVariableOrigin::Existential { .. } => None,
104                    }
105                    .unwrap_or_else(|| {
106                        ty::Region::new_error_with_message(
107                            infcx.tcx,
108                            concrete_type.span,
109                            "opaque type with non-universal region args",
110                        )
111                    });
112
113                    arg_regions.push((vid, named));
114                    named
115                });
116            debug!(?opaque_type_key, ?arg_regions);
117
118            let concrete_type = fold_regions(infcx.tcx, concrete_type, |region, _| {
119                arg_regions
120                    .iter()
121                    .find(|&&(arg_vid, _)| self.eval_equal(region.as_var(), arg_vid))
122                    .map(|&(_, arg_named)| arg_named)
123                    .unwrap_or(infcx.tcx.lifetimes.re_erased)
124            });
125            debug!(?concrete_type);
126
127            let ty =
128                infcx.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type);
129
130            // Sometimes, when the hidden type is an inference variable, it can happen that
131            // the hidden type becomes the opaque type itself. In this case, this was an opaque
132            // usage of the opaque type and we can ignore it. This check is mirrored in typeck's
133            // writeback.
134            if !infcx.next_trait_solver() {
135                if let ty::Alias(ty::Opaque, alias_ty) = ty.kind()
136                    && alias_ty.def_id == opaque_type_key.def_id.to_def_id()
137                    && alias_ty.args == opaque_type_key.args
138                {
139                    continue;
140                }
141            }
142
143            root_cx.add_concrete_opaque_type(
144                opaque_type_key.def_id,
145                OpaqueHiddenType { span: concrete_type.span, ty },
146            );
147
148            // Check that all opaque types have the same region parameters if they have the same
149            // non-region parameters. This is necessary because within the new solver we perform
150            // various query operations modulo regions, and thus could unsoundly select some impls
151            // that don't hold.
152            if !ty.references_error()
153                && let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert(
154                    infcx.tcx.erase_regions(opaque_type_key),
155                    (opaque_type_key, concrete_type.span),
156                )
157                && let Some((arg1, arg2)) = std::iter::zip(
158                    prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
159                    opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg),
160                )
161                .find(|(arg1, arg2)| arg1 != arg2)
162            {
163                infcx.dcx().emit_err(LifetimeMismatchOpaqueParam {
164                    arg: arg1,
165                    prev: arg2,
166                    span: prev_span,
167                    prev_span: concrete_type.span,
168                });
169            }
170        }
171    }
172
173    /// Map the regions in the type to named regions. This is similar to what
174    /// `infer_opaque_types` does, but can infer any universal region, not only
175    /// ones from the args for the opaque type. It also doesn't double check
176    /// that the regions produced are in fact equal to the named region they are
177    /// replaced with. This is fine because this function is only to improve the
178    /// region names in error messages.
179    ///
180    /// This differs from `MirBorrowckCtxt::name_regions` since it is particularly
181    /// lax with mapping region vids that are *shorter* than a universal region to
182    /// that universal region. This is useful for member region constraints since
183    /// we want to suggest a universal region name to capture even if it's technically
184    /// not equal to the error region.
185    pub(crate) fn name_regions_for_member_constraint<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
186    where
187        T: TypeFoldable<TyCtxt<'tcx>>,
188    {
189        fold_regions(tcx, ty, |region, _| match region.kind() {
190            ty::ReVar(vid) => {
191                let scc = self.constraint_sccs.scc(vid);
192
193                // Special handling of higher-ranked regions.
194                if !self.max_nameable_universe(scc).is_root() {
195                    match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
196                        // If the region contains a single placeholder then they're equal.
197                        Some((0, placeholder)) => {
198                            return ty::Region::new_placeholder(tcx, placeholder);
199                        }
200
201                        // Fallback: this will produce a cryptic error message.
202                        _ => return region,
203                    }
204                }
205
206                // Find something that we can name
207                let upper_bound = self.approx_universal_upper_bound(vid);
208                if let Some(universal_region) = self.definitions[upper_bound].external_name {
209                    return universal_region;
210                }
211
212                // Nothing exact found, so we pick a named upper bound, if there's only one.
213                // If there's >1 universal region, then we probably are dealing w/ an intersection
214                // region which cannot be mapped back to a universal.
215                // FIXME: We could probably compute the LUB if there is one.
216                let scc = self.constraint_sccs.scc(vid);
217                let upper_bounds: Vec<_> = self
218                    .reverse_scc_graph()
219                    .upper_bounds(scc)
220                    .filter_map(|vid| self.definitions[vid].external_name)
221                    .filter(|r| !r.is_static())
222                    .collect();
223                match &upper_bounds[..] {
224                    [universal_region] => *universal_region,
225                    _ => region,
226                }
227            }
228            _ => region,
229        })
230    }
231}
232
233#[extension(pub trait InferCtxtExt<'tcx>)]
234impl<'tcx> InferCtxt<'tcx> {
235    /// Given the fully resolved, instantiated type for an opaque
236    /// type, i.e., the value of an inference variable like C1 or C2
237    /// (*), computes the "definition type" for an opaque type
238    /// definition -- that is, the inferred value of `Foo1<'x>` or
239    /// `Foo2<'x>` that we would conceptually use in its definition:
240    /// ```ignore (illustrative)
241    /// type Foo1<'x> = impl Bar<'x> = AAA;  // <-- this type AAA
242    /// type Foo2<'x> = impl Bar<'x> = BBB;  // <-- or this type BBB
243    /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
244    /// ```
245    /// Note that these values are defined in terms of a distinct set of
246    /// generic parameters (`'x` instead of `'a`) from C1 or C2. The main
247    /// purpose of this function is to do that translation.
248    ///
249    /// (*) C1 and C2 were introduced in the comments on
250    /// `register_member_constraints`. Read that comment for more context.
251    ///
252    /// # Parameters
253    ///
254    /// - `def_id`, the `impl Trait` type
255    /// - `args`, the args used to instantiate this opaque type
256    /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
257    ///   `opaque_defn.concrete_ty`
258    #[instrument(level = "debug", skip(self))]
259    fn infer_opaque_definition_from_instantiation(
260        &self,
261        opaque_type_key: OpaqueTypeKey<'tcx>,
262        instantiated_ty: OpaqueHiddenType<'tcx>,
263    ) -> Ty<'tcx> {
264        if let Some(e) = self.tainted_by_errors() {
265            return Ty::new_error(self.tcx, e);
266        }
267
268        if let Err(err) = check_opaque_type_parameter_valid(
269            self,
270            opaque_type_key,
271            instantiated_ty.span,
272            DefiningScopeKind::MirBorrowck,
273        ) {
274            return Ty::new_error(self.tcx, err.report(self));
275        }
276
277        let definition_ty = instantiated_ty
278            .remap_generic_params_to_declaration_params(
279                opaque_type_key,
280                self.tcx,
281                DefiningScopeKind::MirBorrowck,
282            )
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}