Skip to content

overly restrictive closure borrow-checking #104477

Open
@aliemjay

Description

@aliemjay
Member

Both of these test cases should pass borrowck: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2015&gist=50a263a9626cb0b08f5cee334df8a852

mod case1 {
    struct MyTy<'x, 'a, 'b>(std::cell::Cell<(&'x &'a u8, &'x &'b u8)>);
    fn wf<T>(_: T) {}
    fn test<'a, 'b>() {
        |_: &'a u8, x: MyTy<'_, 'a, 'b>| wf(x); // FAIL!
        |x: MyTy<'_, 'a, 'b>, _: &'a u8| wf(x); // PASS!!!
    }
}

mod case2 {
    struct MyTy<'a, 'b, 'x>(std::cell::Cell<(&'a &'x u8, &'b &'x u8)>);
    fn wf<T>(_: T) {}
    fn test<'a, 'b>() {
        |x: MyTy<'a, 'b, '_>| wf(x); // FAIL!
    }
}

They both fail because of the unnecessarily restrictive algorithm in try_propagate_universal_region_error:

  • case1 fails because '_ has multiple "non-local upper bounds" ['a, 'b] and we do propagate the error to both of them while choosing a single one would be sufficient.
  • case2 fails because '_ has multiple "non-local lower bounds" ['a, 'b] and the call to non_lcal_lower_bound simply returns None in this case!
  • The different behavior in case1 when swapping arguments is due to the shortcut in
    if representative != longer_fr {
    which is sensitive to region numbering, but I think that's irrelevant here.

@rustbot label C-bug T-types A-NLL NLL-complete A-borrow-checker
@rustbot claim

Activity

added
A-NLLArea: Non-lexical lifetimes (NLL)
C-bugCategory: This is a bug.
NLL-completeWorking towards the "valid code works" goal
T-typesRelevant to the types team, which will review and decide on the PR/issue.
on Nov 16, 2022
Rageking8

Rageking8 commented on Nov 16, 2022

@Rageking8
compiler-errors

compiler-errors commented on Nov 16, 2022

@compiler-errors
lcnr

lcnr commented on Jan 15, 2025

@lcnr
Contributor

This issue also affects type tests (and i expect to this error even if region outlives were fixed)

fn reg_outlives<'a, 'b: 'a>() {}
fn type_outlives<'a, T: 'a>() {}

struct Constrain<'a: 'c, 'b: 'c, 'c: 'a>(*mut (&'a (), &'b (), &'c ()));

impl<'a, 'b, 'c> Constrain<'a, 'b, 'c> {
    fn req(&self)
    where
        'c: 'a,
    {
    }
}

fn promote_type_test<'a, 'b: 'a, T>(x: &'a T) {
    |x: Constrain<'a, 'b, '_>| {
        x.req(); // 'c: 'a
        type_outlives::<'a, T>(); // we've got `T: 'a`
    };
}

// For each region outlived by lower_bound find a non-local,
// universal region (it may be the same region) and add it to
// `ClosureOutlivesRequirement`.
let mut found_outlived_universal_region = false;
for ur in self.scc_values.universal_regions_outlived_by(r_scc) {

There's no reason to look at all lower bounds of our region in case that region is already external.

aliemjay

aliemjay commented on Jan 15, 2025

@aliemjay
MemberAuthor

@lcnr Your issue can be reproduced and fixed independently using this alternative example:

#![feature(closure_lifetime_binder)]

fn type_outlives<'a, T: 'a>() {}

struct Constrain<'a: 'c, 'b: 'c, 'c: 'a>(*mut (&'a (), &'b (), &'c ()));

fn promote_type_test<'a, 'b, T>(_: &'a T) {
    for<'x> |_: Constrain<'a, 'b, 'x>| -> () {
        type_outlives::<'x, T>(); //~ `T` may not live long enough
        // unnecessarily requires T: 'b
    };
}

There's no reason to look at all lower bounds of our region in case that region is already external.

I think the way to fix it is a bit more subtle. Consider the example above where 'x is not external. The real problem is that non_local_upper_bounds('x) incorrectly returns ['a, 'b] when it should return ['a] only.

let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-NLLArea: Non-lexical lifetimes (NLL)A-borrow-checkerArea: The borrow checkerC-bugCategory: This is a bug.NLL-completeWorking towards the "valid code works" goalT-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

        Participants

        @compiler-errors@aliemjay@lcnr@rustbot@Rageking8

        Issue actions

          overly restrictive closure borrow-checking · Issue #104477 · rust-lang/rust