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.
This commit is contained in:
@@ -33,10 +33,6 @@ def err_asm_goto_cannot_have_output : Error<
|
||||
|
||||
let CategoryName = "Parse Issue" in {
|
||||
|
||||
def warn_cxx2a_compat_explicit_bool : Warning<
|
||||
"this expression will be parsed as explicit(bool) in C++2a">,
|
||||
InGroup<CXX2aCompat>, DefaultIgnore;
|
||||
|
||||
def ext_empty_translation_unit : Extension<
|
||||
"ISO C requires a translation unit to contain at least one declaration">,
|
||||
InGroup<DiagGroup<"empty-translation-unit">>;
|
||||
@@ -684,6 +680,15 @@ def err_ms_property_expected_comma_or_rparen : Error<
|
||||
def err_ms_property_initializer : Error<
|
||||
"property declaration cannot have an in-class initializer">;
|
||||
|
||||
def warn_cxx2a_compat_explicit_bool : Warning<
|
||||
"this expression will be parsed as explicit(bool) in C++2a">,
|
||||
InGroup<CXX2aCompat>, DefaultIgnore;
|
||||
def warn_cxx17_compat_explicit_bool : Warning<
|
||||
"explicit(bool) is incompatible with C++ standards before C++2a">,
|
||||
InGroup<CXXPre2aCompat>, DefaultIgnore;
|
||||
def ext_explicit_bool : ExtWarn<"explicit(bool) is a C++2a extension">,
|
||||
InGroup<CXX2a>;
|
||||
|
||||
/// C++ Templates
|
||||
def err_expected_template : Error<"expected template">;
|
||||
def err_unknown_template_name : Error<
|
||||
|
||||
@@ -806,6 +806,16 @@ public:
|
||||
bool IsNewScope);
|
||||
bool TryAnnotateCXXScopeToken(bool EnteringContext = false);
|
||||
|
||||
bool MightBeCXXScopeToken() {
|
||||
return Tok.is(tok::identifier) || Tok.is(tok::coloncolon) ||
|
||||
(Tok.is(tok::annot_template_id) &&
|
||||
NextToken().is(tok::coloncolon)) ||
|
||||
Tok.is(tok::kw_decltype) || Tok.is(tok::kw___super);
|
||||
}
|
||||
bool TryAnnotateOptionalCXXScopeToken(bool EnteringContext = false) {
|
||||
return MightBeCXXScopeToken() && TryAnnotateCXXScopeToken(EnteringContext);
|
||||
}
|
||||
|
||||
private:
|
||||
enum AnnotatedNameKind {
|
||||
/// Annotation has failed and emitted an error.
|
||||
@@ -2395,6 +2405,11 @@ private:
|
||||
/// rather than a less-than expression.
|
||||
TPResult isTemplateArgumentList(unsigned TokensToSkip);
|
||||
|
||||
/// Determine whether an '(' after an 'explicit' keyword is part of a C++20
|
||||
/// 'explicit(bool)' declaration, in earlier language modes where that is an
|
||||
/// extension.
|
||||
TPResult isExplicitBool();
|
||||
|
||||
/// Determine whether an identifier has been tentatively declared as a
|
||||
/// non-type. Such tentative declarations should not be found to name a type
|
||||
/// during a tentative parse, but also should not be annotated as a non-type.
|
||||
|
||||
@@ -3617,7 +3617,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
||||
ConsumedEnd = ExplicitLoc;
|
||||
ConsumeToken(); // kw_explicit
|
||||
if (Tok.is(tok::l_paren)) {
|
||||
if (getLangOpts().CPlusPlus2a) {
|
||||
if (getLangOpts().CPlusPlus2a || isExplicitBool() == TPResult::True) {
|
||||
Diag(Tok.getLocation(), getLangOpts().CPlusPlus2a
|
||||
? diag::warn_cxx17_compat_explicit_bool
|
||||
: diag::ext_explicit_bool);
|
||||
|
||||
ExprResult ExplicitExpr(static_cast<Expr *>(nullptr));
|
||||
BalancedDelimiterTracker Tracker(*this, tok::l_paren);
|
||||
Tracker.consumeOpen();
|
||||
@@ -3630,8 +3634,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
||||
Actions.ActOnExplicitBoolSpecifier(ExplicitExpr.get());
|
||||
} else
|
||||
Tracker.skipToEnd();
|
||||
} else
|
||||
} else {
|
||||
Diag(Tok.getLocation(), diag::warn_cxx2a_compat_explicit_bool);
|
||||
}
|
||||
}
|
||||
isInvalid = DS.setFunctionSpecExplicit(ExplicitLoc, PrevSpec, DiagID,
|
||||
ExplicitSpec, CloseParenLoc);
|
||||
|
||||
@@ -202,9 +202,7 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() {
|
||||
}
|
||||
}
|
||||
|
||||
if (Tok.isOneOf(tok::identifier, tok::coloncolon, tok::kw_decltype,
|
||||
tok::annot_template_id) &&
|
||||
TryAnnotateCXXScopeToken())
|
||||
if (TryAnnotateOptionalCXXScopeToken())
|
||||
return TPResult::Error;
|
||||
if (Tok.is(tok::annot_cxxscope))
|
||||
ConsumeAnnotationToken();
|
||||
@@ -785,9 +783,8 @@ Parser::isCXX11AttributeSpecifier(bool Disambiguate,
|
||||
|
||||
Parser::TPResult Parser::TryParsePtrOperatorSeq() {
|
||||
while (true) {
|
||||
if (Tok.isOneOf(tok::coloncolon, tok::identifier))
|
||||
if (TryAnnotateCXXScopeToken(true))
|
||||
return TPResult::Error;
|
||||
if (TryAnnotateOptionalCXXScopeToken(true))
|
||||
return TPResult::Error;
|
||||
|
||||
if (Tok.isOneOf(tok::star, tok::amp, tok::caret, tok::ampamp) ||
|
||||
(Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) {
|
||||
@@ -2137,3 +2134,58 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
|
||||
return TPResult::Ambiguous;
|
||||
return TPResult::False;
|
||||
}
|
||||
|
||||
/// Determine whether we might be looking at the '(' of a C++20 explicit(bool)
|
||||
/// in an earlier language mode.
|
||||
Parser::TPResult Parser::isExplicitBool() {
|
||||
assert(Tok.is(tok::l_paren) && "expected to be looking at a '(' token");
|
||||
|
||||
RevertingTentativeParsingAction PA(*this);
|
||||
ConsumeParen();
|
||||
|
||||
// We can only have 'explicit' on a constructor, conversion function, or
|
||||
// deduction guide. The declarator of a deduction guide cannot be
|
||||
// parenthesized, so we know this isn't a deduction guide. So the only
|
||||
// thing we need to check for is some number of parens followed by either
|
||||
// the current class name or 'operator'.
|
||||
while (Tok.is(tok::l_paren))
|
||||
ConsumeParen();
|
||||
|
||||
if (TryAnnotateOptionalCXXScopeToken())
|
||||
return TPResult::Error;
|
||||
|
||||
// Class-scope constructor and conversion function names can't really be
|
||||
// qualified, but we get better diagnostics if we assume they can be.
|
||||
CXXScopeSpec SS;
|
||||
if (Tok.is(tok::annot_cxxscope)) {
|
||||
Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
|
||||
Tok.getAnnotationRange(),
|
||||
SS);
|
||||
ConsumeAnnotationToken();
|
||||
}
|
||||
|
||||
// 'explicit(operator' might be explicit(bool) or the declaration of a
|
||||
// conversion function, but it's probably a conversion function.
|
||||
if (Tok.is(tok::kw_operator))
|
||||
return TPResult::Ambiguous;
|
||||
|
||||
// If this can't be a constructor name, it can only be explicit(bool).
|
||||
if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id))
|
||||
return TPResult::True;
|
||||
if (!Actions.isCurrentClassName(Tok.is(tok::identifier)
|
||||
? *Tok.getIdentifierInfo()
|
||||
: *takeTemplateIdAnnotation(Tok)->Name,
|
||||
getCurScope(), &SS))
|
||||
return TPResult::True;
|
||||
// Formally, we must have a right-paren after the constructor name to match
|
||||
// the grammar for a constructor. But clang permits a parenthesized
|
||||
// constructor declarator, so also allow a constructor declarator to follow
|
||||
// with no ')' token after the constructor name.
|
||||
if (!NextToken().is(tok::r_paren) &&
|
||||
!isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(),
|
||||
/*DeductionGuide=*/false))
|
||||
return TPResult::True;
|
||||
|
||||
// Might be explicit(bool) or a parenthesized constructor name.
|
||||
return TPResult::Ambiguous;
|
||||
}
|
||||
|
||||
@@ -2005,10 +2005,7 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS,
|
||||
bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext) {
|
||||
assert(getLangOpts().CPlusPlus &&
|
||||
"Call sites of this function should be guarded by checking for C++");
|
||||
assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) ||
|
||||
(Tok.is(tok::annot_template_id) && NextToken().is(tok::coloncolon)) ||
|
||||
Tok.is(tok::kw_decltype) || Tok.is(tok::kw___super)) &&
|
||||
"Cannot be a type or scope token!");
|
||||
assert(MightBeCXXScopeToken() && "Cannot be a type or scope token!");
|
||||
|
||||
CXXScopeSpec SS;
|
||||
if (ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// RUN: %clang_cc1 -std=c++17 -fsyntax-only %s -verify -Wno-c++2a-extensions
|
||||
// RUN: %clang_cc1 -std=c++2a -fsyntax-only %s -verify
|
||||
|
||||
template <bool b, auto val> struct enable_ifv {};
|
||||
|
||||
Reference in New Issue
Block a user