Auto merge of #61775 - nikomatsakis:issue-56238-multiple-lifetimes-async-fn-region-solver, r=MatthewJasper

generalize impl trait to permit multiple lifetime bounds

Generalizes the region solver to support "pick constraints". These have the form:

```
pick R0 from [R1..Rn]
```

where `R1..Rn` are called the "option regions". The idea is that `R0` must be equal to *some* region in the set `R1..Rn`. These constraints are then used to handle cases like this:

```rust
fn foo<'a, 'b>(...) -> impl Trait<'a, 'b> { .. }
```

The problem here is that every region R in the hidden type must be equal to *either* `'a` *or* `'b` (or `'static`) -- in the past, the only kinds of constraints we had were outlives constraints, and since `'a` and `'b` are unrelated, there was no outlives constraint we could issue that would enforce that (`R: 'a` and `R: 'b` are both too strict, for example). But now we can issue a pick constraint: `pick R from ['a, 'b]`.

In general, solving pick constraints is tricky. We integrate them into the solver as follows. In general, during the propagation phase, we are monotonically growing a set of inference regions. To handle a case like `pick R from [O...]`, where `O...` represents the option regions, we do the following:

- Look for all the *lower bounds* of the region R -- that is, every region LB such that `R: LB` must hold.
- Look for all the *upper bounds* of the region R -- that is, every region UB such that `UB: R` must hold.
- Let the *viable options* be each option region O such that `UB: O` and `O: LB` for each UB, LB bound.
- Find the *minimal viable option* M, where `O: M` holds for every option region O.

If there is such a *minimal viable option*, then we make `R: M`. (This may in turn influence other bits of inference.) If there is no minimal viable option, either because all options were eliminated or because none of the remaining options are minimal, we do nothing. Ultimately, if the pick constraint is not satisfied, an error is reported.

For this logic, we currently require that the option regions O are always lifetime parameters. To determine the bounds, we walk the various outlives edges that were otherwise introduced.

r? @matthewjasper
cc @cramertj

Fixes #56238

TODO:

- [ ] Error messages include region variable info sometimes, how to fix?
- [ ] Tests for bare `existential type`  and other impl Trait usage
This commit is contained in:
bors 2019-07-03 03:47:47 +00:00
commit 8301de16da
71 changed files with 2581 additions and 549 deletions

View File

@ -0,0 +1,29 @@
# `member_constraints`
The tracking issue for this feature is: [#61977]
[#61977]: https://github.com/rust-lang/rust/issues/61977
------------------------
The `member_constraints` feature gate lets you use `impl Trait` syntax with
multiple unrelated lifetime parameters.
A simple example is:
```rust
#![feature(member_constraints)]
trait Trait<'a, 'b> { }
impl<T> Trait<'_, '_> for T {}
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> {
(x, y)
}
fn main() { }
```
Without the `member_constraints` feature gate, the above example is an
error because both `'a` and `'b` appear in the impl Trait bounds, but
neither outlives the other.

View File

@ -23,6 +23,7 @@
use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind};
use crate::infer::region_constraints::MemberConstraint;
use crate::mir::interpret::ConstValue;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_macros::HashStable;
@ -189,11 +190,25 @@ pub enum CanonicalTyVarKind {
#[derive(Clone, Debug, HashStable)]
pub struct QueryResponse<'tcx, R> {
pub var_values: CanonicalVarValues<'tcx>,
pub region_constraints: Vec<QueryRegionConstraint<'tcx>>,
pub region_constraints: QueryRegionConstraints<'tcx>,
pub certainty: Certainty,
pub value: R,
}
#[derive(Clone, Debug, Default, HashStable)]
pub struct QueryRegionConstraints<'tcx> {
pub outlives: Vec<QueryOutlivesConstraint<'tcx>>,
pub member_constraints: Vec<MemberConstraint<'tcx>>,
}
impl QueryRegionConstraints<'_> {
/// Represents an empty (trivially true) set of region
/// constraints.
pub fn is_empty(&self) -> bool {
self.outlives.is_empty() && self.member_constraints.is_empty()
}
}
pub type Canonicalized<'tcx, V> = Canonical<'tcx, V>;
pub type CanonicalizedQueryResponse<'tcx, T> =
@ -292,7 +307,8 @@ impl<'tcx, V> Canonical<'tcx, V> {
}
}
pub type QueryRegionConstraint<'tcx> = ty::Binder<ty::OutlivesPredicate<Kind<'tcx>, Region<'tcx>>>;
pub type QueryOutlivesConstraint<'tcx> =
ty::Binder<ty::OutlivesPredicate<Kind<'tcx>, Region<'tcx>>>;
impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
/// Creates a substitution S for the canonical value with fresh
@ -540,6 +556,19 @@ BraceStructLiftImpl! {
} where R: Lift<'tcx>
}
BraceStructTypeFoldableImpl! {
impl<'tcx> TypeFoldable<'tcx> for QueryRegionConstraints<'tcx> {
outlives, member_constraints
}
}
BraceStructLiftImpl! {
impl<'a, 'tcx> Lift<'tcx> for QueryRegionConstraints<'a> {
type Lifted = QueryRegionConstraints<'tcx>;
outlives, member_constraints
}
}
impl<'tcx> Index<BoundVar> for CanonicalVarValues<'tcx> {
type Output = Kind<'tcx>;

View File

@ -11,7 +11,7 @@ use crate::arena::ArenaAllocatable;
use crate::infer::canonical::substitute::substitute_value;
use crate::infer::canonical::{
Canonical, CanonicalVarValues, CanonicalizedQueryResponse, Certainty,
OriginalQueryValues, QueryRegionConstraint, QueryResponse,
OriginalQueryValues, QueryRegionConstraints, QueryOutlivesConstraint, QueryResponse,
};
use crate::infer::region_constraints::{Constraint, RegionConstraintData};
use crate::infer::InferCtxtBuilder;
@ -132,7 +132,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
{
self.canonicalize_response(&QueryResponse {
var_values: inference_vars,
region_constraints: vec![],
region_constraints: QueryRegionConstraints::default(),
certainty: Certainty::Proven, // Ambiguities are OK!
value: answer,
})
@ -174,7 +174,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
let region_obligations = self.take_registered_region_obligations();
let region_constraints = self.with_region_constraints(|region_constraints| {
make_query_outlives(
make_query_region_constraints(
tcx,
region_obligations
.iter()
@ -222,10 +222,10 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
mut obligations,
} = self.query_response_substitution(cause, param_env, original_values, query_response)?;
obligations.extend(self.query_region_constraints_into_obligations(
obligations.extend(self.query_outlives_constraints_into_obligations(
cause,
param_env,
&query_response.value.region_constraints,
&query_response.value.region_constraints.outlives,
&result_subst,
));
@ -248,9 +248,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
/// that come out of these queries, which it wants to convert into
/// MIR-based constraints and solve. Therefore, it is most
/// convenient for the NLL Type Checker to **directly consume**
/// the `QueryRegionConstraint` values that arise from doing a
/// the `QueryOutlivesConstraint` values that arise from doing a
/// query. This is contrast to other parts of the compiler, which
/// would prefer for those `QueryRegionConstraint` to be converted
/// would prefer for those `QueryOutlivesConstraint` to be converted
/// into the older infcx-style constraints (e.g., calls to
/// `sub_regions` or `register_region_obligation`).
///
@ -263,7 +263,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
/// result. If any errors arise, they are propagated back as an
/// `Err` result.
/// - In the case of a successful substitution, we will append
/// `QueryRegionConstraint` values onto the
/// `QueryOutlivesConstraint` values onto the
/// `output_query_region_constraints` vector for the solver to
/// use (if an error arises, some values may also be pushed, but
/// they should be ignored).
@ -279,7 +279,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
param_env: ty::ParamEnv<'tcx>,
original_values: &OriginalQueryValues<'tcx>,
query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>,
output_query_region_constraints: &mut Vec<QueryRegionConstraint<'tcx>>,
output_query_region_constraints: &mut QueryRegionConstraints<'tcx>,
) -> InferResult<'tcx, R>
where
R: Debug + TypeFoldable<'tcx>,
@ -287,7 +287,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
let result_subst =
self.query_response_substitution_guess(cause, original_values, query_response);
// Compute `QueryRegionConstraint` values that unify each of
// Compute `QueryOutlivesConstraint` values that unify each of
// the original values `v_o` that was canonicalized into a
// variable...
let mut obligations = vec![];
@ -306,8 +306,10 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
// To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`.
if v_o != v_r {
output_query_region_constraints
.outlives
.push(ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r)));
output_query_region_constraints
.outlives
.push(ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o)));
}
}
@ -333,12 +335,12 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
}
// ...also include the other query region constraints from the query.
output_query_region_constraints.extend(
query_response.value.region_constraints.iter().filter_map(|r_c| {
output_query_region_constraints.outlives.extend(
query_response.value.region_constraints.outlives.iter().filter_map(|r_c| {
let r_c = substitute_value(self.tcx, &result_subst, r_c);
// Screen out `'a: 'a` cases -- we skip the binder here but
// only care the inner values to one another, so they are still at
// only compare the inner values to one another, so they are still at
// consistent binding levels.
let &ty::OutlivesPredicate(k1, r2) = r_c.skip_binder();
if k1 != r2.into() {
@ -349,6 +351,13 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
})
);
// ...also include the query member constraints.
output_query_region_constraints.member_constraints.extend(
query_response.value.region_constraints.member_constraints.iter().map(|p_c| {
substitute_value(self.tcx, &result_subst, p_c)
})
);
let user_result: R =
query_response.substitute_projected(self.tcx, &result_subst, |q_r| &q_r.value);
@ -560,11 +569,11 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
/// Converts the region constraints resulting from a query into an
/// iterator of obligations.
fn query_region_constraints_into_obligations<'a>(
fn query_outlives_constraints_into_obligations<'a>(
&'a self,
cause: &'a ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
unsubstituted_region_constraints: &'a [QueryRegionConstraint<'tcx>],
unsubstituted_region_constraints: &'a [QueryOutlivesConstraint<'tcx>],
result_subst: &'a CanonicalVarValues<'tcx>,
) -> impl Iterator<Item = PredicateObligation<'tcx>> + 'a + Captures<'tcx> {
unsubstituted_region_constraints
@ -645,15 +654,16 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
/// Given the region obligations and constraints scraped from the infcx,
/// creates query region constraints.
pub fn make_query_outlives<'tcx>(
pub fn make_query_region_constraints<'tcx>(
tcx: TyCtxt<'tcx>,
outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>)>,
region_constraints: &RegionConstraintData<'tcx>,
) -> Vec<QueryRegionConstraint<'tcx>> {
) -> QueryRegionConstraints<'tcx> {
let RegionConstraintData {
constraints,
verifys,
givens,
member_constraints,
} = region_constraints;
assert!(verifys.is_empty());
@ -684,5 +694,5 @@ pub fn make_query_outlives<'tcx>(
)
.collect();
outlives
QueryRegionConstraints { outlives, member_constraints: member_constraints.clone() }
}

View File

@ -53,6 +53,7 @@ use crate::infer::{self, SuppressRegionErrors};
use crate::hir;
use crate::hir::def_id::DefId;
use crate::hir::Node;
use crate::infer::opaque_types;
use crate::middle::region;
use crate::traits::{ObligationCause, ObligationCauseCode};
use crate::ty::error::TypeError;
@ -375,6 +376,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
);
}
}
RegionResolutionError::MemberConstraintFailure {
opaque_type_def_id,
hidden_ty,
member_region,
span: _,
choice_regions: _,
} => {
let hidden_ty = self.resolve_vars_if_possible(&hidden_ty);
opaque_types::unexpected_hidden_region_diagnostic(
self.tcx,
Some(region_scope_tree),
opaque_type_def_id,
hidden_ty,
member_region,
).emit();
}
}
}
}
@ -411,7 +429,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e {
RegionResolutionError::GenericBoundFailure(..) => true,
RegionResolutionError::ConcreteFailure(..)
| RegionResolutionError::SubSupConflict(..) => false,
| RegionResolutionError::SubSupConflict(..)
| RegionResolutionError::MemberConstraintFailure { .. } => false,
};
let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
@ -429,6 +448,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(),
RegionResolutionError::MemberConstraintFailure { span, .. } => span,
});
errors
}

View File

@ -1,13 +1,20 @@
//! Lexical region resolution.
use crate::hir::def_id::DefId;
use crate::infer::region_constraints::Constraint;
use crate::infer::region_constraints::GenericKind;
use crate::infer::region_constraints::MemberConstraint;
use crate::infer::region_constraints::RegionConstraintData;
use crate::infer::region_constraints::VarInfos;
use crate::infer::region_constraints::VerifyBound;
use crate::infer::RegionVariableOrigin;
use crate::infer::SubregionOrigin;
use crate::middle::free_region::RegionRelations;
use crate::ty::fold::TypeFoldable;
use crate::ty::{self, Ty, TyCtxt};
use crate::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic};
use crate::ty::{ReLateBound, RePlaceholder, ReScope, ReVar};
use crate::ty::{Region, RegionVid};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::implementation::{
Direction, Graph, NodeIndex, INCOMING, OUTGOING,
@ -15,12 +22,7 @@ use rustc_data_structures::graph::implementation::{
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use smallvec::SmallVec;
use std::fmt;
use std::u32;
use crate::ty::fold::TypeFoldable;
use crate::ty::{self, Ty, TyCtxt};
use crate::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic};
use crate::ty::{ReLateBound, ReScope, RePlaceholder, ReVar};
use crate::ty::{Region, RegionVid};
use syntax_pos::Span;
mod graphviz;
@ -36,11 +38,7 @@ pub fn resolve<'tcx>(
) -> (LexicalRegionResolutions<'tcx>, Vec<RegionResolutionError<'tcx>>) {
debug!("RegionConstraintData: resolve_regions()");
let mut errors = vec![];
let mut resolver = LexicalResolver {
region_rels,
var_infos,
data,
};
let mut resolver = LexicalResolver { region_rels, var_infos, data };
let values = resolver.infer_variable_values(&mut errors);
(values, errors)
}
@ -84,6 +82,17 @@ pub enum RegionResolutionError<'tcx> {
SubregionOrigin<'tcx>,
Region<'tcx>,
),
/// Indicates a failure of a `MemberConstraint`. These arise during
/// impl trait processing explicitly -- basically, the impl trait's hidden type
/// included some region that it was not supposed to.
MemberConstraintFailure {
span: Span,
opaque_type_def_id: DefId,
hidden_ty: Ty<'tcx>,
member_region: Region<'tcx>,
choice_regions: Vec<Region<'tcx>>,
},
}
struct RegionAndOrigin<'tcx> {
@ -121,7 +130,12 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
let graph = self.construct_graph();
self.expand_givens(&graph);
self.expansion(&mut var_data);
loop {
self.expansion(&mut var_data);
if !self.enforce_member_constraints(&graph, &mut var_data) {
break;
}
}
self.collect_errors(&mut var_data, errors);
self.collect_var_errors(&var_data, &graph, errors);
var_data
@ -136,7 +150,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
fn construct_var_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> {
LexicalRegionResolutions {
error_region: tcx.lifetimes.re_static,
values: IndexVec::from_elem_n(VarValue::Value(tcx.lifetimes.re_empty), self.num_vars())
values: IndexVec::from_elem_n(VarValue::Value(tcx.lifetimes.re_empty), self.num_vars()),
}
}
@ -182,6 +196,113 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}
}
/// Enforce all member constraints and return true if anything
/// changed. See `enforce_member_constraint` for more details.
fn enforce_member_constraints(
&self,
graph: &RegionGraph<'tcx>,
var_values: &mut LexicalRegionResolutions<'tcx>,
) -> bool {
// Note: we don't use the `any` combinator because we don't
// want to stop at the first constraint that makes a change.
let mut any_changed = false;
for member_constraint in &self.data.member_constraints {
if self.enforce_member_constraint(graph, member_constraint, var_values) {
any_changed = true;
}
}
any_changed
}
/// Enforce a constraint like
///
/// ```
/// 'r member of ['c...]
/// ```
///
/// We look for all choice regions from the list `'c...` that:
///
/// (a) are greater than the current value of `'r` (which is a lower bound)
///
/// and
///
/// (b) are compatible with the upper bounds of `'r` that we can
/// find by traversing the graph.
///
/// From that list, we look for a *minimal* option `'c_min`. If we
/// find one, then we can enforce that `'r: 'c_min`.
fn enforce_member_constraint(
&self,
graph: &RegionGraph<'tcx>,
member_constraint: &MemberConstraint<'tcx>,
var_values: &mut LexicalRegionResolutions<'tcx>,
) -> bool {
debug!("enforce_member_constraint(member_constraint={:#?})", member_constraint);
// The constraint is some inference variable (`vid`) which
// must be equal to one of the options.
let member_vid = match member_constraint.member_region {
ty::ReVar(vid) => *vid,
_ => return false,
};
// The current value of `vid` is a lower bound LB -- i.e., we
// know that `LB <= vid` must be true.
let member_lower_bound: ty::Region<'tcx> = match var_values.value(member_vid) {
VarValue::ErrorValue => return false,
VarValue::Value(r) => r,
};
// Find all the "upper bounds" -- that is, each region `b` such that
// `r0 <= b` must hold.
let (member_upper_bounds, _) = self.collect_concrete_regions(
graph,
member_vid,
OUTGOING,
None,
);
// Get an iterator over the *available choice* -- that is,
// each choice region `c` where `lb <= c` and `c <= ub` for all the
// upper bounds `ub`.
debug!("enforce_member_constraint: upper_bounds={:#?}", member_upper_bounds);
let mut options = member_constraint.choice_regions.iter().filter(|option| {
self.sub_concrete_regions(member_lower_bound, option)
&& member_upper_bounds
.iter()
.all(|upper_bound| self.sub_concrete_regions(option, upper_bound.region))
});
// If there is more than one 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: ty::Region<'tcx> = match options.next() {
Some(&r) => r,
None => return false,
};
debug!("enforce_member_constraint: least_choice={:?}", least_choice);
for &option in options {
debug!("enforce_member_constraint: option={:?}", option);
if !self.sub_concrete_regions(least_choice, option) {
if self.sub_concrete_regions(option, least_choice) {
debug!("enforce_member_constraint: new least choice");
least_choice = option;
} else {
debug!("enforce_member_constraint: no least choice");
return false;
}
}
}
debug!("enforce_member_constraint: final least choice = {:?}", least_choice);
if least_choice != member_lower_bound {
*var_values.value_mut(member_vid) = VarValue::Value(least_choice);
true
} else {
false
}
}
fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) {
self.iterate_until_fixed_point("Expansion", |constraint| {
debug!("expansion: constraint={:?}", constraint);
@ -196,7 +317,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
let b_data = var_values.value_mut(b_vid);
let retain = match *b_data {
VarValue::Value(ReStatic) | VarValue::ErrorValue => false,
_ => true
_ => true,
};
(a_region, b_vid, b_data, retain)
}
@ -204,7 +325,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
Constraint::RegSubReg(..) | Constraint::VarSubReg(..) => {
// These constraints are checked after expansion
// is done, in `collect_errors`.
return (false, false)
return (false, false);
}
};
@ -226,16 +347,16 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
match *a_region {
// Check if this relationship is implied by a given.
ty::ReEarlyBound(_) | ty::ReFree(_) => if self.data.givens.contains(&(a_region, b_vid))
{
debug!("given");
return false;
},
ty::ReEarlyBound(_) | ty::ReFree(_) => {
if self.data.givens.contains(&(a_region, b_vid)) {
debug!("given");
return false;
}
}
_ => {}
}
match *b_data {
VarValue::Value(cur_region) => {
// Identical scopes can show up quite often, if the fixed point
@ -267,10 +388,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}
}
debug!(
"Expanding value of {:?} from {:?} to {:?}",
b_vid, cur_region, lub
);
debug!("Expanding value of {:?} from {:?} to {:?}", b_vid, cur_region, lub);
*b_data = VarValue::Value(lub);
return true;
@ -282,6 +400,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();
@ -321,17 +445,16 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// at least as big as fr.scope". So, we can
// reasonably compare free regions and scopes:
let fr_scope = match (a, b) {
(&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => self.region_rels
.region_scope_tree
.early_free_scope(self.tcx(), br),
(&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => self.region_rels
.region_scope_tree
.free_scope(self.tcx(), fr),
(&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => {
self.region_rels.region_scope_tree.early_free_scope(self.tcx(), br)
}
(&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => {
self.region_rels.region_scope_tree.free_scope(self.tcx(), fr)
}
_ => bug!(),
};
let r_id = self.region_rels
.region_scope_tree
.nearest_common_ancestor(fr_scope, s_id);
let r_id =
self.region_rels.region_scope_tree.nearest_common_ancestor(fr_scope, s_id);
if r_id == fr_scope {
// if the free region's scope `fr.scope` is bigger than
// the scope region `s_id`, then the LUB is the free
@ -352,9 +475,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// The region corresponding to an outer block is a
// subtype of the region corresponding to an inner
// block.
let lub = self.region_rels
.region_scope_tree
.nearest_common_ancestor(a_id, b_id);
let lub = self.region_rels.region_scope_tree.nearest_common_ancestor(a_id, b_id);
tcx.mk_region(ReScope(lub))
}
@ -365,11 +486,13 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// For these types, we cannot define any additional
// relationship:
(&RePlaceholder(..), _) | (_, &RePlaceholder(..)) => if a == b {
a
} else {
tcx.lifetimes.re_static
},
(&RePlaceholder(..), _) | (_, &RePlaceholder(..)) => {
if a == b {
a
} else {
tcx.lifetimes.re_static
}
}
}
}
@ -382,10 +505,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
errors: &mut Vec<RegionResolutionError<'tcx>>,
) {
for (constraint, origin) in &self.data.constraints {
debug!(
"collect_errors: constraint={:?} origin={:?}",
constraint, origin
);
debug!("collect_errors: constraint={:?} origin={:?}", constraint, origin);
match *constraint {
Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => {
// Expansion will ensure that these constraints hold. Ignore.
@ -433,6 +553,25 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}
}
// Check that all member constraints are satisfied.
for member_constraint in &self.data.member_constraints {
let member_region = var_data.normalize(self.tcx(), member_constraint.member_region);
let choice_regions = member_constraint
.choice_regions
.iter()
.map(|&choice_region| var_data.normalize(self.tcx(), choice_region));
if !choice_regions.clone().any(|choice_region| member_region == choice_region) {
let span = self.tcx().def_span(member_constraint.opaque_type_def_id);
errors.push(RegionResolutionError::MemberConstraintFailure {
span,
opaque_type_def_id: member_constraint.opaque_type_def_id,
hidden_ty: member_constraint.hidden_ty,
member_region,
choice_regions: choice_regions.collect(),
});
}
}
for verify in &self.data.verifys {
debug!("collect_errors: verify={:?}", verify);
let sub = var_data.normalize(self.tcx(), verify.region);
@ -483,34 +622,35 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// idea is to report errors that derive from independent
// regions of the graph, but not those that derive from
// overlapping locations.
let mut dup_vec = vec![u32::MAX; self.num_vars()];
let mut dup_vec = IndexVec::from_elem_n(None, self.num_vars());
for (node_vid, value) in var_data.values.iter_enumerated() {
match *value {
VarValue::Value(_) => { /* Inference successful */ }
VarValue::ErrorValue => {
/* Inference impossible: this value contains
inconsistent constraints.
I think that in this case we should report an
error now -- unlike the case above, we can't
wait to see whether the user needs the result
of this variable. The reason is that the mere
existence of this variable implies that the
region graph is inconsistent, whether or not it
is used.
For example, we may have created a region
variable that is the GLB of two other regions
which do not have a GLB. Even if that variable
is not used, it implies that those two regions
*should* have a GLB.
At least I think this is true. It may be that
the mere existence of a conflict in a region variable
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. =) */
// Inference impossible: this value contains
// inconsistent constraints.
//
// I think that in this case we should report an
// error now -- unlike the case above, we can't
// wait to see whether the user needs the result
// of this variable. The reason is that the mere
// existence of this variable implies that the
// region graph is inconsistent, whether or not it
// is used.
//
// For example, we may have created a region
// variable that is the GLB of two other regions
// which do not have a GLB. Even if that variable
// is not used, it implies that those two regions
// *should* have a GLB.
//
// At least I think this is true. It may be that
// the mere existence of a conflict in a region
// variable 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. =) -- nikomatsakis
self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors);
}
}
@ -562,16 +702,16 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
fn collect_error_for_expanding_node(
&self,
graph: &RegionGraph<'tcx>,
dup_vec: &mut [u32],
dup_vec: &mut IndexVec<RegionVid, Option<RegionVid>>,
node_idx: RegionVid,
errors: &mut Vec<RegionResolutionError<'tcx>>,
) {
// Errors in expanding nodes result from a lower-bound that is
// not contained by an upper-bound.
let (mut lower_bounds, lower_dup) =
self.collect_concrete_regions(graph, node_idx, INCOMING, dup_vec);
self.collect_concrete_regions(graph, node_idx, INCOMING, Some(dup_vec));
let (mut upper_bounds, upper_dup) =
self.collect_concrete_regions(graph, node_idx, OUTGOING, dup_vec);
self.collect_concrete_regions(graph, node_idx, OUTGOING, Some(dup_vec));
if lower_dup || upper_dup {
return;
@ -604,9 +744,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
};
for upper_bound in &upper_bounds {
if !self.region_rels
.is_subregion_of(effective_lower_bound, upper_bound.region)
{
if !self.region_rels.is_subregion_of(effective_lower_bound, upper_bound.region) {
let origin = self.var_infos[node_idx].origin.clone();
debug!(
"region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \
@ -643,7 +781,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
graph: &RegionGraph<'tcx>,
orig_node_idx: RegionVid,
dir: Direction,
dup_vec: &mut [u32],
mut dup_vec: Option<&mut IndexVec<RegionVid, Option<RegionVid>>>,
) -> (Vec<RegionAndOrigin<'tcx>>, bool) {
struct WalkState<'tcx> {
set: FxHashSet<RegionVid>,
@ -667,23 +805,23 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
let node_idx = state.stack.pop().unwrap();
// check whether we've visited this node on some previous walk
if dup_vec[node_idx.index() as usize] == u32::MAX {
dup_vec[node_idx.index() as usize] = orig_node_idx.index() as u32;
} else if dup_vec[node_idx.index() as usize] != orig_node_idx.index() as u32 {
state.dup_found = true;
}
if let Some(dup_vec) = &mut dup_vec {
if dup_vec[node_idx].is_none() {
dup_vec[node_idx] = Some(orig_node_idx);
} else if dup_vec[node_idx] != Some(orig_node_idx) {
state.dup_found = true;
}
debug!(
"collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})",
orig_node_idx, node_idx
);
debug!(
"collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})",
orig_node_idx, node_idx
);
}
process_edges(&self.data, &mut state, graph, node_idx, dir);
}
let WalkState {
result, dup_found, ..
} = state;
let WalkState { result, dup_found, .. } = state;
return (result, dup_found);
fn process_edges<'tcx>(
@ -699,11 +837,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
for (_, edge) in graph.adjacent_edges(source_node_index, dir) {
match edge.data {
Constraint::VarSubVar(from_vid, to_vid) => {
let opp_vid = if from_vid == source_vid {
to_vid
} else {
from_vid
};
let opp_vid = if from_vid == source_vid { to_vid } else { from_vid };
if state.set.insert(opp_vid) {
state.stack.push(opp_vid);
}
@ -726,7 +860,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}
fn iterate_until_fixed_point<F>(&self, tag: &str, mut body: F)
where F: FnMut(&Constraint<'tcx>) -> (bool, bool),
where
F: FnMut(&Constraint<'tcx>) -> (bool, bool),
{
let mut constraints: SmallVec<[_; 16]> = self.data.constraints.keys().collect();
let mut iteration = 0;
@ -760,17 +895,17 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
&& self.bound_is_met(b, var_values, generic_ty, min)
}
VerifyBound::OutlivedBy(r) =>
self.region_rels.is_subregion_of(
min,
var_values.normalize(self.tcx(), r),
),
VerifyBound::OutlivedBy(r) => {
self.region_rels.is_subregion_of(min, var_values.normalize(self.tcx(), r))
}
VerifyBound::AnyBound(bs) => bs.iter()
.any(|b| self.bound_is_met(b, var_values, generic_ty, min)),
VerifyBound::AnyBound(bs) => {
bs.iter().any(|b| self.bound_is_met(b, var_values, generic_ty, min))
}
VerifyBound::AllBounds(bs) => bs.iter()
.all(|b| self.bound_is_met(b, var_values, generic_ty, min)),
VerifyBound::AllBounds(bs) => {
bs.iter().all(|b| self.bound_is_met(b, var_values, generic_ty, min))
}
}
}
}

View File

@ -26,6 +26,7 @@ use crate::ty::{FloatVid, IntVid, TyVid, ConstVid};
use crate::util::nodemap::FxHashMap;
use errors::DiagnosticBuilder;
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::unify as ut;
use std::cell::{Cell, Ref, RefCell, RefMut};
use std::collections::BTreeMap;
@ -904,6 +905,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
.make_subregion(origin, a, b);
}
/// Require that the region `r` be equal to one of the regions in
/// the set `regions`.
pub fn member_constraint(
&self,
opaque_type_def_id: DefId,
definition_span: Span,
hidden_ty: Ty<'tcx>,
region: ty::Region<'tcx>,
in_regions: &Lrc<Vec<ty::Region<'tcx>>>,
) {
debug!("member_constraint({:?} <: {:?})", region, in_regions);
self.borrow_region_constraints()
.member_constraint(opaque_type_def_id, definition_span, hidden_ty, region, in_regions);
}
pub fn subtype_predicate(
&self,
cause: &ObligationCause<'tcx>,

View File

@ -1,16 +1,19 @@
use rustc_data_structures::fx::FxHashMap;
use syntax_pos::Span;
use crate::hir::def_id::DefId;
use crate::hir;
use crate::hir::def_id::DefId;
use crate::hir::Node;
use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::outlives::free_region_map::FreeRegionRelations;
use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin, TypeVariableOriginKind};
use crate::middle::region;
use crate::traits::{self, PredicateObligation};
use crate::ty::{self, Ty, TyCtxt, GenericParamDefKind};
use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor};
use crate::ty::subst::{Kind, InternalSubsts, SubstsRef, UnpackedKind};
use crate::ty::subst::{InternalSubsts, Kind, SubstsRef, UnpackedKind};
use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt};
use crate::util::nodemap::DefIdMap;
use errors::DiagnosticBuilder;
use rustc::session::config::nightly_options;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use syntax_pos::Span;
pub type OpaqueTypeMap<'tcx> = DefIdMap<OpaqueTypeDecl<'tcx>>;
@ -32,6 +35,20 @@ pub struct OpaqueTypeDecl<'tcx> {
/// then `substs` would be `['a, T]`.
pub substs: SubstsRef<'tcx>,
/// The span of this particular definition of the opaque type. So
/// for example:
///
/// ```
/// existential type Foo;
/// fn bar() -> Foo {
/// ^^^ This is the span we are looking for!
/// ```
///
/// In cases where the fn returns `(impl Trait, impl Trait)` or
/// other such combinations, the result is currently
/// over-approximated, but better than nothing.
pub definition_span: Span,
/// The type variable that represents the value of the abstract type
/// that we require. In other words, after we compile this function,
/// we will be created a constraint like:
@ -98,30 +115,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
/// - `param_env` -- the in-scope parameter environment to be used for
/// obligations
/// - `value` -- the value within which we are instantiating opaque types
/// - `value_span` -- the span where the value came from, used in error reporting
pub fn instantiate_opaque_types<T: TypeFoldable<'tcx>>(
&self,
parent_def_id: DefId,
body_id: hir::HirId,
param_env: ty::ParamEnv<'tcx>,
value: &T,
value_span: Span,
) -> InferOk<'tcx, (T, OpaqueTypeMap<'tcx>)> {
debug!("instantiate_opaque_types(value={:?}, parent_def_id={:?}, body_id={:?}, \
param_env={:?})",
value, parent_def_id, body_id, param_env,
debug!(
"instantiate_opaque_types(value={:?}, parent_def_id={:?}, body_id={:?}, \
param_env={:?})",
value, parent_def_id, body_id, param_env,
);
let mut instantiator = Instantiator {
infcx: self,
parent_def_id,
body_id,
param_env,
value_span,
opaque_types: Default::default(),
obligations: vec![],
};
let value = instantiator.instantiate_opaque_types_in_map(value);
InferOk {
value: (value, instantiator.opaque_types),
obligations: instantiator.obligations,
}
InferOk { value: (value, instantiator.opaque_types), obligations: instantiator.obligations }
}
/// Given the map `opaque_types` containing the existential `impl
@ -216,13 +234,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
///
/// # The Solution
///
/// We make use of the constraint that we *do* have in the `<=`
/// relation. To do that, we find the "minimum" of all the
/// arguments that appear in the substs: that is, some region
/// which is less than all the others. In the case of `Foo1<'a>`,
/// that would be `'a` (it's the only choice, after all). Then we
/// apply that as a least bound to the variables (e.g., `'a <=
/// '0`).
/// We generally prefer to make `<=` constraints, since they
/// integrate best into the region solver. To do that, we find the
/// "minimum" of all the arguments that appear in the substs: that
/// is, some region which is less than all the others. In the case
/// of `Foo1<'a>`, that would be `'a` (it's the only choice, after
/// all). Then we apply that as a least bound to the variables
/// (e.g., `'a <= '0`).
///
/// In some cases, there is no minimum. Consider this example:
///
@ -230,8 +248,32 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
/// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... }
/// ```
///
/// Here we would report an error, because `'a` and `'b` have no
/// relation to one another.
/// Here we would report a more complex "in constraint", like `'r
/// in ['a, 'b, 'static]` (where `'r` is some regon appearing in
/// the hidden type).
///
/// # Constrain regions, not the hidden concrete type
///
/// Note that generating constraints on each region `Rc` is *not*
/// the same as generating an outlives constraint on `Tc` iself.
/// For example, if we had a function like this:
///
/// ```rust
/// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> {
/// (x, y)
/// }
///
/// // Equivalent to:
/// existential type FooReturn<'a, T>: Foo<'a>;
/// fn foo<'a, T>(..) -> FooReturn<'a, T> { .. }
/// ```
///
/// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0`
/// is an inference variable). If we generated a constraint that
/// `Tc: 'a`, then this would incorrectly require that `T: 'a` --
/// but this is not necessary, because the existential type we
/// create will be allowed to reference `T`. So we only generate a
/// constraint that `'0: 'a`.
///
/// # The `free_region_relations` parameter
///
@ -274,6 +316,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
}
/// See `constrain_opaque_types` for documentation.
pub fn constrain_opaque_type<FRR: FreeRegionRelations<'tcx>>(
&self,
def_id: DefId,
@ -290,32 +333,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
debug!("constrain_opaque_type: concrete_ty={:?}", concrete_ty);
let abstract_type_generics = tcx.generics_of(def_id);
let opaque_type_generics = tcx.generics_of(def_id);
let span = tcx.def_span(def_id);
// If there are required region bounds, we can use them.
if opaque_defn.has_required_region_bounds {
let predicates_of = tcx.predicates_of(def_id);
debug!(
"constrain_opaque_type: predicates: {:#?}",
predicates_of,
);
debug!("constrain_opaque_type: predicates: {:#?}", predicates_of,);
let bounds = predicates_of.instantiate(tcx, opaque_defn.substs);
debug!("constrain_opaque_type: bounds={:#?}", bounds);
let opaque_type = tcx.mk_opaque(def_id, opaque_defn.substs);
let required_region_bounds = tcx.required_region_bounds(
opaque_type,
bounds.predicates,
);
let required_region_bounds = tcx.required_region_bounds(opaque_type, bounds.predicates);
debug_assert!(!required_region_bounds.is_empty());
for region in required_region_bounds {
concrete_ty.visit_with(&mut OpaqueTypeOutlivesVisitor {
infcx: self,
least_region: region,
span,
for required_region in required_region_bounds {
concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
tcx: self.tcx,
op: |r| self.sub_regions(infer::CallReturn(span), required_region, r),
});
}
return;
@ -329,11 +365,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// `['a]` for the first impl trait and `'b` for the
// second.
let mut least_region = None;
for param in &abstract_type_generics.params {
for param in &opaque_type_generics.params {
match param.kind {
GenericParamDefKind::Lifetime => {}
_ => continue
_ => continue,
}
// Get the value supplied for this region from the substs.
let subst_arg = opaque_defn.substs.region_at(param.index as usize);
@ -350,44 +387,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
least_region = Some(subst_arg);
} else {
// There are two regions (`lr` and
// `subst_arg`) which are not relatable. We can't
// find a best choice.
let context_name = match opaque_defn.origin {
hir::ExistTyOrigin::ExistentialType => "existential type",
hir::ExistTyOrigin::ReturnImplTrait => "impl Trait",
hir::ExistTyOrigin::AsyncFn => "async fn",
};
let msg = format!("ambiguous lifetime bound in `{}`", context_name);
let mut err = self.tcx
.sess
.struct_span_err(span, &msg);
let lr_name = lr.to_string();
let subst_arg_name = subst_arg.to_string();
let label_owned;
let label = match (&*lr_name, &*subst_arg_name) {
("'_", "'_") => "the elided lifetimes here do not outlive one another",
_ => {
label_owned = format!(
"neither `{}` nor `{}` outlives the other",
lr_name,
subst_arg_name,
);
&label_owned
}
};
err.span_label(span, label);
if let hir::ExistTyOrigin::AsyncFn = opaque_defn.origin {
err.note("multiple unrelated lifetimes are not allowed in \
`async fn`.");
err.note("if you're using argument-position elided lifetimes, consider \
switching to a single named lifetime.");
}
err.emit();
least_region = Some(self.tcx.mk_region(ty::ReEmpty));
break;
// `subst_arg`) which are not relatable. We
// can't find a best choice. Therefore,
// instead of creating a single bound like
// `'r: 'a` (which is our preferred choice),
// we will create a "in bound" like `'r in
// ['a, 'b, 'c]`, where `'a..'c` are the
// regions that appear in the impl trait.
return self.generate_member_constraint(
concrete_ty,
opaque_type_generics,
opaque_defn,
def_id,
lr,
subst_arg,
);
}
}
}
@ -396,13 +410,121 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
let least_region = least_region.unwrap_or(tcx.lifetimes.re_static);
debug!("constrain_opaque_types: least_region={:?}", least_region);
concrete_ty.visit_with(&mut OpaqueTypeOutlivesVisitor {
infcx: self,
least_region,
span,
concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
tcx: self.tcx,
op: |r| self.sub_regions(infer::CallReturn(span), least_region, r),
});
}
/// As a fallback, we sometimes generate an "in constraint". For
/// a case like `impl Foo<'a, 'b>`, where `'a` and `'b` cannot be
/// related, we would generate a constraint `'r in ['a, 'b,
/// 'static]` for each region `'r` that appears in the hidden type
/// (i.e., it must be equal to `'a`, `'b`, or `'static`).
///
/// `conflict1` and `conflict2` are the two region bounds that we
/// detected which were unrelated. They are used for diagnostics.
fn generate_member_constraint(
&self,
concrete_ty: Ty<'tcx>,
opaque_type_generics: &ty::Generics,
opaque_defn: &OpaqueTypeDecl<'tcx>,
opaque_type_def_id: DefId,
conflict1: ty::Region<'tcx>,
conflict2: ty::Region<'tcx>,
) {
// For now, enforce a feature gate outside of async functions.
if self.member_constraint_feature_gate(
opaque_defn,
opaque_type_def_id,
conflict1,
conflict2,
) {
return;
}
// Create the set of choice regions: each region in the hidden
// type can be equal to any of the region parameters of the
// opaque type definition.
let choice_regions: Lrc<Vec<ty::Region<'tcx>>> = Lrc::new(
opaque_type_generics
.params
.iter()
.filter(|param| match param.kind {
GenericParamDefKind::Lifetime => true,
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => false,
})
.map(|param| opaque_defn.substs.region_at(param.index as usize))
.chain(std::iter::once(self.tcx.lifetimes.re_static))
.collect(),
);
concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
tcx: self.tcx,
op: |r| self.member_constraint(
opaque_type_def_id,
opaque_defn.definition_span,
concrete_ty,
r,
&choice_regions,
),
});
}
/// Member constraints are presently feature-gated except for
/// async-await. We expect to lift this once we've had a bit more
/// time.
fn member_constraint_feature_gate(
&self,
opaque_defn: &OpaqueTypeDecl<'tcx>,
opaque_type_def_id: DefId,
conflict1: ty::Region<'tcx>,
conflict2: ty::Region<'tcx>,
) -> bool {
// If we have `#![feature(member_constraints)]`, no problems.
if self.tcx.features().member_constraints {
return false;
}
let span = self.tcx.def_span(opaque_type_def_id);
// Without a feature-gate, we only generate member-constraints for async-await.
let context_name = match opaque_defn.origin {
// No feature-gate required for `async fn`.
hir::ExistTyOrigin::AsyncFn => return false,
// Otherwise, generate the label we'll use in the error message.
hir::ExistTyOrigin::ExistentialType => "existential type",
hir::ExistTyOrigin::ReturnImplTrait => "impl Trait",
};
let msg = format!("ambiguous lifetime bound in `{}`", context_name);
let mut err = self.tcx.sess.struct_span_err(span, &msg);
let conflict1_name = conflict1.to_string();
let conflict2_name = conflict2.to_string();
let label_owned;
let label = match (&*conflict1_name, &*conflict2_name) {
("'_", "'_") => "the elided lifetimes here do not outlive one another",
_ => {
label_owned = format!(
"neither `{}` nor `{}` outlives the other",
conflict1_name, conflict2_name,
);
&label_owned
}
};
err.span_label(span, label);
if nightly_options::is_nightly_build() {
help!(err,
"add #![feature(member_constraints)] to the crate attributes \
to enable");
}
err.emit();
true
}
/// Given the fully resolved, instantiated type for an opaque
/// type, i.e., the value of an inference variable like C1 or C2
/// (*), computes the "definition type" for an abstract type
@ -456,23 +578,98 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// Convert the type from the function into a type valid outside
// the function, by replacing invalid regions with 'static,
// after producing an error for each of them.
let definition_ty =
instantiated_ty.fold_with(&mut ReverseMapper::new(
self.tcx,
self.is_tainted_by_errors(),
def_id,
map,
instantiated_ty,
));
debug!(
"infer_opaque_definition_from_instantiation: definition_ty={:?}",
definition_ty
);
let definition_ty = instantiated_ty.fold_with(&mut ReverseMapper::new(
self.tcx,
self.is_tainted_by_errors(),
def_id,
map,
instantiated_ty,
));
debug!("infer_opaque_definition_from_instantiation: definition_ty={:?}", definition_ty);
definition_ty
}
}
pub fn unexpected_hidden_region_diagnostic(
tcx: TyCtxt<'tcx>,
region_scope_tree: Option<&region::ScopeTree>,
opaque_type_def_id: DefId,
hidden_ty: Ty<'tcx>,
hidden_region: ty::Region<'tcx>,
) -> DiagnosticBuilder<'tcx> {
let span = tcx.def_span(opaque_type_def_id);
let mut err = struct_span_err!(
tcx.sess,
span,
E0700,
"hidden type for `impl Trait` captures lifetime that does not appear in bounds",
);
// Explain the region we are capturing.
if let ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty = hidden_region {
// 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.
tcx.note_and_explain_free_region(
&mut err,
&format!("hidden type `{}` captures ", hidden_ty),
hidden_region,
"",
);
} else {
// Ugh. This is a painful case: the hidden region is not one
// that we can easily summarize or explain. This can happen
// in a case like
// `src/test/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`:
//
// ```
// fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> {
// if condition() { a } else { b }
// }
// ```
//
// Here the captured lifetime is the intersection of `'a` and
// `'b`, which we can't quite express.
if let Some(region_scope_tree) = region_scope_tree {
// If the `region_scope_tree` is available, this is being
// invoked from the "region inferencer error". We can at
// least report a really cryptic error for now.
tcx.note_and_explain_region(
region_scope_tree,
&mut err,
&format!("hidden type `{}` captures ", hidden_ty),
hidden_region,
"",
);
} else {
// If the `region_scope_tree` is *unavailable*, this is
// being invoked by the code that comes *after* region
// inferencing. This is a bug, as the region inferencer
// ought to have noticed the failed constraint and invoked
// error reporting, which in turn should have prevented us
// from getting trying to infer the hidden type
// completely.
tcx.sess.delay_span_bug(
span,
&format!(
"hidden type captures unexpected lifetime `{:?}` \
but no region inference failure",
hidden_region,
),
);
}
}
err
}
// Visitor that requires that (almost) all regions in the type visited outlive
// `least_region`. We cannot use `push_outlives_components` because regions in
// closure signatures are not included in their outlives components. We need to
@ -486,13 +683,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
//
// We ignore any type parameters because impl trait values are assumed to
// capture all the in-scope type parameters.
struct OpaqueTypeOutlivesVisitor<'a, 'tcx> {
infcx: &'a InferCtxt<'a, 'tcx>,
least_region: ty::Region<'tcx>,
span: Span,
struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP>
where
OP: FnMut(ty::Region<'tcx>),
{
tcx: TyCtxt<'tcx>,
op: OP,
}
impl<'tcx> TypeVisitor<'tcx> for OpaqueTypeOutlivesVisitor<'_, 'tcx> {
impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP>
where
OP: FnMut(ty::Region<'tcx>),
{
fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> bool {
t.skip_binder().visit_with(self);
false // keep visiting
@ -503,7 +705,7 @@ impl<'tcx> TypeVisitor<'tcx> for OpaqueTypeOutlivesVisitor<'_, 'tcx> {
// ignore bound regions, keep visiting
ty::ReLateBound(_, _) => false,
_ => {
self.infcx.sub_regions(infer::CallReturn(self.span), self.least_region, r);
(self.op)(r);
false
}
}
@ -519,23 +721,23 @@ impl<'tcx> TypeVisitor<'tcx> for OpaqueTypeOutlivesVisitor<'_, 'tcx> {
ty::Closure(def_id, ref substs) => {
// Skip lifetime parameters of the enclosing item(s)
for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) {
for upvar_ty in substs.upvar_tys(def_id, self.tcx) {
upvar_ty.visit_with(self);
}
substs.closure_sig_ty(def_id, self.infcx.tcx).visit_with(self);
substs.closure_sig_ty(def_id, self.tcx).visit_with(self);
}
ty::Generator(def_id, ref substs, _) => {
// Skip lifetime parameters of the enclosing item(s)
// Also skip the witness type, because that has no free regions.
for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) {
for upvar_ty in substs.upvar_tys(def_id, self.tcx) {
upvar_ty.visit_with(self);
}
substs.return_ty(def_id, self.infcx.tcx).visit_with(self);
substs.yield_ty(def_id, self.infcx.tcx).visit_with(self);
substs.return_ty(def_id, self.tcx).visit_with(self);
substs.yield_ty(def_id, self.tcx).visit_with(self);
}
_ => {
ty.super_visit_with(self);
@ -616,40 +818,17 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> {
None => {
if !self.map_missing_regions_to_empty && !self.tainted_by_errors {
if let Some(hidden_ty) = self.hidden_ty.take() {
let span = self.tcx.def_span(self.opaque_type_def_id);
let mut err = struct_span_err!(
self.tcx.sess,
span,
E0700,
"hidden type for `impl Trait` captures lifetime that \
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),
unexpected_hidden_region_diagnostic(
self.tcx,
None,
self.opaque_type_def_id,
hidden_ty,
r,
""
);
err.emit();
).emit();
}
}
self.tcx.lifetimes.re_empty
},
}
}
}
@ -681,8 +860,8 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> {
// during codegen.
let generics = self.tcx.generics_of(def_id);
let substs = self.tcx.mk_substs(substs.substs.iter().enumerate().map(
|(index, &kind)| {
let substs =
self.tcx.mk_substs(substs.substs.iter().enumerate().map(|(index, &kind)| {
if index < generics.parent_count {
// Accommodate missing regions in the parent kinds...
self.fold_kind_mapping_missing_regions_to_empty(kind)
@ -690,16 +869,15 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> {
// ...but not elsewhere.
self.fold_kind_normally(kind)
}
},
));
}));
self.tcx.mk_closure(def_id, ty::ClosureSubsts { substs })
}
ty::Generator(def_id, substs, movability) => {
let generics = self.tcx.generics_of(def_id);
let substs = self.tcx.mk_substs(substs.substs.iter().enumerate().map(
|(index, &kind)| {
let substs =
self.tcx.mk_substs(substs.substs.iter().enumerate().map(|(index, &kind)| {
if index < generics.parent_count {
// Accommodate missing regions in the parent kinds...
self.fold_kind_mapping_missing_regions_to_empty(kind)
@ -707,8 +885,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> {
// ...but not elsewhere.
self.fold_kind_normally(kind)
}
},
));
}));
self.tcx.mk_generator(def_id, ty::GeneratorSubsts { substs }, movability)
}
@ -723,6 +900,7 @@ struct Instantiator<'a, 'tcx> {
parent_def_id: DefId,
body_id: hir::HirId,
param_env: ty::ParamEnv<'tcx>,
value_span: Span,
opaque_types: OpaqueTypeMap<'tcx>,
obligations: Vec<PredicateObligation<'tcx>>,
}
@ -773,12 +951,10 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> {
let parent_def_id = self.parent_def_id;
let def_scope_default = || {
let opaque_parent_hir_id = tcx.hir().get_parent_item(opaque_hir_id);
parent_def_id == tcx.hir()
.local_def_id_from_hir_id(opaque_parent_hir_id)
parent_def_id
== tcx.hir().local_def_id_from_hir_id(opaque_parent_hir_id)
};
let (in_definition_scope, origin) =
match tcx.hir().find(opaque_hir_id)
{
let (in_definition_scope, origin) = match tcx.hir().find(opaque_hir_id) {
Some(Node::Item(item)) => match item.node {
// Anonymous `impl Trait`
hir::ItemKind::Existential(hir::ExistTy {
@ -847,10 +1023,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> {
let infcx = self.infcx;
let tcx = infcx.tcx;
debug!(
"instantiate_opaque_types: Opaque(def_id={:?}, substs={:?})",
def_id, substs
);
debug!("instantiate_opaque_types: Opaque(def_id={:?}, substs={:?})", def_id, substs);
// Use the same type variable if the exact same opaque type appears more
// than once in the return type (e.g., if it's passed to a type alias).
@ -858,41 +1031,35 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> {
return opaque_defn.concrete_ty;
}
let span = tcx.def_span(def_id);
let ty_var = infcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeInference,
span,
});
let ty_var = infcx
.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span });
let predicates_of = tcx.predicates_of(def_id);
debug!(
"instantiate_opaque_types: predicates={:#?}",
predicates_of,
);
debug!("instantiate_opaque_types: predicates={:#?}", predicates_of,);
let bounds = predicates_of.instantiate(tcx, substs);
debug!("instantiate_opaque_types: bounds={:?}", bounds);
let required_region_bounds = tcx.required_region_bounds(ty, bounds.predicates.clone());
debug!(
"instantiate_opaque_types: required_region_bounds={:?}",
required_region_bounds
);
debug!("instantiate_opaque_types: required_region_bounds={:?}", required_region_bounds);
// Make sure that we are in fact defining the *entire* type
// (e.g., `existential type Foo<T: Bound>: Bar;` needs to be
// defined by a function like `fn foo<T: Bound>() -> Foo<T>`).
debug!(
"instantiate_opaque_types: param_env={:#?}",
self.param_env,
);
debug!(
"instantiate_opaque_types: generics={:#?}",
tcx.generics_of(def_id),
);
debug!("instantiate_opaque_types: param_env={:#?}", self.param_env,);
debug!("instantiate_opaque_types: generics={:#?}", tcx.generics_of(def_id),);
// Ideally, we'd get the span where *this specific `ty` came
// from*, but right now we just use the span from the overall
// value being folded. In simple cases like `-> impl Foo`,
// these are the same span, but not in cases like `-> (impl
// Foo, impl Bar)`.
let definition_span = self.value_span;
self.opaque_types.insert(
def_id,
OpaqueTypeDecl {
substs,
definition_span,
concrete_ty: ty_var,
has_required_region_bounds: !required_region_bounds.is_empty(),
origin,
@ -911,8 +1078,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> {
// Require that the predicate holds for the concrete type.
debug!("instantiate_opaque_types: predicate={:?}", predicate);
self.obligations
.push(traits::Obligation::new(cause, self.param_env, predicate));
self.obligations.push(traits::Obligation::new(cause, self.param_env, predicate));
}
ty_var
@ -950,9 +1116,7 @@ pub fn may_define_existential_type(
);
// Named existential types can be defined by any siblings or children of siblings.
let scope = tcx.hir()
.get_defining_scope(opaque_hir_id)
.expect("could not get defining scope");
let scope = tcx.hir().get_defining_scope(opaque_hir_id).expect("could not get defining scope");
// We walk up the node tree until we hit the root or the scope of the opaque type.
while hir_id != scope && hir_id != hir::CRATE_HIR_ID {
hir_id = tcx.hir().get_parent_item(hir_id);

View File

@ -8,11 +8,14 @@ use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::unify as ut;
use crate::hir::def_id::DefId;
use crate::ty::ReStatic;
use crate::ty::{self, Ty, TyCtxt};
use crate::ty::{ReLateBound, ReVar};
use crate::ty::{Region, RegionVid};
use syntax_pos::Span;
use std::collections::BTreeMap;
use std::{cmp, fmt, mem};
@ -78,6 +81,11 @@ pub struct RegionConstraintData<'tcx> {
/// be a region variable (or neither, as it happens).
pub constraints: BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>,
/// Constraints of the form `R0 member of [R1, ..., Rn]`, meaning that
/// `R0` must be equal to one of the regions `R1..Rn`. These occur
/// with `impl Trait` quite frequently.
pub member_constraints: Vec<MemberConstraint<'tcx>>,
/// A "verify" is something that we need to verify after inference
/// is done, but which does not directly affect inference in any
/// way.
@ -137,6 +145,43 @@ impl Constraint<'_> {
}
}
/// Requires that `region` must be equal to one of the regions in `choice_regions`.
/// We often denote this using the syntax:
///
/// ```
/// R0 member of [O1..On]
/// ```
#[derive(Debug, Clone, HashStable)]
pub struct MemberConstraint<'tcx> {
/// The `DefId` of the opaque type causing this constraint: used for error reporting.
pub opaque_type_def_id: DefId,
/// The span where the hidden type was instantiated.
pub definition_span: Span,
/// The hidden type in which `member_region` appears: used for error reporting.
pub hidden_ty: Ty<'tcx>,
/// The region `R0`.
pub member_region: Region<'tcx>,
/// The options `O1..On`.
pub choice_regions: Lrc<Vec<Region<'tcx>>>,
}
BraceStructTypeFoldableImpl! {
impl<'tcx> TypeFoldable<'tcx> for MemberConstraint<'tcx> {
opaque_type_def_id, definition_span, hidden_ty, member_region, choice_regions
}
}
BraceStructLiftImpl! {
impl<'a, 'tcx> Lift<'tcx> for MemberConstraint<'a> {
type Lifted = MemberConstraint<'tcx>;
opaque_type_def_id, definition_span, hidden_ty, member_region, choice_regions
}
}
/// `VerifyGenericBound(T, _, R, RS)`: the parameter type `T` (or
/// associated type) must outlive the region `R`. `T` is known to
/// outlive `RS`. Therefore, verify that `R <= RS[i]` for some
@ -643,6 +688,30 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
}
}
pub fn member_constraint(
&mut self,
opaque_type_def_id: DefId,
definition_span: Span,
hidden_ty: Ty<'tcx>,
member_region: ty::Region<'tcx>,
choice_regions: &Lrc<Vec<ty::Region<'tcx>>>,
) {
debug!("member_constraint({:?} in {:#?})", member_region, choice_regions);
if choice_regions.iter().any(|&r| r == member_region) {
return;
}
self.data.member_constraints.push(MemberConstraint {
opaque_type_def_id,
definition_span,
hidden_ty,
member_region,
choice_regions: choice_regions.clone()
});
}
pub fn make_subregion(
&mut self,
origin: SubregionOrigin<'tcx>,
@ -906,9 +975,13 @@ impl<'tcx> RegionConstraintData<'tcx> {
pub fn is_empty(&self) -> bool {
let RegionConstraintData {
constraints,
member_constraints,
verifys,
givens,
} = self;
constraints.is_empty() && verifys.is_empty() && givens.is_empty()
constraints.is_empty() &&
member_constraints.is_empty() &&
verifys.is_empty() &&
givens.is_empty()
}
}

View File

@ -3,7 +3,7 @@ use std::fmt;
use crate::traits::query::Fallible;
use crate::infer::canonical::query_response;
use crate::infer::canonical::QueryRegionConstraint;
use crate::infer::canonical::QueryRegionConstraints;
use std::rc::Rc;
use syntax::source_map::DUMMY_SP;
use crate::traits::{ObligationCause, TraitEngine, TraitEngineExt};
@ -39,7 +39,7 @@ where
fn fully_perform(
self,
infcx: &InferCtxt<'_, 'tcx>,
) -> Fallible<(Self::Output, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)> {
) -> Fallible<(Self::Output, Option<Rc<QueryRegionConstraints<'tcx>>>)> {
if cfg!(debug_assertions) {
info!("fully_perform({:?})", self);
}
@ -62,7 +62,7 @@ where
fn scrape_region_constraints<'tcx, R>(
infcx: &InferCtxt<'_, 'tcx>,
op: impl FnOnce() -> Fallible<InferOk<'tcx, R>>,
) -> Fallible<(R, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)> {
) -> Fallible<(R, Option<Rc<QueryRegionConstraints<'tcx>>>)> {
let mut fulfill_cx = TraitEngine::new(infcx.tcx);
let dummy_body_id = ObligationCause::dummy().body_id;
@ -92,7 +92,7 @@ fn scrape_region_constraints<'tcx, R>(
let region_constraint_data = infcx.take_and_reset_region_constraints();
let outlives = query_response::make_query_outlives(
let region_constraints = query_response::make_query_region_constraints(
infcx.tcx,
region_obligations
.iter()
@ -101,9 +101,9 @@ fn scrape_region_constraints<'tcx, R>(
&region_constraint_data,
);
if outlives.is_empty() {
if region_constraints.is_empty() {
Ok((value, None))
} else {
Ok((value, Some(Rc::new(outlives))))
Ok((value, Some(Rc::new(region_constraints))))
}
}

View File

@ -1,6 +1,6 @@
use crate::infer::canonical::{
Canonical, Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues,
QueryRegionConstraint, QueryResponse,
QueryRegionConstraints, QueryResponse,
};
use crate::infer::{InferCtxt, InferOk};
use std::fmt;
@ -32,7 +32,7 @@ pub trait TypeOp<'tcx>: Sized + fmt::Debug {
fn fully_perform(
self,
infcx: &InferCtxt<'_, 'tcx>,
) -> Fallible<(Self::Output, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)>;
) -> Fallible<(Self::Output, Option<Rc<QueryRegionConstraints<'tcx>>>)>;
}
/// "Query type ops" are type ops that are implemented using a
@ -85,7 +85,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Sized + TypeFoldable<'tcx> + 'tcx {
fn fully_perform_into(
query_key: ParamEnvAnd<'tcx, Self>,
infcx: &InferCtxt<'_, 'tcx>,
output_query_region_constraints: &mut Vec<QueryRegionConstraint<'tcx>>,
output_query_region_constraints: &mut QueryRegionConstraints<'tcx>,
) -> Fallible<Self::QueryResponse> {
if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) {
return Ok(result);
@ -140,16 +140,16 @@ where
fn fully_perform(
self,
infcx: &InferCtxt<'_, 'tcx>,
) -> Fallible<(Self::Output, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)> {
let mut qrc = vec![];
let r = Q::fully_perform_into(self, infcx, &mut qrc)?;
) -> Fallible<(Self::Output, Option<Rc<QueryRegionConstraints<'tcx>>>)> {
let mut region_constraints = QueryRegionConstraints::default();
let r = Q::fully_perform_into(self, infcx, &mut region_constraints)?;
// Promote the final query-region-constraints into a
// (optional) ref-counted vector:
let opt_qrc = if qrc.is_empty() {
let opt_qrc = if region_constraints.is_empty() {
None
} else {
Some(Rc::new(qrc))
Some(Rc::new(region_constraints))
};
Ok((r, opt_qrc))

View File

@ -15,6 +15,7 @@ use crate::mir::interpret;
use std::fmt;
use std::rc::Rc;
use std::sync::Arc;
impl fmt::Debug for ty::GenericParamDef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -348,7 +349,7 @@ impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C)
tcx.lift(&self.0).and_then(|a| {
tcx.lift(&self.1).and_then(|b| tcx.lift(&self.2).map(|c| (a, b, c)))
})
}
}
}
impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option<T> {
@ -378,6 +379,20 @@ impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Box<T> {
}
}
impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Rc<T> {
type Lifted = Rc<T::Lifted>;
fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
tcx.lift(&**self).map(Rc::new)
}
}
impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Arc<T> {
type Lifted = Arc<T::Lifted>;
fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
tcx.lift(&**self).map(Arc::new)
}
}
impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for [T] {
type Lifted = Vec<T::Lifted>;
fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
@ -838,6 +853,16 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc<T> {
}
}
impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Arc<T> {
fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
Arc::new((**self).fold_with(folder))
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
(**self).visit_with(visitor)
}
}
impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<T> {
fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
let content: T = (**self).fold_with(folder);

View File

@ -0,0 +1,49 @@
#[cfg(test)]
mod test;
/// Uses a sorted slice `data: &[E]` as a kind of "multi-map". The
/// `key_fn` extracts a key of type `K` from the data, and this
/// function finds the range of elements that match the key. `data`
/// must have been sorted as if by a call to `sort_by_key` for this to
/// work.
pub fn binary_search_slice<E, K>(data: &'d [E], key_fn: impl Fn(&E) -> K, key: &K) -> &'d [E]
where
K: Ord,
{
let mid = match data.binary_search_by_key(key, &key_fn) {
Ok(mid) => mid,
Err(_) => return &[],
};
// We get back *some* element with the given key -- so
// search backwards to find the *first* one.
//
// (It'd be more efficient to use a "galloping" search
// here, but it's not really worth it for small-ish
// amounts of data.)
let mut start = mid;
while start > 0 {
if key_fn(&data[start - 1]) == *key {
start -= 1;
} else {
break;
}
}
// Now search forward to find the *last* one.
//
// (It'd be more efficient to use a "galloping" search
// here, but it's not really worth it for small-ish
// amounts of data.)
let mut end = mid + 1;
let max = data.len();
while end < max {
if key_fn(&data[end]) == *key {
end += 1;
} else {
break;
}
}
&data[start..end]
}

View File

@ -0,0 +1,23 @@
use super::*;
type Element = (usize, &'static str);
fn test_map() -> Vec<Element> {
let mut data = vec![(3, "three-a"), (0, "zero"), (3, "three-b"), (22, "twenty-two")];
data.sort_by_key(get_key);
data
}
fn get_key(data: &Element) -> usize {
data.0
}
#[test]
fn binary_search_slice_test() {
let map = test_map();
assert_eq!(binary_search_slice(&map, get_key, &0), &[(0, "zero")]);
assert_eq!(binary_search_slice(&map, get_key, &1), &[]);
assert_eq!(binary_search_slice(&map, get_key, &3), &[(3, "three-a"), (3, "three-b")]);
assert_eq!(binary_search_slice(&map, get_key, &22), &[(22, "twenty-two")]);
assert_eq!(binary_search_slice(&map, get_key, &23), &[]);
}

View File

@ -1,5 +1,6 @@
use super::super::indexed_vec::IndexVec;
use super::{DirectedGraph, WithSuccessors, WithNumNodes};
use super::{DirectedGraph, WithNumNodes, WithSuccessors};
use crate::bit_set::BitSet;
#[cfg(test)]
mod test;
@ -51,3 +52,36 @@ pub fn reverse_post_order<G: DirectedGraph + WithSuccessors + WithNumNodes>(
vec.reverse();
vec
}
/// A "depth-first search" iterator for a directed graph.
pub struct DepthFirstSearch<'graph, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
{
graph: &'graph G,
stack: Vec<G::Node>,
visited: BitSet<G::Node>,
}
impl<G> DepthFirstSearch<'graph, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
{
pub fn new(graph: &'graph G, start_node: G::Node) -> Self {
Self { graph, stack: vec![start_node], visited: BitSet::new_empty(graph.num_nodes()) }
}
}
impl<G> Iterator for DepthFirstSearch<'_, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
{
type Item = G::Node;
fn next(&mut self) -> Option<G::Node> {
let DepthFirstSearch { stack, visited, graph } = self;
let n = stack.pop()?;
stack.extend(graph.successors(n).filter(|&m| visited.insert(m)));
Some(n)
}
}

View File

@ -5,6 +5,7 @@ pub mod implementation;
pub mod iterate;
mod reference;
pub mod scc;
pub mod vec_graph;
#[cfg(test)]
mod test;
@ -17,6 +18,10 @@ pub trait WithNumNodes: DirectedGraph {
fn num_nodes(&self) -> usize;
}
pub trait WithNumEdges: DirectedGraph {
fn num_edges(&self) -> usize;
}
pub trait WithSuccessors: DirectedGraph
where
Self: for<'graph> GraphSuccessors<'graph, Item = <Self as DirectedGraph>::Node>,
@ -25,6 +30,13 @@ where
&'graph self,
node: Self::Node,
) -> <Self as GraphSuccessors<'graph>>::Iter;
fn depth_first_search(&self, from: Self::Node) -> iterate::DepthFirstSearch<'_, Self>
where
Self: WithNumNodes,
{
iterate::DepthFirstSearch::new(self, from)
}
}
pub trait GraphSuccessors<'graph> {

View File

@ -4,7 +4,8 @@
//! O(n) time.
use crate::fx::FxHashSet;
use crate::graph::{DirectedGraph, WithNumNodes, WithSuccessors};
use crate::graph::{DirectedGraph, WithNumNodes, WithNumEdges, WithSuccessors, GraphSuccessors};
use crate::graph::vec_graph::VecGraph;
use crate::indexed_vec::{Idx, IndexVec};
use std::ops::Range;
@ -58,6 +59,49 @@ impl<N: Idx, S: Idx> Sccs<N, S> {
pub fn successors(&self, scc: S) -> &[S] {
self.scc_data.successors(scc)
}
/// Construct the reverse graph of the SCC graph.
pub fn reverse(&self) -> VecGraph<S> {
VecGraph::new(
self.num_sccs(),
self.all_sccs()
.flat_map(|source| self.successors(source).iter().map(move |&target| {
(target, source)
}))
.collect(),
)
}
}
impl<N: Idx, S: Idx> DirectedGraph for Sccs<N, S> {
type Node = S;
}
impl<N: Idx, S: Idx> WithNumNodes for Sccs<N, S> {
fn num_nodes(&self) -> usize {
self.num_sccs()
}
}
impl<N: Idx, S: Idx> WithNumEdges for Sccs<N, S> {
fn num_edges(&self) -> usize {
self.scc_data.all_successors.len()
}
}
impl<N: Idx, S: Idx> GraphSuccessors<'graph> for Sccs<N, S> {
type Item = S;
type Iter = std::iter::Cloned<std::slice::Iter<'graph, S>>;
}
impl<N: Idx, S: Idx> WithSuccessors for Sccs<N, S> {
fn successors<'graph>(
&'graph self,
node: S
) -> <Self as GraphSuccessors<'graph>>::Iter {
self.successors(node).iter().cloned()
}
}
impl<S: Idx> SccData<S> {

View File

@ -0,0 +1,113 @@
use crate::indexed_vec::{Idx, IndexVec};
use crate::graph::{DirectedGraph, WithNumNodes, WithNumEdges, WithSuccessors, GraphSuccessors};
#[cfg(test)]
mod test;
pub struct VecGraph<N: Idx> {
/// Maps from a given node to an index where the set of successors
/// for that node starts. The index indexes into the `edges`
/// vector. To find the range for a given node, we look up the
/// start for that node and then the start for the next node
/// (i.e., with an index 1 higher) and get the range between the
/// two. This vector always has an extra entry so that this works
/// even for the max element.
node_starts: IndexVec<N, usize>,
edge_targets: Vec<N>,
}
impl<N: Idx> VecGraph<N> {
pub fn new(
num_nodes: usize,
mut edge_pairs: Vec<(N, N)>,
) -> Self {
// Sort the edges by the source -- this is important.
edge_pairs.sort();
let num_edges = edge_pairs.len();
// Store the *target* of each edge into `edge_targets`.
let edge_targets: Vec<N> = edge_pairs.iter().map(|&(_, target)| target).collect();
// Create the *edge starts* array. We are iterating over over
// the (sorted) edge pairs. We maintain the invariant that the
// length of the `node_starts` arary is enough to store the
// current source node -- so when we see that the source node
// for an edge is greater than the current length, we grow the
// edge-starts array by just enough.
let mut node_starts = IndexVec::with_capacity(num_edges);
for (index, &(source, _)) in edge_pairs.iter().enumerate() {
// If we have a list like `[(0, x), (2, y)]`:
//
// - Start out with `node_starts` of `[]`
// - Iterate to `(0, x)` at index 0:
// - Push one entry because `node_starts.len()` (0) is <= the source (0)
// - Leaving us with `node_starts` of `[0]`
// - Iterate to `(2, y)` at index 1:
// - Push one entry because `node_starts.len()` (1) is <= the source (2)
// - Push one entry because `node_starts.len()` (2) is <= the source (2)
// - Leaving us with `node_starts` of `[0, 1, 1]`
// - Loop terminates
while node_starts.len() <= source.index() {
node_starts.push(index);
}
}
// Pad out the `node_starts` array so that it has `num_nodes +
// 1` entries. Continuing our example above, if `num_nodes` is
// be `3`, we would push one more index: `[0, 1, 1, 2]`.
//
// Interpretation of that vector:
//
// [0, 1, 1, 2]
// ---- range for N=2
// ---- range for N=1
// ---- range for N=0
while node_starts.len() <= num_nodes {
node_starts.push(edge_targets.len());
}
assert_eq!(node_starts.len(), num_nodes + 1);
Self { node_starts, edge_targets }
}
/// Gets the successors for `source` as a slice.
pub fn successors(&self, source: N) -> &[N] {
let start_index = self.node_starts[source];
let end_index = self.node_starts[source.plus(1)];
&self.edge_targets[start_index..end_index]
}
}
impl<N: Idx> DirectedGraph for VecGraph<N> {
type Node = N;
}
impl<N: Idx> WithNumNodes for VecGraph<N> {
fn num_nodes(&self) -> usize {
self.node_starts.len() - 1
}
}
impl<N: Idx> WithNumEdges for VecGraph<N> {
fn num_edges(&self) -> usize {
self.edge_targets.len()
}
}
impl<N: Idx> GraphSuccessors<'graph> for VecGraph<N> {
type Item = N;
type Iter = std::iter::Cloned<std::slice::Iter<'graph, N>>;
}
impl<N: Idx> WithSuccessors for VecGraph<N> {
fn successors<'graph>(
&'graph self,
node: N
) -> <Self as GraphSuccessors<'graph>>::Iter {
self.successors(node).iter().cloned()
}
}

View File

@ -0,0 +1,51 @@
use super::*;
fn create_graph() -> VecGraph<usize> {
// Create a simple graph
//
// 5
// |
// V
// 0 --> 1 --> 2
// |
// v
// 3 --> 4
//
// 6
VecGraph::new(
7,
vec![
(0, 1),
(1, 2),
(1, 3),
(3, 4),
(5, 1),
],
)
}
#[test]
fn num_nodes() {
let graph = create_graph();
assert_eq!(graph.num_nodes(), 7);
}
#[test]
fn succesors() {
let graph = create_graph();
assert_eq!(graph.successors(0), &[1]);
assert_eq!(graph.successors(1), &[2, 3]);
assert_eq!(graph.successors(2), &[]);
assert_eq!(graph.successors(3), &[4]);
assert_eq!(graph.successors(4), &[]);
assert_eq!(graph.successors(5), &[1]);
assert_eq!(graph.successors(6), &[]);
}
#[test]
fn dfs() {
let graph = create_graph();
let dfs: Vec<_> = graph.depth_first_search(0).collect();
assert_eq!(dfs, vec![0, 1, 3, 4, 2]);
}

View File

@ -19,8 +19,11 @@ pub trait Idx: Copy + 'static + Ord + Debug + Hash {
fn index(self) -> usize;
fn increment_by(&mut self, amount: usize) {
let v = self.index() + amount;
*self = Self::new(v);
*self = self.plus(amount);
}
fn plus(self, amount: usize) -> Self {
Self::new(self.index() + amount)
}
}
@ -167,6 +170,14 @@ macro_rules! newtype_index {
}
}
impl std::ops::Add<usize> for $type {
type Output = Self;
fn add(self, other: usize) -> Self {
Self::new(self.index() + other)
}
}
impl Idx for $type {
#[inline]
fn new(value: usize) -> Self {

View File

@ -72,6 +72,7 @@ macro_rules! unlikely {
pub mod macros;
pub mod svh;
pub mod base_n;
pub mod binary_search_util;
pub mod bit_set;
pub mod box_region;
pub mod const_cstr;

View File

@ -1,6 +1,6 @@
use crate::borrow_check::nll::type_check::Locations;
use crate::borrow_check::nll::constraints::ConstraintIndex;
use crate::borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint};
use crate::borrow_check::nll::constraints::OutlivesConstraintIndex;
use crate::borrow_check::nll::constraints::{OutlivesConstraintSet, OutlivesConstraint};
use rustc::mir::ConstraintCategory;
use rustc::ty::RegionVid;
use rustc_data_structures::graph;
@ -12,8 +12,8 @@ use syntax_pos::DUMMY_SP;
/// -> R2` or `R2 -> R1` depending on the direction type `D`.
crate struct ConstraintGraph<D: ConstraintGraphDirecton> {
_direction: D,
first_constraints: IndexVec<RegionVid, Option<ConstraintIndex>>,
next_constraints: IndexVec<ConstraintIndex, Option<ConstraintIndex>>,
first_constraints: IndexVec<RegionVid, Option<OutlivesConstraintIndex>>,
next_constraints: IndexVec<OutlivesConstraintIndex, Option<OutlivesConstraintIndex>>,
}
crate type NormalConstraintGraph = ConstraintGraph<Normal>;
@ -77,13 +77,13 @@ impl<D: ConstraintGraphDirecton> ConstraintGraph<D> {
/// reporting.
crate fn new(
direction: D,
set: &ConstraintSet,
set: &OutlivesConstraintSet,
num_region_vars: usize,
) -> Self {
let mut first_constraints = IndexVec::from_elem_n(None, num_region_vars);
let mut next_constraints = IndexVec::from_elem(None, &set.constraints);
let mut next_constraints = IndexVec::from_elem(None, &set.outlives);
for (idx, constraint) in set.constraints.iter_enumerated().rev() {
for (idx, constraint) in set.outlives.iter_enumerated().rev() {
let head = &mut first_constraints[D::start_region(constraint)];
let next = &mut next_constraints[idx];
debug_assert!(next.is_none());
@ -103,7 +103,7 @@ impl<D: ConstraintGraphDirecton> ConstraintGraph<D> {
/// and not constraints.
crate fn region_graph<'rg>(
&'rg self,
set: &'rg ConstraintSet,
set: &'rg OutlivesConstraintSet,
static_region: RegionVid,
) -> RegionGraph<'rg, D> {
RegionGraph::new(set, self, static_region)
@ -113,7 +113,7 @@ impl<D: ConstraintGraphDirecton> ConstraintGraph<D> {
crate fn outgoing_edges<'a>(
&'a self,
region_sup: RegionVid,
constraints: &'a ConstraintSet,
constraints: &'a OutlivesConstraintSet,
static_region: RegionVid,
) -> Edges<'a, D> {
//if this is the `'static` region and the graph's direction is normal,
@ -142,8 +142,8 @@ impl<D: ConstraintGraphDirecton> ConstraintGraph<D> {
crate struct Edges<'s, D: ConstraintGraphDirecton> {
graph: &'s ConstraintGraph<D>,
constraints: &'s ConstraintSet,
pointer: Option<ConstraintIndex>,
constraints: &'s OutlivesConstraintSet,
pointer: Option<OutlivesConstraintIndex>,
next_static_idx: Option<usize>,
static_region: RegionVid,
}
@ -180,7 +180,7 @@ impl<'s, D: ConstraintGraphDirecton> Iterator for Edges<'s, D> {
/// reverse) constraint graph. It implements the graph traits and is
/// usd for doing the SCC computation.
crate struct RegionGraph<'s, D: ConstraintGraphDirecton> {
set: &'s ConstraintSet,
set: &'s OutlivesConstraintSet,
constraint_graph: &'s ConstraintGraph<D>,
static_region: RegionVid,
}
@ -191,7 +191,7 @@ impl<'s, D: ConstraintGraphDirecton> RegionGraph<'s, D> {
/// construct SCCs for region inference but also for error
/// reporting.
crate fn new(
set: &'s ConstraintSet,
set: &'s OutlivesConstraintSet,
constraint_graph: &'s ConstraintGraph<D>,
static_region: RegionVid,
) -> Self {

View File

@ -1,37 +1,40 @@
use crate::borrow_check::nll::type_check::Locations;
use rustc::mir::ConstraintCategory;
use rustc::ty::RegionVid;
use rustc_data_structures::graph::scc::Sccs;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use crate::borrow_check::nll::type_check::Locations;
use std::fmt;
use std::ops::Deref;
use std::ops::Index;
crate mod graph;
/// A set of NLL region constraints. These include "outlives"
/// constraints of the form `R1: R2`. Each constraint is identified by
/// a unique `OutlivesConstraintIndex` and you can index into the set
/// (`constraint_set[i]`) to access the constraint details.
#[derive(Clone, Default)]
crate struct ConstraintSet {
constraints: IndexVec<ConstraintIndex, OutlivesConstraint>,
crate struct OutlivesConstraintSet {
outlives: IndexVec<OutlivesConstraintIndex, OutlivesConstraint>,
}
impl ConstraintSet {
impl OutlivesConstraintSet {
crate fn push(&mut self, constraint: OutlivesConstraint) {
debug!(
"ConstraintSet::push({:?}: {:?} @ {:?}",
"OutlivesConstraintSet::push({:?}: {:?} @ {:?}",
constraint.sup, constraint.sub, constraint.locations
);
if constraint.sup == constraint.sub {
// 'a: 'a is pretty uninteresting
return;
}
self.constraints.push(constraint);
self.outlives.push(constraint);
}
/// Constructs a "normal" graph from the constraint set; the graph makes it
/// easy to find the constraints affecting a particular region.
///
/// N.B., this graph contains a "frozen" view of the current
/// constraints. Any new constraints added to the `ConstraintSet`
/// constraints. Any new constraints added to the `OutlivesConstraintSet`
/// after the graph is built will not be present in the graph.
crate fn graph(&self, num_region_vars: usize) -> graph::NormalConstraintGraph {
graph::ConstraintGraph::new(graph::Normal, self, num_region_vars)
@ -54,13 +57,17 @@ impl ConstraintSet {
let region_graph = &constraint_graph.region_graph(self, static_region);
Sccs::new(region_graph)
}
crate fn outlives(&self) -> &IndexVec<OutlivesConstraintIndex, OutlivesConstraint> {
&self.outlives
}
}
impl Deref for ConstraintSet {
type Target = IndexVec<ConstraintIndex, OutlivesConstraint>;
impl Index<OutlivesConstraintIndex> for OutlivesConstraintSet {
type Output = OutlivesConstraint;
fn deref(&self) -> &Self::Target {
&self.constraints
fn index(&self, i: OutlivesConstraintIndex) -> &Self::Output {
&self.outlives[i]
}
}
@ -94,8 +101,8 @@ impl fmt::Debug for OutlivesConstraint {
}
newtype_index! {
pub struct ConstraintIndex {
DEBUG_FORMAT = "ConstraintIndex({})"
pub struct OutlivesConstraintIndex {
DEBUG_FORMAT = "OutlivesConstraintIndex({})"
}
}

View File

@ -0,0 +1,235 @@
use crate::rustc::ty::{self, Ty};
use rustc::hir::def_id::DefId;
use rustc::infer::region_constraints::MemberConstraint;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use std::hash::Hash;
use std::ops::Index;
use syntax_pos::Span;
/// Compactly stores a set of `R0 member of [R1...Rn]` constraints,
/// indexed by the region `R0`.
crate struct MemberConstraintSet<'tcx, R>
where
R: Copy + Hash + Eq,
{
/// Stores the first "member" constraint for a given `R0`. This is an
/// index into the `constraints` vector below.
first_constraints: FxHashMap<R, NllMemberConstraintIndex>,
/// Stores the data about each `R0 member of [R1..Rn]` constraint.
/// These are organized into a linked list, so each constraint
/// contains the index of the next constraint with the same `R0`.
constraints: IndexVec<NllMemberConstraintIndex, NllMemberConstraint<'tcx>>,
/// Stores the `R1..Rn` regions for *all* sets. For any given
/// constraint, we keep two indices so that we can pull out a
/// slice.
choice_regions: Vec<ty::RegionVid>,
}
/// Represents a `R0 member of [R1..Rn]` constraint
crate struct NllMemberConstraint<'tcx> {
next_constraint: Option<NllMemberConstraintIndex>,
/// The opaque type whose hidden type is being inferred. (Used in error reporting.)
crate opaque_type_def_id: DefId,
/// The span where the hidden type was instantiated.
crate definition_span: Span,
/// The hidden type in which `R0` appears. (Used in error reporting.)
crate hidden_ty: Ty<'tcx>,
/// The region `R0`.
crate member_region_vid: ty::RegionVid,
/// Index of `R1` in `choice_regions` vector from `MemberConstraintSet`.
start_index: usize,
/// Index of `Rn` in `choice_regions` vector from `MemberConstraintSet`.
end_index: usize,
}
newtype_index! {
crate struct NllMemberConstraintIndex {
DEBUG_FORMAT = "MemberConstraintIndex({})"
}
}
impl Default for MemberConstraintSet<'tcx, ty::RegionVid> {
fn default() -> Self {
Self {
first_constraints: Default::default(),
constraints: Default::default(),
choice_regions: Default::default(),
}
}
}
impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> {
/// Pushes a member constraint into the set.
///
/// The input member constraint `m_c` is in the form produced by
/// the the `rustc::infer` code.
///
/// The `to_region_vid` callback fn is used to convert the regions
/// within into `RegionVid` format -- it typically consults the
/// `UniversalRegions` data structure that is known to the caller
/// (but which this code is unaware of).
crate fn push_constraint(
&mut self,
m_c: &MemberConstraint<'tcx>,
mut to_region_vid: impl FnMut(ty::Region<'tcx>) -> ty::RegionVid,
) {
debug!("push_constraint(m_c={:?})", m_c);
let member_region_vid: ty::RegionVid = to_region_vid(m_c.member_region);
let next_constraint = self.first_constraints.get(&member_region_vid).cloned();
let start_index = self.choice_regions.len();
let end_index = start_index + m_c.choice_regions.len();
debug!("push_constraint: member_region_vid={:?}", member_region_vid);
let constraint_index = self.constraints.push(NllMemberConstraint {
next_constraint,
member_region_vid,
opaque_type_def_id: m_c.opaque_type_def_id,
definition_span: m_c.definition_span,
hidden_ty: m_c.hidden_ty,
start_index,
end_index,
});
self.first_constraints.insert(member_region_vid, constraint_index);
self.choice_regions.extend(m_c.choice_regions.iter().map(|&r| to_region_vid(r)));
}
}
impl<R1> MemberConstraintSet<'tcx, R1>
where
R1: Copy + Hash + Eq,
{
/// Remap the "member region" key using `map_fn`, producing a new
/// member constraint set. This is used in the NLL code to map from
/// the original `RegionVid` to an scc index. In some cases, we
/// may have multiple `R1` values mapping to the same `R2` key -- that
/// is ok, the two sets will be merged.
crate fn into_mapped<R2>(
self,
mut map_fn: impl FnMut(R1) -> R2,
) -> MemberConstraintSet<'tcx, R2>
where
R2: Copy + Hash + Eq,
{
// We can re-use most of the original data, just tweaking the
// linked list links a bit.
//
// For example if we had two keys `Ra` and `Rb` that both now
// wind up mapped to the same key `S`, we would append the
// linked list for `Ra` onto the end of the linked list for
// `Rb` (or vice versa) -- this basically just requires
// rewriting the final link from one list to point at the othe
// other (see `append_list`).
let MemberConstraintSet { first_constraints, mut constraints, choice_regions } = self;
let mut first_constraints2 = FxHashMap::default();
first_constraints2.reserve(first_constraints.len());
for (r1, start1) in first_constraints {
let r2 = map_fn(r1);
if let Some(&start2) = first_constraints2.get(&r2) {
append_list(&mut constraints, start1, start2);
}
first_constraints2.insert(r2, start1);
}
MemberConstraintSet {
first_constraints: first_constraints2,
constraints,
choice_regions,
}
}
}
impl<R> MemberConstraintSet<'tcx, R>
where
R: Copy + Hash + Eq,
{
crate fn all_indices(
&self,
) -> impl Iterator<Item = NllMemberConstraintIndex> {
self.constraints.indices()
}
/// Iterate down the constraint indices associated with a given
/// peek-region. You can then use `choice_regions` and other
/// methods to access data.
crate fn indices(
&self,
member_region_vid: R,
) -> impl Iterator<Item = NllMemberConstraintIndex> + '_ {
let mut next = self.first_constraints.get(&member_region_vid).cloned();
std::iter::from_fn(move || -> Option<NllMemberConstraintIndex> {
if let Some(current) = next {
next = self.constraints[current].next_constraint;
Some(current)
} else {
None
}
})
}
/// Returns the "choice regions" for a given member
/// constraint. This is the `R1..Rn` from a constraint like:
///
/// ```
/// R0 member of [R1..Rn]
/// ```
crate fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] {
let NllMemberConstraint { start_index, end_index, .. } = &self.constraints[pci];
&self.choice_regions[*start_index..*end_index]
}
}
impl<'tcx, R> Index<NllMemberConstraintIndex> for MemberConstraintSet<'tcx, R>
where
R: Copy + Hash + Eq,
{
type Output = NllMemberConstraint<'tcx>;
fn index(&self, i: NllMemberConstraintIndex) -> &NllMemberConstraint<'tcx> {
&self.constraints[i]
}
}
/// Given a linked list starting at `source_list` and another linked
/// list starting at `target_list`, modify `target_list` so that it is
/// followed by `source_list`.
///
/// Before:
///
/// ```
/// target_list: A -> B -> C -> (None)
/// source_list: D -> E -> F -> (None)
/// ```
///
/// After:
///
/// ```
/// target_list: A -> B -> C -> D -> E -> F -> (None)
/// ```
fn append_list(
constraints: &mut IndexVec<NllMemberConstraintIndex, NllMemberConstraint<'_>>,
target_list: NllMemberConstraintIndex,
source_list: NllMemberConstraintIndex,
) {
let mut p = target_list;
loop {
let mut r = &mut constraints[p];
match r.next_constraint {
Some(q) => p = q,
None => {
r.next_constraint = Some(source_list);
return;
}
}
}
}

View File

@ -37,6 +37,7 @@ crate mod type_check;
mod universal_regions;
mod constraints;
mod member_constraints;
use self::facts::AllFacts;
use self::region_infer::RegionInferenceContext;
@ -129,6 +130,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
placeholder_index_to_region: _,
mut liveness_constraints,
outlives_constraints,
member_constraints,
closure_bounds_mapping,
type_tests,
} = constraints;
@ -150,6 +152,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
universal_region_relations,
body,
outlives_constraints,
member_constraints,
closure_bounds_mapping,
type_tests,
liveness_constraints,

View File

@ -71,7 +71,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
let mut constraints: Vec<_> = self.constraints.iter().collect();
let mut constraints: Vec<_> = self.constraints.outlives().iter().collect();
constraints.sort();
for constraint in &constraints {
let OutlivesConstraint {

View File

@ -1,4 +1,5 @@
use crate::borrow_check::nll::constraints::OutlivesConstraint;
use crate::borrow_check::nll::region_infer::AppliedMemberConstraint;
use crate::borrow_check::nll::region_infer::RegionInferenceContext;
use crate::borrow_check::nll::type_check::Locations;
use crate::borrow_check::nll::universal_regions::DefiningTy;
@ -195,6 +196,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
Trace::NotVisited => {
bug!("found unvisited region {:?} on path to {:?}", p, r)
}
Trace::FromOutlivesConstraint(c) => {
result.push(c);
p = c.sup;
@ -211,10 +213,30 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Otherwise, walk over the outgoing constraints and
// enqueue any regions we find, keeping track of how we
// reached them.
// A constraint like `'r: 'x` can come from our constraint
// graph.
let fr_static = self.universal_regions.fr_static;
for constraint in self.constraint_graph
.outgoing_edges(r, &self.constraints, fr_static)
{
let outgoing_edges_from_graph = self.constraint_graph
.outgoing_edges(r, &self.constraints, fr_static);
// But member constraints can also give rise to `'r: 'x`
// edges that were not part of the graph initially, so
// watch out for those.
let outgoing_edges_from_picks = self.applied_member_constraints(r)
.iter()
.map(|&AppliedMemberConstraint { min_choice, member_constraint_index, .. }| {
let p_c = &self.member_constraints[member_constraint_index];
OutlivesConstraint {
sup: r,
sub: min_choice,
locations: Locations::All(p_c.definition_span),
category: ConstraintCategory::OpaqueType,
}
});
for constraint in outgoing_edges_from_graph.chain(outgoing_edges_from_picks) {
debug_assert_eq!(constraint.sup, r);
let sub_region = constraint.sub;
if let Trace::NotVisited = context[sub_region] {
@ -687,7 +709,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Finds some region R such that `fr1: R` and `R` is live at
// `elem`.
crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
crate fn find_sub_region_live_at(
&self,
fr1: RegionVid,
elem: Location,
) -> RegionVid {
debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem);
self.find_constraint_paths_between_regions(fr1, |r| {
// First look for some `r` such that `fr1: r` and `r` is live at `elem`
@ -729,8 +755,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
fr1: RegionVid,
fr2: RegionVid,
) -> (ConstraintCategory, Span) {
let (category, _, span) =
self.best_blame_constraint(body, fr1, |r| self.provides_universal_region(r, fr1, fr2));
let (category, _, span) = self.best_blame_constraint(
body,
fr1,
|r| self.provides_universal_region(r, fr1, fr2),
);
(category, span)
}

View File

@ -63,7 +63,7 @@ impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for RawConstraints<'a, 'tcx> {
vids.into()
}
fn edges(&'this self) -> dot::Edges<'this, OutlivesConstraint> {
(&self.regioncx.constraints.raw[..]).into()
(&self.regioncx.constraints.outlives().raw[..]).into()
}
// Render `a: b` as `a -> b`, indicating the flow

View File

@ -1,25 +1,32 @@
use super::universal_regions::UniversalRegions;
use crate::borrow_check::nll::constraints::graph::NormalConstraintGraph;
use crate::borrow_check::nll::constraints::{ConstraintSccIndex, ConstraintSet, OutlivesConstraint};
use crate::borrow_check::nll::region_infer::values::{
PlaceholderIndices, RegionElement, ToElementIndex
use crate::borrow_check::nll::constraints::{
ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet,
};
use crate::borrow_check::nll::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex};
use crate::borrow_check::nll::region_infer::values::{
PlaceholderIndices, RegionElement, ToElementIndex,
};
use crate::borrow_check::Upvar;
use crate::borrow_check::nll::type_check::free_region_relations::UniversalRegionRelations;
use crate::borrow_check::nll::type_check::Locations;
use crate::borrow_check::Upvar;
use rustc::hir::def_id::DefId;
use rustc::infer::canonical::QueryRegionConstraint;
use rustc::infer::canonical::QueryOutlivesConstraint;
use rustc::infer::opaque_types;
use rustc::infer::region_constraints::{GenericKind, VarInfos, VerifyBound};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin};
use rustc::mir::{
ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements,
ConstraintCategory, Local, Location, Body,
Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements,
ConstraintCategory, Local, Location,
};
use rustc::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable};
use rustc::util::common::{self, ErrorReported};
use rustc_data_structures::binary_search_util;
use rustc_data_structures::bit_set::BitSet;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::graph::WithSuccessors;
use rustc_data_structures::graph::scc::Sccs;
use rustc_data_structures::graph::vec_graph::VecGraph;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_errors::{Diagnostic, DiagnosticBuilder};
use syntax_pos::Span;
@ -49,17 +56,31 @@ pub struct RegionInferenceContext<'tcx> {
liveness_constraints: LivenessValues<RegionVid>,
/// The outlives constraints computed by the type-check.
constraints: Rc<ConstraintSet>,
constraints: Rc<OutlivesConstraintSet>,
/// The constraint-set, but in graph form, making it easy to traverse
/// the constraints adjacent to a particular region. Used to construct
/// the SCC (see `constraint_sccs`) and for error reporting.
constraint_graph: Rc<NormalConstraintGraph>,
/// The SCC computed from `constraints` and the constraint graph. Used to
/// The SCC computed from `constraints` and the constraint
/// graph. We have an edge from SCC A to SCC B if `A: B`. Used to
/// compute the values of each region.
constraint_sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>,
/// Reverse of the SCC constraint graph -- i.e., an edge `A -> B`
/// exists if `B: A`. Computed lazilly.
rev_constraint_graph: Option<Rc<VecGraph<ConstraintSccIndex>>>,
/// The "R0 member of [R1..Rn]" constraints, indexed by SCC.
member_constraints: Rc<MemberConstraintSet<'tcx, ConstraintSccIndex>>,
/// Records the member constraints that we applied to each scc.
/// This is useful for error reporting. Once constraint
/// propagation is done, this vector is sorted according to
/// `member_region_scc`.
member_constraints_applied: Vec<AppliedMemberConstraint>,
/// Map closure bounds to a `Span` that should be used for error reporting.
closure_bounds_mapping:
FxHashMap<Location, FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>>,
@ -95,6 +116,32 @@ pub struct RegionInferenceContext<'tcx> {
universal_region_relations: Rc<UniversalRegionRelations<'tcx>>,
}
/// Each time that `apply_member_constraint` is successful, it appends
/// one of these structs to the `member_constraints_applied` field.
/// This is used in error reporting to trace out what happened.
///
/// The way that `apply_member_constraint` works is that it effectively
/// adds a new lower bound to the SCC it is analyzing: so you wind up
/// with `'R: 'O` where `'R` is the pick-region and `'O` is the
/// minimal viable option.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
struct AppliedMemberConstraint {
/// The SCC that was affected. (The "member region".)
///
/// The vector if `AppliedMemberConstraint` elements is kept sorted
/// by this field.
member_region_scc: ConstraintSccIndex,
/// The "best option" that `apply_member_constraint` found -- this was
/// added as an "ad-hoc" lower-bound to `member_region_scc`.
min_choice: ty::RegionVid,
/// The "member constraint index" -- we can find out details about
/// the constraint from
/// `set.member_constraints[member_constraint_index]`.
member_constraint_index: NllMemberConstraintIndex,
}
struct RegionDefinition<'tcx> {
/// What kind of variable is this -- a free region? existential
/// variable? etc. (See the `NLLRegionVariableOrigin` for more
@ -186,7 +233,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
placeholder_indices: Rc<PlaceholderIndices>,
universal_region_relations: Rc<UniversalRegionRelations<'tcx>>,
_body: &Body<'tcx>,
outlives_constraints: ConstraintSet,
outlives_constraints: OutlivesConstraintSet,
member_constraints_in: MemberConstraintSet<'tcx, RegionVid>,
closure_bounds_mapping: FxHashMap<
Location,
FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>,
@ -218,12 +266,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions);
let member_constraints =
Rc::new(member_constraints_in.into_mapped(|r| constraint_sccs.scc(r)));
let mut result = Self {
definitions,
liveness_constraints,
constraints,
constraint_graph,
constraint_sccs,
rev_constraint_graph: None,
member_constraints,
member_constraints_applied: Vec::new(),
closure_bounds_mapping,
scc_universes,
scc_representatives,
@ -341,9 +395,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
debug!(
"init_free_and_bound_regions: placeholder {:?} is \
not compatible with universe {:?} of its SCC {:?}",
placeholder,
scc_universe,
scc,
placeholder, scc_universe, scc,
);
self.add_incompatible_universe(scc);
}
@ -394,6 +446,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.scc_universes[scc]
}
/// Once region solving has completed, this function will return
/// the member constraints that were applied to the value of a given
/// region `r`. See `AppliedMemberConstraint`.
fn applied_member_constraints(&self, r: impl ToRegionVid) -> &[AppliedMemberConstraint] {
let scc = self.constraint_sccs.scc(r.to_region_vid());
binary_search_util::binary_search_slice(
&self.member_constraints_applied,
|applied| applied.member_region_scc,
&scc,
)
}
/// Performs region inference and report errors if we see any
/// unsatisfiable constraints. If this is a closure, returns the
/// region requirements to propagate to our creator, if any.
@ -428,11 +492,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// to store those. Otherwise, we'll pass in `None` to the
// functions below, which will trigger them to report errors
// eagerly.
let mut outlives_requirements = if infcx.tcx.is_closure(mir_def_id) {
Some(vec![])
} else {
None
};
let mut outlives_requirements =
if infcx.tcx.is_closure(mir_def_id) { Some(vec![]) } else { None };
self.check_type_tests(
infcx,
@ -451,16 +512,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
errors_buffer,
);
self.check_member_constraints(infcx, mir_def_id, errors_buffer);
let outlives_requirements = outlives_requirements.unwrap_or(vec![]);
if outlives_requirements.is_empty() {
None
} else {
let num_external_vids = self.universal_regions.num_global_and_external_regions();
Some(ClosureRegionRequirements {
num_external_vids,
outlives_requirements,
})
Some(ClosureRegionRequirements { num_external_vids, outlives_requirements })
}
}
@ -472,7 +532,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
debug!("propagate_constraints()");
debug!("propagate_constraints: constraints={:#?}", {
let mut constraints: Vec<_> = self.constraints.iter().collect();
let mut constraints: Vec<_> = self.constraints.outlives().iter().collect();
constraints.sort();
constraints
.into_iter()
@ -488,8 +548,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
for scc_index in self.constraint_sccs.all_sccs() {
self.propagate_constraint_sccs_if_new(scc_index, visited);
}
// Sort the applied member constraints so we can binary search
// through them later.
self.member_constraints_applied.sort_by_key(|applied| applied.member_region_scc);
}
/// Computes the value of the SCC `scc_a` if it has not already
/// been computed. The `visited` parameter is a bitset
#[inline]
fn propagate_constraint_sccs_if_new(
&mut self,
@ -501,6 +567,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
/// Computes the value of the SCC `scc_a`, which has not yet been
/// computed. This works by first computing all successors of the
/// SCC (if they haven't been computed already) and then unioning
/// together their elements.
fn propagate_constraint_sccs_new(
&mut self,
scc_a: ConstraintSccIndex,
@ -510,10 +580,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Walk each SCC `B` such that `A: B`...
for &scc_b in constraint_sccs.successors(scc_a) {
debug!(
"propagate_constraint_sccs: scc_a = {:?} scc_b = {:?}",
scc_a, scc_b
);
debug!("propagate_constraint_sccs: scc_a = {:?} scc_b = {:?}", scc_a, scc_b);
// ...compute the value of `B`...
self.propagate_constraint_sccs_if_new(scc_b, visited);
@ -531,6 +598,16 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
// Now take member constraints into account.
let member_constraints = self.member_constraints.clone();
for m_c_i in member_constraints.indices(scc_a) {
self.apply_member_constraint(
scc_a,
m_c_i,
member_constraints.choice_regions(m_c_i),
);
}
debug!(
"propagate_constraint_sccs: scc_a = {:?} has value {:?}",
scc_a,
@ -538,6 +615,167 @@ impl<'tcx> RegionInferenceContext<'tcx> {
);
}
/// Invoked for each `R0 member of [R1..Rn]` constraint.
///
/// `scc` is the SCC containing R0, and `choice_regions` are the
/// `R1..Rn` regions -- they are always known to be universal
/// regions (and if that's not true, we just don't attempt to
/// enforce the constraint).
///
/// The current value of `scc` at the time the method is invoked
/// is considered a *lower bound*. If possible, we will modify
/// the constraint to set it equal to one of the option regions.
/// If we make any changes, returns true, else false.
fn apply_member_constraint(
&mut self,
scc: ConstraintSccIndex,
member_constraint_index: NllMemberConstraintIndex,
choice_regions: &[ty::RegionVid],
) -> bool {
debug!("apply_member_constraint(scc={:?}, choice_regions={:#?})", scc, choice_regions,);
if let Some(uh_oh) =
choice_regions.iter().find(|&&r| !self.universal_regions.is_universal_region(r))
{
// FIXME(#61773): This case can only occur with
// `impl_trait_in_bindings`, I believe, and we are just
// opting not to handle it for now. See #61773 for
// details.
bug!(
"member constraint for `{:?}` has an option region `{:?}` \
that is not a universal region",
self.member_constraints[member_constraint_index].opaque_type_def_id,
uh_oh,
);
}
// Create a mutable vector of the options. We'll try to winnow
// them down.
let mut choice_regions: Vec<ty::RegionVid> = choice_regions.to_vec();
// The 'member region' in a member constraint is part of the
// hidden type, which must be in the root universe. Therefore,
// it cannot have any placeholders in its value.
assert!(self.scc_universes[scc] == ty::UniverseIndex::ROOT);
debug_assert!(
self.scc_values.placeholders_contained_in(scc).next().is_none(),
"scc {:?} in a member constraint has placeholder value: {:?}",
scc,
self.scc_values.region_value_str(scc),
);
// The existing value for `scc` is a lower-bound. This will
// consist of some set `{P} + {LB}` of points `{P}` and
// lower-bound free regions `{LB}`. As each choice region `O`
// is a free region, it will outlive the points. But we can
// only consider the option `O` if `O: LB`.
choice_regions.retain(|&o_r| {
self.scc_values
.universal_regions_outlived_by(scc)
.all(|lb| self.universal_region_relations.outlives(o_r, lb))
});
debug!("apply_member_constraint: after lb, choice_regions={:?}", choice_regions);
// Now find all the *upper bounds* -- that is, each UB is a
// free region that must outlive the member region `R0` (`UB:
// R0`). Therefore, we need only keep an option `O` if `UB: O`
// for all UB.
if choice_regions.len() > 1 {
let universal_region_relations = self.universal_region_relations.clone();
let rev_constraint_graph = self.rev_constraint_graph();
for ub in self.upper_bounds(scc, &rev_constraint_graph) {
debug!("apply_member_constraint: ub={:?}", ub);
choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r));
}
debug!("apply_member_constraint: after ub, choice_regions={:?}", choice_regions);
}
// If we ruled everything out, we're done.
if choice_regions.is_empty() {
return false;
}
// Otherwise, we need to find the minimum remaining choice, if
// any, and take that.
debug!("apply_member_constraint: choice_regions remaining are {:#?}", choice_regions);
let min = |r1: ty::RegionVid, r2: ty::RegionVid| -> Option<ty::RegionVid> {
let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2);
let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1);
if r1_outlives_r2 && r2_outlives_r1 {
Some(r1.min(r2))
} else if r1_outlives_r2 {
Some(r2)
} else if r2_outlives_r1 {
Some(r1)
} else {
None
}
};
let mut min_choice = choice_regions[0];
for &other_option in &choice_regions[1..] {
debug!(
"apply_member_constraint: min_choice={:?} other_option={:?}",
min_choice, other_option,
);
match min(min_choice, other_option) {
Some(m) => min_choice = m,
None => {
debug!(
"apply_member_constraint: {:?} and {:?} are incomparable; no min choice",
min_choice, other_option,
);
return false;
}
}
}
let min_choice_scc = self.constraint_sccs.scc(min_choice);
debug!(
"apply_member_constraint: min_choice={:?} best_choice_scc={:?}",
min_choice,
min_choice_scc,
);
if self.scc_values.add_region(scc, min_choice_scc) {
self.member_constraints_applied.push(AppliedMemberConstraint {
member_region_scc: scc,
min_choice,
member_constraint_index,
});
true
} else {
false
}
}
/// Compute and return the reverse SCC-based constraint graph (lazilly).
fn upper_bounds(
&'a mut self,
scc0: ConstraintSccIndex,
rev_constraint_graph: &'a VecGraph<ConstraintSccIndex>,
) -> impl Iterator<Item = RegionVid> + 'a {
let scc_values = &self.scc_values;
let mut duplicates = FxHashSet::default();
rev_constraint_graph
.depth_first_search(scc0)
.skip(1)
.flat_map(move |scc1| scc_values.universal_regions_outlived_by(scc1))
.filter(move |&r| duplicates.insert(r))
}
/// Compute and return the reverse SCC-based constraint graph (lazilly).
fn rev_constraint_graph(
&mut self,
) -> Rc<VecGraph<ConstraintSccIndex>> {
if let Some(g) = &self.rev_constraint_graph {
return g.clone();
}
let rev_graph = Rc::new(self.constraint_sccs.reverse());
self.rev_constraint_graph = Some(rev_graph.clone());
rev_graph
}
/// Returns `true` if all the elements in the value of `scc_b` are nameable
/// in `scc_a`. Used during constraint propagation, and only once
/// the value of `scc_b` has been computed.
@ -554,9 +792,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Otherwise, we have to iterate over the universe elements in
// B's value, and check whether all of them are nameable
// from universe_a
self.scc_values
.placeholders_contained_in(scc_b)
.all(|p| universe_a.can_name(p.universe))
self.scc_values.placeholders_contained_in(scc_b).all(|p| universe_a.can_name(p.universe))
}
/// Extend `scc` so that it can outlive some placeholder region
@ -731,12 +967,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
) -> bool {
let tcx = infcx.tcx;
let TypeTest {
generic_kind,
lower_bound,
locations,
verify_bound: _,
} = type_test;
let TypeTest { generic_kind, lower_bound, locations, verify_bound: _ } = type_test;
let generic_ty = generic_kind.to_ty(tcx);
let subject = match self.try_promote_type_test_subject(infcx, generic_ty) {
@ -886,11 +1117,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// except that it converts further takes the non-local upper
/// bound of `'y`, so that the final result is non-local.
fn non_local_universal_upper_bound(&self, r: RegionVid) -> RegionVid {
debug!(
"non_local_universal_upper_bound(r={:?}={})",
r,
self.region_value_str(r)
);
debug!("non_local_universal_upper_bound(r={:?}={})", r, self.region_value_str(r));
let lub = self.universal_upper_bound(r);
@ -898,10 +1125,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// creator.
let non_local_lub = self.universal_region_relations.non_local_upper_bound(lub);
debug!(
"non_local_universal_upper_bound: non_local_lub={:?}",
non_local_lub
);
debug!("non_local_universal_upper_bound: non_local_lub={:?}", non_local_lub);
non_local_lub
}
@ -921,11 +1145,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding
/// a result `'y`.
fn universal_upper_bound(&self, r: RegionVid) -> RegionVid {
debug!(
"universal_upper_bound(r={:?}={})",
r,
self.region_value_str(r)
);
debug!("universal_upper_bound(r={:?}={})", r, self.region_value_str(r));
// Find the smallest universal region that contains all other
// universal regions within `region`.
@ -950,10 +1170,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
lower_bound: RegionVid,
verify_bound: &VerifyBound<'tcx>,
) -> bool {
debug!(
"eval_verify_bound(lower_bound={:?}, verify_bound={:?})",
lower_bound, verify_bound
);
debug!("eval_verify_bound(lower_bound={:?}, verify_bound={:?})", lower_bound, verify_bound);
match verify_bound {
VerifyBound::IfEq(test_ty, verify_bound1) => {
@ -962,7 +1179,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
VerifyBound::OutlivedBy(r) => {
let r_vid = self.to_region_vid(r);
self.eval_outlives(body, r_vid, lower_bound)
self.eval_outlives(r_vid, lower_bound)
}
VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| {
@ -1035,22 +1252,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
})
}
// Evaluate whether `sup_region: sub_region @ point`.
fn eval_outlives(
&self,
_body: &Body<'tcx>,
sup_region: RegionVid,
sub_region: RegionVid,
) -> bool {
// Evaluate whether `sup_region == sub_region`.
fn eval_equal(&self, r1: RegionVid, r2: RegionVid) -> bool {
self.eval_outlives(r1, r2) && self.eval_outlives(r2, r1)
}
// Evaluate whether `sup_region: sub_region`.
fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool {
debug!("eval_outlives({:?}: {:?})", sup_region, sub_region);
debug!(
"eval_outlives: sup_region's value = {:?}",
"eval_outlives: sup_region's value = {:?} universal={:?}",
self.region_value_str(sup_region),
self.universal_regions.is_universal_region(sup_region),
);
debug!(
"eval_outlives: sub_region's value = {:?}",
"eval_outlives: sub_region's value = {:?} universal={:?}",
self.region_value_str(sub_region),
self.universal_regions.is_universal_region(sub_region),
);
let sub_region_scc = self.constraint_sccs.scc(sub_region);
@ -1062,9 +1281,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// now). Therefore, the sup-region outlives the sub-region if,
// for each universal region R1 in the sub-region, there
// exists some region R2 in the sup-region that outlives R1.
let universal_outlives = self.scc_values
.universal_regions_outlived_by(sub_region_scc)
.all(|r1| {
let universal_outlives =
self.scc_values.universal_regions_outlived_by(sub_region_scc).all(|r1| {
self.scc_values
.universal_regions_outlived_by(sup_region_scc)
.any(|r2| self.universal_region_relations.outlives(r2, r1))
@ -1082,8 +1300,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
return true;
}
self.scc_values
.contains_points(sup_region_scc, sub_region_scc)
self.scc_values.contains_points(sup_region_scc, sub_region_scc)
}
/// Once regions have been propagated, this method is used to see
@ -1165,12 +1382,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Because this free region must be in the ROOT universe, we
// know it cannot contain any bound universes.
assert!(self.scc_universes[longer_fr_scc] == ty::UniverseIndex::ROOT);
debug_assert!(
self.scc_values
.placeholders_contained_in(longer_fr_scc)
.next()
.is_none()
);
debug_assert!(self.scc_values.placeholders_contained_in(longer_fr_scc).next().is_none());
// Only check all of the relations for the main representative of each
// SCC, otherwise just check that we outlive said representative. This
@ -1224,9 +1436,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
errors_buffer: &mut Vec<Diagnostic>,
) -> Option<ErrorReported> {
// If it is known that `fr: o`, carry on.
if self.universal_region_relations
.outlives(longer_fr, shorter_fr)
{
if self.universal_region_relations.outlives(longer_fr, shorter_fr) {
return None;
}
@ -1240,9 +1450,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// We'll call it `fr-` -- it's ever so slightly smaller than
// `longer_fr`.
if let Some(fr_minus) = self
.universal_region_relations
.non_local_lower_bound(longer_fr)
if let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr)
{
debug!("check_universal_region: fr_minus={:?}", fr_minus);
@ -1252,12 +1460,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Grow `shorter_fr` until we find some non-local regions. (We
// always will.) We'll call them `shorter_fr+` -- they're ever
// so slightly larger than `shorter_fr`.
let shorter_fr_plus = self.universal_region_relations
.non_local_upper_bounds(&shorter_fr);
debug!(
"check_universal_region: shorter_fr_plus={:?}",
shorter_fr_plus
);
let shorter_fr_plus =
self.universal_region_relations.non_local_upper_bounds(&shorter_fr);
debug!("check_universal_region: shorter_fr_plus={:?}", shorter_fr_plus);
for &&fr in &shorter_fr_plus {
// Push the constraint `fr-: shorter_fr+`
propagated_outlives_requirements.push(ClosureOutlivesRequirement {
@ -1289,28 +1494,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
longer_fr: RegionVid,
placeholder: ty::PlaceholderRegion,
) {
debug!(
"check_bound_universal_region(fr={:?}, placeholder={:?})",
longer_fr, placeholder,
);
debug!("check_bound_universal_region(fr={:?}, placeholder={:?})", longer_fr, placeholder,);
let longer_fr_scc = self.constraint_sccs.scc(longer_fr);
debug!(
"check_bound_universal_region: longer_fr_scc={:?}",
longer_fr_scc,
);
debug!("check_bound_universal_region: longer_fr_scc={:?}", longer_fr_scc,);
// If we have some bound universal region `'a`, then the only
// elements it can contain is itself -- we don't know anything
// else about it!
let error_element = match {
self.scc_values
.elements_contained_in(longer_fr_scc)
.find(|element| match element {
RegionElement::Location(_) => true,
RegionElement::RootUniversalRegion(_) => true,
RegionElement::PlaceholderRegion(placeholder1) => placeholder != *placeholder1,
})
self.scc_values.elements_contained_in(longer_fr_scc).find(|element| match element {
RegionElement::Location(_) => true,
RegionElement::RootUniversalRegion(_) => true,
RegionElement::PlaceholderRegion(placeholder1) => placeholder != *placeholder1,
})
} {
Some(v) => v,
None => return,
@ -1321,7 +1518,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let error_region = match error_element {
RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l),
RegionElement::RootUniversalRegion(r) => r,
RegionElement::PlaceholderRegion(error_placeholder) => self.definitions
RegionElement::PlaceholderRegion(error_placeholder) => self
.definitions
.iter_enumerated()
.filter_map(|(r, definition)| match definition.origin {
NLLRegionVariableOrigin::Placeholder(p) if p == error_placeholder => Some(r),
@ -1339,12 +1537,50 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// the AST-based checker uses a more conservative check,
// so to even see this error, one must pass in a special
// flag.
let mut diag = infcx
.tcx
.sess
.struct_span_err(span, "higher-ranked subtype error");
let mut diag = infcx.tcx.sess.struct_span_err(span, "higher-ranked subtype error");
diag.emit();
}
fn check_member_constraints(
&self,
infcx: &InferCtxt<'_, 'tcx>,
mir_def_id: DefId,
errors_buffer: &mut Vec<Diagnostic>,
) {
let member_constraints = self.member_constraints.clone();
for m_c_i in member_constraints.all_indices() {
debug!("check_member_constraint(m_c_i={:?})", m_c_i);
let m_c = &member_constraints[m_c_i];
let member_region_vid = m_c.member_region_vid;
debug!(
"check_member_constraint: member_region_vid={:?} with value {}",
member_region_vid,
self.region_value_str(member_region_vid),
);
let choice_regions = member_constraints.choice_regions(m_c_i);
debug!("check_member_constraint: choice_regions={:?}", choice_regions);
// Did the member region wind up equal to any of the option regions?
if let Some(o) = choice_regions.iter().find(|&&o_r| {
self.eval_equal(o_r, m_c.member_region_vid)
}) {
debug!("check_member_constraint: evaluated as equal to {:?}", o);
continue;
}
// If not, report an error.
let region_scope_tree = &infcx.tcx.region_scope_tree(mir_def_id);
let member_region = infcx.tcx.mk_region(ty::ReVar(member_region_vid));
opaque_types::unexpected_hidden_region_diagnostic(
infcx.tcx,
Some(region_scope_tree),
m_c.opaque_type_def_id,
m_c.hidden_ty,
member_region,
)
.buffer(errors_buffer);
}
}
}
impl<'tcx> RegionDefinition<'tcx> {
@ -1358,11 +1594,7 @@ impl<'tcx> RegionDefinition<'tcx> {
_ => NLLRegionVariableOrigin::Existential,
};
Self {
origin,
universe,
external_name: None,
}
Self { origin, universe, external_name: None }
}
}
@ -1372,7 +1604,7 @@ pub trait ClosureRegionRequirementsExt<'tcx> {
tcx: TyCtxt<'tcx>,
closure_def_id: DefId,
closure_substs: SubstsRef<'tcx>,
) -> Vec<QueryRegionConstraint<'tcx>>;
) -> Vec<QueryOutlivesConstraint<'tcx>>;
fn subst_closure_mapping<T>(
&self,
@ -1402,7 +1634,7 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx
tcx: TyCtxt<'tcx>,
closure_def_id: DefId,
closure_substs: SubstsRef<'tcx>,
) -> Vec<QueryRegionConstraint<'tcx>> {
) -> Vec<QueryOutlivesConstraint<'tcx>> {
debug!(
"apply_requirements(closure_def_id={:?}, closure_substs={:?})",
closure_def_id, closure_substs
@ -1465,10 +1697,7 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx
if let ty::ReClosureBound(vid) = r {
closure_mapping[*vid]
} else {
bug!(
"subst_closure_mapping: encountered non-closure bound free region {:?}",
r
)
bug!("subst_closure_mapping: encountered non-closure bound free region {:?}", r)
}
})
}

View File

@ -3,7 +3,8 @@ use crate::borrow_check::nll::region_infer::TypeTest;
use crate::borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints};
use crate::borrow_check::nll::universal_regions::UniversalRegions;
use crate::borrow_check::nll::ToRegionVid;
use rustc::infer::canonical::QueryRegionConstraint;
use rustc::infer::canonical::QueryRegionConstraints;
use rustc::infer::canonical::QueryOutlivesConstraint;
use rustc::infer::outlives::env::RegionBoundPairs;
use rustc::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate};
use rustc::infer::region_constraints::{GenericKind, VerifyBound};
@ -49,13 +50,33 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
}
}
pub(super) fn convert_all(&mut self, query_constraints: &[QueryRegionConstraint<'tcx>]) {
for query_constraint in query_constraints {
pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) {
debug!("convert_all(query_constraints={:#?})", query_constraints);
let QueryRegionConstraints { outlives, member_constraints } = query_constraints;
// Annoying: to invoke `self.to_region_vid`, we need access to
// `self.constraints`, but we also want to be mutating
// `self.member_constraints`. For now, just swap out the value
// we want and replace at the end.
let mut tmp = std::mem::replace(
&mut self.constraints.member_constraints,
Default::default(),
);
for member_constraint in member_constraints {
tmp.push_constraint(
member_constraint,
|r| self.to_region_vid(r),
);
}
self.constraints.member_constraints = tmp;
for query_constraint in outlives {
self.convert(query_constraint);
}
}
pub(super) fn convert(&mut self, query_constraint: &QueryRegionConstraint<'tcx>) {
pub(super) fn convert(&mut self, query_constraint: &QueryOutlivesConstraint<'tcx>) {
debug!("generate: constraints at: {:#?}", self.locations);
// Extract out various useful fields we'll need below.

View File

@ -2,7 +2,7 @@ use crate::borrow_check::nll::type_check::constraint_conversion;
use crate::borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints};
use crate::borrow_check::nll::universal_regions::UniversalRegions;
use crate::borrow_check::nll::ToRegionVid;
use rustc::infer::canonical::QueryRegionConstraint;
use rustc::infer::canonical::QueryRegionConstraints;
use rustc::infer::outlives::free_region_map::FreeRegionRelations;
use rustc::infer::region_constraints::GenericKind;
use rustc::infer::InferCtxt;
@ -287,7 +287,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
self.relations.relate_universal_regions(fr, fr_fn_body);
}
for data in constraint_sets {
for data in &constraint_sets {
constraint_conversion::ConstraintConversion::new(
self.infcx,
&self.universal_regions,
@ -297,7 +297,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
Locations::All(DUMMY_SP),
ConstraintCategory::Internal,
&mut self.constraints,
).convert_all(&data);
).convert_all(data);
}
CreateResult {
@ -311,7 +311,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
/// either the return type of the MIR or one of its arguments. At
/// the same time, compute and add any implied bounds that come
/// from this local.
fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option<Rc<Vec<QueryRegionConstraint<'tcx>>>> {
fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option<Rc<QueryRegionConstraints<'tcx>>> {
debug!("add_implied_bounds(ty={:?})", ty);
let (bounds, constraints) =
self.param_env

View File

@ -1,5 +1,5 @@
use crate::borrow_check::location::LocationTable;
use crate::borrow_check::nll::constraints::ConstraintSet;
use crate::borrow_check::nll::constraints::OutlivesConstraintSet;
use crate::borrow_check::nll::facts::{AllFacts, AllFactsExt};
use crate::borrow_check::nll::region_infer::values::RegionValueElements;
use crate::borrow_check::nll::universal_regions::UniversalRegions;
@ -107,7 +107,7 @@ fn compute_live_locals(
fn regions_that_outlive_free_regions(
num_region_vars: usize,
universal_regions: &UniversalRegions<'tcx>,
constraint_set: &ConstraintSet,
constraint_set: &OutlivesConstraintSet,
) -> FxHashSet<RegionVid> {
// Build a graph of the outlives constraints thus far. This is
// a reverse graph, so for each constraint `R1: R2` we have an

View File

@ -6,7 +6,7 @@ use crate::borrow_check::nll::type_check::TypeChecker;
use crate::dataflow::indexes::MovePathIndex;
use crate::dataflow::move_paths::MoveData;
use crate::dataflow::{FlowAtLocation, FlowsAtLocation, MaybeInitializedPlaces};
use rustc::infer::canonical::QueryRegionConstraint;
use rustc::infer::canonical::QueryRegionConstraints;
use rustc::mir::{BasicBlock, ConstraintCategory, Local, Location, Body};
use rustc::traits::query::dropck_outlives::DropckOutlivesResult;
use rustc::traits::query::type_op::outlives::DropckOutlives;
@ -88,7 +88,7 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> {
struct DropData<'tcx> {
dropck_result: DropckOutlivesResult<'tcx>,
region_constraint_data: Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>,
region_constraint_data: Option<Rc<QueryRegionConstraints<'tcx>>>,
}
struct LivenessResults<'me, 'typeck, 'flow, 'tcx> {

View File

@ -4,7 +4,8 @@
use crate::borrow_check::borrow_set::BorrowSet;
use crate::borrow_check::location::LocationTable;
use crate::borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint};
use crate::borrow_check::nll::constraints::{OutlivesConstraintSet, OutlivesConstraint};
use crate::borrow_check::nll::member_constraints::MemberConstraintSet;
use crate::borrow_check::nll::facts::AllFacts;
use crate::borrow_check::nll::region_infer::values::LivenessValues;
use crate::borrow_check::nll::region_infer::values::PlaceholderIndex;
@ -23,7 +24,7 @@ use crate::dataflow::MaybeInitializedPlaces;
use either::Either;
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::infer::canonical::QueryRegionConstraint;
use rustc::infer::canonical::QueryRegionConstraints;
use rustc::infer::outlives::env::RegionBoundPairs;
use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin};
use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@ -127,7 +128,8 @@ pub(crate) fn type_check<'tcx>(
placeholder_indices: PlaceholderIndices::default(),
placeholder_index_to_region: IndexVec::default(),
liveness_constraints: LivenessValues::new(elements.clone()),
outlives_constraints: ConstraintSet::default(),
outlives_constraints: OutlivesConstraintSet::default(),
member_constraints: MemberConstraintSet::default(),
closure_bounds_mapping: Default::default(),
type_tests: Vec::default(),
};
@ -215,7 +217,7 @@ fn translate_outlives_facts(cx: &mut BorrowCheckContext<'_, '_>) {
let location_table = cx.location_table;
facts
.outlives
.extend(cx.constraints.outlives_constraints.iter().flat_map(
.extend(cx.constraints.outlives_constraints.outlives().iter().flat_map(
|constraint: &OutlivesConstraint| {
if let Some(from_location) = constraint.locations.from_location() {
Either::Left(iter::once((
@ -582,7 +584,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
);
let locations = location.to_locations();
for constraint in constraints.iter() {
for constraint in constraints.outlives().iter() {
let mut constraint = *constraint;
constraint.locations = locations;
if let ConstraintCategory::Return
@ -834,6 +836,7 @@ struct TypeChecker<'a, 'tcx> {
infcx: &'a InferCtxt<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
last_span: Span,
body: &'a Body<'tcx>,
/// User type annotations are shared between the main MIR and the MIR of
/// all of the promoted items.
user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>,
@ -884,7 +887,9 @@ crate struct MirTypeckRegionConstraints<'tcx> {
/// hence it must report on their liveness constraints.
crate liveness_constraints: LivenessValues<RegionVid>,
crate outlives_constraints: ConstraintSet,
crate outlives_constraints: OutlivesConstraintSet,
crate member_constraints: MemberConstraintSet<'tcx, RegionVid>,
crate closure_bounds_mapping:
FxHashMap<Location, FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>>,
@ -992,6 +997,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
infcx,
last_span: DUMMY_SP,
mir_def_id,
body,
user_type_annotations: &body.user_type_annotations,
param_env,
region_bound_pairs,
@ -1093,7 +1099,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
&mut self,
locations: Locations,
category: ConstraintCategory,
data: &[QueryRegionConstraint<'tcx>],
data: &QueryRegionConstraints<'tcx>,
) {
debug!(
"push_region_constraints: constraints generated at {:?} are {:#?}",
@ -1109,7 +1115,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
locations,
category,
&mut self.borrowck_context.constraints,
).convert_all(&data);
).convert_all(data);
}
/// Convenient wrapper around `relate_tys::relate_types` -- see
@ -1229,6 +1235,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let infcx = self.infcx;
let tcx = infcx.tcx;
let param_env = self.param_env;
let body = self.body;
debug!("eq_opaque_type_and_type: mir_def_id={:?}", self.mir_def_id);
let opaque_type_map = self.fully_perform_op(
locations,
@ -1244,6 +1251,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
dummy_body_id,
param_env,
&anon_ty,
locations.span(body),
));
debug!(
"eq_opaque_type_and_type: \
@ -2508,10 +2516,20 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
location: Location,
) -> ty::InstantiatedPredicates<'tcx> {
if let Some(closure_region_requirements) = tcx.mir_borrowck(def_id).closure_requirements {
let closure_constraints =
closure_region_requirements.apply_requirements(tcx, def_id, substs);
let closure_constraints = QueryRegionConstraints {
outlives: closure_region_requirements.apply_requirements(tcx, def_id, substs),
// Presently, closures never propagate member
// constraints to their parents -- they are enforced
// locally. This is largely a non-issue as member
// constraints only come from `-> impl Trait` and
// friends which don't appear (thus far...) in
// closures.
member_constraints: vec![],
};
let bounds_mapping = closure_constraints
.outlives
.iter()
.enumerate()
.filter_map(|(idx, constraint)| {

View File

@ -17,6 +17,7 @@ use rustc::infer::canonical::{
CanonicalVarValues,
OriginalQueryValues,
QueryResponse,
QueryRegionConstraints,
Certainty,
};
use rustc::traits::{
@ -151,14 +152,14 @@ impl context::AggregateOps<ChalkArenas<'tcx>> for ChalkContext<'tcx> {
let solution = constrained_subst.unchecked_map(|cs| match ambiguous {
true => QueryResponse {
var_values: cs.subst.make_identity(self.tcx),
region_constraints: Vec::new(),
region_constraints: QueryRegionConstraints::default(),
certainty: Certainty::Ambiguous,
value: (),
},
false => QueryResponse {
var_values: cs.subst,
region_constraints: Vec::new(),
region_constraints: QueryRegionConstraints::default(),
// FIXME: restore this later once we get better at handling regions
// region_constraints: cs.constraints

View File

@ -856,7 +856,8 @@ fn typeck_tables_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx ty::TypeckT
let revealed_ty = if tcx.features().impl_trait_in_bindings {
fcx.instantiate_opaque_types_from_value(
id,
&expected_type
&expected_type,
body.value.span,
)
} else {
expected_type
@ -962,7 +963,8 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
let revealed_ty = if self.fcx.tcx.features().impl_trait_in_bindings {
self.fcx.instantiate_opaque_types_from_value(
self.parent_id,
&o_ty
&o_ty,
ty.span,
)
} else {
o_ty
@ -1058,7 +1060,11 @@ fn check_fn<'a, 'tcx>(
let declared_ret_ty = fn_sig.output();
fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
let revealed_ret_ty = fcx.instantiate_opaque_types_from_value(fn_id, &declared_ret_ty);
let revealed_ret_ty = fcx.instantiate_opaque_types_from_value(
fn_id,
&declared_ret_ty,
decl.output.span(),
);
fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(revealed_ret_ty)));
fn_sig = fcx.tcx.mk_fn_sig(
fn_sig.inputs().iter().cloned(),
@ -2445,6 +2451,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
parent_id: hir::HirId,
value: &T,
value_span: Span,
) -> T {
let parent_def_id = self.tcx.hir().local_def_id_from_hir_id(parent_id);
debug!("instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})",
@ -2457,6 +2464,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.body_id,
self.param_env,
value,
value_span,
)
);

View File

@ -570,6 +570,9 @@ declare_features! (
// Allows explicit discriminants on non-unit enum variants.
(active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None),
// Allows `impl Trait` with multiple unrelated lifetimes.
(active, member_constraints, "1.37.0", Some(61977), None),
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------

View File

@ -389,6 +389,7 @@ symbols! {
match_beginning_vert,
match_default_bindings,
may_dangle,
member_constraints,
message,
meta,
min_const_fn,

View File

@ -1,19 +0,0 @@
// edition:2018
#![feature(arbitrary_self_types, async_await, await_macro, pin)]
use std::ops::Add;
async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {}
//~^ ERROR ambiguous lifetime bound in `async fn`
async fn multiple_hrtb_and_single_named_lifetime_ok<'c>(
_: impl for<'a> Add<&'a u8>,
_: impl for<'b> Add<&'b u8>,
_: &'c u8,
) {}
async fn multiple_elided_lifetimes(_: &u8, _: &u8) {}
//~^ ambiguous lifetime bound in `async fn`
fn main() {}

View File

@ -1,20 +0,0 @@
error: ambiguous lifetime bound in `async fn`
--> $DIR/async-fn-multiple-lifetimes.rs:7:65
|
LL | async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {}
| ^ neither `'a` nor `'b` outlives the other
|
= note: multiple unrelated lifetimes are not allowed in `async fn`.
= note: if you're using argument-position elided lifetimes, consider switching to a single named lifetime.
error: ambiguous lifetime bound in `async fn`
--> $DIR/async-fn-multiple-lifetimes.rs:16:52
|
LL | async fn multiple_elided_lifetimes(_: &u8, _: &u8) {}
| ^ the elided lifetimes here do not outlive one another
|
= note: multiple unrelated lifetimes are not allowed in `async fn`.
= note: if you're using argument-position elided lifetimes, consider switching to a single named lifetime.
error: aborting due to 2 previous errors

View File

@ -0,0 +1,12 @@
// edition:2018
// run-pass
// Test that we can use async fns with multiple arbitrary lifetimes.
#![feature(async_await)]
async fn multiple_elided_lifetimes(_: &u8, _: &u8) {}
fn main() {
let _ = multiple_elided_lifetimes(&22, &44);
}

View File

@ -0,0 +1,14 @@
// edition:2018
// run-pass
// Test that we can use async fns with multiple arbitrary lifetimes.
#![feature(async_await)]
async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8, _: fn(&u8)) {}
fn gimme(_: &u8) { }
fn main() {
let _ = multiple_named_lifetimes(&22, &44, gimme);
}

View File

@ -0,0 +1,17 @@
// edition:2018
// run-pass
// Test that we can use async fns with multiple arbitrary lifetimes.
#![feature(arbitrary_self_types, async_await, await_macro)]
#![allow(dead_code)]
use std::ops::Add;
async fn multiple_hrtb_and_single_named_lifetime_ok<'c>(
_: impl for<'a> Add<&'a u8>,
_: impl for<'b> Add<&'b u8>,
_: &'c u8,
) {}
fn main() {}

View File

@ -0,0 +1,12 @@
// edition:2018
// run-pass
// Test that we can use async fns with multiple arbitrary lifetimes.
#![feature(arbitrary_self_types, async_await, await_macro)]
async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {}
fn main() {
let _ = multiple_named_lifetimes(&22, &44);
}

View File

@ -0,0 +1,15 @@
// edition:2018
// run-pass
#![feature(async_await)]
async fn lotsa_lifetimes<'a, 'b, 'c>(a: &'a u32, b: &'b u32, c: &'c u32) -> (&'a u32, &'b u32)
where 'b: 'a
{
drop((a, c));
(b, b)
}
fn main() {
let _ = lotsa_lifetimes(&22, &44, &66);
}

View File

@ -0,0 +1,18 @@
// edition:2018
// run-pass
// Test that a feature gate is needed to use `impl Trait` as the
// return type of an async.
#![feature(async_await, member_constraints)]
trait Trait<'a, 'b> { }
impl<T> Trait<'_, '_> for T { }
async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
(a, b)
}
fn main() {
let _ = async_ret_impl_trait(&22, &44);
}

View File

@ -0,0 +1,18 @@
// edition:2018
// Test that a feature gate is needed to use `impl Trait` as the
// return type of an async.
#![feature(async_await)]
trait Trait<'a, 'b> { }
impl<T> Trait<'_, '_> for T { }
async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
//~^ ERROR ambiguous lifetime bound
(a, b)
}
fn main() {
let _ = async_ret_impl_trait(&22, &44);
}

View File

@ -0,0 +1,10 @@
error: ambiguous lifetime bound in `impl Trait`
--> $DIR/ret-impl-trait-no-fg.rs:11:64
|
LL | async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
| ^^^^^^^^^^^^^^^^^^ neither `'a` nor `'b` outlives the other
|
= help: add #![feature(member_constraints)] to the crate attributes to enable
error: aborting due to previous error

View File

@ -0,0 +1,15 @@
error: lifetime may not live long enough
--> $DIR/ret-impl-trait-one.rs:12:80
|
LL | async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> {
| ________________________________--__--__________________________________________^
| | | |
| | | lifetime `'b` defined here
| | lifetime `'a` defined here
LL | |
LL | | (a, b)
LL | | }
| |_^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
error: aborting due to previous error

View File

@ -0,0 +1,27 @@
// edition:2018
// Test that a feature gate is needed to use `impl Trait` as the
// return type of an async.
#![feature(async_await, member_constraints)]
trait Trait<'a> { }
impl<T> Trait<'_> for T { }
// Only `'a` permitted in return type, not `'b`.
async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> {
//~^ ERROR lifetime mismatch
(a, b)
}
// As above, but `'b: 'a`, so return type can be inferred to `(&'a u8,
// &'a u8)`.
async fn async_ret_impl_trait2<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a>
where
'b: 'a,
{
(a, b)
}
fn main() {
}

View File

@ -0,0 +1,11 @@
error[E0623]: lifetime mismatch
--> $DIR/ret-impl-trait-one.rs:12:65
|
LL | async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> {
| ------ ^^^^^^^^^^^^^^
| | |
| | ...but data from `b` is returned here
| this parameter and the return type are declared with different lifetimes...
error: aborting due to previous error

View File

@ -0,0 +1,46 @@
// edition:2018
// Test that we get the expected borrow check errors when an async
// function (which takes multiple lifetimes) only returns data from
// one of them.
#![feature(async_await)]
async fn multiple_named_lifetimes<'a, 'b>(a: &'a u8, _: &'b u8) -> &'a u8 {
a
}
// Both are borrowed whilst the future is live.
async fn future_live() {
let mut a = 22;
let mut b = 44;
let future = multiple_named_lifetimes(&a, &b);
a += 1; //~ ERROR cannot assign
b += 1; //~ ERROR cannot assign
let p = future.await;
drop(p);
}
// Just the return value is live after future is awaited.
async fn just_return_live() {
let mut a = 22;
let mut b = 44;
let future = multiple_named_lifetimes(&a, &b);
let p = future.await;
a += 1; //~ ERROR cannot assign
b += 1;
drop(p);
}
// Once `p` is dead, both `a` and `b` are unborrowed.
async fn after_both_dead() {
let mut a = 22;
let mut b = 44;
let future = multiple_named_lifetimes(&a, &b);
let p = future.await;
drop(p);
a += 1;
b += 1;
}
fn main() { }

View File

@ -0,0 +1,37 @@
error[E0506]: cannot assign to `a` because it is borrowed
--> $DIR/ret-ref.rs:18:5
|
LL | let future = multiple_named_lifetimes(&a, &b);
| -- borrow of `a` occurs here
LL | a += 1;
| ^^^^^^ assignment to borrowed `a` occurs here
LL | b += 1;
LL | let p = future.await;
| ------ borrow later used here
error[E0506]: cannot assign to `b` because it is borrowed
--> $DIR/ret-ref.rs:19:5
|
LL | let future = multiple_named_lifetimes(&a, &b);
| -- borrow of `b` occurs here
LL | a += 1;
LL | b += 1;
| ^^^^^^ assignment to borrowed `b` occurs here
LL | let p = future.await;
| ------ borrow later used here
error[E0506]: cannot assign to `a` because it is borrowed
--> $DIR/ret-ref.rs:30:5
|
LL | let future = multiple_named_lifetimes(&a, &b);
| -- borrow of `a` occurs here
LL | let p = future.await;
LL | a += 1;
| ^^^^^^ assignment to borrowed `a` occurs here
LL | b += 1;
LL | drop(p);
| - borrow later used here
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0506`.

View File

@ -0,0 +1,18 @@
// edition:2018
// run-pass
// Test for async fn where the parameters have distinct lifetime
// parameters that appear in all possible variances.
#![feature(async_await)]
#[allow(dead_code)]
async fn lotsa_lifetimes<'a, 'b, 'c>(_: fn(&'a u8), _: fn(&'b u8) -> &'b u8, _: fn() -> &'c u8) { }
fn take_any(_: &u8) { }
fn identify(x: &u8) -> &u8 { x }
fn give_back() -> &'static u8 { &22 }
fn main() {
let _ = lotsa_lifetimes(take_any, identify, give_back);
}

View File

@ -0,0 +1,9 @@
trait Trait<'a, 'b> { }
impl<T> Trait<'_, '_> for T {}
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> {
//~^ ERROR ambiguous lifetime bound
(x, y)
}
fn main() { }

View File

@ -0,0 +1,10 @@
error: ambiguous lifetime bound in `impl Trait`
--> $DIR/feature-gate-member-constraints.rs:4:43
|
LL | fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> {
| ^^^^^^^^^^^^^^^^^^ neither `'a` nor `'b` outlives the other
|
= help: add #![feature(member_constraints)] to the crate attributes to enable
error: aborting due to previous error

View File

@ -0,0 +1,22 @@
// compile-flags:-Zborrowck=mir
#![feature(member_constraints)]
#![feature(existential_type)]
#[derive(Clone)]
struct CopyIfEq<T, U>(T, U);
impl<T: Copy> Copy for CopyIfEq<T, T> {}
existential type E<'a, 'b>: Sized;
fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> {
//~^ ERROR lifetime may not live long enough
let v = CopyIfEq::<*mut _, *mut _>(&mut {x}, &mut y);
let u = v;
let _: *mut &'a i32 = u.1;
unsafe { let _: &'b i32 = *u.0; }
u.0
}
fn main() {}

View File

@ -0,0 +1,12 @@
error: lifetime may not live long enough
--> $DIR/error-handling.rs:13:56
|
LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> {
| -- lifetime `'a` defined here ^^^^^^^^^ opaque type requires that `'a` must outlive `'static`
help: to allow this `impl Trait` to capture borrowed data with lifetime `'a`, add `'a` as a constraint
|
LL | existential type E<'a, 'b>: Sized; + 'a
|
error: aborting due to previous error

View File

@ -0,0 +1,54 @@
// edition:2018
// run-pass
// revisions: migrate mir
//[mir]compile-flags: -Z borrowck=mir
#![feature(member_constraints)]
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>
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() {}

View File

@ -0,0 +1,19 @@
warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
--> $DIR/inverse-bounds.rs:16:70
|
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e>
| ^^^^^^^^^^^^^^^^^^
|
= note: hidden type `Invert<'_>` captures lifetime '_#8r
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
= note: for more information, try `rustc --explain E0729`
warning: the feature `pin` has been stable since 1.33.0 and no longer requires an attribute to enable
--> $DIR/inverse-bounds.rs:4:60
|
LL | #![feature(arbitrary_self_types, async_await, await_macro, pin)]
| ^^^
|
= note: #[warn(stable_features)] on by default

View File

@ -0,0 +1,29 @@
// edition:2018
// compile-pass
// revisions: migrate mir
//[mir]compile-flags: -Z borrowck=mir
#![feature(member_constraints)]
trait Trait<'a, 'b> { }
impl<T> Trait<'_, '_> for T { }
// Test case where we have elision in the impl trait and we have to
// pick the right region.
// Ultimately `Trait<'x, 'static>`.
fn upper_bounds1(a: &u8) -> impl Trait<'_, 'static> {
(a, a)
}
// Ultimately `Trait<'x, 'x>`, so not really multiple bounds.
fn upper_bounds2(a: &u8) -> impl Trait<'_, '_> {
(a, a)
}
// Kind of a weird annoying case.
fn upper_bounds3<'b>(a: &u8) -> impl Trait<'_, 'b> {
(a, a)
}
fn main() { }

View File

@ -0,0 +1,32 @@
// edition:2018
// compile-pass
// revisions: migrate mir
//[mir]compile-flags: -Z borrowck=mir
#![feature(member_constraints)]
#![feature(existential_type)]
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 in the original values.
existential type Foo<'a, 'b>: Trait<'a, 'b>;
fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> Foo<'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() { }

View File

@ -0,0 +1,29 @@
// edition:2018
// compile-pass
// revisions: migrate mir
//[mir]compile-flags: -Z borrowck=mir
#![feature(member_constraints)]
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 in the 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() { }

View File

@ -0,0 +1,46 @@
// edition:2018
// compile-pass
// revisions: migrate mir
//[mir]compile-flags: -Z borrowck=mir
#![feature(member_constraints)]
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() {}

View File

@ -0,0 +1,9 @@
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>
| ^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0700`.

View File

@ -0,0 +1,38 @@
// edition:2018
#![feature(member_constraints)]
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() {}

View File

@ -0,0 +1,21 @@
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 the scope of call-site for function at 23:1
--> $DIR/ordinary-bounds-unrelated.rs:23:1
|
LL | / {
LL | | // Hidden type `Ordinary<'0>` with constraints:
LL | | //
LL | | // ```
... |
LL | | if condition() { a } else { b }
LL | | }
| |_^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0700`.

View File

@ -0,0 +1,9 @@
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>
| ^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0700`.

View File

@ -0,0 +1,41 @@
// edition:2018
#![feature(member_constraints)]
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 ['a, 'b]
// ```
//
// 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() {}

View File

@ -0,0 +1,21 @@
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 the scope of call-site for function at 22:1
--> $DIR/ordinary-bounds-unsuited.rs:22:1
|
LL | / {
LL | | // We return a value:
LL | | //
LL | | // ```
... |
LL | | if condition() { a } else { b }
LL | | }
| |_^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0700`.

View File

@ -1,10 +1,24 @@
// run-pass
#![feature(member_constraints)]
use std::fmt::Debug;
trait MultiRegionTrait<'a, 'b> {}
impl<'a, 'b> MultiRegionTrait<'a, 'b> for (&'a u32, &'b u32) {}
fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> {
//~^ ERROR ambiguous lifetime bound
// Here we have a constraint that:
//
// (x, y) has type (&'0 u32, &'1 u32)
//
// where
//
// 'a: '0
//
// then we require that `('0 u32, &'1 u32): MultiRegionTrait<'a,
// 'b>`, which winds up imposing a requirement that `'0 = 'a` and
// `'1 = 'b`.
(x, y)
}

View File

@ -1,8 +0,0 @@
error: ambiguous lifetime bound in `impl Trait`
--> $DIR/needs_least_region_or_bound.rs:6:55
|
LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ neither `'a` nor `'b` outlives the other
error: aborting due to previous error