This is an archive of the discontinued LLVM Phabricator instance.

[Clang][SemaCXX] Add unused warning for variables declared in condition expressions
ClosedPublic

Authored by hazohelet on Jun 8 2023, 10:07 PM.

Diff Detail

Event Timeline

hazohelet created this revision.Jun 8 2023, 10:07 PM
Herald added a project: Restricted Project. · View Herald TranscriptJun 8 2023, 10:07 PM
hazohelet requested review of this revision.Jun 8 2023, 10:07 PM
Herald added a project: Restricted Project. · View Herald TranscriptJun 8 2023, 10:07 PM
tbaeder added inline comments.Jun 9 2023, 2:27 AM
clang/lib/CodeGen/CGExpr.cpp
2852 ↗(On Diff #529824)

Question for other reviewers: If we want to keep this assertion, would it instead make sense to mark the decl as used after emitting the diagnostic, in https://github.com/llvm/llvm-project/blob/main/clang/lib/Sema/SemaDecl.cpp#L2154 ?

shafik added inline comments.Jun 22 2023, 11:10 PM
clang/lib/Sema/SemaExprCXX.cpp
4018–4019
tbaeder added inline comments.Jun 30 2023, 2:17 AM
clang/lib/Sema/SemaExprCXX.cpp
4018–4019

That seem a bit over the top given that it's the only boolean parameter.

shafik added inline comments.Jul 6 2023, 2:22 PM
clang/lib/Sema/SemaExprCXX.cpp
4018–4019

Yeah, in this case it is blindingly obvious but I do see many cases in the code where folks use bugprone-argument-comment for single bool parameters and I just use it everywhere for consistency sake.

This is a bit of an odd situation -- the condition variable *is* used (e.g., its value is loaded to determine whether to go into the if branch or the else branch), so we should not be marking it as unused in Sema::CheckConditionVariable(). Instead, I think we need to decide what constitutes "unused" in this case. Consider:

struct RAII {
  int &x;

  RAII(int &ref) : x(ref) {}
  ~RAII() { x = 0; }

  operator bool() const { return true; }
};

void func() {
  int i = 12;
  if (RAII name{i}) {
  }
}

I don't think name is unused in the same way that i is unused in if (int i = 1) {}. So I think we only want this to apply to scalars and not class types. I think the changes should be happening in Sema::ShouldDiagnoseUnusedDecl() (and you might need to thread the Scope object through to that call -- my naive thinking is that a variable declaration in a condition scope should be diagnosed as unused if it is used but not referenced, but I could be off-base).

clang/docs/ReleaseNotes.rst
142

This is a bit of an odd situation -- the condition variable *is* used (e.g., its value is loaded to determine whether to go into the if branch or the else branch), so we should not be marking it as unused in Sema::CheckConditionVariable(). Instead, I think we need to decide what constitutes "unused" in this case. Consider:

struct RAII {
  int &x;

  RAII(int &ref) : x(ref) {}
  ~RAII() { x = 0; }

  operator bool() const { return true; }
};

void func() {
  int i = 12;
  if (RAII name{i}) {
  }
}

I don't think name is unused in the same way that i is unused in if (int i = 1) {}. So I think we only want this to apply to scalars and not class types. I think the changes should be happening in Sema::ShouldDiagnoseUnusedDecl() (and you might need to thread the Scope object through to that call -- my naive thinking is that a variable declaration in a condition scope should be diagnosed as unused if it is used but not referenced, but I could be off-base).

Thank you for the insightful feedback!
I agree that we should not be emitting Wunused warnings in condition expressions when the declaration is of class type.

About the Scope object, if you mean ConditionVarScope flag by "condition scope", it seems to be used only for the condition of for-statements. (https://github.com/llvm/llvm-project/blob/f36b0169b8821e431644cb240f081538eb2b55ef/clang/lib/Parse/ParseExprCXX.cpp#L2045-L2057) Also it does not push new scope but modifies the flag of the current scope.
So it does not seem to be usable for our purpose here.

Instead, I think used-but-not-referenced variable declarations can be diagnosed as unused regardless of whether they are declared in condition expressions. This saves us from deleting the assertion from CodeGen and modifying AST tests.

hazohelet updated this revision to Diff 538591.Jul 10 2023, 5:24 AM
hazohelet marked 5 inline comments as done.
  • Stop marking condition variable declarations as unused and diagnose all VarDecl that is marked used but not referenced
  • Added test not to warn when the declaration is not aggregate type
  • NFC changes (comments, typo)
aaron.ballman added inline comments.Jul 13 2023, 6:35 AM
clang/lib/Sema/SemaExprCXX.cpp
4016–4019

isAggregateType() includes arrays and I think we still want to diagnose an unused array.

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
5356

Which test case covers this change?

clang/test/SemaCXX/warn-unused-variables.cpp
2
317–318

This should diagnose the decomposition declaration as being unused, but that already works today (so let's make sure we don't get duplicate diagnostics for this case): https://godbolt.org/z/6jP6sW1x6

An additional test case after this would be to add:

else if (auto [a, b] = (int[2]){ 1, 2 }; a)
  return;

which should get no diagnostics.

We should also have these tests for the other kinds of conditions (for, switch).

321

Can you also add coverage for switch?

cor3ntin added inline comments.
clang/lib/Sema/SemaExprCXX.cpp
4016–4019

Should it not be Condition->hasSideEffects() ?
Hopefully we want to be consistent with existing behavior https://godbolt.org/z/6abbPhn4G

hazohelet marked an inline comment as done.Jul 13 2023, 6:59 AM
hazohelet added inline comments.
clang/lib/Sema/SemaExprCXX.cpp
4016–4019

The condition here only applies to condition variables.
C++ does not allow array type in condition variable, correct? I think it's OK to use isAggregateType as well then.

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
5356

I wanted to suppress warnings against implicitly declared variables in range statements like __range2. I'll add a test for this.

cor3ntin added inline comments.Jul 13 2023, 7:19 AM
clang/lib/Sema/SemaExprCXX.cpp
4016–4019

After a chat with Aaron, I have a few questions: Do we call Scope::AddDecl - or PushOnScopeChains somewhere?
My intuition is that if the condition is added to a scope, and we call ConditionVar->setReferenced(false);, the all the diagnostic mechanism in DiagnoseUnusedDecl should be triggered. and it seen to be because we do get a diagnostic.
So neither if (!T->isAggregateType()) or any check should be needed there

Can you just remove that if statement (just set setReferenced(false)) and see if it works? Otherwise, i think we need to understand why it doesn't but trying to reimplement the logic of ShouldDiagnoseUnusedDeclseems fraught with unbounded complexity.

hazohelet marked 6 inline comments as done.

Address comments from Aaron and Corentin

  • Call ConditionVar->setReferenced(false) without any checks
  • Added some more tests
hazohelet added inline comments.Jul 14 2023, 10:22 AM
clang/lib/Sema/SemaExprCXX.cpp
4016–4019

It didn't break any tests locally, but when the record type does not have a user-provided dtor, it is diagnosed as unused, which seems not nice. (example written in the test trivial_dtor)

This looks reasonable to me now.
I'll wait next week before approving it so other get a chance to look at it :)

clang/lib/Sema/SemaExprCXX.cpp
4016–4019

It's still a declaration that does nothing, so diagnosing let you remove it
https://compiler-explorer.com/z/a1GGs9cxa this is consistent with local variables. I like the simplicity of the change.

hazohelet added inline comments.Jul 14 2023, 10:42 AM
clang/lib/Sema/SemaExprCXX.cpp
4016–4019

Ah, I see. Thanks!

@cor3ntin It's next week :)

cor3ntin accepted this revision.Jul 27 2023, 6:59 AM

LGTM, thanks. Make sure you rebase the changes to the release notes

This revision is now accepted and ready to land.Jul 27 2023, 6:59 AM
This revision was landed with ongoing or failed builds.Aug 6 2023, 12:25 AM
This revision was automatically updated to reflect the committed changes.

I reverted this change because it broke sanitizer buildbots.
https://lab.llvm.org/buildbot/#/builders/19/builds/18369
The cause of the errors here are relatively clear and I can speculatively replace if (std::error_code EC = makeCanonical(Path)) with if (makeCanonical(Path))

https://lab.llvm.org/buildbot/#/builders/94/builds/15865
https://lab.llvm.org/buildbot/#/builders/240/builds/12947
These 2 are not clear for me, so I'll take time to find the cause.

The fuzzer timeouts fail all the time; I usually just assume it's unrelated.

I reverted this change because it broke sanitizer buildbots.
https://lab.llvm.org/buildbot/#/builders/19/builds/18369
The cause of the errors here are relatively clear and I can speculatively replace if (std::error_code EC = makeCanonical(Path)) with if (makeCanonical(Path))

That changes makes sense to me.

https://lab.llvm.org/buildbot/#/builders/94/builds/15865
https://lab.llvm.org/buildbot/#/builders/240/builds/12947
These 2 are not clear for me, so I'll take time to find the cause.

Neither of these are problems with your patch -- they're flaky tests in this case.

I reverted this change because it broke sanitizer buildbots.
https://lab.llvm.org/buildbot/#/builders/19/builds/18369
The cause of the errors here are relatively clear and I can speculatively replace if (std::error_code EC = makeCanonical(Path)) with if (makeCanonical(Path))

That changes makes sense to me.

https://lab.llvm.org/buildbot/#/builders/94/builds/15865
https://lab.llvm.org/buildbot/#/builders/240/builds/12947
These 2 are not clear for me, so I'll take time to find the cause.

Neither of these are problems with your patch -- they're flaky tests in this case.

Thanks! I'll reland this patch with the unused std::error_code fix. I'll revert again if the same CI keeps failing for a few hours.

Would this cause many warnings in Clang/LLVM tree?
https://lab.llvm.org/buildbot/#/builders/36/builds/36560

I hope you to fix possible warnings at first.

Would this cause many warnings in Clang/LLVM tree?
https://lab.llvm.org/buildbot/#/builders/36/builds/36560

I hope you to fix possible warnings at first.

Thanks. I'll revert this change.
I was thinking of pushing speculative fixes upstream, but the lines that need fixing are not only a few lines, so I now think it needs a review.

Fixed newly-issued warnings when trying check-clang,llvm,lld,compiler-rt,cxx,cxxabi with -Werror=unused-variable locally using local build of clang

Herald added projects: Restricted Project, Restricted Project. · View Herald Transcript
Herald added a reviewer: Restricted Project. · View Herald Transcript
hazohelet reopened this revision.Aug 15 2023, 12:43 PM
This revision is now accepted and ready to land.Aug 15 2023, 12:43 PM
nikic added a subscriber: nikic.Aug 15 2023, 12:47 PM

Please split the warning fixes off into a separate patch.

hazohelet updated this revision to Diff 550443.Aug 15 2023, 1:14 PM

Removed warning fixes that are now in D158016

goncharov added a subscriber: goncharov.EditedAug 30 2023, 5:39 AM

there is a number of unused vaiables in conditional loops that are firing now, I have submitted https://github.com/llvm/llvm-project/commit/74f4daef0412be33002bd4e24a30cb47d0187ecf but I suspect it's just a start.
How did you checked the project code for that?

there is a number of unused vaiables in conditional loops that are firing now, I have submitted https://github.com/llvm/llvm-project/commit/74f4daef0412be33002bd4e24a30cb47d0187ecf but I suspect it's just a start.
How did you checked the project code for that?

Per rG01b88dd66d9d52d3f531a7d490e18c549f36f445, unused dyn_cast results should turn into isa.

It is used, but only in an assert. Seems like the right fix!

there is a number of unused vaiables in conditional loops that are firing now, I have submitted https://github.com/llvm/llvm-project/commit/74f4daef0412be33002bd4e24a30cb47d0187ecf but I suspect it's just a start.
How did you checked the project code for that?

Per rG01b88dd66d9d52d3f531a7d490e18c549f36f445, unused dyn_cast results should turn into isa.

due to this change we have a enourmous number of new warnings, on the other hand -Wunused-variable is a valuable warning. I am not sure what is the policy and best practices for warnings but maybe there is a possiblity to make a transition period for enabling this type of warning separetely and to allow updating existing code?

hans added a subscriber: hans.Aug 30 2023, 10:04 AM

It is used, but only in an assert. Seems like the right fix!

I suppose it is technically, but I'm not sure the fix reads like an improvement to me... I guess that's often the case with unused variables vs. asserts though.

due to this change we have a enourmous number of new warnings, on the other hand -Wunused-variable is a valuable warning. I am not sure what is the policy and best practices for warnings but maybe there is a possiblity to make a transition period for enabling this type of warning separetely and to allow updating existing code?

The usual policy is to put new warnings behind new flags so users can disable them selectively. It gets trickier when it's existing warnings getting enhanced like this. Would it be possible to put this new functionality behind a flag?

It is used, but only in an assert. Seems like the right fix!

I suppose it is technically, but I'm not sure the fix reads like an improvement to me... I guess that's often the case with unused variables vs. asserts though.

due to this change we have a enourmous number of new warnings, on the other hand -Wunused-variable is a valuable warning. I am not sure what is the policy and best practices for warnings but maybe there is a possiblity to make a transition period for enabling this type of warning separetely and to allow updating existing code?

The usual policy is to put new warnings behind new flags so users can disable them selectively. It gets trickier when it's existing warnings getting enhanced like this. Would it be possible to put this new functionality behind a flag?

That might not be a bad idea in this case -- perhaps -Wunused-condition-variable and group it under -Wunused-variable?

due to this change we have a enourmous number of new warnings, on the other hand -Wunused-variable is a valuable warning. I am not sure what is the policy and best practices for warnings but maybe there is a possiblity to make a transition period for enabling this type of warning separetely and to allow updating existing code?

The usual policy is to put new warnings behind new flags so users can disable them selectively. It gets trickier when it's existing warnings getting enhanced like this. Would it be possible to put this new functionality behind a flag?

Yeah, this has been a recurring issue when existing warnings get enhanced. It's often not feasible to fix all new instances right away, but you don't want to disable or -Wno-error the warning either cos then new issues can start creeping in, leaving you with no good options. Maybe we should have a policy around even enhancements to existing warnings always going in their own subgroup, so that we can disable them selectively while not regressing anything? @aaron.ballman, what are your thoughts?

hans added a comment.Aug 30 2023, 11:01 AM

That might not be a bad idea in this case -- perhaps -Wunused-condition-variable and group it under -Wunused-variable?

Sounds god to me conceptually. (I don't know the code, so don't know how hard it is practically.)

due to this change we have a enourmous number of new warnings, on the other hand -Wunused-variable is a valuable warning. I am not sure what is the policy and best practices for warnings but maybe there is a possiblity to make a transition period for enabling this type of warning separetely and to allow updating existing code?

The usual policy is to put new warnings behind new flags so users can disable them selectively. It gets trickier when it's existing warnings getting enhanced like this. Would it be possible to put this new functionality behind a flag?

Yeah, this has been a recurring issue when existing warnings get enhanced. It's often not feasible to fix all new instances right away, but you don't want to disable or -Wno-error the warning either cos then new issues can start creeping in, leaving you with no good options.

(I haven't really read the technical side of this patch yet.)

Yes, from me observing how we internally treat such warnings.

Maybe we should have a policy around even enhancements to existing warnings always going in their own subgroup, so that we can disable them selectively while not regressing anything? @aaron.ballman, what are your thoughts?

Sometimes an improved diagnostic just triggers in very few new places that can be easily handled.
Sometimes there are many more places so that it is difficult to fix in a satisfactory timeline (as a rolling update user doesn't want to postpone releases a long time).

I think sometimes it's just difficult for contributors to figure out whether a diagnostic is going to require substantial cleanup or just a little effort. In this case rolling update users (like we) providing feedback is useful whether a new sub -Wxxx is needed.

I agree -Wunused-condition-variable sounds like a good idea. There are still numerous instances of this warning/error showing up when doing a self-build of LLVM, let alone warnings/errors that are showing up in internal code bases who are using LLVM HEAD for compiling their codebases. -Wunused-condition-variable would make this transition smoother.

Here's a more blatant regression caused by this patch.

https://godbolt.org/z/q19Ge64G3

Causing breakage for the Linux kernel:
https://github.com/ClangBuiltLinux/linux/issues/1926

Here's a more blatant regression caused by this patch.

https://godbolt.org/z/q19Ge64G3

Causing breakage for the Linux kernel:
https://github.com/ClangBuiltLinux/linux/issues/1926

Because of the amount of disruption and the fact that we're trying to get 17.x out the door while transitioning to a new code review workflow, let's revert this for now. The __attribute__((used)) problem seems to be something deeper, as [[maybe_unused]] does work properly (https://godbolt.org/z/e7YK1G69e), and putting this diagnostic under its own warning group will help folks adjust to the new behavior.

Thank you for the feedback and the revert!

Distinguishing condition variables from other unused variables in Wunused warnings was something I was trying initially, and I agree that it's the best way forward.
The difficulty is in how to know whether an unused VarDecl is a condition variable or not in the phase of emitting Wunused-variable. We may need to add a boolean flag IsConditionVar in VarDecl or something.

The reason [[maybe_unused]] works well is that it is explicitly handled in Sema::ShouldDiagnoseUnusedDecl (https://github.com/llvm/llvm-project/blob/e7bd43675753476e97e63aa13c13b3498407ed1c/clang/lib/Sema/SemaDecl.cpp#L2005-L2007)

The used attribute regression was caused by the "used but not-referenced variables should be diagnosed as unused" change. Once we have some way of knowing that a VarDecl is a condition variable, we should only apply this change to condition variables, thereby avoiding regression.