Author: JJ Marr
Date: 2025-05-14T08:47:38+02:00
New Revision: b77109ff8c17cd20ac2c791761028a358c9e3447

URL: 
https://github.com/llvm/llvm-project/commit/b77109ff8c17cd20ac2c791761028a358c9e3447
DIFF: 
https://github.com/llvm/llvm-project/commit/b77109ff8c17cd20ac2c791761028a358c9e3447.diff

LOG: Better diagnostics when assertion fails in `consteval` (#130458)

Take this piece of code:
```cpp
#include <cassert>

consteval int square(int x) {
  int result = x * x;
  assert(result == 42);
  return result;
}

void test() {
  auto val = square(2);
}
```
The assertion will fail, and `clang++` will output
(https://godbolt.org/z/hjz3KbTTv):
```cpp
<source>:10:14: error: call to consteval function 'square' is not a constant 
expression
   10 |   auto val = square(2);
      |              ^
<source>:5:3: note: non-constexpr function '__assert_fail' cannot be used in a 
constant expression
    5 |   assert(result == 42);
      |   ^
/usr/include/assert.h:95:9: note: expanded from macro 'assert'
   95 |       : __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION))
      |         ^
<source>:10:14: note: in call to 'square(2)'
   10 |   auto val = square(2);
      |              ^~~~~~~~~
/usr/include/assert.h:69:13: note: declared here
   69 | extern void __assert_fail (const char *__assertion, const char *__file,
      |             ^
1 error generated.
Compiler returned: 1
```
This is confusing because it implies that the issue was using an
assertion in a constant-evaluted context, and not that the assertion
failed (`assert()` is OK in constant evaluation). This PR changes the
error message to:
```cpp
test.cpp:10:14: error: call to consteval function 'square' is not a constant 
expression
   10 |   auto val = square(2);
      |              ^
test.cpp:5:3: note: assertion failed in consteval context: 'result == 42'
    5 |   assert(result == 42);
      |   ^
/nix/store/lw21wr626v5sdcaxxkv2k4zf1121hfc9-glibc-2.40-36-dev/include/assert.h:102:9:
 note: expanded from macro 'assert'
  102 |       : __assert_fail (#expr, __ASSERT_FILE, __ASSERT_LINE,             
\
      |         ^
test.cpp:10:14: note: in call to 'square(2)'
   10 |   auto val = square(2);
      |              ^~~~~~~~~
1 error generated.```

Added: 
    clang/test/SemaCXX/consteval-assert.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/AST/ExprConstant.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 611758dc145ca..da8bc6ea755a5 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -525,6 +525,10 @@ Improvements to Clang's diagnostics
 
 - An error is now emitted when OpenMP ``collapse`` and ``ordered`` clauses 
have an
   argument larger than what can fit within a 64-bit integer.
+  
+- Explanatory note is printed when ``assert`` fails during evaluation of a
+  constant expression. Prior to this, the error inaccurately implied that 
assert
+  could not be used at all in a constant expression (#GH130458)
 
 - A new off-by-default warning ``-Wms-bitfield-padding`` has been added to 
alert to cases where bit-field
   packing may 
diff er under the MS struct ABI (#GH117428).

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 87c2f57837ddd..6c74336addddd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -164,6 +164,8 @@ def err_ice_too_large : Error<
   "integer constant expression evaluates to value %0 that cannot be "
   "represented in a %1-bit %select{signed|unsigned}2 integer type">;
 def err_expr_not_string_literal : Error<"expression is not a string literal">;
+def note_constexpr_assert_failed : Note<
+  "assertion failed during evaluation of constant expression">;
 
 // Semantic analysis of constant literals.
 def ext_predef_outside_function : Warning<

diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 500d43accb082..c09afef9c4bf9 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5975,9 +5975,22 @@ static bool CheckConstexprFunction(EvalInfo &Info, 
SourceLocation CallLoc,
                                         
Definition->hasAttr<MSConstexprAttr>())))
     return true;
 
-  if (Info.getLangOpts().CPlusPlus11) {
-    const FunctionDecl *DiagDecl = Definition ? Definition : Declaration;
+  const FunctionDecl *DiagDecl = Definition ? Definition : Declaration;
+  // Special note for the assert() macro, as the normal error message falsely
+  // implies we cannot use an assertion during constant evaluation.
+  if (CallLoc.isMacroID() && DiagDecl->getIdentifier()) {
+    // FIXME: Instead of checking for an implementation-defined function,
+    // check and evaluate the assert() macro.
+    StringRef Name = DiagDecl->getName();
+    bool AssertFailed =
+        Name == "__assert_rtn" || Name == "__assert_fail" || Name == 
"_wassert";
+    if (AssertFailed) {
+      Info.FFDiag(CallLoc, diag::note_constexpr_assert_failed);
+      return false;
+    }
+  }
 
+  if (Info.getLangOpts().CPlusPlus11) {
     // If this function is not constexpr because it is an inherited
     // non-constexpr constructor, diagnose that directly.
     auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl);

diff  --git a/clang/test/SemaCXX/consteval-assert.cpp 
b/clang/test/SemaCXX/consteval-assert.cpp
new file mode 100644
index 0000000000000..b54a38ff26105
--- /dev/null
+++ b/clang/test/SemaCXX/consteval-assert.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -std=c++23 -verify=expected,cxx20_plus -DTEST_LINUX %s
+// RUN: %clang_cc1 -std=c++23 -verify=expected,cxx20_plus -DTEST_WINDOWS %s
+// RUN: %clang_cc1 -std=c++23 -verify=expected,cxx20_plus -DTEST_DARWIN %s
+
+#ifdef __ASSERT_FUNCTION
+#undef __ASSERT_FUNCTION
+#endif
+
+#if defined(TEST_LINUX)
+  extern "C" void __assert_fail(const char*, const char*, unsigned, const 
char*);
+  #define assert(cond) \
+    ((cond) ? (void)0 : __assert_fail(#cond, __FILE__, __LINE__, __func__))
+#elif defined(TEST_DARWIN)
+  void __assert_rtn(const char *, const char *, int, const char *);
+  #define assert(cond) \
+  (__builtin_expect(!(cond), 0) ? __assert_rtn(__func__, __FILE__, __LINE__, 
#cond) : (void)0)
+#elif defined(TEST_WINDOWS)
+  void /*__cdecl*/ _wassert(const wchar_t*, const wchar_t*, unsigned);
+  #define _CRT_WIDE_(s) L ## s
+  #define _CRT_WIDE(s) _CRT_WIDE_(s)
+  #define assert(cond) \
+    (void)((!!(cond)) || (_wassert(_CRT_WIDE(#cond), _CRT_WIDE(__FILE__), 
(unsigned)(__LINE__)), 0))
+#endif
+
+consteval int square(int x) {
+  int result = x * x;
+  assert(result == 42); // expected-note {{assertion failed during evaluation 
of constant expression}}
+  return result;
+}
+
+void test() {
+  auto val = square(2); // expected-note {{in call to 'square(2)'}} \
+  // expected-error {{call to consteval function 'square' is not a constant 
expression}}
+}


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to