add some tests, currently ICE-ing
This commit is contained in:
parent
dfcd1c6328
commit
c36205b48e
@ -2,6 +2,7 @@
|
||||
|
||||
use crate::infer::region_constraints::Constraint;
|
||||
use crate::infer::region_constraints::GenericKind;
|
||||
use crate::infer::region_constraints::InConstraint;
|
||||
use crate::infer::region_constraints::RegionConstraintData;
|
||||
use crate::infer::region_constraints::VarInfos;
|
||||
use crate::infer::region_constraints::VerifyBound;
|
||||
@ -117,6 +118,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
|
||||
|
||||
let graph = self.construct_graph();
|
||||
self.expand_givens(&graph);
|
||||
self.enforce_in_constraints(&graph, &mut var_data);
|
||||
self.expansion(&mut var_data);
|
||||
self.collect_errors(&mut var_data, errors);
|
||||
self.collect_var_errors(&var_data, &graph, errors);
|
||||
@ -178,6 +180,87 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enforce constraints of the form:
|
||||
///
|
||||
/// ```
|
||||
/// 'r0 in ['o1...'oN]
|
||||
/// ```
|
||||
///
|
||||
/// such a constraint simply means that `'r0` must be equal to one
|
||||
/// of the regions `'o1...'oN`. This is an annoying constraint to
|
||||
/// integrate into our inference, which generally works by
|
||||
/// iteratively growing regions until we find a match -- that's
|
||||
/// not an option here.
|
||||
///
|
||||
/// What we currently do:
|
||||
///
|
||||
/// - Search forward in the graph from `'r0` to find each region `'b`
|
||||
/// where `'r0 <= 'b` must hold.
|
||||
/// - Try to rule out some of the `'o1..'oN` options:
|
||||
/// - if `'o[i] <= 'b` is false, then `'o[i]` is not an option
|
||||
///
|
||||
/// Hopefully this narrows it down to just one option.
|
||||
fn enforce_in_constraints(
|
||||
&self,
|
||||
graph: &RegionGraph<'tcx>,
|
||||
var_values: &mut LexicalRegionResolutions<'tcx>,
|
||||
) {
|
||||
for in_constraint in &self.data.in_constraints {
|
||||
let _ = self.enforce_in_constraint(graph, in_constraint, var_values);
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_in_constraint(
|
||||
&self,
|
||||
graph: &RegionGraph<'tcx>,
|
||||
in_constraint: &InConstraint<'tcx>,
|
||||
var_values: &mut LexicalRegionResolutions<'tcx>,
|
||||
) -> Result<(), ()> {
|
||||
debug!("enforce_in_constraint(in_constraint={:#?})", in_constraint);
|
||||
|
||||
// the constraint is some inference variable (`vid`) which
|
||||
// must be equal to one of the options
|
||||
let vid = match in_constraint.region {
|
||||
ty::ReVar(vid) => *vid,
|
||||
_ => return Err(()),
|
||||
};
|
||||
|
||||
// find all the "bounds" -- that is, each region `b` such that
|
||||
// `r0 <= b` must hold.
|
||||
let (bounds, _) = self.collect_concrete_regions(graph, vid, OUTGOING, None);
|
||||
|
||||
// get an iterator over the *available options* -- that is,
|
||||
// each constraint regions `o` where `o <= b` for all the
|
||||
// bounds `b`.
|
||||
debug!("enforce_in_constraint: bounds={:#?}", bounds);
|
||||
let mut options = in_constraint.in_regions.iter().filter(|option| {
|
||||
bounds.iter().all(|bound| self.sub_concrete_regions(option, bound.region))
|
||||
});
|
||||
|
||||
// if there >1 option, we only make a choice if there is a
|
||||
// single *least* choice -- i.e., some available region that
|
||||
// is `<=` all the others.
|
||||
let mut least_choice = options.next().ok_or(())?;
|
||||
debug!("enforce_in_constraint: least_choice={:?}", least_choice);
|
||||
for option in options {
|
||||
debug!("enforce_in_constraint: option={:?}", option);
|
||||
if !self.sub_concrete_regions(least_choice, option) {
|
||||
if self.sub_concrete_regions(option, least_choice) {
|
||||
debug!("enforce_in_constraint: new least choice");
|
||||
least_choice = option;
|
||||
} else {
|
||||
debug!("enforce_in_constraint: no least choice");
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("enforce_in_constraint: final least choice = {:?}", least_choice);
|
||||
*var_values.value_mut(vid) = VarValue::Value(least_choice);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) {
|
||||
self.iterate_until_fixed_point("Expansion", |constraint| {
|
||||
debug!("expansion: constraint={:?}", constraint);
|
||||
@ -275,6 +358,12 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// True if `a <= b`, but not defined over inference variables.
|
||||
fn sub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> bool {
|
||||
self.lub_concrete_regions(a, b) == b
|
||||
}
|
||||
|
||||
/// Returns the smallest region `c` such that `a <= c` and `b <= c`.
|
||||
fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
|
||||
let tcx = self.tcx();
|
||||
|
||||
@ -500,12 +589,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
|
||||
that is not used is not a problem, so if this rule
|
||||
starts to create problems we'll have to revisit
|
||||
this portion of the code and think hard about it. =) */
|
||||
self.collect_error_for_expanding_node(
|
||||
graph,
|
||||
&mut dup_vec,
|
||||
node_vid,
|
||||
errors,
|
||||
);
|
||||
self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -445,6 +445,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `def_id`, the `impl Trait` type
|
||||
|
||||
/// - `opaque_defn`, the opaque definition created in `instantiate_opaque_types`
|
||||
/// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
|
||||
/// `opaque_defn.concrete_ty`
|
||||
@ -648,25 +649,34 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> {
|
||||
does not appear in bounds",
|
||||
);
|
||||
|
||||
// Assuming regionck succeeded, then we must
|
||||
// be capturing *some* region from the fn
|
||||
// header, and hence it must be free, so it's
|
||||
// ok to invoke this fn (which doesn't accept
|
||||
// all regions, and would ICE if an
|
||||
// inappropriate region is given). We check
|
||||
// `is_tainted_by_errors` by errors above, so
|
||||
// we don't get in here unless regionck
|
||||
// succeeded. (Note also that if regionck
|
||||
// failed, then the regions we are attempting
|
||||
// to map here may well be giving errors
|
||||
// *because* the constraints were not
|
||||
// satisfiable.)
|
||||
self.tcx.note_and_explain_free_region(
|
||||
&mut err,
|
||||
&format!("hidden type `{}` captures ", hidden_ty),
|
||||
r,
|
||||
"",
|
||||
);
|
||||
// Explain the region we are capturing.
|
||||
match r {
|
||||
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty => {
|
||||
// Assuming regionck succeeded (*), we
|
||||
// ought to always be capturing *some* region
|
||||
// from the fn header, and hence it ought to
|
||||
// be free. So under normal circumstances, we will
|
||||
// go down this path which gives a decent human readable
|
||||
// explanation.
|
||||
//
|
||||
// (*) if not, the `tainted_by_errors`
|
||||
// flag would be set to true in any
|
||||
// case, so we wouldn't be here at
|
||||
// all.
|
||||
self.tcx.note_and_explain_free_region(
|
||||
&mut err,
|
||||
&format!("hidden type `{}` captures ", hidden_ty),
|
||||
r,
|
||||
"",
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
// This case should not happen: it indicates that regionck
|
||||
// failed to enforce an "in constraint".
|
||||
err.note(&format!("hidden type `{}` captures `{:?}`", hidden_ty, r));
|
||||
err.note(&format!("this is likely a bug in the compiler, please file an issue on github"));
|
||||
}
|
||||
}
|
||||
|
||||
err.emit();
|
||||
}
|
||||
|
54
src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs
Normal file
54
src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs
Normal file
@ -0,0 +1,54 @@
|
||||
// edition:2018
|
||||
|
||||
#![feature(arbitrary_self_types, async_await, await_macro, pin)]
|
||||
|
||||
trait Trait<'a, 'b> {}
|
||||
impl<T> Trait<'_, '_> for T {}
|
||||
|
||||
// `Invert<'a> <: Invert<'b>` if `'b: 'a`, unlike most types.
|
||||
//
|
||||
// I am purposefully avoiding the terms co- and contra-variant because
|
||||
// their application to regions depends on how you interpreted Rust
|
||||
// regions. -nikomatsakis
|
||||
struct Invert<'a>(fn(&'a u8));
|
||||
|
||||
fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e>
|
||||
//~^ ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds
|
||||
// FIXME -- we ought to be able to pick `'d` here, but our handling of in constraints
|
||||
// is not smart enough
|
||||
where
|
||||
'c: 'a,
|
||||
'c: 'b,
|
||||
'd: 'c,
|
||||
{
|
||||
// Representing the where clauses as a graph, where `A: B` is an
|
||||
// edge `B -> A`:
|
||||
//
|
||||
// ```
|
||||
// 'a -> 'c -> 'd
|
||||
// ^
|
||||
// |
|
||||
// 'b
|
||||
// ```
|
||||
//
|
||||
// Meanwhile we return a value &'0 u8 where we have the constraints:
|
||||
//
|
||||
// ```
|
||||
// '0: 'a
|
||||
// '0: 'b
|
||||
// '0 in ['d, 'e]
|
||||
// ```
|
||||
//
|
||||
// Here, ignoring the "in" constraint, the minimal choice for `'0`
|
||||
// is `'c`, but that is not in the "in set". Still, that reduces
|
||||
// the range of options in the "in set" to just `'d` (`'e: 'c`
|
||||
// does not hold).
|
||||
let p = if condition() { a } else { b };
|
||||
p
|
||||
}
|
||||
|
||||
fn condition() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,15 @@
|
||||
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
|
||||
--> $DIR/inverse-bounds.rs:15:70
|
||||
|
|
||||
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e>
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: hidden type `Invert<'c>` captures the lifetime 'c as defined on the function body at 15:25
|
||||
--> $DIR/inverse-bounds.rs:15:25
|
||||
|
|
||||
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e>
|
||||
| ^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0700`.
|
@ -0,0 +1,26 @@
|
||||
// edition:2018
|
||||
|
||||
#![feature(arbitrary_self_types, async_await, await_macro, pin)]
|
||||
|
||||
trait Trait<'a, 'b> { }
|
||||
impl<T> Trait<'_, '_> for T { }
|
||||
|
||||
// Here we wind up selecting `'a` and `'b` in the hidden type because
|
||||
// those are the types that appear inth e original values.
|
||||
|
||||
fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
|
||||
// In this simple case, you have a hidden type `(&'0 u8, &'1 u8)` and constraints like
|
||||
//
|
||||
// ```
|
||||
// 'a: '0
|
||||
// 'b: '1
|
||||
// '0 in ['a, 'b]
|
||||
// '1 in ['a, 'b]
|
||||
// ```
|
||||
//
|
||||
// We use the fact that `'a: 0'` must hold (combined with the in
|
||||
// constraint) to determine that `'0 = 'a` must be the answer.
|
||||
(a, b)
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,43 @@
|
||||
// edition:2018
|
||||
|
||||
#![feature(arbitrary_self_types, async_await, await_macro, pin)]
|
||||
|
||||
trait Trait<'a, 'b> {}
|
||||
impl<T> Trait<'_, '_> for T {}
|
||||
|
||||
// `Ordinary<'a> <: Ordinary<'b>` if `'a: 'b`, as with most types.
|
||||
//
|
||||
// I am purposefully avoiding the terms co- and contra-variant because
|
||||
// their application to regions depends on how you interpreted Rust
|
||||
// regions. -nikomatsakis
|
||||
struct Ordinary<'a>(&'a u8);
|
||||
|
||||
// Here we wind up selecting `'e` in the hidden type because
|
||||
// we need something outlived by both `'a` and `'b` and only `'e` applies.
|
||||
|
||||
fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e>
|
||||
where
|
||||
'a: 'e,
|
||||
'b: 'e,
|
||||
'a: 'd,
|
||||
{
|
||||
// We return a value:
|
||||
//
|
||||
// ```
|
||||
// 'a: '0
|
||||
// 'b: '1
|
||||
// '0 in ['d, 'e]
|
||||
// ```
|
||||
//
|
||||
// but we don't have it.
|
||||
//
|
||||
// We are forced to pick that '0 = 'e, because only 'e is outlived by *both* 'a and 'b.
|
||||
let p = if condition() { a } else { b };
|
||||
p
|
||||
}
|
||||
|
||||
fn condition() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,38 @@
|
||||
// edition:2018
|
||||
|
||||
#![feature(arbitrary_self_types, async_await, await_macro, pin)]
|
||||
|
||||
trait Trait<'a, 'b> {}
|
||||
impl<T> Trait<'_, '_> for T {}
|
||||
|
||||
// `Ordinary<'a> <: Ordinary<'b>` if `'a: 'b`, as with most types.
|
||||
//
|
||||
// I am purposefully avoiding the terms co- and contra-variant because
|
||||
// their application to regions depends on how you interpreted Rust
|
||||
// regions. -nikomatsakis
|
||||
struct Ordinary<'a>(&'a u8);
|
||||
|
||||
// Here we get an error because none of our choices (either `'d` nor `'e`) are outlived
|
||||
// by both `'a` and `'b`.
|
||||
|
||||
fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e>
|
||||
//~^ ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds
|
||||
where
|
||||
'a: 'e,
|
||||
'b: 'd,
|
||||
{
|
||||
// Hidden type `Ordinary<'0>` with constraints:
|
||||
//
|
||||
// ```
|
||||
// 'a: '0
|
||||
// 'b: '0
|
||||
// 'a in ['d, 'e]
|
||||
// ```
|
||||
if condition() { a } else { b }
|
||||
}
|
||||
|
||||
fn condition() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,12 @@
|
||||
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
|
||||
--> $DIR/ordinary-bounds-unrelated.rs:18:74
|
||||
|
|
||||
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e>
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: hidden type `Ordinary<'_>` captures `ReScope(CallSite(24))`
|
||||
= note: this is likely a bug in the compiler, please file an issue on github
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0700`.
|
@ -0,0 +1,41 @@
|
||||
// edition:2018
|
||||
|
||||
#![feature(arbitrary_self_types, async_await, await_macro, pin)]
|
||||
|
||||
trait Trait<'a, 'b> {}
|
||||
impl<T> Trait<'_, '_> for T {}
|
||||
|
||||
// `Ordinary<'a> <: Ordinary<'b>` if `'a: 'b`, as with most types.
|
||||
//
|
||||
// I am purposefully avoiding the terms co- and contra-variant because
|
||||
// their application to regions depends on how you interpreted Rust
|
||||
// regions. -nikomatsakis
|
||||
struct Ordinary<'a>(&'a u8);
|
||||
|
||||
// Here we need something outlived by `'a` *and* outlived by `'b`, but
|
||||
// we can only name `'a` and `'b` (and neither suits). So we get an
|
||||
// error. Somewhat unfortunate, though, since the caller would have to
|
||||
// consider the loans for both `'a` and `'b` alive.
|
||||
|
||||
fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b>
|
||||
//~^ ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds
|
||||
{
|
||||
// We return a value:
|
||||
//
|
||||
// ```
|
||||
// 'a: '0
|
||||
// 'b: '1
|
||||
// '0 in ['d, 'e]
|
||||
// ```
|
||||
//
|
||||
// but we don't have it.
|
||||
//
|
||||
// We are forced to pick that '0 = 'e, because only 'e is outlived by *both* 'a and 'b.
|
||||
if condition() { a } else { b }
|
||||
}
|
||||
|
||||
fn condition() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,12 @@
|
||||
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
|
||||
--> $DIR/ordinary-bounds-unsuited.rs:20:62
|
||||
|
|
||||
LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b>
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: hidden type `Ordinary<'_>` captures `ReScope(CallSite(24))`
|
||||
= note: this is likely a bug in the compiler, please file an issue on github
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0700`.
|
Loading…
x
Reference in New Issue
Block a user