llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Jongmyeong Choi (jongmyeong-choi) <details> <summary>Changes</summary> ## 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 --- Full diff: https://github.com/llvm/llvm-project/pull/152896.diff 3 Files Affected: - (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+2) - (modified) clang/lib/Parse/ParseDecl.cpp (+58-20) - (added) clang/test/Parser/explicit-bool-pre-cxx17.cpp (+16) ``````````diff 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 `````````` </details> https://github.com/llvm/llvm-project/pull/152896 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits