Author: Richard Smith Date: 2020-01-15T18:38:23-08:00 New Revision: 45d70806f4386adfb62b0d75949a8aad58e0576f
URL: https://github.com/llvm/llvm-project/commit/45d70806f4386adfb62b0d75949a8aad58e0576f DIFF: https://github.com/llvm/llvm-project/commit/45d70806f4386adfb62b0d75949a8aad58e0576f.diff LOG: 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. Added: Modified: clang/include/clang/Basic/DiagnosticParseKinds.td clang/include/clang/Parse/Parser.h clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/ParseTentative.cpp clang/lib/Parse/Parser.cpp clang/test/SemaCXX/cxx2a-explicit-bool.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index cc6a74ac3e6d..41f788e7d9bd 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -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< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index e320c9647818..b7bed4713992 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -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. @@ -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. diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 69a3ed9cbad7..d8c5a0ab02d3 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -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); diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 4d69fb4693fb..d5068fb11b86 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -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; +} diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 4249de361b89..0fb0a5217d54 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -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)) diff --git a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp index df776b390548..45385972cab8 100644 --- a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp +++ b/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 {}; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits