Skip to content

Commit 623ac73

Browse files
author
Jongmyeong Choi
committed
[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
1 parent 5d26e3c commit 623ac73

File tree

3 files changed

+76
-20
lines changed

3 files changed

+76
-20
lines changed

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,8 @@ def warn_cxx17_compat_explicit_bool : Warning<
842842
InGroup<CXXPre20Compat>, DefaultIgnore;
843843
def ext_explicit_bool : ExtWarn<"explicit(bool) is a C++20 extension">,
844844
InGroup<CXX20>;
845+
def err_explicit_bool_requires_cpp20
846+
: Error<"explicit(bool) requires C++20 or later">;
845847

846848
/// C++ Templates
847849
def err_expected_template : Error<"expected template">;

clang/lib/Parse/ParseDecl.cpp

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4174,28 +4174,66 @@ void Parser::ParseDeclarationSpecifiers(
41744174
ConsumedEnd = ExplicitLoc;
41754175
ConsumeToken(); // kw_explicit
41764176
if (Tok.is(tok::l_paren)) {
4177-
if (getLangOpts().CPlusPlus20 || isExplicitBool() == TPResult::True) {
4178-
Diag(Tok.getLocation(), getLangOpts().CPlusPlus20
4179-
? diag::warn_cxx17_compat_explicit_bool
4180-
: diag::ext_explicit_bool);
4181-
4182-
ExprResult ExplicitExpr(static_cast<Expr *>(nullptr));
4183-
BalancedDelimiterTracker Tracker(*this, tok::l_paren);
4184-
Tracker.consumeOpen();
4185-
4186-
EnterExpressionEvaluationContext ConstantEvaluated(
4187-
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
4177+
TPResult ExplicitBoolResult = isExplicitBool();
4178+
if (getLangOpts().CPlusPlus20) {
4179+
// C++20: Support explicit(bool) with compatibility warning
4180+
if (ExplicitBoolResult == TPResult::True ||
4181+
ExplicitBoolResult == TPResult::Ambiguous) {
4182+
Diag(Tok.getLocation(), diag::warn_cxx17_compat_explicit_bool);
4183+
4184+
ExprResult ExplicitExpr(static_cast<Expr *>(nullptr));
4185+
BalancedDelimiterTracker Tracker(*this, tok::l_paren);
4186+
Tracker.consumeOpen();
4187+
4188+
EnterExpressionEvaluationContext ConstantEvaluated(
4189+
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
4190+
4191+
ExplicitExpr = ParseConstantExpressionInExprEvalContext();
4192+
ConsumedEnd = Tok.getLocation();
4193+
if (ExplicitExpr.isUsable()) {
4194+
CloseParenLoc = Tok.getLocation();
4195+
Tracker.consumeClose();
4196+
ExplicitSpec =
4197+
Actions.ActOnExplicitBoolSpecifier(ExplicitExpr.get());
4198+
} else
4199+
Tracker.skipToEnd();
4200+
}
4201+
} else if (ExplicitBoolResult == TPResult::True) {
4202+
if (getLangOpts().CPlusPlus17) {
4203+
// C++17: Allow explicit(bool) as extension for compatibility
4204+
// This maintains backward compatibility with existing code that
4205+
// relied on this extension warning behavior
4206+
Diag(Tok.getLocation(), diag::ext_explicit_bool);
4207+
4208+
ExprResult ExplicitExpr(static_cast<Expr *>(nullptr));
4209+
BalancedDelimiterTracker Tracker(*this, tok::l_paren);
4210+
Tracker.consumeOpen();
4211+
4212+
EnterExpressionEvaluationContext ConstantEvaluated(
4213+
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
4214+
4215+
ExplicitExpr = ParseConstantExpressionInExprEvalContext();
4216+
ConsumedEnd = Tok.getLocation();
4217+
if (ExplicitExpr.isUsable()) {
4218+
CloseParenLoc = Tok.getLocation();
4219+
Tracker.consumeClose();
4220+
ExplicitSpec =
4221+
Actions.ActOnExplicitBoolSpecifier(ExplicitExpr.get());
4222+
} else
4223+
Tracker.skipToEnd();
4224+
} else {
4225+
// C++14 and earlier: explicit(bool) causes assertion failure
4226+
// Emit proper error message instead of crashing
4227+
Diag(Tok.getLocation(), diag::err_explicit_bool_requires_cpp20);
41884228

4189-
ExplicitExpr = ParseConstantExpressionInExprEvalContext();
4190-
ConsumedEnd = Tok.getLocation();
4191-
if (ExplicitExpr.isUsable()) {
4192-
CloseParenLoc = Tok.getLocation();
4193-
Tracker.consumeClose();
4194-
ExplicitSpec =
4195-
Actions.ActOnExplicitBoolSpecifier(ExplicitExpr.get());
4196-
} else
4229+
// Error recovery: skip the parenthesized expression
4230+
BalancedDelimiterTracker Tracker(*this, tok::l_paren);
4231+
Tracker.consumeOpen();
41974232
Tracker.skipToEnd();
4198-
} else {
4233+
ConsumedEnd = Tok.getLocation();
4234+
}
4235+
} else if (ExplicitBoolResult == TPResult::Ambiguous) {
4236+
// Ambiguous case: warn about potential C++20 interpretation
41994237
Diag(Tok.getLocation(), diag::warn_cxx20_compat_explicit_bool);
42004238
}
42014239
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Regression test for assertion failure when explicit(bool) is used in pre-C++17
2+
// This test ensures no crash occurs and appropriate error messages are shown.
3+
// RUN: %clang_cc1 -std=c++03 -verify %s
4+
// RUN: %clang_cc1 -std=c++11 -verify %s
5+
// RUN: %clang_cc1 -std=c++14 -verify %s
6+
7+
struct S {
8+
// Before the fix, this would cause assertion failure in BuildConvertedConstantExpression
9+
// Now it should produce a proper error message in C++14 and earlier modes
10+
// Note: C++17 allows this as an extension for compatibility
11+
explicit(true) S(int);
12+
// expected-error@-1 {{explicit(bool) requires C++20 or later}}
13+
14+
explicit(false) S(float);
15+
// expected-error@-1 {{explicit(bool) requires C++20 or later}}
16+
};

0 commit comments

Comments
 (0)