Cherry-picked in 0a08d2c4e7830a1b2428c2c77f205ac74fa29899 and 2d2d057ae23036baecb5a2a4a7f929626f46921a. Thanks!
On Fri, Jan 17, 2020 at 3:14 AM Richard Smith <rich...@metafoo.co.uk> wrote: > > Also b78e8e0d79c47a6698a0abc10a37b8a253cb6064 which has an extra test file > that I forgot to git add. > > On Wed, 15 Jan 2020 at 18:52, Richard Smith <rich...@metafoo.co.uk> wrote: >> >> Hans, could this change be ported to the Clang 10 branch? In PR42694 the >> MSVC stdlib developers requested that Clang support this because their >> standard library will soon rely on it. >> >> On Wed, 15 Jan 2020 at 18:50, Richard Smith via cfe-commits >> <cfe-commits@lists.llvm.org> wrote: >>> >>> >>> 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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits