Skip to content

Commit 13d579b

Browse files
committed
introduce the idea of an "approximate match"
In fact, however, we currently always give back the same exact answers as ever. But we don't rely on them being exact anymore.
1 parent db0e626 commit 13d579b

File tree

2 files changed

+56
-39
lines changed

2 files changed

+56
-39
lines changed

src/librustc/infer/outlives/obligations.rs

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -388,20 +388,24 @@ where
388388
// rule might not apply (but another rule might). For now, we err
389389
// on the side of adding too few edges into the graph.
390390

391-
// Compute the bounds we can derive from the environment or trait
392-
// definition. We know that the projection outlives all the
393-
// regions in this list.
394-
let mut declared_bounds = self.verify_bound
395-
.projection_declared_bounds_from_env(projection_ty);
396-
397-
declared_bounds.extend(
398-
self.verify_bound
399-
.projection_declared_bounds_from_trait(projection_ty),
391+
// Compute the bounds we can derive from the environment. This
392+
// is an "approximate" match -- in some cases, these bounds
393+
// may not apply.
394+
let approx_env_bounds = self.verify_bound
395+
.projection_approx_declared_bounds_from_env(projection_ty);
396+
debug!(
397+
"projection_must_outlive: approx_env_bounds={:?}",
398+
approx_env_bounds
400399
);
401400

401+
// Compute the bounds we can derive from the trait definition.
402+
// These are guaranteed to apply, no matter the inference
403+
// results.
404+
let trait_bounds = self.verify_bound
405+
.projection_declared_bounds_from_trait(projection_ty);
402406
debug!(
403-
"projection_must_outlive: declared_bounds={:?}",
404-
declared_bounds
407+
"projection_must_outlive: trait_bounds={:?}",
408+
trait_bounds
405409
);
406410

407411
// If declared bounds list is empty, the only applicable rule is
@@ -419,7 +423,7 @@ where
419423
// inference variables, we use a verify constraint instead of adding
420424
// edges, which winds up enforcing the same condition.
421425
let needs_infer = projection_ty.needs_infer();
422-
if declared_bounds.is_empty() && needs_infer {
426+
if approx_env_bounds.is_empty() && trait_bounds.is_empty() && needs_infer {
423427
debug!("projection_must_outlive: no declared bounds");
424428

425429
for component_ty in projection_ty.substs.types() {
@@ -434,34 +438,31 @@ where
434438
return;
435439
}
436440

437-
// If we find that there is a unique declared bound `'b`, and this bound
438-
// appears in the trait reference, then the best action is to require that `'b:'r`,
439-
// so do that. This is best no matter what rule we use:
441+
// If we found a unique bound `'b` from the trait, and we
442+
// found nothing else from the environment, then the best
443+
// action is to require that `'b: 'r`, so do that.
444+
//
445+
// This is best no matter what rule we use:
440446
//
441-
// - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to
442-
// the requirement that `'b:'r`
443-
// - OutlivesProjectionComponent: this would require `'b:'r` in addition to
444-
// other conditions
445-
if !declared_bounds.is_empty()
446-
&& declared_bounds[1..]
447+
// - OutlivesProjectionEnv: these would translate to the requirement that `'b:'r`
448+
// - OutlivesProjectionTraitDef: these would translate to the requirement that `'b:'r`
449+
// - OutlivesProjectionComponent: this would require `'b:'r`
450+
// in addition to other conditions
451+
if !trait_bounds.is_empty()
452+
&& trait_bounds[1..]
447453
.iter()
448-
.all(|b| *b == declared_bounds[0])
454+
.chain(&approx_env_bounds)
455+
.all(|b| *b == trait_bounds[0])
449456
{
450-
let unique_bound = declared_bounds[0];
457+
let unique_bound = trait_bounds[0];
451458
debug!(
452-
"projection_must_outlive: unique declared bound = {:?}",
459+
"projection_must_outlive: unique trait bound = {:?}",
453460
unique_bound
454461
);
455-
if projection_ty
456-
.substs
457-
.regions()
458-
.any(|r| declared_bounds.contains(&r))
459-
{
460-
debug!("projection_must_outlive: unique declared bound appears in trait ref");
461-
self.delegate
462-
.push_sub_region_constraint(origin.clone(), region, unique_bound);
463-
return;
464-
}
462+
debug!("projection_must_outlive: unique declared bound appears in trait ref");
463+
self.delegate
464+
.push_sub_region_constraint(origin.clone(), region, unique_bound);
465+
return;
465466
}
466467

467468
// Fallback to verifying after the fact that there exists a

src/librustc/infer/outlives/verify.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,18 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
7474

7575
/// Given a projection like `T::Item`, searches the environment
7676
/// for where-clauses like `T::Item: 'a`. Returns the set of
77-
/// regions `'a` that it finds. This is a "conservative" check --
78-
/// it may not find all applicable bounds, but all the bounds it
79-
/// returns can be relied upon.
80-
pub fn projection_declared_bounds_from_env(
77+
/// regions `'a` that it finds.
78+
///
79+
/// This is an "approximate" check -- it may not find all
80+
/// applicable bounds, and not all the bounds it returns can be
81+
/// relied upon. In particular, this check ignores region
82+
/// identity. So, for example, if we have `<T as
83+
/// Trait<'0>>::Item` where `'0` is a region variable, and the
84+
/// user has `<T as Trait<'a>>::Item: 'b` in the environment, then
85+
/// the clause from the environment only applies if `'0 = 'a`,
86+
/// which we don't know yet. But we would still include `'b` in
87+
/// this list.
88+
pub fn projection_approx_declared_bounds_from_env(
8189
&self,
8290
projection_ty: ty::ProjectionTy<'tcx>,
8391
) -> Vec<ty::Region<'tcx>> {
@@ -103,9 +111,11 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
103111
projection_ty
104112
);
105113

114+
// Search the env for where clauses like `P: 'a`.
106115
let mut declared_bounds =
107-
self.projection_declared_bounds_from_env(projection_ty);
116+
self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty));
108117

118+
// Extend with bounds that we can find from the trait.
109119
declared_bounds.extend(
110120
self.projection_declared_bounds_from_trait(projection_ty)
111121
);
@@ -139,6 +149,12 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
139149
}
140150
}
141151

152+
/// Searches the environment for where-clauses like `G: 'a` where
153+
/// `G` is either some type parameter `T` or a projection like
154+
/// `T::Item`. Returns a vector of the `'a` bounds it can find.
155+
///
156+
/// This is a conservative check -- it may not find all applicable
157+
/// bounds, but all the bounds it returns can be relied upon.
142158
fn declared_generic_bounds_from_env(
143159
&self,
144160
generic: GenericKind<'tcx>,

0 commit comments

Comments
 (0)