This is an archive of the discontinued LLVM Phabricator instance.

[clang:diagnostics] Turning off warn_self_assignment_overloaded for user-defined compound assignments
AcceptedPublic

Authored by python3kgae on Mar 25 2023, 10:31 PM.

Details

Summary

Fixes 42469 https://github.com/llvm/llvm-project/issues/42469

Only check self assignment on BO_Assign when BuildOverloadedBinOp.

Diff Detail

Event Timeline

python3kgae created this revision.Mar 25 2023, 10:31 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 25 2023, 10:31 PM
python3kgae requested review of this revision.Mar 25 2023, 10:31 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 25 2023, 10:31 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript
rsmith added inline comments.Apr 4 2023, 11:17 AM
clang/lib/Sema/SemaExpr.cpp
15669

This is the same thing, but for this->x += this->x. I think we should also suppress those warnings for compound assignment, except when one of the operands is an implicit member access and one is an explicit member access (this->x += x; should still warn if the latter x is also interpreted as this->x).

python3kgae added inline comments.Apr 4 2023, 12:06 PM
clang/lib/Sema/SemaExpr.cpp
15669

For case like this:

class Vector {
public:
    Vector& operator+=(const Vector &v) { return *this; }
    Vector& operator-=(const Vector &v) { return *this; }
};

class A {
public:
  A(){}
  Vector x;
  void foo() {
     this->x -= this->x;
     this->x -= x;
     this->x += this->x;
     this->x += x;
  }
};

clang will report 2 warning:

<source>:14:14: warning: assigning field to itself [-Wself-assign-field]
     this->x -= this->x;
             ^
<source>:15:14: warning: assigning field to itself [-Wself-assign-field]
     this->x -= x;

Do you mean we should make it report warning on this->x -= x; and this->x += x; ?

rsmith added inline comments.Apr 4 2023, 12:57 PM
clang/lib/Sema/SemaExpr.cpp
15669

Let's not add any new warnings. Pretend my comment was about -=, not +=.

skip warning on field for compound assignment when isImplicitCXXThis matches.

aaron.ballman added inline comments.Apr 6 2023, 8:45 AM
clang/lib/Sema/SemaExpr.cpp
14143

Spurious whitespace change.

clang/test/SemaCXX/warn-self-assign-field-overloaded.cpp
71–91 ↗(On Diff #510976)

The behavior of this diagnostic is almost inscrutable without really sitting down to think about it... and I'm not even certain I get the logic for it despite thinking about it heavily for a while now. I think my confusion boils down to this diagnostic trying to diagnose two different kinds of problems.

Situation 1 is where there's a possible typo and the user meant a different object:

a /= a;
this->a /= this->a;

// Less-contrived example
a00 /= a01;
a01 /= a02;
a02 /= a02; // Oops, typo!

(Note, I think this->a and a should be handled the same in this case because there are coding styles out there that mandate adding this-> to any member lookup, so there's no reason to think that this->a /= this->a; is any less of a typo than a /= a;.)

Situation 2 is where the user is trying to work around the user-unfriendly lookup rules of C++, or there is a typo, but got confused because lookup found the same object on both sides:

this->a /= a;
a /= this->a;

// Less-contrived example
int a;
struct S {
  int a;

  void foo() {
    // Divide the member variable by the global variable
    this->a /= a; // Oops, that's not how this works in practice
  }

  void bar(int ab) {
    // Divide the member variable by the parameter
    this->a /= a; // Oops, typo, meant to use 'ab' instead
  }
};

So I'm surprised that we'd want to silence the warnings on a /= a; as we're doing here. Frankly, I think we should be silent on a *= a; because that's an extremely common way to square a variable, but diagnose almost all the other cases with a "did you mean?" diagnostic. a -= a; and a %= a; are bad ways to write a = 0, a += a; is a bad way to write a *= 2; (but might be common enough we want to silence it as well), and a /= a; is a bad way to write a = 1; (The bitwise operators may still be reasonable to silence the warning on, but even a &= a; and a |= a; seems like weird no-ops...)

All that said, my understanding of the crux of #42469 is that we wanted to silence the warning whenever the operator being used is overloaded because we have no idea whether self-assign is intended in that case. So I would expect these cases to all consistently not diagnose because they're all using the overloaded operator.

Yeah, I feel there are three ideas here:

  • Clang should warn about self simple assignment in all cases, because it's probably a mistake. We can assume it's a mistake because it's reasonable to assume that the simple assignment operator behaves like a value assignment, even if it's user-defined, and overwriting a value with itself is a pointless operation.
  • Clang should warn about self compound assignment when the type is arithmetic for these specific operators where algebraically x OP x would yield a constant value, because it's probably a mistake. This is because we know the exact behavior of the operator, and producing a constant value by cancellation is a pointless operation.
  • Clang should warn about inconsistent use of this on self compound assignment for all operators, because it's probably a mistake. This is because the implication of writing the member reference two different ways is that there are two different variables in play, but in fact there are not.

The third idea seems like a valuable warning, but it's basically a *different* warning and shouldn't be lumped into this one. I understand the instinct to carve it out here so that we don't regress on warning about it, but I think we should just do it separately. And we should do it much more generally, because there's no reason that logic is limited to just these specific compound operators, or in fact to any individual operator; we should probably warn about all inconsistent references to the same variable within a single statement. (I'd draw the line at statement boundaries, though, because requiring function-wide consistency could be really noisy.)

we should probably warn about all inconsistent references to the same variable within a single statement.

+1 here, that that seems like the suitable generalization/unrelated to the use in/restriction to assignments

The third idea seems like a valuable warning, but it's basically a *different* warning and shouldn't be lumped into this one. I understand the instinct to carve it out here so that we don't regress on warning about it, but I think we should just do it separately. And we should do it much more generally, because there's no reason that logic is limited to just these specific compound operators, or in fact to any individual operator; we should probably warn about all inconsistent references to the same variable within a single statement.

Yeah, good point; I'm convinced.

(I'd draw the line at statement boundaries, though, because requiring function-wide consistency could be really noisy.)

At least, I don't think we should warn if both a and this->a are used in different statements in the same function, and a is shadowed by a local declaration wherever this->a is used, and I think it might be reasonable to not warn on a member a if the name a is ever declared in the function regardless of where it's in scope. For the remaining cases, where a is never declared locally, I suspect the false positive rate for warning if the function uses both a and this->a would be lower, but we could probably get some data on that pretty easily. Speaking of data, it'd be nice to get some evidence that this is a mistake that actually happens before landing a dedicated warning for it :)

Totally fair :)

Revert change for self-assign-field which deserve its own pull request.

rjmccall added inline comments.Apr 7 2023, 11:46 AM
clang/lib/Sema/SemaExpr.cpp
15660

Update the comment done by rjmccall.

rjmccall accepted this revision.Apr 7 2023, 2:14 PM
This revision is now accepted and ready to land.Apr 7 2023, 2:14 PM