Author: Richard Smith Date: 2020-01-24T18:53:50-08:00 New Revision: 04f131da0b19abff611773c03be9bafb53c753ce
URL: https://github.com/llvm/llvm-project/commit/04f131da0b19abff611773c03be9bafb53c753ce DIFF: https://github.com/llvm/llvm-project/commit/04f131da0b19abff611773c03be9bafb53c753ce.diff LOG: DR1753: Don't permit x.NS::~T() as a pseudo-destructor name. When used as qualified names, pseudo-destructors are always named as if they were members of the type, never as members of the namespace enclosing the type. Added: Modified: clang/lib/Parse/ParseExprCXX.cpp clang/test/CXX/drs/dr17xx.cpp clang/test/SemaCXX/pseudo-destructors.cpp clang/www/cxx_dr_status.html Removed: ################################################################################ diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 036eabb94dd7..73d15cbc20c1 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -418,8 +418,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, } if (Next.is(tok::coloncolon)) { - if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde) && - !Actions.isNonTypeNestedNameSpecifier(getCurScope(), SS, IdInfo)) { + if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde)) { *MayBePseudoDestructor = true; return false; } @@ -548,7 +547,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, // Even if we didn't see any pieces of a nested-name-specifier, we // still check whether there is a tilde in this position, which // indicates a potential pseudo-destructor. - if (CheckForDestructor && Tok.is(tok::tilde)) + if (CheckForDestructor && !HasScopeSpecifier && Tok.is(tok::tilde)) *MayBePseudoDestructor = true; return false; @@ -1689,31 +1688,42 @@ ExprResult Parser::ParseCXXUuidof() { /// Parse a C++ pseudo-destructor expression after the base, /// . or -> operator, and nested-name-specifier have already been -/// parsed. +/// parsed. We're handling this fragment of the grammar: +/// +/// postfix-expression: [C++2a expr.post] +/// postfix-expression . template[opt] id-expression +/// postfix-expression -> template[opt] id-expression /// -/// postfix-expression: [C++ 5.2] -/// postfix-expression . pseudo-destructor-name -/// postfix-expression -> pseudo-destructor-name +/// id-expression: +/// qualified-id +/// unqualified-id /// -/// pseudo-destructor-name: -/// ::[opt] nested-name-specifier[opt] type-name :: ~type-name -/// ::[opt] nested-name-specifier template simple-template-id :: -/// ~type-name -/// ::[opt] nested-name-specifier[opt] ~type-name +/// qualified-id: +/// nested-name-specifier template[opt] unqualified-id /// +/// nested-name-specifier: +/// type-name :: +/// decltype-specifier :: FIXME: not implemented, but probably only +/// allowed in C++ grammar by accident +/// nested-name-specifier identifier :: +/// nested-name-specifier template[opt] simple-template-id :: +/// [...] +/// +/// unqualified-id: +/// ~ type-name +/// ~ decltype-specifier +/// [...] +/// +/// ... where the all but the last component of the nested-name-specifier +/// has already been parsed, and the base expression is not of a non-dependent +/// class type. ExprResult Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc, tok::TokenKind OpKind, CXXScopeSpec &SS, ParsedType ObjectType) { - // We're parsing either a pseudo-destructor-name or a dependent - // member access that has the same form as a - // pseudo-destructor-name. We parse both in the same way and let - // the action model sort them out. - // - // Note that the ::[opt] nested-name-specifier[opt] has already - // been parsed, and if there was a simple-template-id, it has - // been coalesced into a template-id annotation token. + // If the last component of the (optional) nested-name-specifier is + // template[opt] simple-template-id, it has already been annotated. UnqualifiedId FirstTypeName; SourceLocation CCLoc; if (Tok.is(tok::identifier)) { @@ -1722,14 +1732,13 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc, assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail"); CCLoc = ConsumeToken(); } else if (Tok.is(tok::annot_template_id)) { - // FIXME: retrieve TemplateKWLoc from template-id annotation and - // store it in the pseudo-dtor node (to be used when instantiating it). FirstTypeName.setTemplateId( (TemplateIdAnnotation *)Tok.getAnnotationValue()); ConsumeAnnotationToken(); assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail"); CCLoc = ConsumeToken(); } else { + assert(SS.isEmpty() && "missing last component of nested name specifier"); FirstTypeName.setIdentifier(nullptr, SourceLocation()); } @@ -1737,7 +1746,7 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc, assert(Tok.is(tok::tilde) && "ParseOptionalCXXScopeSpecifier fail"); SourceLocation TildeLoc = ConsumeToken(); - if (Tok.is(tok::kw_decltype) && !FirstTypeName.isValid() && SS.isEmpty()) { + if (Tok.is(tok::kw_decltype) && !FirstTypeName.isValid()) { DeclSpec DS(AttrFactory); ParseDecltypeSpecifier(DS); if (DS.getTypeSpecType() == TST_error) diff --git a/clang/test/CXX/drs/dr17xx.cpp b/clang/test/CXX/drs/dr17xx.cpp index ca55c42977df..c9f5b2df95a5 100644 --- a/clang/test/CXX/drs/dr17xx.cpp +++ b/clang/test/CXX/drs/dr17xx.cpp @@ -3,10 +3,6 @@ // RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++1z %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -#if __cplusplus < 201103L -// expected-no-diagnostics -#endif - namespace dr1715 { // dr1715: 3.9 #if __cplusplus >= 201103L struct B { @@ -47,6 +43,32 @@ S s(q); // expected-note {{instantiation of}} #endif } +namespace dr1753 { // dr1753: 11 + typedef int T; + struct A { typedef int T; }; + namespace B { typedef int T; } + + void f(T n) { + n.~T(); + n.T::~T(); + + n.dr1753::~T(); // expected-error {{'dr1753' does not refer to a type name in pseudo-destructor}} + n.dr1753::T::~T(); + + n.A::~T(); // expected-error {{the type of object expression ('dr1753::T' (aka 'int')) does not match the type being destroyed ('dr1753::A') in pseudo-destructor expression}} + n.A::T::~T(); + + n.B::~T(); // expected-error {{'B' does not refer to a type name in pseudo-destructor expression}} + n.B::T::~T(); + + #if __cplusplus >= 201103L + n.decltype(n)::~T(); // expected-error {{not a class, namespace, or enumeration}} + n.T::~decltype(n)(); // expected-error {{expected a class name after '~'}} + n.~decltype(n)(); // OK + #endif + } +} + namespace dr1756 { // dr1756: 3.7 #if __cplusplus >= 201103L // Direct-list-initialization of a non-class object diff --git a/clang/test/SemaCXX/pseudo-destructors.cpp b/clang/test/SemaCXX/pseudo-destructors.cpp index fb2d0afdc3fa..dfdd1174b8a4 100644 --- a/clang/test/SemaCXX/pseudo-destructors.cpp +++ b/clang/test/SemaCXX/pseudo-destructors.cpp @@ -33,17 +33,21 @@ void f(A* a, Foo *f, int *i, double *d, int ii) { g().~Bar(); // expected-error{{non-scalar}} - f->::~Bar(); + f->::~Bar(); // expected-error {{not a structure or union}} + f->::Bar::~Bar(); f->N::~Wibble(); // expected-error{{'N' does not refer to a type}} expected-error{{'Wibble' does not refer to a type}} - f->::~Bar(17, 42); // expected-error{{cannot have any arguments}} + f->Bar::~Bar(17, 42); // expected-error{{cannot have any arguments}} i->~Integer(); i->Integer::~Integer(); - i->N::~OtherInteger(); + i->N::~OtherInteger(); // expected-error{{'N' does not refer to a type name in pseudo-destructor expression; expected the name of type 'int'}} + // expected-error@-1{{'OtherInteger' does not refer to a type name in pseudo-destructor expression; expected the name of type 'int'}} i->N::OtherInteger::~OtherInteger(); + i->N::OtherInteger::~OtherInteger(); + i->N::OtherInteger::~Integer(); // expected-error{{'Integer' does not refer to a type name in pseudo-destructor expression; expected the name of type 'int'}} + i->N::~Integer(); // expected-error{{'N' does not refer to a type name in pseudo-destructor expression; expected the name of type 'int'}} i->N::OtherInteger::~Integer(); // expected-error{{'Integer' does not refer to a type name in pseudo-destructor expression; expected the name of type 'int'}} - i->N::~Integer(); // expected-error{{'Integer' does not refer to a type name in pseudo-destructor expression; expected the name of type 'int'}} i->Integer::~Double(); // expected-error{{the type of object expression ('int') does not match the type being destroyed ('Double' (aka 'double')) in pseudo-destructor expression}} ii->~Integer(); // expected-error{{member reference type 'int' is not a pointer; did you mean to use '.'?}} diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index eff9fd5dc185..1a00b4e06860 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -10333,7 +10333,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://wg21.link/cwg1753";>1753</a></td> <td>CD4</td> <td><I>decltype-specifier</I> in <I>nested-name-specifier</I> of destructor</td> - <td class="none" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 11</td> </tr> <tr id="1754"> <td><a href="https://wg21.link/cwg1754";>1754</a></td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits