[clang] 45d7080 - PR42694 Support explicit(bool) in older language modes as an extension.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 15 18:52:24 PST 2020


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 at 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 at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20200115/88fb15cd/attachment-0001.html>


More information about the cfe-commits mailing list