Skip to content

unnecessary closure constraint propagation #148289

@lcnr

Description

@lcnr
#[derive(Clone, Copy)]
struct Inv<'a>(*mut &'a ());
impl<'a> Inv<'a> {
    fn outlived_by<'b: 'a>(self, _: Inv<'b>) {}
}
struct OutlivedBy<'a, 'b: 'a>(Inv<'a>, Inv<'b>);
fn closure_arg<'b, 'c, 'd>(_: impl for<'a> FnOnce(Inv<'a>, OutlivedBy<'a, 'b>, OutlivedBy<'a, 'c>, Inv<'d>)) {}
fn foo<'b, 'c, 'd: 'b>() {
    closure_arg::<'b, 'c, 'd>(|a, b, c, d| {
        a.outlived_by(b.1);
        a.outlived_by(c.1);
        b.1.outlived_by(d);
    });
}

should compile but results in the following error

error: lifetime may not live long enough
  --> src/main.rs:9:5
   |
 8 |   fn foo<'b, 'c, 'd: 'b>() {
   |              --  -- lifetime `'d` defined here
   |              |
   |              lifetime `'c` defined here
 9 | /     closure_arg::<'b, 'c, 'd>(|a, b, c, d| {
10 | |         a.outlived_by(b.1);
11 | |         a.outlived_by(c.1);
12 | |         b.1.outlived_by(d);
13 | |     });
   | |______^ argument requires that `'d` must outlive `'c`
   |
   = help: consider adding the following bound: `'d: 'c`
   = note: requirement occurs because of the type `Inv<'_>`, which makes the generic argument `'_` invariant
   = note: the struct `Inv<'a>` is invariant over the parameter `'a`
   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

What's going on is that the closure has the signature for<'a> fn(Inv<'a>, OutlivedBy<'a, 'b>, OutlivedBy<'a, 'c>, Inv<'d>) where 'b, 'c, and 'd are external regions.

We have assumptions 'b: 'a and 'c: 'a.

Borrow checking the closure results in requirements 'b: 'a, 'c: 'a, 'd: 'b.

for shorter_fr in self.scc_values.universal_regions_outlived_by(longer_fr_scc) {
if let RegionRelationCheckResult::Error = self.check_universal_region_relation(
longer_fr,
shorter_fr,
propagated_outlives_requirements,
) {

fn check_universal_region then checks for every universal region in the closure, whether it transitively outlives any other regions. We check 'b: 'a, 'c: 'a, 'd: 'b AND 'd: 'a (transitive)

'b: 'a and 'c: 'a are implied by our assumptions. We can propagate 'd: 'b to the parent without issues.

Propagating 'd: 'a is scuffed. Figuring out how to propagate this looks at upper bounds on 'a: 'b and 'c and simply requires 'd to outlive both. This is unnecessary and results in the incorrect outlives error in the parent.

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

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions