Skip to content

Commit 9f3f69e

Browse files
committedAug 12, 2015
regionck.rs: experimentally adopt a more conservative strategy for
projection outlives relations that prefers not to add extract edges to region graph
1 parent ad700ab commit 9f3f69e

File tree

1 file changed

+73
-106
lines changed

1 file changed

+73
-106
lines changed
 

‎src/librustc_typeck/check/regionck.rs

+73-106
Original file line numberDiff line numberDiff line change
@@ -1580,116 +1580,60 @@ fn param_ty_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
15801580
fn projection_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
15811581
origin: infer::SubregionOrigin<'tcx>,
15821582
region: ty::Region,
1583-
projection_ty: ty::ProjectionTy<'tcx>) {
1583+
projection_ty: ty::ProjectionTy<'tcx>)
1584+
{
15841585
debug!("projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})",
15851586
region, projection_ty, origin);
15861587

1587-
// This is a particularly thorny situation for inference, and for
1588-
// now we don't have a complete solution, we just do the best we
1589-
// can. The problem is that there are multiple ways for `<P0 as
1590-
// TraitRef<P1..Pn>>::Foo: 'r` to be satisfied:
1591-
//
1592-
// 1. If `Pi: 'r` forall i, it is satisfied.
1593-
// 2. If there is a suitable where-clause, it can be satisfied.
1594-
// 3. The trait declaration may declare `'static` bounds on `Foo` as well.
1595-
//
1596-
// The fact that there are so many options here makes this thorny.
1597-
// In the case of parameter relations like `T: 'r`, it's somewhat
1598-
// simpler, because checking such a relation does not affect
1599-
// inference. This is true because the region bounds we can
1600-
// derive for `T` never involve region variables -- they are
1601-
// always free regions. The only place a region variable can come
1602-
// is on the RHS, and in that case, the smaller the region, the
1603-
// better. This means that our current inference, which always
1604-
// infers the smallest region it can, can just be used, and we'll
1605-
// know what the smallest value for `'r` is when it's done. We can
1606-
// then compare that to the regions in the LHS, which are already
1607-
// as big as possible, and we're all done.
1608-
//
1609-
// Projections can in fact be this simple as well. In particular,
1610-
// if the parameters `P0..Pn` do not involve any region variables,
1611-
// that's the same situation.
1612-
//
1613-
// Where things get thorny is when region variables are involved,
1614-
// because in that case relating `Pi: 'r` may influence the
1615-
// inference process, since it could cause `'r` to be inferred to
1616-
// a larger value. But the problem is that if we add that as a
1617-
// constraint into our dataflow graph, we've essentially committed
1618-
// to using option 1 (above) to show that `<P0 as
1619-
// Trait<P1..Pn>>::Foo: 'r` is satisfied, and it may be that
1620-
// Option 1 does not apply, but Option 2 or 3 does. But we can't
1621-
// know that now.
1622-
//
1623-
// For now we choose to accept this. It's a conservative choice,
1624-
// so we can move to a more sophisticated inference model later.
1625-
// And it's sometimes possible to workaround by introducing
1626-
// explicit type parameters or type annotations. But it ain't
1627-
// great!
1628-
1629-
let declared_bounds = projection_declared_bounds(rcx, origin.span(), projection_ty);
1630-
1631-
debug!("projection_must_outlive: declared_bounds={:?}",
1632-
declared_bounds);
1633-
1634-
// If we know that the projection outlives 'static, then we're done here.
1635-
if declared_bounds.contains(&ty::ReStatic) {
1588+
// This case is thorny for inference. The fundamental problem is
1589+
// that there are many cases where we have choice, and inference
1590+
// doesn't like choice (the current region inference in
1591+
// particular). :) First off, we have to choose between using the
1592+
// OutlivesProjectionEnv, OutlivesProjectionTraitDef, and
1593+
// OutlivesProjectionComponent rules, any one of which is
1594+
// sufficient. If there are no inference variables involved, it's
1595+
// not hard to pick the right rule, but if there are, we're in a
1596+
// bit of a catch 22: if we picked which rule we were going to
1597+
// use, we could add constraints to the region inference graph
1598+
// that make it apply, but if we don't add those constraints, the
1599+
// rule might not apply (but another rule might). For now, we err
1600+
// on the side of adding too few edges into the graph.
1601+
1602+
// Compute the bounds we can derive from the environment or trait
1603+
// definition. We know that the projection outlives all the
1604+
// regions in this list.
1605+
let env_bounds = projection_declared_bounds(rcx, origin.span(), projection_ty);
1606+
1607+
debug!("projection_must_outlive: env_bounds={:?}",
1608+
env_bounds);
1609+
1610+
// If we know that the projection outlives 'static, then we're
1611+
// done here.
1612+
if env_bounds.contains(&ty::ReStatic) {
1613+
debug!("projection_must_outlive: 'static as declared bound");
16361614
return;
16371615
}
16381616

1639-
// Determine whether any of regions that appear in the projection
1640-
// were declared as bounds by the user. This is typically a situation
1641-
// like this:
1642-
//
1643-
// trait Foo<'a> {
1644-
// type Bar: 'a;
1645-
// }
1646-
//
1647-
// where we are checking `<T as Foo<'_#0r>>: '_#1r`. In such a
1648-
// case, if we use the conservative rule, we will check that
1649-
// BOTH of the following hold:
1650-
//
1651-
// T: _#1r
1652-
// _#0r: _#1r
1617+
// If declared bounds list is empty, the only applicable rule is
1618+
// OutlivesProjectionComponent. If there are inference variables,
1619+
// then, we can break down the outlives into more primitive
1620+
// components without adding unnecessary edges.
16531621
//
1654-
// This is overkill, since the declared bounds tell us that the
1655-
// the latter is sufficient.
1656-
let intersection_bounds: Vec<_> =
1657-
projection_ty.trait_ref.substs.regions()
1658-
.iter()
1659-
.filter(|r| declared_bounds.contains(r))
1660-
.collect();
1661-
let intersection_bounds_needs_infer =
1662-
intersection_bounds.iter()
1663-
.any(|r| r.needs_infer());
1664-
if intersection_bounds_needs_infer {
1665-
// If the upper bound(s) (`_#0r` in the above example) are
1666-
// region variables, then introduce edges into the inference
1667-
// graph, because we need to ensure that `_#0r` is inferred to
1668-
// something big enough. But if the upper bound has no
1669-
// inference, then fallback (below) to the verify path, where
1670-
// we just check after the fact that it was big enough. This
1671-
// is more flexible, because it only requires that there
1672-
// exists SOME intersection bound that is big enough, whereas
1673-
// this path requires that ALL intersection bounds be big
1674-
// enough.
1675-
debug!("projection_must_outlive: intersection_bounds={:?}",
1676-
intersection_bounds);
1677-
for &r in intersection_bounds {
1678-
rcx.fcx.mk_subr(origin.clone(), region, r);
1679-
}
1680-
return;
1681-
}
1682-
1683-
// If there are no intersection bounds, but there are still
1684-
// inference variables involves, then fallback to the most
1685-
// conservative rule, where we require all components of the
1686-
// projection outlive the bound.
1687-
if
1688-
intersection_bounds.is_empty() && (
1689-
projection_ty.trait_ref.substs.types.iter().any(|t| t.needs_infer()) ||
1690-
projection_ty.trait_ref.substs.regions().iter().any(|r| r.needs_infer()))
1691-
{
1692-
debug!("projection_must_outlive: fallback to rule #1");
1622+
// If there are *no* inference variables, however, we COULD do
1623+
// this, but we choose not to, because the error messages are less
1624+
// good. For example, a requirement like `T::Item: 'r` would be
1625+
// translated to a requirement that `T: 'r`; when this is reported
1626+
// to the user, it will thus say "T: 'r must hold so that T::Item:
1627+
// 'r holds". But that makes it sound like the only way to fix
1628+
// the problem is to add `T: 'r`, which isn't true. So, if there are no
1629+
// inference variables, we use a verify constraint instead of adding
1630+
// edges, which winds up enforcing the same condition.
1631+
let needs_infer = {
1632+
projection_ty.trait_ref.substs.types.iter().any(|t| t.needs_infer()) ||
1633+
projection_ty.trait_ref.substs.regions().iter().any(|r| r.needs_infer())
1634+
};
1635+
if env_bounds.is_empty() && needs_infer {
1636+
debug!("projection_must_outlive: no declared bounds");
16931637

16941638
for &component_ty in &projection_ty.trait_ref.substs.types {
16951639
type_must_outlive(rcx, origin.clone(), component_ty, region);
@@ -1702,9 +1646,32 @@ fn projection_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
17021646
return;
17031647
}
17041648

1705-
// Inform region inference that this generic must be properly
1706-
// bounded.
1707-
let verify_bound = projection_bound(rcx, origin.span(), declared_bounds, projection_ty);
1649+
// If we find that there is a unique declared bound `'b`, and this bound
1650+
// appears in the trait reference, then the best action is to require that `'b:'r`,
1651+
// so do that. This is best no matter what rule we use:
1652+
//
1653+
// - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to
1654+
// the requirement that `'b:'r`
1655+
// - OutlivesProjectionComponent: this would require `'b:'r` in addition to other conditions
1656+
if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) {
1657+
let unique_bound = env_bounds[0];
1658+
debug!("projection_must_outlive: unique declared bound = {:?}", unique_bound);
1659+
if projection_ty.trait_ref.substs.regions()
1660+
.iter()
1661+
.any(|r| env_bounds.contains(r))
1662+
{
1663+
debug!("projection_must_outlive: unique declared bound appears in trait ref");
1664+
rcx.fcx.mk_subr(origin.clone(), region, unique_bound);
1665+
return;
1666+
}
1667+
}
1668+
1669+
// Fallback to verifying after the fact that there exists a
1670+
// declared bound, or that all the components appearing in the
1671+
// projection outlive; in some cases, this may add insufficient
1672+
// edges into the inference graph, leading to inference failures
1673+
// even though a satisfactory solution exists.
1674+
let verify_bound = projection_bound(rcx, origin.span(), env_bounds, projection_ty);
17081675
let generic = GenericKind::Projection(projection_ty);
17091676
rcx.fcx.infcx().verify_generic_bound(origin, generic.clone(), region, verify_bound);
17101677
}

0 commit comments

Comments
 (0)