]> git.lizzy.rs Git - rust.git/blob - src/librustc/infer/canonical/query_response.rs
Auto merge of #55236 - petrochenkov:pfail, r=davidtwco
[rust.git] / src / librustc / infer / canonical / query_response.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! This module contains the code to instantiate a "query result", and
12 //! in particular to extract out the resulting region obligations and
13 //! encode them therein.
14 //!
15 //! For an overview of what canonicaliation is and how it fits into
16 //! rustc, check out the [chapter in the rustc guide][c].
17 //!
18 //! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html
19
20 use infer::canonical::substitute::substitute_value;
21 use infer::canonical::{
22     Canonical, CanonicalVarValues, CanonicalizedQueryResponse, Certainty,
23     OriginalQueryValues, QueryRegionConstraint, QueryResponse,
24 };
25 use infer::region_constraints::{Constraint, RegionConstraintData};
26 use infer::InferCtxtBuilder;
27 use infer::{InferCtxt, InferOk, InferResult};
28 use rustc_data_structures::indexed_vec::Idx;
29 use rustc_data_structures::indexed_vec::IndexVec;
30 use rustc_data_structures::sync::Lrc;
31 use std::fmt::Debug;
32 use syntax_pos::DUMMY_SP;
33 use traits::query::{Fallible, NoSolution};
34 use traits::{FulfillmentContext, TraitEngine};
35 use traits::{Obligation, ObligationCause, PredicateObligation};
36 use ty::fold::TypeFoldable;
37 use ty::subst::{Kind, UnpackedKind};
38 use ty::{self, BoundTyIndex, Lift, Ty, TyCtxt};
39
40 impl<'cx, 'gcx, 'tcx> InferCtxtBuilder<'cx, 'gcx, 'tcx> {
41     /// The "main method" for a canonicalized trait query. Given the
42     /// canonical key `canonical_key`, this method will create a new
43     /// inference context, instantiate the key, and run your operation
44     /// `op`. The operation should yield up a result (of type `R`) as
45     /// well as a set of trait obligations that must be fully
46     /// satisfied. These obligations will be processed and the
47     /// canonical result created.
48     ///
49     /// Returns `NoSolution` in the event of any error.
50     ///
51     /// (It might be mildly nicer to implement this on `TyCtxt`, and
52     /// not `InferCtxtBuilder`, but that is a bit tricky right now.
53     /// In part because we would need a `for<'gcx: 'tcx>` sort of
54     /// bound for the closure and in part because it is convenient to
55     /// have `'tcx` be free on this function so that we can talk about
56     /// `K: TypeFoldable<'tcx>`.)
57     pub fn enter_canonical_trait_query<K, R>(
58         &'tcx mut self,
59         canonical_key: &Canonical<'tcx, K>,
60         operation: impl FnOnce(&InferCtxt<'_, 'gcx, 'tcx>, &mut FulfillmentContext<'tcx>, K)
61             -> Fallible<R>,
62     ) -> Fallible<CanonicalizedQueryResponse<'gcx, R>>
63     where
64         K: TypeFoldable<'tcx>,
65         R: Debug + Lift<'gcx> + TypeFoldable<'tcx>,
66     {
67         self.enter_with_canonical(
68             DUMMY_SP,
69             canonical_key,
70             |ref infcx, key, canonical_inference_vars| {
71                 let fulfill_cx = &mut FulfillmentContext::new();
72                 let value = operation(infcx, fulfill_cx, key)?;
73                 infcx.make_canonicalized_query_response(canonical_inference_vars, value, fulfill_cx)
74             },
75         )
76     }
77 }
78
79 impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
80     /// This method is meant to be invoked as the final step of a canonical query
81     /// implementation. It is given:
82     ///
83     /// - the instantiated variables `inference_vars` created from the query key
84     /// - the result `answer` of the query
85     /// - a fulfillment context `fulfill_cx` that may contain various obligations which
86     ///   have yet to be proven.
87     ///
88     /// Given this, the function will process the obligations pending
89     /// in `fulfill_cx`:
90     ///
91     /// - If all the obligations can be proven successfully, it will
92     ///   package up any resulting region obligations (extracted from
93     ///   `infcx`) along with the fully resolved value `answer` into a
94     ///   query result (which is then itself canonicalized).
95     /// - If some obligations can be neither proven nor disproven, then
96     ///   the same thing happens, but the resulting query is marked as ambiguous.
97     /// - Finally, if any of the obligations result in a hard error,
98     ///   then `Err(NoSolution)` is returned.
99     pub fn make_canonicalized_query_response<T>(
100         &self,
101         inference_vars: CanonicalVarValues<'tcx>,
102         answer: T,
103         fulfill_cx: &mut FulfillmentContext<'tcx>,
104     ) -> Fallible<CanonicalizedQueryResponse<'gcx, T>>
105     where
106         T: Debug + Lift<'gcx> + TypeFoldable<'tcx>,
107     {
108         let query_response = self.make_query_response(inference_vars, answer, fulfill_cx)?;
109         let canonical_result = self.canonicalize_response(&query_response);
110
111         debug!(
112             "make_canonicalized_query_response: canonical_result = {:#?}",
113             canonical_result
114         );
115
116         Ok(Lrc::new(canonical_result))
117     }
118
119     /// Helper for `make_canonicalized_query_response` that does
120     /// everything up until the final canonicalization.
121     fn make_query_response<T>(
122         &self,
123         inference_vars: CanonicalVarValues<'tcx>,
124         answer: T,
125         fulfill_cx: &mut FulfillmentContext<'tcx>,
126     ) -> Result<QueryResponse<'tcx, T>, NoSolution>
127     where
128         T: Debug + TypeFoldable<'tcx> + Lift<'gcx>,
129     {
130         let tcx = self.tcx;
131
132         debug!(
133             "make_query_response(\
134              inference_vars={:?}, \
135              answer={:?})",
136             inference_vars, answer,
137         );
138
139         // Select everything, returning errors.
140         let true_errors = fulfill_cx.select_where_possible(self).err().unwrap_or_else(Vec::new);
141         debug!("true_errors = {:#?}", true_errors);
142
143         if !true_errors.is_empty() {
144             // FIXME -- we don't indicate *why* we failed to solve
145             debug!("make_query_response: true_errors={:#?}", true_errors);
146             return Err(NoSolution);
147         }
148
149         // Anything left unselected *now* must be an ambiguity.
150         let ambig_errors = fulfill_cx.select_all_or_error(self).err().unwrap_or_else(Vec::new);
151         debug!("ambig_errors = {:#?}", ambig_errors);
152
153         let region_obligations = self.take_registered_region_obligations();
154         let region_constraints = self.with_region_constraints(|region_constraints| {
155             make_query_outlives(
156                 tcx,
157                 region_obligations
158                     .iter()
159                     .map(|(_, r_o)| (r_o.sup_type, r_o.sub_region)),
160                 region_constraints,
161             )
162         });
163
164         let certainty = if ambig_errors.is_empty() {
165             Certainty::Proven
166         } else {
167             Certainty::Ambiguous
168         };
169
170         Ok(QueryResponse {
171             var_values: inference_vars,
172             region_constraints,
173             certainty,
174             value: answer,
175         })
176     }
177
178     /// Given the (canonicalized) result to a canonical query,
179     /// instantiates the result so it can be used, plugging in the
180     /// values from the canonical query. (Note that the result may
181     /// have been ambiguous; you should check the certainty level of
182     /// the query before applying this function.)
183     ///
184     /// To get a good understanding of what is happening here, check
185     /// out the [chapter in the rustc guide][c].
186     ///
187     /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#processing-the-canonicalized-query-result
188     pub fn instantiate_query_response_and_region_obligations<R>(
189         &self,
190         cause: &ObligationCause<'tcx>,
191         param_env: ty::ParamEnv<'tcx>,
192         original_values: &OriginalQueryValues<'tcx>,
193         query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>,
194     ) -> InferResult<'tcx, R>
195     where
196         R: Debug + TypeFoldable<'tcx>,
197     {
198         let InferOk {
199             value: result_subst,
200             mut obligations,
201         } = self.query_response_substitution(cause, param_env, original_values, query_response)?;
202
203         obligations.extend(self.query_region_constraints_into_obligations(
204             cause,
205             param_env,
206             &query_response.value.region_constraints,
207             &result_subst,
208         ));
209
210         let user_result: R =
211             query_response.substitute_projected(self.tcx, &result_subst, |q_r| &q_r.value);
212
213         Ok(InferOk {
214             value: user_result,
215             obligations,
216         })
217     }
218
219     /// An alternative to
220     /// `instantiate_query_response_and_region_obligations` that is more
221     /// efficient for NLL. NLL is a bit more advanced in the
222     /// "transition to chalk" than the rest of the compiler. During
223     /// the NLL type check, all of the "processing" of types and
224     /// things happens in queries -- the NLL checker itself is only
225     /// interested in the region obligations (`'a: 'b` or `T: 'b`)
226     /// that come out of these queries, which it wants to convert into
227     /// MIR-based constraints and solve. Therefore, it is most
228     /// convenient for the NLL Type Checker to **directly consume**
229     /// the `QueryRegionConstraint` values that arise from doing a
230     /// query. This is contrast to other parts of the compiler, which
231     /// would prefer for those `QueryRegionConstraint` to be converted
232     /// into the older infcx-style constraints (e.g., calls to
233     /// `sub_regions` or `register_region_obligation`).
234     ///
235     /// Therefore, `instantiate_nll_query_response_and_region_obligations` performs the same
236     /// basic operations as `instantiate_query_response_and_region_obligations` but
237     /// it returns its result differently:
238     ///
239     /// - It creates a substitution `S` that maps from the original
240     ///   query variables to the values computed in the query
241     ///   result. If any errors arise, they are propagated back as an
242     ///   `Err` result.
243     /// - In the case of a successful substitution, we will append
244     ///   `QueryRegionConstraint` values onto the
245     ///   `output_query_region_constraints` vector for the solver to
246     ///   use (if an error arises, some values may also be pushed, but
247     ///   they should be ignored).
248     /// - It **can happen** (though it rarely does currently) that
249     ///   equating types and things will give rise to subobligations
250     ///   that must be processed.  In this case, those subobligations
251     ///   are propagated back in the return value.
252     /// - Finally, the query result (of type `R`) is propagated back,
253     ///   after applying the substitution `S`.
254     pub fn instantiate_nll_query_response_and_region_obligations<R>(
255         &self,
256         cause: &ObligationCause<'tcx>,
257         param_env: ty::ParamEnv<'tcx>,
258         original_values: &OriginalQueryValues<'tcx>,
259         query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>,
260         output_query_region_constraints: &mut Vec<QueryRegionConstraint<'tcx>>,
261     ) -> InferResult<'tcx, R>
262     where
263         R: Debug + TypeFoldable<'tcx>,
264     {
265         let result_subst =
266             self.query_response_substitution_guess(cause, original_values, query_response);
267
268         // Compute `QueryRegionConstraint` values that unify each of
269         // the original values `v_o` that was canonicalized into a
270         // variable...
271         let mut obligations = vec![];
272
273         for (index, original_value) in original_values.var_values.iter().enumerate() {
274             // ...with the value `v_r` of that variable from the query.
275             let result_value = query_response.substitute_projected(self.tcx, &result_subst, |v| {
276                 &v.var_values[BoundTyIndex::new(index)]
277             });
278             match (original_value.unpack(), result_value.unpack()) {
279                 (UnpackedKind::Lifetime(ty::ReErased), UnpackedKind::Lifetime(ty::ReErased)) => {
280                     // no action needed
281                 }
282
283                 (UnpackedKind::Lifetime(v_o), UnpackedKind::Lifetime(v_r)) => {
284                     // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`.
285                     if v_o != v_r {
286                         output_query_region_constraints
287                             .push(ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r)));
288                         output_query_region_constraints
289                             .push(ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o)));
290                     }
291                 }
292
293                 (UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => {
294                     let ok = self.at(cause, param_env).eq(v1, v2)?;
295                     obligations.extend(ok.into_obligations());
296                 }
297
298                 _ => {
299                     bug!(
300                         "kind mismatch, cannot unify {:?} and {:?}",
301                         original_value,
302                         result_value
303                     );
304                 }
305             }
306         }
307
308         // ...also include the other query region constraints from the query.
309         output_query_region_constraints.extend(
310             query_response.value.region_constraints.iter().filter_map(|r_c| {
311                 let &ty::OutlivesPredicate(k1, r2) = r_c.skip_binder(); // reconstructed below
312                 let k1 = substitute_value(self.tcx, &result_subst, &k1);
313                 let r2 = substitute_value(self.tcx, &result_subst, &r2);
314                 if k1 != r2.into() {
315                     Some(ty::Binder::bind(ty::OutlivesPredicate(k1, r2)))
316                 } else {
317                     None
318                 }
319             })
320         );
321
322         let user_result: R =
323             query_response.substitute_projected(self.tcx, &result_subst, |q_r| &q_r.value);
324
325         Ok(InferOk {
326             value: user_result,
327             obligations,
328         })
329     }
330
331     /// Given the original values and the (canonicalized) result from
332     /// computing a query, returns a substitution that can be applied
333     /// to the query result to convert the result back into the
334     /// original namespace.
335     ///
336     /// The substitution also comes accompanied with subobligations
337     /// that arose from unification; these might occur if (for
338     /// example) we are doing lazy normalization and the value
339     /// assigned to a type variable is unified with an unnormalized
340     /// projection.
341     fn query_response_substitution<R>(
342         &self,
343         cause: &ObligationCause<'tcx>,
344         param_env: ty::ParamEnv<'tcx>,
345         original_values: &OriginalQueryValues<'tcx>,
346         query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>,
347     ) -> InferResult<'tcx, CanonicalVarValues<'tcx>>
348     where
349         R: Debug + TypeFoldable<'tcx>,
350     {
351         debug!(
352             "query_response_substitution(original_values={:#?}, query_response={:#?})",
353             original_values, query_response,
354         );
355
356         let result_subst =
357             self.query_response_substitution_guess(cause, original_values, query_response);
358
359         let obligations = self.unify_query_response_substitution_guess(
360             cause,
361             param_env,
362             original_values,
363             &result_subst,
364             query_response,
365         )?
366             .into_obligations();
367
368         Ok(InferOk {
369             value: result_subst,
370             obligations,
371         })
372     }
373
374     /// Given the original values and the (canonicalized) result from
375     /// computing a query, returns a **guess** at a substitution that
376     /// can be applied to the query result to convert the result back
377     /// into the original namespace. This is called a **guess**
378     /// because it uses a quick heuristic to find the values for each
379     /// canonical variable; if that quick heuristic fails, then we
380     /// will instantiate fresh inference variables for each canonical
381     /// variable instead. Therefore, the result of this method must be
382     /// properly unified
383     fn query_response_substitution_guess<R>(
384         &self,
385         cause: &ObligationCause<'tcx>,
386         original_values: &OriginalQueryValues<'tcx>,
387         query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>,
388     ) -> CanonicalVarValues<'tcx>
389     where
390         R: Debug + TypeFoldable<'tcx>,
391     {
392         debug!(
393             "query_response_substitution_guess(original_values={:#?}, query_response={:#?})",
394             original_values, query_response,
395         );
396
397         // Every canonical query result includes values for each of
398         // the inputs to the query. Therefore, we begin by unifying
399         // these values with the original inputs that were
400         // canonicalized.
401         let result_values = &query_response.value.var_values;
402         assert_eq!(original_values.var_values.len(), result_values.len());
403
404         // Quickly try to find initial values for the canonical
405         // variables in the result in terms of the query. We do this
406         // by iterating down the values that the query gave to each of
407         // the canonical inputs. If we find that one of those values
408         // is directly equal to one of the canonical variables in the
409         // result, then we can type the corresponding value from the
410         // input. See the example above.
411         let mut opt_values: IndexVec<BoundTyIndex, Option<Kind<'tcx>>> =
412             IndexVec::from_elem_n(None, query_response.variables.len());
413
414         // In terms of our example above, we are iterating over pairs like:
415         // [(?A, Vec<?0>), ('static, '?1), (?B, ?0)]
416         for (original_value, result_value) in original_values.var_values.iter().zip(result_values) {
417             match result_value.unpack() {
418                 UnpackedKind::Type(result_value) => {
419                     // e.g., here `result_value` might be `?0` in the example above...
420                     if let ty::Infer(ty::InferTy::BoundTy(b)) = result_value.sty {
421                         // in which case we would set `canonical_vars[0]` to `Some(?U)`.
422                         opt_values[b.var] = Some(*original_value);
423                     }
424                 }
425                 UnpackedKind::Lifetime(result_value) => {
426                     // e.g., here `result_value` might be `'?1` in the example above...
427                     if let &ty::RegionKind::ReCanonical(index) = result_value {
428                         // in which case we would set `canonical_vars[0]` to `Some('static)`.
429                         opt_values[index] = Some(*original_value);
430                     }
431                 }
432             }
433         }
434
435         // Create a result substitution: if we found a value for a
436         // given variable in the loop above, use that. Otherwise, use
437         // a fresh inference variable.
438         let result_subst = CanonicalVarValues {
439             var_values: query_response
440                 .variables
441                 .iter()
442                 .enumerate()
443                 .map(|(index, info)| opt_values[BoundTyIndex::new(index)].unwrap_or_else(||
444                     self.fresh_inference_var_for_canonical_var(cause.span, *info)
445                 ))
446                 .collect(),
447         };
448
449         result_subst
450     }
451
452     /// Given a "guess" at the values for the canonical variables in
453     /// the input, try to unify with the *actual* values found in the
454     /// query result.  Often, but not always, this is a no-op, because
455     /// we already found the mapping in the "guessing" step.
456     ///
457     /// See also: `query_response_substitution_guess`
458     fn unify_query_response_substitution_guess<R>(
459         &self,
460         cause: &ObligationCause<'tcx>,
461         param_env: ty::ParamEnv<'tcx>,
462         original_values: &OriginalQueryValues<'tcx>,
463         result_subst: &CanonicalVarValues<'tcx>,
464         query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>,
465     ) -> InferResult<'tcx, ()>
466     where
467         R: Debug + TypeFoldable<'tcx>,
468     {
469         // A closure that yields the result value for the given
470         // canonical variable; this is taken from
471         // `query_response.var_values` after applying the substitution
472         // `result_subst`.
473         let substituted_query_response = |index: BoundTyIndex| -> Kind<'tcx> {
474             query_response.substitute_projected(self.tcx, &result_subst, |v| &v.var_values[index])
475         };
476
477         // Unify the original value for each variable with the value
478         // taken from `query_response` (after applying `result_subst`).
479         Ok(self.unify_canonical_vars(
480             cause,
481             param_env,
482             original_values,
483             substituted_query_response,
484         )?)
485     }
486
487     /// Converts the region constraints resulting from a query into an
488     /// iterator of obligations.
489     fn query_region_constraints_into_obligations<'a>(
490         &'a self,
491         cause: &'a ObligationCause<'tcx>,
492         param_env: ty::ParamEnv<'tcx>,
493         unsubstituted_region_constraints: &'a [QueryRegionConstraint<'tcx>],
494         result_subst: &'a CanonicalVarValues<'tcx>,
495     ) -> impl Iterator<Item = PredicateObligation<'tcx>> + 'a {
496         Box::new(
497             unsubstituted_region_constraints
498                 .iter()
499                 .map(move |constraint| {
500                     let ty::OutlivesPredicate(k1, r2) = constraint.skip_binder(); // restored below
501                     let k1 = substitute_value(self.tcx, result_subst, k1);
502                     let r2 = substitute_value(self.tcx, result_subst, r2);
503
504                     Obligation::new(
505                         cause.clone(),
506                         param_env,
507                         match k1.unpack() {
508                             UnpackedKind::Lifetime(r1) => ty::Predicate::RegionOutlives(
509                                 ty::Binder::dummy(
510                                     ty::OutlivesPredicate(r1, r2)
511                             )),
512                             UnpackedKind::Type(t1) => ty::Predicate::TypeOutlives(
513                                 ty::Binder::dummy(ty::OutlivesPredicate(
514                                     t1, r2
515                             )))
516                         }
517                     )
518                 })
519         ) as Box<dyn Iterator<Item = _>>
520     }
521
522     /// Given two sets of values for the same set of canonical variables, unify them.
523     /// The second set is produced lazilly by supplying indices from the first set.
524     fn unify_canonical_vars(
525         &self,
526         cause: &ObligationCause<'tcx>,
527         param_env: ty::ParamEnv<'tcx>,
528         variables1: &OriginalQueryValues<'tcx>,
529         variables2: impl Fn(BoundTyIndex) -> Kind<'tcx>,
530     ) -> InferResult<'tcx, ()> {
531         self.commit_if_ok(|_| {
532             let mut obligations = vec![];
533             for (index, value1) in variables1.var_values.iter().enumerate() {
534                 let value2 = variables2(BoundTyIndex::new(index));
535
536                 match (value1.unpack(), value2.unpack()) {
537                     (UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => {
538                         obligations
539                             .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations());
540                     }
541                     (
542                         UnpackedKind::Lifetime(ty::ReErased),
543                         UnpackedKind::Lifetime(ty::ReErased),
544                     ) => {
545                         // no action needed
546                     }
547                     (UnpackedKind::Lifetime(v1), UnpackedKind::Lifetime(v2)) => {
548                         obligations
549                             .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations());
550                     }
551                     _ => {
552                         bug!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,);
553                     }
554                 }
555             }
556             Ok(InferOk {
557                 value: (),
558                 obligations,
559             })
560         })
561     }
562 }
563
564 /// Given the region obligations and constraints scraped from the infcx,
565 /// creates query region constraints.
566 pub fn make_query_outlives<'tcx>(
567     tcx: TyCtxt<'_, '_, 'tcx>,
568     outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>)>,
569     region_constraints: &RegionConstraintData<'tcx>,
570 ) -> Vec<QueryRegionConstraint<'tcx>> {
571     let RegionConstraintData {
572         constraints,
573         verifys,
574         givens,
575     } = region_constraints;
576
577     assert!(verifys.is_empty());
578     assert!(givens.is_empty());
579
580     let outlives: Vec<_> = constraints
581         .into_iter()
582         .map(|(k, _)| match *k {
583             // Swap regions because we are going from sub (<=) to outlives
584             // (>=).
585             Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate(
586                 tcx.mk_region(ty::ReVar(v2)).into(),
587                 tcx.mk_region(ty::ReVar(v1)),
588             ),
589             Constraint::VarSubReg(v1, r2) => {
590                 ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1)))
591             }
592             Constraint::RegSubVar(r1, v2) => {
593                 ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1)
594             }
595             Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1),
596         })
597         .map(ty::Binder::dummy) // no bound regions in the code above
598         .chain(
599             outlives_obligations
600                 .map(|(ty, r)| ty::OutlivesPredicate(ty.into(), r))
601                 .map(ty::Binder::dummy), // no bound regions in the code above
602         )
603         .collect();
604
605     outlives
606 }