Skip to content

[clang] Avoid -Wshadow warning when init-capture named same as class field #74512

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Feb 12, 2024

Conversation

Fznamznon
Copy link
Contributor

Shadowing warning doesn't make much sense since field is not available in lambda's body without capturing this.

Fixes #71976

…field

Shadowing warning doesn't make much sense since field is not available
in lambda's body without capturing this.

Fixes llvm#71976
@Fznamznon Fznamznon requested a review from cor3ntin December 5, 2023 18:35
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Dec 5, 2023
@llvmbot
Copy link
Member

llvmbot commented Dec 5, 2023

@llvm/pr-subscribers-clang

Author: Mariya Podchishchaeva (Fznamznon)

Changes

Shadowing warning doesn't make much sense since field is not available in lambda's body without capturing this.

Fixes #71976


Full diff: https://github.com/llvm/llvm-project/pull/74512.diff

3 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+3)
  • (modified) clang/lib/Sema/SemaDecl.cpp (+5-3)
  • (modified) clang/test/SemaCXX/warn-shadow-in-lambdas.cpp (+18)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 828dd10e3d6db..7ac81e16492d1 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -655,6 +655,9 @@ Bug Fixes in This Version
   Fixes (`#64467 <https://github.com/llvm/llvm-project/issues/64467>`_)
 - Clang's ``-Wchar-subscripts`` no longer warns on chars whose values are known non-negative constants.
   Fixes (`#18763 <https://github.com/llvm/llvm-project/issues/18763>`_)
+- Clang's ``-Wshadow`` no longer warns when init-capture named same as class
+  field.
+  Fixes (`#71976 <https://github.com/llvm/llvm-project/issues/71976>`_)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index f12424d33b7da..65d095b2431dd 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8395,10 +8395,11 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl *ShadowedDecl,
 
   unsigned WarningDiag = diag::warn_decl_shadow;
   SourceLocation CaptureLoc;
