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 <cheesec...@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@-1 {{explicit(bool) requires C++20 or later}}
+  
+  explicit(false) S(float);
+  // expected-error@-1 {{explicit(bool) requires C++20 or later}}
+};
\ No newline at end of file

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

Reply via email to