diff options
author | JJ Marr <168750718+jj-marr@users.noreply.github.com> | 2025-05-14 02:47:38 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-05-14 08:47:38 +0200 |
commit | b77109ff8c17cd20ac2c791761028a358c9e3447 (patch) | |
tree | 52b8e7953a800901f35b37ed553bfaf25343b75a /clang/lib/AST | |
parent | 6c1bb48cc45396894597c8cb897c31205d1bdeb6 (diff) | |
download | llvm-b77109ff8c17cd20ac2c791761028a358c9e3447.zip llvm-b77109ff8c17cd20ac2c791761028a358c9e3447.tar.gz llvm-b77109ff8c17cd20ac2c791761028a358c9e3447.tar.bz2 |
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.```
Diffstat (limited to 'clang/lib/AST')
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 17 |
1 files changed, 15 insertions, 2 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 500d43a..c09afef9 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); |