Skip to content

unnecessary closure constraint propagation V2 #154267

@LorrensP-2158466

Description

@LorrensP-2158466

I tried this code (credit to @lcnr):

struct Arg<'a: 'c, 'b: 'c, 'c, T> {
    field: *mut (&'a (), &'b (), &'c (), T),
}

impl<'a, 'b, 'c, T> Arg<'a, 'b, 'c, T> {
    fn constrain(self)
    where
        T: 'a,
    {
    }
}

fn takes_closure<'a, 'b, T>(_: impl for<'c> FnOnce(Arg<'a, 'b, 'c, T>)) {}

fn error<'a, 'b, T: 'a>() {
    takes_closure::<'a, 'b, T>(|arg| arg.constrain());
}

fn main() {}

This should compile fine, but instead this error is reported:

error[E0309]: the parameter type `T` may not live long enough
  --> src/lib.rs:19:38
   |
18 | fn error<'a, 'b, T: 'a>() {
   |              -- the parameter type `T` must be valid for the lifetime `'b` as defined here...
19 |     takes_closure::<'a, 'b, T>(|arg| arg.constrain());
   |                                      ^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
   |
help: consider adding an explicit lifetime bound
   |
18 | fn error<'a, 'b, T: 'a + 'b>() {
   |                        ++++

For more information about this error, try `rustc --explain E0309`.

So what's happening here is that during the type test T: 'a we have to propagate some requirements so we can prove the type test. This happens here:

let mut found_outlived_universal_region = false;
for ur in self.scc_values.universal_regions_outlived_by(r_scc) {
found_outlived_universal_region = true;
debug!("universal_region_outlived_by ur={:?}", ur);
let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur);
debug!(?non_local_ub);
// This is slightly too conservative. To show T: '1, given `'2: '1`
// and `'3: '1` we only need to prove that T: '2 *or* T: '3, but to
// avoid potential non-determinism we approximate this by requiring
// T: '1 and T: '2.
for upper_bound in non_local_ub {
debug_assert!(self.universal_regions().is_universal_region(upper_bound));
debug_assert!(!self.universal_regions().is_local_free_region(upper_bound));
let requirement = ClosureOutlivesRequirement {
subject,
outlived_free_region: upper_bound,
blame_span,
category: ConstraintCategory::Boring,
};
debug!(?requirement, "adding closure requirement");
propagated_outlives_requirements.push(requirement);
}
}

We loop over lower_bound_universal_regions of 'a, these are ['a, 'c].

Then for each of these regions we loop over their non_local_upper_bounds and propagate T: 'ub. Nothing special happens with 'abut it does with 'c. Its non_local_upper_bounds are ['a, 'b] and we thus propagate T: 'a and T: 'b, propagating T: 'b is incorrect and results in the above error.

Metadata

Metadata

Labels

A-borrow-checkerArea: The borrow checkerA-closuresArea: Closures (`|…| { … }`)A-higher-rankedArea: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs)A-lifetimesArea: Lifetimes / regionsC-bugCategory: This is a bug.T-typesRelevant to the types team, which will review and decide on the PR/issue.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions