propagate type tests from closure to closure creators

Currently, we only propagate type tests that exclude all regions from
the type.
This commit is contained in:
Niko Matsakis 2017-12-04 13:04:38 -05:00
parent 5804637a81
commit 85e1d4749e
7 changed files with 581 additions and 31 deletions

View file

@ -12,15 +12,18 @@ use super::universal_regions::UniversalRegions;
use rustc::hir::def_id::DefId;
use rustc::infer::InferCtxt;
use rustc::infer::NLLRegionVariableOrigin;
use rustc::infer::RegionObligation;
use rustc::infer::RegionVariableOrigin;
use rustc::infer::SubregionOrigin;
use rustc::infer::region_constraints::{GenericKind, VarOrigins};
use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements,
Location, Mir};
use rustc::ty::{self, RegionVid};
use rustc::traits::ObligationCause;
use rustc::ty::{self, RegionVid, TypeFoldable};
use rustc_data_structures::indexed_vec::IndexVec;
use std::fmt;
use std::rc::Rc;
use syntax::ast;
use syntax_pos::Span;
mod annotation;
@ -361,7 +364,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
None
};
self.check_type_tests(infcx, mir);
self.check_type_tests(infcx, mir, outlives_requirements.as_mut());
self.check_universal_regions(infcx, outlives_requirements.as_mut());
@ -439,21 +442,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// therefore add `end('a)` into the region for `'b` -- but we
/// have no evidence that `'b` outlives `'a`, so we want to report
/// an error.
fn check_type_tests(&self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) {
fn check_type_tests<'gcx>(
&self,
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
) {
let tcx = infcx.tcx;
for type_test in &self.type_tests {
debug!("check_type_test: {:?}", type_test);
if self.eval_region_test(
mir,
type_test.point,
type_test.lower_bound,
&type_test.test,
) {
if self.eval_region_test(mir, type_test.point, type_test.lower_bound, &type_test.test) {
continue;
}
if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements {
if self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) {
continue;
}
}
// Oh the humanity. Obviously we will do better than this error eventually.
tcx.sess.span_err(
type_test.span,
@ -462,6 +471,103 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
fn try_promote_type_test<'gcx>(
&self,
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
type_test: &TypeTest<'tcx>,
propagated_outlives_requirements: &mut Vec<ClosureOutlivesRequirement<'gcx>>,
) -> bool {
let tcx = infcx.tcx;
let gcx = tcx.global_tcx();
let TypeTest {
generic_kind,
lower_bound,
point: _,
span,
test: _,
} = type_test;
// TODO. For now, just fail to promote anything with a
// region. This is obviously too strict: we will for example
// fail to promote `<T as Foo<'static>>::Bar` to our
// caller. But it is always sound not to promote, that just
// means more errors, and ignoring regions is a convenient
// starting point. This is because we would want to promote to
// a type that references the region-vids of the closure, for
// which we have no global representation just now.
let generic_ty = generic_kind.to_ty(tcx);
if generic_ty.has_free_regions() {
return false;
}
let generic_ty = gcx.lift(&generic_ty).unwrap();
// Find some bounding subject-region R+ that is a super-region
// of the existing subject-region R. This should be a non-local, universal
// region, which ensures it can be encoded in a `ClosureOutlivesRequirement`.
let lower_bound_plus = self.promoted_type_test_bound(*lower_bound);
assert!(self.universal_regions.is_universal_region(lower_bound_plus));
assert!(!self.universal_regions
.is_local_free_region(lower_bound_plus));
propagated_outlives_requirements.push(ClosureOutlivesRequirement {
subject: ClosureOutlivesSubject::Ty(generic_ty),
outlived_free_region: lower_bound_plus,
blame_span: *span,
});
true
}
/// Here, `lower_bound` (henceforth, `'r`) represents the bound from
/// some type-test `T: 'r`. We are a closure and have found that
/// `T: 'r` is not locally satisfiable, so we want to propagate
/// this constraint to our creator. It is sound for us to do so
/// with some `'r+` known to our creator, where `'r+: 'r`.
///
/// The tricky bit here: this region `'r` may contain (a) any
/// number of points in the CFG and (b) any number of `end('x)`
/// elements of universally quantified regions. To communicate with
/// our creator, however, we have to pick exactly one universally
/// quantified region -- in other words, exactly one `end('x)`
/// element -- that they understand and which will be `'r+`.
///
/// We do this as follows:
///
/// - Ignore the CFG points in `'r`. All universally quantified regions
/// include the CFG anyhow.
/// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding
/// a result `'y`.
/// - Finally, we take the non-local upper bound of `'y`.
fn promoted_type_test_bound(&self, lower_bound: RegionVid) -> RegionVid {
let inferred_values = self.inferred_values.as_ref().unwrap();
debug!(
"promoted_type_test_bound(lower_bound={:?}={})",
lower_bound,
inferred_values.region_value_str(lower_bound)
);
// Find the smallest universal region that contains all other
// universal regions within `region`.
let mut lub = self.universal_regions.fr_fn_body;
for ur in inferred_values.universal_regions_outlived_by(lower_bound) {
lub = self.universal_regions.postdom_upper_bound(lub, ur);
}
debug!("promoted_type_test_bound: lub={:?}", lub);
// Grow further to get smallest universal region known to
// creator.
let non_local_lub = self.universal_regions.non_local_upper_bound(lub);
debug!(
"promoted_type_test_bound: non_local_lub={:?}",
non_local_lub
);
non_local_lub
}
/// Test if `test` is true when applied to `lower_bound` at
/// `point`, and returns true or false.
fn eval_region_test(
@ -487,13 +593,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
.iter()
.any(|&r| self.eval_outlives(mir, r, lower_bound, point)),
RegionTest::Any(tests) => tests.iter().any(|test| {
self.eval_region_test(mir, point, lower_bound, test)
}),
RegionTest::Any(tests) => tests
.iter()
.any(|test| self.eval_region_test(mir, point, lower_bound, test)),
RegionTest::All(tests) => tests.iter().all(|test| {
self.eval_region_test(mir, point, lower_bound, test)
}),
RegionTest::All(tests) => tests
.iter()
.all(|test| self.eval_region_test(mir, point, lower_bound, test)),
}
}
@ -772,17 +878,18 @@ impl fmt::Debug for Constraint {
}
}
pub trait ClosureRegionRequirementsExt {
pub trait ClosureRegionRequirementsExt<'gcx> {
fn apply_requirements<'tcx>(
&self,
infcx: &InferCtxt<'_, '_, 'tcx>,
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
body_id: ast::NodeId,
location: Location,
closure_def_id: DefId,
closure_substs: ty::ClosureSubsts<'tcx>,
);
}
impl<'gcx> ClosureRegionRequirementsExt for ClosureRegionRequirements<'gcx> {
impl<'gcx> ClosureRegionRequirementsExt<'gcx> for ClosureRegionRequirements<'gcx> {
/// Given an instance T of the closure type, this method
/// instantiates the "extra" requirements that we computed for the
/// closure into the inference context. This has the effect of
@ -797,7 +904,8 @@ impl<'gcx> ClosureRegionRequirementsExt for ClosureRegionRequirements<'gcx> {
/// requirements.
fn apply_requirements<'tcx>(
&self,
infcx: &InferCtxt<'_, '_, 'tcx>,
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
body_id: ast::NodeId,
location: Location,
closure_def_id: DefId,
closure_substs: ty::ClosureSubsts<'tcx>,
@ -843,8 +951,15 @@ impl<'gcx> ClosureRegionRequirementsExt for ClosureRegionRequirements<'gcx> {
infcx.sub_regions(origin, outlived_region, region);
}
ClosureOutlivesSubject::Ty(_ty) => {
bug!("TODO not yet implemented -- closure outlives subject of a type");
ClosureOutlivesSubject::Ty(ty) => {
infcx.register_region_obligation(
body_id,
RegionObligation {
sup_type: ty,
sub_region: outlived_region,
cause: ObligationCause::misc(outlives_requirement.blame_span, body_id),
},
);
}
}
}

View file

@ -1409,6 +1409,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id) {
closure_region_requirements.apply_requirements(
self.infcx,
self.body_id,
location,
*def_id,
*substs,

View file

@ -267,6 +267,20 @@ impl<'tcx> UniversalRegions<'tcx> {
self.num_universals
}
/// Given two universal regions, returns the postdominating
/// upper-bound (effectively the least upper bound).
///
/// (See `TransitiveRelation::postdom_upper_bound` for details on
/// the postdominating upper bound in general.)
pub fn postdom_upper_bound(&self, fr1: RegionVid, fr2: RegionVid) -> RegionVid {
assert!(self.is_universal_region(fr1));
assert!(self.is_universal_region(fr2));
*self.relations
.inverse_outlives
.postdom_upper_bound(&fr1, &fr2)
.unwrap_or(&self.fr_static)
}
/// Finds an "upper bound" for `fr` that is not local. In other
/// words, returns the smallest (*) known region `fr1` that (a)
/// outlives `fr` and (b) is not local. This cannot fail, because
@ -431,7 +445,10 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> {
// - `'r: 'fn_body` for every (other) universally quantified
// region `'r`, all of which are provided by our caller
for fr in (FIRST_GLOBAL_INDEX..num_universals).map(RegionVid::new) {
debug!("build: relating free region {:?} to itself and to 'static", fr);
debug!(
"build: relating free region {:?} to itself and to 'static",
fr
);
self.relations.relate_universal_regions(fr, fr);
self.relations.relate_universal_regions(fr_static, fr);
self.relations.relate_universal_regions(fr, fr_fn_body);
@ -442,15 +459,21 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> {
// we should not have created any more variables
assert_eq!(self.infcx.num_region_vars(), num_universals);
debug!("build: global regions = {}..{}",
FIRST_GLOBAL_INDEX,
first_extern_index);
debug!("build: extern regions = {}..{}",
first_extern_index,
first_local_index);
debug!("build: local regions = {}..{}",
first_local_index,
num_universals);
debug!(
"build: global regions = {}..{}",
FIRST_GLOBAL_INDEX,
first_extern_index
);
debug!(
"build: extern regions = {}..{}",
first_extern_index,
first_local_index
);
debug!(
"build: local regions = {}..{}",
first_local_index,
num_universals
);
UniversalRegions {
indices,

View file

@ -0,0 +1,66 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:-Znll -Zborrowck=mir -Zverbose
#![allow(warnings)]
#![feature(dyn_trait)]
#![feature(rustc_attrs)]
use std::fmt::Debug;
fn with_signature<'a, T, F>(x: Box<T>, op: F) -> Box<dyn Debug + 'a>
where F: FnOnce(Box<T>) -> Box<dyn Debug + 'a>
{
op(x)
}
#[rustc_regions]
fn no_region<'a, T>(x: Box<T>) -> Box<dyn Debug + 'a>
where
T: Debug,
{
// Here, the closure winds up being required to prove that `T:
// 'a`. In principle, it could know that, except that it is
// type-checked in a fully generic way, and hence it winds up with
// a propagated requirement that `T: '_#2`, where `'_#2` appears
// in the return type. The caller makes the mapping from `'_#2` to
// `'a` (and subsequently reports an error).
with_signature(x, |y| y)
//~^ WARNING not reporting region error due to -Znll
//~| ERROR failed type test
}
fn correct_region<'a, T>(x: Box<T>) -> Box<Debug + 'a>
where
T: 'a + Debug,
{
x
}
fn wrong_region<'a, 'b, T>(x: Box<T>) -> Box<Debug + 'a>
where
T: 'b + Debug,
{
x
//~^ WARNING not reporting region error due to -Znll
//~| ERROR failed type test
}
fn outlives_region<'a, 'b, T>(x: Box<T>) -> Box<Debug + 'a>
where
T: 'b + Debug,
'b: 'a,
{
x
}
fn main() {}

View file

@ -0,0 +1,58 @@
warning: not reporting region error due to -Znll
--> $DIR/ty-param-closure-outlives-from-return-type.rs:37:27
|
37 | with_signature(x, |y| y)
| ^
warning: not reporting region error due to -Znll
--> $DIR/ty-param-closure-outlives-from-return-type.rs:53:5
|
53 | x
| ^
note: External requirements
--> $DIR/ty-param-closure-outlives-from-return-type.rs:37:23
|
37 | with_signature(x, |y| y)
| ^^^^^
|
= note: defining type: DefId(0/1:14 ~ ty_param_closure_outlives_from_return_type[317d]::no_region[0]::{{closure}}[0]) with closure substs [
'_#1r,
T,
i32,
extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<std::fmt::Debug + '_#2r>
]
= note: number of external vids: 3
= note: where T: '_#2r
error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#4r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-return-type.rs:37:23: 37:28, test: IsOutlivedByAnyRegionIn(['_#2r]) }
--> $DIR/ty-param-closure-outlives-from-return-type.rs:37:23
|
37 | with_signature(x, |y| y)
| ^^^^^
note: No external requirements
--> $DIR/ty-param-closure-outlives-from-return-type.rs:26:1
|
26 | / fn no_region<'a, T>(x: Box<T>) -> Box<dyn Debug + 'a>
27 | | where
28 | | T: Debug,
29 | | {
... |
39 | | //~| ERROR failed type test
40 | | }
| |_^
|
= note: defining type: DefId(0/0:5 ~ ty_param_closure_outlives_from_return_type[317d]::no_region[0]) with substs [
'_#1r,
T
]
error: failed type test: TypeTest { generic_kind: T/#2, lower_bound: '_#4r, point: bb0[3], span: $DIR/ty-param-closure-outlives-from-return-type.rs:53:5: 53:6, test: IsOutlivedByAnyRegionIn(['_#2r, '_#3r]) }
--> $DIR/ty-param-closure-outlives-from-return-type.rs:53:5
|
53 | x
| ^
error: aborting due to 2 previous errors

View file

@ -0,0 +1,96 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we can propagate `T: 'a` obligations to our caller. See
// `correct_region` for an explanation of how this test is setup; it's
// somewhat intricate.
// compile-flags:-Znll -Zborrowck=mir -Zverbose
#![allow(warnings)]
#![feature(dyn_trait)]
#![feature(rustc_attrs)]
use std::cell::Cell;
fn with_signature<'a, T, F>(a: Cell<&'a ()>, b: T, op: F)
where
F: FnOnce(Cell<&'a ()>, T),
{
op(a, b)
}
fn require<'a, T>(_a: &Cell<&'a ()>, _b: &T)
where
T: 'a,
{
}
#[rustc_regions]
fn no_region<'a, T>(a: Cell<&'a ()>, b: T) {
with_signature(a, b, |x, y| {
//~^ ERROR failed type test
//
// See `correct_region`, which explains the point of this
// test. The only difference is that, in the case of this
// function, there is no where clause *anywhere*, and hence we
// get an error (but reported by the closure creator).
require(&x, &y)
//~^ WARNING not reporting region error due to -Znll
})
}
#[rustc_regions]
fn correct_region<'a, T>(a: Cell<&'a ()>, b: T)
where
T: 'a,
{
with_signature(a, b, |x, y| {
// Key point of this test:
//
// The *closure* is being type-checked with all of its free
// regions "universalized". In particular, it does not know
// that `x` has the type `Cell<&'a ()>`, but rather treats it
// as if the type of `x` is `Cell<&'A ()>`, where `'A` is some
// fresh, independent region distinct from the `'a` which
// appears in the environment. The call to `require` here
// forces us then to prove that `T: 'A`, but the closure
// cannot do it on its own. It has to surface this requirement
// to its creator (which knows that `'a == 'A`).
require(&x, &y)
})
}
#[rustc_regions]
fn wrong_region<'a, 'b, T>(a: Cell<&'a ()>, b: T)
where
T: 'b,
{
with_signature(a, b, |x, y| {
//~^ ERROR failed type test
// See `correct_region`
require(&x, &y)
//~^ WARNING not reporting region error due to -Znll
})
}
#[rustc_regions]
fn outlives_region<'a, 'b, T>(a: Cell<&'a ()>, b: T)
where
T: 'b,
'b: 'a,
{
with_signature(a, b, |x, y| {
// See `correct_region`
require(&x, &y)
})
}
fn main() {}

View file

@ -0,0 +1,191 @@
warning: not reporting region error due to -Znll
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:45:9
|
45 | require(&x, &y)
| ^^^^^^^
warning: not reporting region error due to -Znll
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:79:9
|
79 | require(&x, &y)
| ^^^^^^^
note: External requirements
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26
|
38 | with_signature(a, b, |x, y| {
| __________________________^
39 | | //~^ ERROR failed type test
40 | | //
41 | | // See `correct_region`, which explains the point of this
... |
46 | | //~^ WARNING not reporting region error due to -Znll
47 | | })
| |_____^
|
= note: defining type: DefId(0/1:16 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]::{{closure}}[0]) with closure substs [
T,
i32,
extern "rust-call" fn((std::cell::Cell<&'_#1r ()>, T))
]
= note: number of external vids: 2
= note: where T: '_#1r
note: External requirements
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:55:26
|
55 | with_signature(a, b, |x, y| {
| __________________________^
56 | | // Key point of this test:
57 | | //
58 | | // The *closure* is being type-checked with all of its free
... |
67 | | require(&x, &y)
68 | | })
| |_____^
|
= note: defining type: DefId(0/1:19 ~ ty_param_closure_outlives_from_where_clause[317d]::correct_region[0]::{{closure}}[0]) with closure substs [
'_#1r,
T,
i32,
extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T))
]
= note: number of external vids: 3
= note: where T: '_#2r
note: External requirements
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26
|
76 | with_signature(a, b, |x, y| {
| __________________________^
77 | | //~^ ERROR failed type test
78 | | // See `correct_region`
79 | | require(&x, &y)
80 | | //~^ WARNING not reporting region error due to -Znll
81 | | })
| |_____^
|
= note: defining type: DefId(0/1:23 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]::{{closure}}[0]) with closure substs [
'_#1r,
T,
i32,
extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T))
]
= note: number of external vids: 3
= note: where T: '_#2r
note: External requirements
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:90:26
|
90 | with_signature(a, b, |x, y| {
| __________________________^
91 | | // See `correct_region`
92 | | require(&x, &y)
93 | | })
| |_____^
|
= note: defining type: DefId(0/1:27 ~ ty_param_closure_outlives_from_where_clause[317d]::outlives_region[0]::{{closure}}[0]) with closure substs [
'_#1r,
'_#2r,
T,
i32,
extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
]
= note: number of external vids: 4
= note: where T: '_#3r
error: failed type test: TypeTest { generic_kind: T/#0, lower_bound: '_#3r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26: 47:6, test: IsOutlivedByAnyRegionIn(['_#2r]) }
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26
|
38 | with_signature(a, b, |x, y| {
| __________________________^
39 | | //~^ ERROR failed type test
40 | | //
41 | | // See `correct_region`, which explains the point of this
... |
46 | | //~^ WARNING not reporting region error due to -Znll
47 | | })
| |_____^
note: No external requirements
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:37:1
|
37 | / fn no_region<'a, T>(a: Cell<&'a ()>, b: T) {
38 | | with_signature(a, b, |x, y| {
39 | | //~^ ERROR failed type test
40 | | //
... |
47 | | })
48 | | }
| |_^
|
= note: defining type: DefId(0/0:6 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]) with substs [
T
]
note: No external requirements
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:51:1
|
51 | / fn correct_region<'a, T>(a: Cell<&'a ()>, b: T)
52 | | where
53 | | T: 'a,
54 | | {
... |
68 | | })
69 | | }
| |_^
|
= note: defining type: DefId(0/0:7 ~ ty_param_closure_outlives_from_where_clause[317d]::correct_region[0]) with substs [
'_#1r,
T
]
error: failed type test: TypeTest { generic_kind: T/#1, lower_bound: '_#5r, point: bb0[5], span: $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26: 81:6, test: IsOutlivedByAnyRegionIn(['_#1r, '_#3r]) }
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26
|
76 | with_signature(a, b, |x, y| {
| __________________________^
77 | | //~^ ERROR failed type test
78 | | // See `correct_region`
79 | | require(&x, &y)
80 | | //~^ WARNING not reporting region error due to -Znll
81 | | })
| |_____^
note: No external requirements
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:72:1
|
72 | / fn wrong_region<'a, 'b, T>(a: Cell<&'a ()>, b: T)
73 | | where
74 | | T: 'b,
75 | | {
... |
81 | | })
82 | | }
| |_^
|
= note: defining type: DefId(0/0:8 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]) with substs [
'_#1r,
T
]
note: No external requirements
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:85:1
|
85 | / fn outlives_region<'a, 'b, T>(a: Cell<&'a ()>, b: T)
86 | | where
87 | | T: 'b,
88 | | 'b: 'a,
... |
93 | | })
94 | | }
| |_^
|
= note: defining type: DefId(0/0:9 ~ ty_param_closure_outlives_from_where_clause[317d]::outlives_region[0]) with substs [
'_#1r,
'_#2r,
T
]
error: aborting due to 2 previous errors