Skip to content

borrowck dataflow: respect borrow shallowness when killing borrows that conflict with assignments - #160902

Open
dianne wants to merge 1 commit into
rust-lang:mainfrom
dianne:preserve-shallow
Open

borrowck dataflow: respect borrow shallowness when killing borrows that conflict with assignments#160902
dianne wants to merge 1 commit into
rust-lang:mainfrom
dianne:preserve-shallow

Conversation

@dianne

@dianne dianne commented Aug 11, 2026

Copy link
Copy Markdown
Member

It's not a conflict to write to a sub-place of a shallowly borrowed place, so it's unsound to kill shallow borrows when we encounter assignments to their sub-places.

Fixes #160599 on the default borrow checker and under -Zpolonius=next. I haven't touched -Zpolonius=legacy since I'm not sure how much we're maintaining it, but at a glance the fix looks like it would be the same.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Aug 11, 2026
place,
self.borrow_set[i].kind,
place.as_ref(),
AccessDepth::Deep,

@dianne dianne Aug 11, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied the AccessDepth::Deep from places_conflict::places_conflict, so this will have the same behavior as before in that regard, but I'm wary about it. kill_borrows_on_place is called on assignments and StorageDeads, which are shallow accesses. Consequently, definitely_conflicting_borrows can contain non-conflicting borrows, which are then killed. e.g.

fn example(x: &mut u8, something_else: &mut u8) {
    let mut y = (x,);
    // This introduces a borrow of `*y.0`.
    let z = &mut *y.0;
    // This kills the borrow of `*y.0`, despite it not conflicting with that.
    y.0 = something_else;
    // At this point, no borrows are in scope, according to `borrows_in_scope`.
    z;
}

I haven't been able to coax unsoundness out of it, but it feels strange. I tried changing it to AccessDepth::Shallow(None) to see what would happen, but it broke some tests that rely on non-conflicting borrows being killed, e.g. tests/ui/borrowck/issue-62007-assign-box.rs and tests/ui/borrowck/issue-62007-assign-field.rs. As I understand it, the borrows there go through derefs, so the assignments don't technically conflict with them (as in the above example), but they need to be killed for the loops to work.

The fast path for assignments to locals also seems not to account for assignments being shallow accesses. e.g.

fn example(x: &mut u8, something_else: &mut u8) {
    let mut y = x;
    // This introduces a borrow of `*y`.
    let z = &mut *y;
    // This kills the borrow of `*y`, despite it not conflicting with that.
    y = something_else;
    // At this point, no borrows are in scope, according to `borrows_in_scope`.
    z;
}

Maybe I'm missing something?

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely a part of borrowck with a confusing implementation. The check here is looking for cases where the assignment overwrites the reference to the borrowed place. The assumption is that this would be cases where there is a conflicts with AccessDepth::Deep but not AccessDepth::Shallow(None). The reason that the kill only checks AccessDepth::Deep here is that the check with AccessDepth::Shallow(None) in visit_after_early_statement_effect is done on the borrow state before the kill happens, so there's an error either way.

@dianne

dianne commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

I thought it'd be easier to ask for forgiveness than permission to fix this myself, but after digging into the parts of Borrows::kill_borrows_on_place I didn't change, I've started to have doubts about that. I'd already written the PR, though. Forgive me!

r? types

@rustbot rustbot added the T-types Relevant to the types team, which will review and decide on the PR/issue. label Aug 11, 2026
@dianne
dianne marked this pull request as ready for review August 11, 2026 13:21
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 11, 2026
@oli-obk

oli-obk commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

r? @lqd but happy to take it again if you think it's general enough to not need your borrowck and polonius expertise

@rustbot rustbot assigned lqd and unassigned oli-obk Aug 12, 2026
@lqd

lqd commented Aug 18, 2026

Copy link
Copy Markdown
Member

@oli-obk I've looked it over, but I'm also not an expert on shallowness, so while this change does make sense to me I would love an additional pair of eyes like yours (or maybe @matthewjasper's) to spot check it as well.

@theemathas theemathas added needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. needs-crater This change needs a crater run to check for possible breakage in the ecosystem. labels Aug 18, 2026
@oli-obk

oli-obk commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

@bors try

let's also crater it

@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 18, 2026
borrowck dataflow: respect borrow shallowness when killing borrows that conflict with assignments
@rust-bors

rust-bors Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: c0dfa9a (c0dfa9ae292642b7683cf7162ffb6880170df098)
Base parent: 444dcce (444dcce484c66c8d626806a8556be3b4d3ef381a)

@lqd

lqd commented Aug 18, 2026

Copy link
Copy Markdown
Member

@craterbot check

@craterbot

Copy link
Copy Markdown
Collaborator

👌 Experiment pr-160902 created and queued.
🤖 Automatically detected try build c0dfa9a
🔍 You can check out the queue and this experiment's details.

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-crater Status: Waiting on a crater run to be completed. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 18, 2026
@theemathas theemathas mentioned this pull request Sep 3, 2026
@theemathas

Copy link
Copy Markdown
Contributor

@craterbot cancel

See #162233

@craterbot

Copy link
Copy Markdown
Collaborator

🗑️ Experiment pr-160902 deleted!

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-crater Status: Waiting on a crater run to be completed. labels Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-crater This change needs a crater run to check for possible breakage in the ecosystem. needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Assignment in match guard leads to unsoundness

7 participants