-  if (isa<VarDecl>(D) && isa<VarDecl>(ShadowedDecl) && NewDC &&
-      isa<CXXMethodDecl>(NewDC)) {
+  if (isa<VarDecl>(D) && NewDC && isa<CXXMethodDecl>(NewDC)) {
     if (const auto *RD = dyn_cast<CXXRecordDecl>(NewDC->getParent())) {
       if (RD->isLambda() && OldDC->Encloses(NewDC->getLexicalParent())) {
+        if (!isa<VarDecl>(ShadowedDecl))
+          return;
         if (RD->getLambdaCaptureDefault() == LCD_None) {
           // Try to avoid warnings for lambdas with an explicit capture list.
           const auto *LSI = cast<LambdaScopeInfo>(getCurFunction());
@@ -8416,7 +8417,8 @@ void Sema::CheckShadow(NamedDecl *D, NamedDecl *ShadowedDecl,
         }
       }
 
-      if (cast<VarDecl>(ShadowedDecl)->hasLocalStorage()) {
+      if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl);
+          VD && VD->hasLocalStorage()) {
         // A variable can't shadow a local variable in an enclosing scope, if
         // they are separated by a non-capturing declaration context.
         for (DeclContext *ParentDC = NewDC;
diff --git a/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp b/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
index bda6a65c02168..58af7a2e65c55 100644
--- a/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
+++ b/clang/test/SemaCXX/warn-shadow-in-lambdas.cpp
@@ -179,3 +179,21 @@ void f() {
 #endif
 }
 }
+
+namespace GH71976 {
+struct A {
+  int b = 5;
+  int foo() {
+    return [b = b]() { return b; }();
+  }
+};
+
+struct B {
+  int a;
+  void foo() {
+    auto b = [a = this->a] {
+
+    };
+  }
+};
+}

Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

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

LGTM modulo nitpick.
Thanks a lot for the quick fix

Copy link
Collaborator

@shafik shafik left a comment

Choose a reason for hiding this comment

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

LGTM w/ minor comments

if (const auto *RD = dyn_cast<CXXRecordDecl>(NewDC->getParent())) {
if (RD->isLambda() && OldDC->Encloses(NewDC->getLexicalParent())) {
if (!isa<VarDecl>(ShadowedDecl))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe a comment here along the lines of "if it is not VarDecl then it can not shadow" I am not sure if that implies it must be a capture but I think it does.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You mean add a comment saying that it is a case when an init-capture references class field?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, just a comment to explain why ShadownDecl not being a VarDecl matters here.

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 changed this all, so probably not relevant anymore.

@Fznamznon Fznamznon requested a review from shafik December 7, 2023 10:49
@philnik777
Copy link
Contributor

I seem to be the only one, but I don't understand how this isn't shadowing. The whole point of the shadowing warning is to avoid confusion about which variable is actually used, which IMO could very well be the case here. I think it would be much better to get a new flag to disable this if it's not wanted than to make it impossible to warn on this shadowing constellation.

@Fznamznon
Copy link
Contributor Author

The whole point of the shadowing warning is to avoid confusion about which variable is actually used, which IMO could very well be the case here.

In the case that is being fixed, a field is not available in lambda's body without capturing this, so there is no such confusion and no shadowing, I think.
I have a concern, that probably in case when this is actually captured, with current implementation there will be no warning as well. Should it be? gcc doesn't give a warning in both cases.

@Fznamznon
Copy link
Contributor Author

@cor3ntin , @shafik WDYT about

I have a concern, that probably in case when this is actually captured, with current implementation of the patch there will be no warning as well.

Should we still emit shadowing warning in case lambda captured this and has an init-capture with the same name as a class field?

@cor3ntin
Copy link
Contributor

@philnik777 I agree strongly with @Fznamznon.
If this is not captured there is no actual shadowing in that they do not occupy encompassing scope, and so removing one declaration would not give you access to the other variable.

Should we still emit shadowing warning in case lambda captured this and has an init-capture with the same name as a class field?

Yes, we should probably do that.

(sorry for the very delayed reply, it fell off my radar)

@philnik777
Copy link
Contributor

@philnik777 I agree strongly with @Fznamznon. If this is not captured there is no actual shadowing in that they do not occupy encompassing scope, and so removing one declaration would not give you access to the other variable.

I don't disagree that this is technically not shadowing, but I don't think that's clear from a users perspective. e.g.

void someFunc(int someVal) {
  // assume 100 LoC here

  auto someLambda = [](int someVal) {
    // another 100 LoC here
    return someVal + 40; // IMO it's trivial to confuse this with the `someVal` from `someFunc`
  };
}

It looks like Clang even has a warning for this specific case and GCC considers it shadowing too: https://godbolt.org/z/Y16njoPxh, which makes me think that people want this - at least as a separate flag.

@Fznamznon
Copy link
Contributor Author

It looks like Clang even has a warning for this specific case and GCC considers it shadowing too: https://godbolt.org/z/Y16njoPxh, which makes me think that people want this - at least as a separate flag.

I already came across -Wshadow-uncaptured-local flag earlier - #71976 (comment) . I don't mind putting the warning that I'm avoiding with the current patch under this flag as well.

@Fznamznon
Copy link
Contributor Author

Fznamznon commented Jan 18, 2024

Should we still emit shadowing warning in case lambda captured this and has an init-capture with the same name as a class field?
Yes, we should probably do that.

I'm struggling with detecting the following case:

struct A {
  int b = 5;

  int foo() {
    return [b = b, this]() { return b; }();
  }
};

Since init capture is checked for shadowing before this capture reached, lambda info doesn't yet know about incoming this capture. There is delayed shadowing reporting for lambdas in DiagnoseShadowingLambdaDecls though, looking there.

@Fznamznon
Copy link
Contributor Author

Ok, now it emits -Wshadow-uncaptured-local when lambda doesn't capture this, and -Wshadow when it does.

@Fznamznon
Copy link
Contributor Author

Ping.

@Fznamznon
Copy link
Contributor Author

Ping!

if (const auto *RD = dyn_cast<CXXRecordDecl>(NewDC->getParent())) {
if (RD->isLambda() && OldDC->Encloses(NewDC->getLexicalParent())) {
if (RD->getLambdaCaptureDefault() == LCD_None) {
// Try to avoid warnings for lambdas with an explicit capture list.
if (const auto *VD = dyn_cast<VarDecl>(ShadowedDecl)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Structured bindings aren't always VarDecl (But ValueDecl) so I guess we never diagnosed them !

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, If we want to, I would prefer doing that in a separate PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

We currently warn on that actually
https://compiler-explorer.com/z/d4W37n9Mn

Can you test that we still do?

The rest of the changes LGTM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, added.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks!
It surprises me that it works, but great!

@cor3ntin
Copy link
Contributor

cor3ntin commented Feb 9, 2024

cc @erichkeane @AaronBallman

@erichkeane
Copy link
Collaborator

cc @erichkeane @AaronBallman

I looked through and don't see anything of concern to me that you haven't covered. I am on the fence as to whether this warning makes sense to have in this situation.

The point of the warning isn't that the language has a problem, it is that it is potentially confusing to readers. And I'm not sure that it STOPS being confusing because of the lack of a capture of 'this'.

@Fznamznon
Copy link
Contributor Author

And I'm not sure that it STOPS being confusing because of the lack of a capture of 'this'.

For that reason, I didn't fully disable the warning, and put it under -Wshadow-uncaptured-local flag instead.

@Fznamznon Fznamznon requested a review from cor3ntin February 9, 2024 17:18
@Fznamznon Fznamznon merged commit c90114c into llvm:main Feb 12, 2024
jeremy-wildlight added a commit to wildlight-entertainment/llvm-project that referenced this pull request Feb 15, 2024
Relaxes incorrect shadowing warning for fields detected as being
shadowed in lambdas that don't capture `this`.
sheredom pushed a commit to sheredom/llvm-project that referenced this pull request Mar 12, 2024
…field (llvm#74512)

Shadowing warning doesn't make much sense since field is not available
in lambda's body without capturing this.

Fixes llvm#71976
tstellar pushed a commit to sheredom/llvm-project that referenced this pull request Apr 2, 2024
…field (llvm#74512)

Shadowing warning doesn't make much sense since field is not available
in lambda's body without capturing this.

Fixes llvm#71976
@pointhex pointhex mentioned this pull request May 7, 2024
xgupta pushed a commit to xgupta/llvm-project that referenced this pull request Sep 9, 2024
…field (llvm#74512)

Shadowing warning doesn't make much sense since field is not available
in lambda's body without capturing this.

Fixes llvm#71976
xgupta pushed a commit to xgupta/llvm-project that referenced this pull request Oct 10, 2024
…field (llvm#74512)

Shadowing warning doesn't make much sense since field is not available
in lambda's body without capturing this.

Fixes llvm#71976
Tedlion pushed a commit to Tedlion/llvm-project that referenced this pull request Jun 15, 2025
…field (llvm#74512)

Shadowing warning doesn't make much sense since field is not available
in lambda's body without capturing this.

Fixes llvm#71976
Tedlion pushed a commit to Tedlion/llvm-project that referenced this pull request Jun 15, 2025
…field (llvm#74512)

Shadowing warning doesn't make much sense since field is not available
in lambda's body without capturing this.

Fixes llvm#71976
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[clang] -Wshadow option produces a warning when explicitly capturing class member using the same name
6 participants