rustc_borrowck/type_check/
opaque_types.rs

1use std::iter;
2
3use rustc_data_structures::fx::FxIndexMap;
4use rustc_middle::span_bug;
5use rustc_middle::ty::{
6    self, GenericArgKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeSuperVisitable,
7    TypeVisitable, TypeVisitableExt, TypeVisitor, fold_regions,
8};
9use tracing::{debug, trace};
10
11use super::{MemberConstraintSet, TypeChecker};
12
13/// Once we're done with typechecking the body, we take all the opaque types
14/// defined by this function and add their 'member constraints'.
15pub(super) fn take_opaques_and_register_member_constraints<'tcx>(
16    typeck: &mut TypeChecker<'_, 'tcx>,
17) -> FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>> {
18    let infcx = typeck.infcx;
19    // Annoying: to invoke `typeck.to_region_vid`, we need access to
20    // `typeck.constraints`, but we also want to be mutating
21    // `typeck.member_constraints`. For now, just swap out the value
22    // we want and replace at the end.
23    let mut member_constraints = std::mem::take(&mut typeck.constraints.member_constraints);
24    let opaque_types = infcx
25        .take_opaque_types()
26        .into_iter()
27        .map(|(opaque_type_key, hidden_type)| {
28            let hidden_type = infcx.resolve_vars_if_possible(hidden_type);
29            register_member_constraints(
30                typeck,
31                &mut member_constraints,
32                opaque_type_key,
33                hidden_type,
34            );
35            trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind());
36            if hidden_type.has_non_region_infer() {
37                span_bug!(hidden_type.span, "could not resolve {:?}", hidden_type.ty);
38            }
39
40            // Convert all regions to nll vars.
41            let (opaque_type_key, hidden_type) =
42                fold_regions(infcx.tcx, (opaque_type_key, hidden_type), |r, _| {
43                    ty::Region::new_var(infcx.tcx, typeck.to_region_vid(r))
44                });
45
46            (opaque_type_key, hidden_type)
47        })
48        .collect();
49    assert!(typeck.constraints.member_constraints.is_empty());
50    typeck.constraints.member_constraints = member_constraints;
51    opaque_types
52}
53
54/// Given the map `opaque_types` containing the opaque
55/// `impl Trait` types whose underlying, hidden types are being
56/// inferred, this method adds constraints to the regions
57/// appearing in those underlying hidden types to ensure that they
58/// at least do not refer to random scopes within the current
59/// function. These constraints are not (quite) sufficient to
60/// guarantee that the regions are actually legal values; that
61/// final condition is imposed after region inference is done.
62///
63/// # The Problem
64///
65/// Let's work through an example to explain how it works. Assume
66/// the current function is as follows:
67///
68/// ```text
69/// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>)
70/// ```
71///
72/// Here, we have two `impl Trait` types whose values are being
73/// inferred (the `impl Bar<'a>` and the `impl
74/// Bar<'b>`). Conceptually, this is sugar for a setup where we
75/// define underlying opaque types (`Foo1`, `Foo2`) and then, in
76/// the return type of `foo`, we *reference* those definitions:
77///
78/// ```text
79/// type Foo1<'x> = impl Bar<'x>;
80/// type Foo2<'x> = impl Bar<'x>;
81/// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
82///                    //  ^^^^ ^^
83///                    //  |    |
84///                    //  |    args
85///                    //  def_id
86/// ```
87///
88/// As indicating in the comments above, each of those references
89/// is (in the compiler) basically generic parameters (`args`)
90/// applied to the type of a suitable `def_id` (which identifies
91/// `Foo1` or `Foo2`).
92///
93/// Now, at this point in compilation, what we have done is to
94/// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with
95/// fresh inference variables C1 and C2. We wish to use the values
96/// of these variables to infer the underlying types of `Foo1` and
97/// `Foo2`. That is, this gives rise to higher-order (pattern) unification
98/// constraints like:
99///
100/// ```text
101/// for<'a> (Foo1<'a> = C1)
102/// for<'b> (Foo1<'b> = C2)
103/// ```
104///
105/// For these equation to be satisfiable, the types `C1` and `C2`
106/// can only refer to a limited set of regions. For example, `C1`
107/// can only refer to `'static` and `'a`, and `C2` can only refer
108/// to `'static` and `'b`. The job of this function is to impose that
109/// constraint.
110///
111/// Up to this point, C1 and C2 are basically just random type
112/// inference variables, and hence they may contain arbitrary
113/// regions. In fact, it is fairly likely that they do! Consider
114/// this possible definition of `foo`:
115///
116/// ```text
117/// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) {
118///         (&*x, &*y)
119///     }
120/// ```
121///
122/// Here, the values for the concrete types of the two impl
123/// traits will include inference variables:
124///
125/// ```text
126/// &'0 i32
127/// &'1 i32
128/// ```
129///
130/// Ordinarily, the subtyping rules would ensure that these are
131/// sufficiently large. But since `impl Bar<'a>` isn't a specific
132/// type per se, we don't get such constraints by default. This
133/// is where this function comes into play. It adds extra
134/// constraints to ensure that all the regions which appear in the
135/// inferred type are regions that could validly appear.
136///
137/// This is actually a bit of a tricky constraint in general. We
138/// want to say that each variable (e.g., `'0`) can only take on
139/// values that were supplied as arguments to the opaque type
140/// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in
141/// scope. We don't have a constraint quite of this kind in the current
142/// region checker.
143///
144/// # The Solution
145///
146/// We generally prefer to make `<=` constraints, since they
147/// integrate best into the region solver. To do that, we find the
148/// "minimum" of all the arguments that appear in the args: that
149/// is, some region which is less than all the others. In the case
150/// of `Foo1<'a>`, that would be `'a` (it's the only choice, after
151/// all). Then we apply that as a least bound to the variables
152/// (e.g., `'a <= '0`).
153///
154/// In some cases, there is no minimum. Consider this example:
155///
156/// ```text
157/// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... }
158/// ```
159///
160/// Here we would report a more complex "in constraint", like `'r
161/// in ['a, 'b, 'static]` (where `'r` is some region appearing in
162/// the hidden type).
163///
164/// # Constrain regions, not the hidden concrete type
165///
166/// Note that generating constraints on each region `Rc` is *not*
167/// the same as generating an outlives constraint on `Tc` itself.
168/// For example, if we had a function like this:
169///
170/// ```
171/// # #![feature(type_alias_impl_trait)]
172/// # fn main() {}
173/// # trait Foo<'a> {}
174/// # impl<'a, T> Foo<'a> for (&'a u32, T) {}
175/// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> {
176///   (x, y)
177/// }
178///
179/// // Equivalent to:
180/// # mod dummy { use super::*;
181/// type FooReturn<'a, T> = impl Foo<'a>;
182/// #[define_opaque(FooReturn)]
183/// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> {
184///   (x, y)
185/// }
186/// # }
187/// ```
188///
189/// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0`
190/// is an inference variable). If we generated a constraint that
191/// `Tc: 'a`, then this would incorrectly require that `T: 'a` --
192/// but this is not necessary, because the opaque type we
193/// create will be allowed to reference `T`. So we only generate a
194/// constraint that `'0: 'a`.
195fn register_member_constraints<'tcx>(
196    typeck: &mut TypeChecker<'_, 'tcx>,
197    member_constraints: &mut MemberConstraintSet<'tcx, ty::RegionVid>,
198    opaque_type_key: OpaqueTypeKey<'tcx>,
199    OpaqueHiddenType { span, ty: hidden_ty }: OpaqueHiddenType<'tcx>,
200) {
201    let tcx = typeck.tcx();
202    let hidden_ty = typeck.infcx.resolve_vars_if_possible(hidden_ty);
203    debug!(?hidden_ty);
204
205    let variances = tcx.variances_of(opaque_type_key.def_id);
206    debug!(?variances);
207
208    // For a case like `impl Foo<'a, 'b>`, we would generate a constraint
209    // `'r in ['a, 'b, 'static]` for each region `'r` that appears in the
210    // hidden type (i.e., it must be equal to `'a`, `'b`, or `'static`).
211    //
212    // `conflict1` and `conflict2` are the two region bounds that we
213    // detected which were unrelated. They are used for diagnostics.
214
215    // Create the set of choice regions: each region in the hidden
216    // type can be equal to any of the region parameters of the
217    // opaque type definition.
218    let fr_static = typeck.universal_regions.fr_static;
219    let choice_regions: Vec<_> = opaque_type_key
220        .args
221        .iter()
222        .enumerate()
223        .filter(|(i, _)| variances[*i] == ty::Invariant)
224        .filter_map(|(_, arg)| match arg.unpack() {
225            GenericArgKind::Lifetime(r) => Some(typeck.to_region_vid(r)),
226            GenericArgKind::Type(_) | GenericArgKind::Const(_) => None,
227        })
228        .chain(iter::once(fr_static))
229        .collect();
230
231    // FIXME(#42940): This should use the `FreeRegionsVisitor`, but that's
232    // not currently sound until we have existential regions.
233    hidden_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
234        tcx,
235        op: |r| {
236            member_constraints.add_member_constraint(
237                opaque_type_key,
238                hidden_ty,
239                span,
240                typeck.to_region_vid(r),
241                &choice_regions,
242            )
243        },
244    });
245}
246
247/// Visitor that requires that (almost) all regions in the type visited outlive
248/// `least_region`. We cannot use `push_outlives_components` because regions in
249/// closure signatures are not included in their outlives components. We need to
250/// ensure all regions outlive the given bound so that we don't end up with,
251/// say, `ReVar` appearing in a return type and causing ICEs when other
252/// functions end up with region constraints involving regions from other
253/// functions.
254///
255/// We also cannot use `for_each_free_region` because for closures it includes
256/// the regions parameters from the enclosing item.
257///
258/// We ignore any type parameters because impl trait values are assumed to
259/// capture all the in-scope type parameters.
260struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> {
261    tcx: TyCtxt<'tcx>,
262    op: OP,
263}
264
265impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP>
266where
267    OP: FnMut(ty::Region<'tcx>),
268{
269    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
270        t.super_visit_with(self);
271    }
272
273    fn visit_region(&mut self, r: ty::Region<'tcx>) {
274        match *r {
275            // ignore bound regions, keep visiting
276            ty::ReBound(_, _) => {}
277            _ => (self.op)(r),
278        }
279    }
280
281    fn visit_ty(&mut self, ty: Ty<'tcx>) {
282        // We're only interested in types involving regions
283        if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
284            return;
285        }
286
287        match *ty.kind() {
288            ty::Closure(_, args) => {
289                // Skip lifetime parameters of the enclosing item(s)
290
291                for upvar in args.as_closure().upvar_tys() {
292                    upvar.visit_with(self);
293                }
294                args.as_closure().sig_as_fn_ptr_ty().visit_with(self);
295            }
296
297            ty::CoroutineClosure(_, args) => {
298                // Skip lifetime parameters of the enclosing item(s)
299
300                for upvar in args.as_coroutine_closure().upvar_tys() {
301                    upvar.visit_with(self);
302                }
303
304                args.as_coroutine_closure().signature_parts_ty().visit_with(self);
305            }
306
307            ty::Coroutine(_, args) => {
308                // Skip lifetime parameters of the enclosing item(s)
309                // Also skip the witness type, because that has no free regions.
310
311                for upvar in args.as_coroutine().upvar_tys() {
312                    upvar.visit_with(self);
313                }
314                args.as_coroutine().return_ty().visit_with(self);
315                args.as_coroutine().yield_ty().visit_with(self);
316                args.as_coroutine().resume_ty().visit_with(self);
317            }
318
319            ty::Alias(kind, ty::AliasTy { def_id, args, .. })
320                if let Some(variances) = self.tcx.opt_alias_variances(kind, def_id) =>
321            {
322                // Skip lifetime parameters that are not captured, since they do
323                // not need member constraints registered for them; we'll erase
324                // them (and hopefully in the future replace them with placeholders).
325                for (v, s) in std::iter::zip(variances, args.iter()) {
326                    if *v != ty::Bivariant {
327                        s.visit_with(self);
328                    }
329                }
330            }
331
332            _ => {
333                ty.super_visit_with(self);
334            }
335        }
336    }
337}