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, DefaultIgnore; def ext_explicit_bool : ExtWarn<"explicit(bool) is a C++20 extension">, InGroup; +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(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(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(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