Skip to content

Commit

Permalink
PR42694 Support explicit(bool) in older language modes as an extension.
Browse files Browse the repository at this point in the history
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.

(cherry picked from commit 45d7080)
  • Loading branch information
zygoloid authored and zmodem committed Jan 17, 2020
1 parent e241c8f commit 0a08d2c
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 16 deletions.
13 changes: 9 additions & 4 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Expand Up @@ -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">>;
Expand Down Expand Up @@ -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<
Expand Down
15 changes: 15 additions & 0 deletions clang/include/clang/Parse/Parser.h
Expand Up @@ -806,6 +806,16 @@ class Parser : public CodeCompletionHandler {
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.
Expand Down Expand Up @@ -2395,6 +2405,11 @@ class Parser : public CodeCompletionHandler {
/// 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.
Expand Down
9 changes: 7 additions & 2 deletions clang/lib/Parse/ParseDecl.cpp
Expand Up @@ -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();
Expand All @@ -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);
Expand Down
64 changes: 58 additions & 6 deletions clang/lib/Parse/ParseTentative.cpp
Expand Up @@ -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();
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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;
}
5 changes: 1 addition & 4 deletions clang/lib/Parse/Parser.cpp
Expand Up @@ -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))
Expand Down
1 change: 1 addition & 0 deletions clang/test/SemaCXX/cxx2a-explicit-bool.cpp
@@ -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 {};
Expand Down

0 comments on commit 0a08d2c

Please sign in to comment.