Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Erroneous borrowck error with early returns #58910

Open
nox opened this issue Mar 4, 2019 · 3 comments
Open

Erroneous borrowck error with early returns #58910

nox opened this issue Mar 4, 2019 · 3 comments
Labels
A-borrow-checker Area: The borrow checker A-NLL Area: Non-lexical lifetimes (NLL) C-enhancement Category: An issue proposing an enhancement or a PR with one. fixed-by-polonius Compiling with `-Zpolonius` fixes this issue. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@nox
Copy link
Contributor

nox commented Mar 4, 2019

Playground

use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Error, Field, Fields};

// Uncommenting the two lines below trigger a borrowck error.

pub fn struct_fields_mut(
    input: &mut DeriveInput,
) -> Result<impl Iterator<Item = &mut Field>, Error> {
    if let Data::Struct(ref mut data_struct) = input.data {
        //if let Fields::Named(_) = data_struct.fields {
            return Ok(data_struct.fields.iter_mut());
        //}
    }
    Err(Error::new(
        input.span(),
        "only structs can be automatically neutralized",
    ))
}
error[E0502]: cannot borrow `input` as immutable because it is also borrowed as mutable
  --> src/lib.rs:15:9
   |
7  |     input: &mut DeriveInput,
   |            - let's call the lifetime of this reference `'1`
8  | ) -> Result<impl Iterator<Item = &mut Field>, Error> {
9  |     if let Data::Struct(ref mut data_struct) = input.data {
   |                         ------------------- mutable borrow occurs here
10 |         if let Fields::Named(_) = data_struct.fields {
11 |             return Ok(data_struct.fields.iter_mut());
   |                    --------------------------------- returning this value requires that `input.data.0` is borrowed for `'1`
...
15 |         input.span(),
   |         ^^^^^ immutable borrow occurs here
@pnkfelix pnkfelix added A-NLL Area: Non-lexical lifetimes (NLL) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. NLL-complete Working towards the "valid code works" goal labels Mar 4, 2019
@nikomatsakis nikomatsakis added NLL-polonius Issues related for using Polonius in the borrow checker and removed NLL-complete Working towards the "valid code works" goal labels Mar 4, 2019
@nikomatsakis
Copy link
Contributor

This, I believe, is the case that we cut out from NLL (what I called "Problem case #3" in my initial blog post) -- specifically, the dreaded "conditionally return borrow". The pattern you have there is:

  • Unconditionally create a borrow at point A (in this case, data_struct, which borrows input)
  • Conditionally return that borrow at point B (in this case, via data_structu.fields.iter_mut())

In that case, with NLL as currently implemented, the borrow (from point A) is extended until the end of the function. This means the borrow is considered ins cope from point A until the end of the function, which in this case includes input.span().

TL;DR: Polonius is supposed to fix this.

@nikomatsakis
Copy link
Contributor

The workaround btw is something like this (playground):

use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Error, Field, Fields};

// Uncommenting the two lines below trigger a borrowck error.

pub fn struct_fields_mut(
    input: &mut DeriveInput,
) -> Result<impl Iterator<Item = &mut Field>, Error> {
    if let Data::Struct(ref data_struct) = input.data {
        if let Fields::Named(_) = data_struct.fields {
            // Repeat the borrow here, where it can never
            // reach the latter use
            if let Data::Struct(ref mut data_struct) = input.data {
                return Ok(data_struct.fields.iter_mut());
            } else {
                panic!()
            }
        }
    }
    Err(Error::new(
        input.span(),
        "only structs can be automatically neutralized",
    ))
}

fn main() { }

@Havvy
Copy link
Contributor

Havvy commented Mar 5, 2019

This looks like a duplicate of #21906. If not, it's a duplicate of #51545.

@crlf0710 crlf0710 added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Jul 3, 2021
@fmease fmease added A-borrow-checker Area: The borrow checker fixed-by-polonius Compiling with `-Zpolonius` fixes this issue. and removed NLL-polonius Issues related for using Polonius in the borrow checker labels Jan 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-borrow-checker Area: The borrow checker A-NLL Area: Non-lexical lifetimes (NLL) C-enhancement Category: An issue proposing an enhancement or a PR with one. fixed-by-polonius Compiling with `-Zpolonius` fixes this issue. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

6 participants