[clang][AST] Improve diagnostic for `nullptr` constexpr function pointer call
authorTakuya Shimizu <shimizu2486@gmail.com>
Mon, 13 Mar 2023 16:53:12 +0000 (12:53 -0400)
committerAaron Ballman <aaron@aaronballman.com>
Mon, 13 Mar 2023 16:53:12 +0000 (12:53 -0400)
This patch improves diagnostic for clang constexpr evaluator by adding
a check for nullptr in function pointer call evaluations.

ex.
```
constexpr int foo(int (*bla)(void)) {
  return bla();
}

static_assert(foo(nullptr) == 1);
```

BEFORE this patch, clang generates the following diagnostic for the
code above:

```
<source>:5:15: error: static assertion expression is not an integral constant expression
static_assert(foo(nullptr) == 1);
              ^~~~~~~~~~~~~~~~~
<source>:2:10: note: subexpression not valid in a constant expression
  return bla();
         ^
<source>:5:15: note: in call to 'foo(nullptr)'
static_assert(foo(nullptr) == 1);
              ^
1 error generated.
```

AFTER this patch, subexpression not valid in a constant expression note
is replaced with 'bla' evaluates to a null function pointer.

Fixes https://github.com/llvm/llvm-project/issues/59872
Differential Revision: https://reviews.llvm.org/D145793

clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticASTKinds.td
clang/lib/AST/ExprConstant.cpp
clang/test/SemaCXX/constant-expression-cxx11.cpp

index 440fd04..a8c6f2f 100644 (file)
@@ -165,6 +165,8 @@ Improvements to Clang's diagnostics
 - Diagnostics relating to macros on the command line of a preprocessed assembly
   file are now reported as coming from the file ``<command line>`` instead of
   ``<built-in>``.
+- Clang constexpr evaluator now provides a more concise diagnostic when calling
+  function pointer that is known to be null.
 
 Bug Fixes in This Version
 -------------------------
index 28120d1..c283ee8 100644 (file)
@@ -127,6 +127,8 @@ def note_constexpr_null_subobject : Note<
   "access array element of|perform pointer arithmetic on|"
   "access real component of|"
   "access imaginary component of}0 null pointer">;
+def note_constexpr_null_callee : Note<
+  "'%0' evaluates to a null function pointer">;
 def note_constexpr_function_param_value_unknown : Note<
   "function parameter %0 with unknown value cannot be used in a constant "
   "expression">;
index 26c2342..fbe92d0 100644 (file)
@@ -7668,6 +7668,11 @@ public:
 
       if (!CalleeLV.getLValueOffset().isZero())
         return Error(Callee);
+      if (CalleeLV.isNullPointer()) {
+        Info.FFDiag(Callee, diag::note_constexpr_null_callee)
+            << const_cast<Expr *>(Callee);
+        return false;
+      }
       FD = dyn_cast_or_null<FunctionDecl>(
           CalleeLV.getLValueBase().dyn_cast<const ValueDecl *>());
       if (!FD)
index fea45d5..5e7a80c 100644 (file)
@@ -279,7 +279,7 @@ namespace FunctionPointers {
   constexpr auto Select(int n) -> int (*)(int) {
     return n == 2 ? &Double : n == 3 ? &Triple : n == 4 ? &Quadruple : 0;
   }
-  constexpr int Apply(int (*F)(int), int n) { return F(n); } // expected-note {{subexpression}}
+  constexpr int Apply(int (*F)(int), int n) { return F(n); } // expected-note {{'F' evaluates to a null function pointer}}
 
   static_assert(1 + Apply(Select(4), 5) + Apply(Select(3), 7) == 42, "");