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