Skip to content

Commit 45d7080

Browse files
committed
PR42694 Support explicit(bool) in older language modes as an extension.
This needs somewhat careful disambiguation, as C++2a explicit(bool) is a breaking change. We only enable it in cases where the source construct could not possibly be anything else.
1 parent 154cd6d commit 45d7080

File tree

6 files changed

+91
-16
lines changed

6 files changed

+91
-16
lines changed

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ def err_asm_goto_cannot_have_output : Error<
3333

3434
let CategoryName = "Parse Issue" in {
3535

36-
def warn_cxx2a_compat_explicit_bool : Warning<
37-
"this expression will be parsed as explicit(bool) in C++2a">,
38-
InGroup<CXX2aCompat>, DefaultIgnore;
39-
4036
def ext_empty_translation_unit : Extension<
4137
"ISO C requires a translation unit to contain at least one declaration">,
4238
InGroup<DiagGroup<"empty-translation-unit">>;
@@ -684,6 +680,15 @@ def err_ms_property_expected_comma_or_rparen : Error<
684680
def err_ms_property_initializer : Error<
685681
"property declaration cannot have an in-class initializer">;
686682

683+
def warn_cxx2a_compat_explicit_bool : Warning<
684+
"this expression will be parsed as explicit(bool) in C++2a">,
685+
InGroup<CXX2aCompat>, DefaultIgnore;
686+
def warn_cxx17_compat_explicit_bool : Warning<
687+
"explicit(bool) is incompatible with C++ standards before C++2a">,
688+
InGroup<CXXPre2aCompat>, DefaultIgnore;
689+
def ext_explicit_bool : ExtWarn<"explicit(bool) is a C++2a extension">,
690+
InGroup<CXX2a>;
691+
687692
/// C++ Templates
688693
def err_expected_template : Error<"expected template">;
689694
def err_unknown_template_name : Error<

clang/include/clang/Parse/Parser.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,16 @@ class Parser : public CodeCompletionHandler {
806806
bool IsNewScope);
807807
bool TryAnnotateCXXScopeToken(bool EnteringContext = false);
808808

809+
bool MightBeCXXScopeToken() {
810+
return Tok.is(tok::identifier) || Tok.is(tok::coloncolon) ||
811+
(Tok.is(tok::annot_template_id) &&
812+
NextToken().is(tok::coloncolon)) ||
813+
Tok.is(tok::kw_decltype) || Tok.is(tok::kw___super);
814+
}
815+
bool TryAnnotateOptionalCXXScopeToken(bool EnteringContext = false) {
816+
return MightBeCXXScopeToken() && TryAnnotateCXXScopeToken(EnteringContext);
817+
}
818+
809819
private:
810820
enum AnnotatedNameKind {
811821
/// Annotation has failed and emitted an error.
@@ -2395,6 +2405,11 @@ class Parser : public CodeCompletionHandler {
23952405
/// rather than a less-than expression.
23962406
TPResult isTemplateArgumentList(unsigned TokensToSkip);
23972407

2408+
/// Determine whether an '(' after an 'explicit' keyword is part of a C++20
2409+
/// 'explicit(bool)' declaration, in earlier language modes where that is an
2410+
/// extension.
2411+
TPResult isExplicitBool();
2412+
23982413
/// Determine whether an identifier has been tentatively declared as a
23992414
/// non-type. Such tentative declarations should not be found to name a type
24002415
/// during a tentative parse, but also should not be annotated as a non-type.

clang/lib/Parse/ParseDecl.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3617,7 +3617,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
36173617
ConsumedEnd = ExplicitLoc;
36183618
ConsumeToken(); // kw_explicit
36193619
if (Tok.is(tok::l_paren)) {
3620-
if (getLangOpts().CPlusPlus2a) {
3620+
if (getLangOpts().CPlusPlus2a || isExplicitBool() == TPResult::True) {
3621+
Diag(Tok.getLocation(), getLangOpts().CPlusPlus2a
3622+
? diag::warn_cxx17_compat_explicit_bool
3623+
: diag::ext_explicit_bool);
3624+
36213625
ExprResult ExplicitExpr(static_cast<Expr *>(nullptr));
36223626
BalancedDelimiterTracker Tracker(*this, tok::l_paren);
36233627
Tracker.consumeOpen();
@@ -3630,8 +3634,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
36303634
Actions.ActOnExplicitBoolSpecifier(ExplicitExpr.get());
36313635
} else
36323636
Tracker.skipToEnd();
3633-
} else
3637+
} else {
36343638
Diag(Tok.getLocation(), diag::warn_cxx2a_compat_explicit_bool);
3639+
}
36353640
}
36363641
isInvalid = DS.setFunctionSpecExplicit(ExplicitLoc, PrevSpec, DiagID,
36373642
ExplicitSpec, CloseParenLoc);

clang/lib/Parse/ParseTentative.cpp

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,7 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() {
202202
}
203203
}
204204

205-
if (Tok.isOneOf(tok::identifier, tok::coloncolon, tok::kw_decltype,
206-
tok::annot_template_id) &&
207-
TryAnnotateCXXScopeToken())
205+
if (TryAnnotateOptionalCXXScopeToken())
208206
return TPResult::Error;
209207
if (Tok.is(tok::annot_cxxscope))
210208
ConsumeAnnotationToken();
@@ -785,9 +783,8 @@ Parser::isCXX11AttributeSpecifier(bool Disambiguate,
785783

786784
Parser::TPResult Parser::TryParsePtrOperatorSeq() {
787785
while (true) {
788-
if (Tok.isOneOf(tok::coloncolon, tok::identifier))
789-
if (TryAnnotateCXXScopeToken(true))
790-
return TPResult::Error;
786+
if (TryAnnotateOptionalCXXScopeToken(true))
787+
return TPResult::Error;
791788

792789
if (Tok.isOneOf(tok::star, tok::amp, tok::caret, tok::ampamp) ||
793790
(Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) {
@@ -2137,3 +2134,58 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
21372134
return TPResult::Ambiguous;
21382135
return TPResult::False;
21392136
}
2137+
2138+
/// Determine whether we might be looking at the '(' of a C++20 explicit(bool)
2139+
/// in an earlier language mode.
2140+
Parser::TPResult Parser::isExplicitBool() {
2141+
assert(Tok.is(tok::l_paren) && "expected to be looking at a '(' token");
2142+
2143+
RevertingTentativeParsingAction PA(*this);
2144+
ConsumeParen();
2145+
2146+
// We can only have 'explicit' on a constructor, conversion function, or
2147+
// deduction guide. The declarator of a deduction guide cannot be
2148+
// parenthesized, so we know this isn't a deduction guide. So the only
2149+
// thing we need to check for is some number of parens followed by either
2150+
// the current class name or 'operator'.
2151+
while (Tok.is(tok::l_paren))
2152+
ConsumeParen();
2153+
2154+
if (TryAnnotateOptionalCXXScopeToken())
2155+
return TPResult::Error;
2156+
2157+
// Class-scope constructor and conversion function names can't really be
2158+
// qualified, but we get better diagnostics if we assume they can be.
2159+
CXXScopeSpec SS;
2160+
if (Tok.is(tok::annot_cxxscope)) {
2161+
Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
2162+
Tok.getAnnotationRange(),
2163+
SS);
2164+
ConsumeAnnotationToken();
2165+
}
2166+
2167+
// 'explicit(operator' might be explicit(bool) or the declaration of a
2168+
// conversion function, but it's probably a conversion function.
2169+
if (Tok.is(tok::kw_operator))
2170+
return TPResult::Ambiguous;
2171+
2172+
// If this can't be a constructor name, it can only be explicit(bool).
2173+
if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id))
2174+
return TPResult::True;
2175+
if (!Actions.isCurrentClassName(Tok.is(tok::identifier)
2176+
? *Tok.getIdentifierInfo()
2177+
: *takeTemplateIdAnnotation(Tok)->Name,
2178+
getCurScope(), &SS))
2179+
return TPResult::True;
2180+
// Formally, we must have a right-paren after the constructor name to match
2181+
// the grammar for a constructor. But clang permits a parenthesized
2182+
// constructor declarator, so also allow a constructor declarator to follow
2183+
// with no ')' token after the constructor name.
2184+
if (!NextToken().is(tok::r_paren) &&
2185+
!isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(),
2186+
/*DeductionGuide=*/false))
2187+
return TPResult::True;
2188+
2189+
// Might be explicit(bool) or a parenthesized constructor name.
2190+
return TPResult::Ambiguous;
2191+
}

clang/lib/Parse/Parser.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2005,10 +2005,7 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS,
20052005
bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext) {
20062006
assert(getLangOpts().CPlusPlus &&
20072007
"Call sites of this function should be guarded by checking for C++");
2008-
assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) ||
2009-
(Tok.is(tok::annot_template_id) && NextToken().is(tok::coloncolon)) ||
2010-
Tok.is(tok::kw_decltype) || Tok.is(tok::kw___super)) &&
2011-
"Cannot be a type or scope token!");
2008+
assert(MightBeCXXScopeToken() && "Cannot be a type or scope token!");
20122009

20132010
CXXScopeSpec SS;
20142011
if (ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext))

clang/test/SemaCXX/cxx2a-explicit-bool.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// RUN: %clang_cc1 -std=c++17 -fsyntax-only %s -verify -Wno-c++2a-extensions
12
// RUN: %clang_cc1 -std=c++2a -fsyntax-only %s -verify
23

34
template <bool b, auto val> struct enable_ifv {};

0 commit comments

Comments
 (0)