Skip to content

Conversation

@profetia
Copy link
Contributor

@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
Contributor 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

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect clippy::panicking_unwrap on nightly

4 participants