Skip to content
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

[Sema] -Wpointer-bool-conversion: suppress lambda function pointer conversion diagnostic during instantiation #83497

Conversation

MaskRay
Copy link
Member

@MaskRay MaskRay commented Feb 29, 2024

I have seen two internal pieces of code that uses a template type
parameter to accept any callable type (function pointer, std::function,
closure type, etc). The diagnostic added in #83152 would require
adaptation to the template, which is difficult and also seems
unnecessary. Example:

template <typename... Ts>
static bool IsFalse(const Ts&...) { return false; }

template <typename T, typename... Ts,
          typename = typename std::enable_if<std::is_constructible<bool, const T&>::value>::type>
static bool IsFalse(const T& p, const Ts&...) {
  return p ? false : true;
}

template <typename... Args>
void Init(Args&&... args) {
  if (IsFalse(absl::implicit_cast<const typename std::decay<Args>::type&>(
              args)...)) {
    // A callable object convertible to false is either a null pointer or a
    // null functor (e.g., a default-constructed std::function).
    empty_ = true;
  } else {
    empty_ = false;
    new (&factory_) Factory(std::forward<Args>(args)...);
  }
}

Created using spr 1.3.4
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Feb 29, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Feb 29, 2024

@llvm/pr-subscribers-clang

Author: Fangrui Song (MaskRay)

Changes

I have seen internal pieces of code that uses a template type parameter
to accept std::function, a closure type, or any callable type. The
diagnostic added in #83152 would require adaptation to the template,
which is difficult and also seems unnecessary.

template &lt;typename... Ts&gt;
static bool IsFalse(const Ts&amp;...) { return false; }

template &lt;typename T, typename... Ts,
          typename = typename std::enable_if&lt;std::is_constructible&lt;bool, const T&amp;&gt;::value&gt;::type&gt;
static bool IsFalse(const Partial&amp;, const T&amp; p, const Ts&amp;...) {
  return p ? false : true;
}

template &lt;typename... Args&gt;
void Init(Args&amp;&amp;... args) {
  if (IsFalse(absl::implicit_cast&lt;const typename std::decay&lt;Args&gt;::type&amp;&gt;(
              args)...)) {
    // A callable object convertible to false is either a null pointer or a
    // null functor (e.g., a default-constructed std::function).
    empty_ = true;
  } else {
    empty_ = false;
    new (&amp;factory_) Factory(std::forward&lt;Args&gt;(args)...);
  }
}

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

2 Files Affected:

  • (modified) clang/lib/Sema/SemaChecking.cpp (+10-7)
  • (modified) clang/test/SemaCXX/warn-bool-conversion.cpp (+13)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 690bdaa63d058b..3533b5c8e0d235 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -16586,13 +16586,16 @@ void Sema::DiagnoseAlwaysNonNullPointer(Expr *E,
   }
 
   // Complain if we are converting a lambda expression to a boolean value
-  if (const auto *MCallExpr = dyn_cast<CXXMemberCallExpr>(E)) {
-    if (const auto *MRecordDecl = MCallExpr->getRecordDecl();
-        MRecordDecl && MRecordDecl->isLambda()) {
-      Diag(E->getExprLoc(), diag::warn_impcast_pointer_to_bool)
-          << /*LambdaPointerConversionOperatorType=*/3
-          << MRecordDecl->getSourceRange() << Range << IsEqual;
-      return;
+  // outside of instantiation.
+  if (!inTemplateInstantiation()) {
+    if (const auto *MCallExpr = dyn_cast<CXXMemberCallExpr>(E)) {
+      if (const auto *MRecordDecl = MCallExpr->getRecordDecl();
+          MRecordDecl && MRecordDecl->isLambda()) {
+        Diag(E->getExprLoc(), diag::warn_impcast_pointer_to_bool)
+            << /*LambdaPointerConversionOperatorType=*/3
+            << MRecordDecl->getSourceRange() << Range << IsEqual;
+        return;
+      }
     }
   }
 
diff --git a/clang/test/SemaCXX/warn-bool-conversion.cpp b/clang/test/SemaCXX/warn-bool-conversion.cpp
index 9e8cf0e4f8944a..8b1bf80af79d26 100644
--- a/clang/test/SemaCXX/warn-bool-conversion.cpp
+++ b/clang/test/SemaCXX/warn-bool-conversion.cpp
@@ -92,6 +92,19 @@ void foo() {
   bool is_true = [](){ return true; };
   // expected-warning@-1{{address of lambda function pointer conversion operator will always evaluate to 'true'}}
 }
+
+template <typename... Ts>
+static bool IsFalse(const Ts&...) { return false; }
+template <typename T>
+static bool IsFalse(const T& p) {
+  bool b;
+  b = f7; // expected-warning {{address of lambda function pointer conversion operator will always evaluate to 'true'}}
+  return p ? false : true;
+}
+
+bool use_instantiation() {
+  return IsFalse([]() { return 0; });
+}
 #endif
 
 void bar() {

@MaskRay
Copy link
Member Author

MaskRay commented Feb 29, 2024

@vinayakdsci

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

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

LGTM with a minor commenting nit. Thank you for catching this and the quick patch!

static bool IsFalse(const T& p) {
bool b;
b = f7; // expected-warning {{address of lambda function pointer conversion operator will always evaluate to 'true'}}
return p ? false : true;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return p ? false : true;
// Intentionally not warned on because p could be a lambda type in one instantiation, but a pointer
// type in another.
return p ? false : true;

(May need to adjust for 80-col limits.)

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for the suggestion! Adjusted to 80-col.

@vinayakdsci
Copy link
Contributor

Great catch! The usage of template instantiation didn't seem very obvious while adding the tests, and hence the missed test case.
Thanks a lot for the fix!

Created using spr 1.3.4
@MaskRay MaskRay merged commit 64216ba into main Mar 1, 2024
3 of 4 checks passed
@MaskRay MaskRay deleted the users/MaskRay/spr/sema-wpointer-bool-conversion-suppress-lambda-function-pointer-conversion-diagnostic-during-instantiation branch March 1, 2024 16:57
@dwblaikie
Copy link
Collaborator

Presumably similar things might show up in macros? But can cross that bridge when we come to it.

Perhaps we have some/could use some generic utility for this sort of contextual warning "if these things are literally written next to each other, warn, but if they came to be via template instantiation, macro, (something else?) then don't" because that sort of situation shows up pretty regularly in diagnostics, I think...

@MaskRay
Copy link
Member Author

MaskRay commented Mar 1, 2024

Presumably similar things might show up in macros? But can cross that bridge when we come to it.

Perhaps we have some/could use some generic utility for this sort of contextual warning "if these things are literally written next to each other, warn, but if they came to be via template instantiation, macro, (something else?) then don't" because that sort of situation shows up pretty regularly in diagnostics, I think...

I modeled the template instantiation suppression after diagnoseTautologicalComparison.

While some macro uses suppress diagnoseTautologicalComparison diagnostics, e.g.

// Don't issue a warning when either the left or right side of the comparison
// results from a macro expansion.
#define R8435950_A i
#define R8435950_B i

int R8435950(int i) {
  if (R8435950_A == R8435950_B) // no-warning
   return 0;
  return 1;
}

some have diagnostics (e.g. https://reviews.llvm.org/D70624). Yes, we can cross that bridge when we come to it.

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.

None yet

5 participants