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