[clang] 45d7080 - PR42694 Support explicit(bool) in older language modes as an extension.
Hans Wennborg via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 17 00:58:05 PST 2020
Cherry-picked in 0a08d2c4e7830a1b2428c2c77f205ac74fa29899 and
2d2d057ae23036baecb5a2a4a7f929626f46921a. Thanks!
On Fri, Jan 17, 2020 at 3:14 AM Richard Smith <richard at 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 <richard at 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 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
More information about the cfe-commits
mailing list