Skip to content

Fix panicking_unwrap FP on field access with implicit deref#16196

Merged
llogiq merged 1 commit intorust-lang:masterfrom
profetia:issue16188
Dec 5, 2025
Merged

Fix panicking_unwrap FP on field access with implicit deref#16196
llogiq merged 1 commit intorust-lang:masterfrom
profetia:issue16188

Conversation

@profetia
Copy link
Member

@profetia profetia commented Dec 5, 2025

Closes #16188


changelog: [panicking_unwrap] fix FP on field access with implicit deref

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Dec 5, 2025
@rustbot
Copy link
Collaborator

rustbot commented Dec 5, 2025

r? @llogiq

rustbot has assigned @llogiq.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@github-actions
Copy link

github-actions bot commented Dec 5, 2025

Lintcheck changes for c905503

Lint Added Removed Changed
clippy::unnecessary_unwrap 0 2 0

This comment will be updated if you push new changes

Copy link
Contributor

@llogiq llogiq left a comment

Choose a reason for hiding this comment

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

@llogiq llogiq added this pull request to the merge queue Dec 5, 2025
Merged via the queue into rust-lang:master with commit c59e7a9 Dec 5, 2025
16 of 19 checks passed
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Dec 5, 2025
Comment on lines +183 to +195
let field_projections = place
.projections
.iter()
.filter(|proj| matches!(proj.kind, ProjectionKind::Field(_, _)))
.collect::<Vec<_>>();
is_potentially_local_place(*local_id, place)
// If there were projections other than field projections, err on the side of caution and say that they
// _might_ be mutating something.
//
// The reason we use `<=` and not `==` is that a mutation of `struct` or `struct.field1` should count as
// mutation of the child fields such as `struct.field1.field2`
&& place.projections.len() <= field_indices.len()
&& iter::zip(&place.projections, field_indices.iter().copied().rev()).all(|(proj, field_idx)| {
&& field_projections.len() <= field_indices.len()
&& iter::zip(&field_projections, field_indices.iter().copied().rev()).all(|(proj, field_idx)| {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does this work though?... It definitely no longer aligns with what the comment is describing

Copy link
Contributor

Choose a reason for hiding this comment

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

I think what happened here is that I got my conditions wrong: when an non-field projections appears, we're supposed to return true, as it might be modifying something somehow -- but what the code was actually doing is returning false inside the .all(), meaning that the whole function would return false as well.

I came up with the following patch, which goes through each case a bit more thouroughly -- what do you think?

{
  if !is_potentially_local_place(*local_id, place) {
      // This can't be a local place -- no point checking any further
      return false;
  }
  
  let mut field_indices = field_indices.iter().rev().copied();
  for proj in &place.projections {
      let ProjectionKind::Field(f_idx, _) = proj.kind else {
          // This is a projection we didn't expect -- it _might_ be mutating something
          return true;
      };
  
      let Some(field_idx) = field_indices.next() else {
          // The field projections are going deeper than our `Local` -- a mutation in there
          // can't change whether the `Local` is the success or error variant
          return false;
      };
  
      if f_idx != field_idx {
          // The projection is going into an unrelated field -- it won't mutate our `Local`
          return false;
      }
  }
  
  // There are no more projections, and we haven't strayed off the field access chain that our `Local`
  // is located on, so a mutation of this place might mutate our `Local`
  true
}

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't really get your point, but for the case of the issue, there is a deref before the field access

Copy link
Contributor

Choose a reason for hiding this comment

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

What I'm trying to say is that just filtering out non-field projections might be dangerous, as they might end up modifying the Local... somehow. So I think we should instead early-return when encountering them. The patch above achieves that using the first let-else

@profetia profetia deleted the issue16188 branch January 24, 2026 20:56
@flip1995 flip1995 added the beta-nominated Nominated for backporting to the compiler in the beta channel. label Jan 26, 2026
rust-bors bot pushed a commit to rust-lang/rust that referenced this pull request Feb 11, 2026
[stable] prepare Rust 1.93.1

This includes the following backported fixes:

- Don't try to recover keyword as non-keyword identifier #150590
- Fix `panicking_unwrap` FP on field access with implicit deref rust-lang/rust-clippy#16196
- Revert "Update wasm-related dependencies in CI" #152259

And these are just to avoid recent CI issues:

- Remove rustdoc GUI flaky test #152116
- Remove the 4 failing tests from rustdoc-gui #152194

r? @rust-lang/release
@rustbot ping relnotes-interest-group
rust-bors bot pushed a commit to rust-lang/rust that referenced this pull request Feb 11, 2026
[stable] prepare Rust 1.93.1

This includes the following backported fixes:

- Don't try to recover keyword as non-keyword identifier #150590
- Fix `panicking_unwrap` FP on field access with implicit deref rust-lang/rust-clippy#16196
- Revert "Update wasm-related dependencies in CI" #152259

And these are just to avoid recent CI issues:

- Remove rustdoc GUI flaky test #152116
- Remove the 4 failing tests from rustdoc-gui #152194

r? @rust-lang/release
@rustbot ping relnotes-interest-group
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

beta-nominated Nominated for backporting to the compiler in the beta channel.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect clippy::panicking_unwrap on nightly

5 participants