[clang] [Clang][Parser] Fix assertion failure with explicit(bool) in pre-C++20 modes (PR #152896)
Jongmyeong Choi via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 9 23:58:45 PDT 2025
https://github.com/jongmyeong-choi created https://github.com/llvm/llvm-project/pull/152896
## Summary
Fixes assertion failure when using `explicit(bool)` syntax in C++98 and C++11 modes. The crash occurred in
`BuildConvertedConstantExpression` when parsing `explicit(true)` or `explicit(false)` in these older C++ standards.
## Problem
The assertion failure only occurs in C++98/C++11 modes:
```cpp
// C++98/C++11 mode - CRASH
struct S {
explicit(true) S(int); // Assertion failure in BuildConvertedConstantExpression
};
```
The assertion that failed:
```
assert((S.getLangOpts().CPlusPlus11 || CCE == CCEKind::TempArgStrict) &&
"converted constant expression outside C++11 or TTP matching");
```
## Solution
- Added Parser-level error handling for explicit(bool) in pre-C++20 modes
- Added new diagnostic: err_explicit_bool_requires_cpp20
- Maintains C++17 extension behavior for backward compatibility
- Prevents reaching the problematic Sema code path that caused crashes
## Implementation Strategy
While crashes only occur in C++98/C++11, the fix applies to all pre-C++20 modes (C++98/C++11/C++14) following consistent language
feature boundaries, with C++17 maintained as an extension for compatibility.
## Changes
- clang/include/clang/Basic/DiagnosticParseKinds.td: Added new error diagnostic
- clang/lib/Parse/ParseDecl.cpp: Enhanced explicit specifier parsing with proper language version checks
- clang/test/Parser/explicit-bool-pre-cxx17.cpp: Added regression test covering C++03/C++11/C++14
>From 623ac7363bd0faa40dd093205b46ac313c4c0c14 Mon Sep 17 00:00:00 2001
From: Jongmyeong Choi <cheesechoi at gmail.com>
Date: Sun, 10 Aug 2025 15:39:47 +0900
Subject: [PATCH] [clang][Parser] Fix assertion failure for explicit(bool) in
pre-C++20
Before this fix, using explicit(bool) syntax in C++98/C++03 modes
would cause an assertion failure in BuildConvertedConstantExpression due
to the parser accepting the syntax but semantic analysis rejecting it.
This patch:
- Adds a new diagnostic error 'err_explicit_bool_requires_cpp20'
- Updates parser logic to reject explicit(bool) in pre-C++17 modes with
proper error recovery instead of proceeding to semantic analysis
- Maintains existing behavior for C++17 (extension warning) and C++20+
for backward compatibility with existing code
The fix prevents crashes and provides clear error messages to users
attempting to use C++20 features in earlier language modes.
Fixes assertion: (S.getLangOpts().CPlusPlus11 || CCE == CCEKind::TempArgStrict)
in BuildConvertedConstantExpression
Test: clang/test/Parser/explicit-bool-pre-cxx17.cpp
---
.../clang/Basic/DiagnosticParseKinds.td | 2 +
clang/lib/Parse/ParseDecl.cpp | 78 ++++++++++++++-----
clang/test/Parser/explicit-bool-pre-cxx17.cpp | 16 ++++
3 files changed, 76 insertions(+), 20 deletions(-)
create mode 100644 clang/test/Parser/explicit-bool-pre-cxx17.cpp
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 165f01514e2b1..ce1087319b326 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -842,6 +842,8 @@ def warn_cxx17_compat_explicit_bool : Warning<
InGroup<CXXPre20Compat>, DefaultIgnore;
def ext_explicit_bool : ExtWarn<"explicit(bool) is a C++20 extension">,
InGroup<CXX20>;
+def err_explicit_bool_requires_cpp20
+ : Error<"explicit(bool) requires C++20 or later">;
/// C++ Templates
def err_expected_template : Error<"expected template">;
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index e47caeb855d0c..db6b979522fa6 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4174,28 +4174,66 @@ void Parser::ParseDeclarationSpecifiers(
ConsumedEnd = ExplicitLoc;
ConsumeToken(); // kw_explicit
if (Tok.is(tok::l_paren)) {
- if (getLangOpts().CPlusPlus20 || isExplicitBool() == TPResult::True) {
- Diag(Tok.getLocation(), getLangOpts().CPlusPlus20
- ? diag::warn_cxx17_compat_explicit_bool
- : diag::ext_explicit_bool);
-
- ExprResult ExplicitExpr(static_cast<Expr *>(nullptr));
- BalancedDelimiterTracker Tracker(*this, tok::l_paren);
- Tracker.consumeOpen();
-
- EnterExpressionEvaluationContext ConstantEvaluated(
- Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ TPResult ExplicitBoolResult = isExplicitBool();
+ if (getLangOpts().CPlusPlus20) {
+ // C++20: Support explicit(bool) with compatibility warning
+ if (ExplicitBoolResult == TPResult::True ||
+ ExplicitBoolResult == TPResult::Ambiguous) {
+ Diag(Tok.getLocation(), diag::warn_cxx17_compat_explicit_bool);
+
+ ExprResult ExplicitExpr(static_cast<Expr *>(nullptr));
+ BalancedDelimiterTracker Tracker(*this, tok::l_paren);
+ Tracker.consumeOpen();
+
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+ ExplicitExpr = ParseConstantExpressionInExprEvalContext();
+ ConsumedEnd = Tok.getLocation();
+ if (ExplicitExpr.isUsable()) {
+ CloseParenLoc = Tok.getLocation();
+ Tracker.consumeClose();
+ ExplicitSpec =
+ Actions.ActOnExplicitBoolSpecifier(ExplicitExpr.get());
+ } else
+ Tracker.skipToEnd();
+ }
+ } else if (ExplicitBoolResult == TPResult::True) {
+ if (getLangOpts().CPlusPlus17) {
+ // C++17: Allow explicit(bool) as extension for compatibility
+ // This maintains backward compatibility with existing code that
+ // relied on this extension warning behavior
+ Diag(Tok.getLocation(), diag::ext_explicit_bool);
+
+ ExprResult ExplicitExpr(static_cast<Expr *>(nullptr));
+ BalancedDelimiterTracker Tracker(*this, tok::l_paren);
+ Tracker.consumeOpen();
+
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+ ExplicitExpr = ParseConstantExpressionInExprEvalContext();
+ ConsumedEnd = Tok.getLocation();
+ if (ExplicitExpr.isUsable()) {
+ CloseParenLoc = Tok.getLocation();
+ Tracker.consumeClose();
+ ExplicitSpec =
+ Actions.ActOnExplicitBoolSpecifier(ExplicitExpr.get());
+ } else
+ Tracker.skipToEnd();
+ } else {
+ // C++14 and earlier: explicit(bool) causes assertion failure
+ // Emit proper error message instead of crashing
+ Diag(Tok.getLocation(), diag::err_explicit_bool_requires_cpp20);
- ExplicitExpr = ParseConstantExpressionInExprEvalContext();
- ConsumedEnd = Tok.getLocation();
- if (ExplicitExpr.isUsable()) {
- CloseParenLoc = Tok.getLocation();
- Tracker.consumeClose();
- ExplicitSpec =
- Actions.ActOnExplicitBoolSpecifier(ExplicitExpr.get());
- } else
+ // Error recovery: skip the parenthesized expression
+ BalancedDelimiterTracker Tracker(*this, tok::l_paren);
+ Tracker.consumeOpen();
Tracker.skipToEnd();
- } else {
+ ConsumedEnd = Tok.getLocation();
+ }
+ } else if (ExplicitBoolResult == TPResult::Ambiguous) {
+ // Ambiguous case: warn about potential C++20 interpretation
Diag(Tok.getLocation(), diag::warn_cxx20_compat_explicit_bool);
}
}
diff --git a/clang/test/Parser/explicit-bool-pre-cxx17.cpp b/clang/test/Parser/explicit-bool-pre-cxx17.cpp
new file mode 100644
index 0000000000000..c890a5c45db1d
--- /dev/null
+++ b/clang/test/Parser/explicit-bool-pre-cxx17.cpp
@@ -0,0 +1,16 @@
+// Regression test for assertion failure when explicit(bool) is used in pre-C++17
+// This test ensures no crash occurs and appropriate error messages are shown.
+// RUN: %clang_cc1 -std=c++03 -verify %s
+// RUN: %clang_cc1 -std=c++11 -verify %s
+// RUN: %clang_cc1 -std=c++14 -verify %s
+
+struct S {
+ // Before the fix, this would cause assertion failure in BuildConvertedConstantExpression
+ // Now it should produce a proper error message in C++14 and earlier modes
+ // Note: C++17 allows this as an extension for compatibility
+ explicit(true) S(int);
+ // expected-error at -1 {{explicit(bool) requires C++20 or later}}
+
+ explicit(false) S(float);
+ // expected-error at -1 {{explicit(bool) requires C++20 or later}}
+};
\ No newline at end of file
More information about the cfe-commits
mailing list