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