diff options
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticParseKinds.td | 8 | ||||
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 | ||||
| -rw-r--r-- | clang/include/clang/Parse/Action.h | 14 | ||||
| -rw-r--r-- | clang/include/clang/Parse/Parser.h | 3 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseDeclCXX.cpp | 6 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseExprCXX.cpp | 27 | ||||
| -rw-r--r-- | clang/lib/Sema/Sema.h | 6 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 226 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 9 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiate.cpp | 6 | ||||
| -rw-r--r-- | clang/lib/Sema/TreeTransform.h | 171 | ||||
| -rw-r--r-- | clang/test/SemaCXX/destructor.cpp | 4 | ||||
| -rw-r--r-- | clang/test/SemaCXX/invalid-member-expr.cpp | 4 | ||||
| -rw-r--r-- | clang/test/SemaCXX/pseudo-destructors.cpp | 6 | ||||
| -rw-r--r-- | clang/test/SemaTemplate/destructor-template.cpp | 13 | 
15 files changed, 394 insertions, 113 deletions
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index bc26c3b0dad..4b8ed447831 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -146,8 +146,6 @@ def err_missing_comma_before_ellipsis : Error<  def err_unexpected_typedef_ident : Error<    "unexpected type name %0: expected identifier">;  def err_expected_class_name : Error<"expected class name">; -def err_destructor_class_name : Error< -  "expected the class name after '~' to name a destructor">;  def err_unspecified_vla_size_with_static : Error<    "'static' may not be used with an unspecified variable length array size">; @@ -247,8 +245,10 @@ def err_expected_catch : Error<"expected catch">;  def err_expected_lbrace_or_comma : Error<"expected '{' or ','">;  def err_using_namespace_in_class : Error<    "'using namespace' is not allowed in classes">; -def err_ident_in_pseudo_dtor_not_a_type : Error< -  "identifier %0 in pseudo-destructor expression does not name a type">; +def err_destructor_tilde_identifier : Error< +  "expected a class name after '~' to name a destructor">; +def err_destructor_template_id : Error< +  "destructor name %0 does not refer to a template">;  // C++ derived classes  def err_dup_virtual : Error<"duplicate 'virtual' in base specifier">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 19b242e86b6..bc834a8478e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -557,6 +557,10 @@ def err_destructor_typedef_name : Error<    "destructor cannot be declared using a typedef %0 of the class name">;  def err_destructor_name : Error<    "expected the class name after '~' to name the enclosing class">; +def err_destructor_class_name : Error< +  "expected the class name after '~' to name a destructor">; +def err_ident_in_pseudo_dtor_not_a_type : Error< +  "identifier %0 in pseudo-destructor expression does not name a type">;  // C++ initialization  def err_init_conversion_failed : Error< diff --git a/clang/include/clang/Parse/Action.h b/clang/include/clang/Parse/Action.h index ec542f08c30..882e5d15f5d 100644 --- a/clang/include/clang/Parse/Action.h +++ b/clang/include/clang/Parse/Action.h @@ -1075,7 +1075,7 @@ public:                                                     SourceLocation RLoc) {      return ExprEmpty();    } -   +    /// \brief Parsed a member access expresion (C99 6.5.2.3, C++ [expr.ref])    /// of the form \c x.m or \c p->m.    /// @@ -1473,6 +1473,18 @@ public:    //===------------------------- C++ Expressions --------------------------===// +  /// \brief Parsed a destructor name or pseudo-destructor name.  +  /// +  /// \returns the type being destructed. +  virtual TypeTy *getDestructorName(SourceLocation TildeLoc, +                                    IdentifierInfo &II, SourceLocation NameLoc, +                                    Scope *S, const CXXScopeSpec &SS, +                                    TypeTy *ObjectType, +                                    bool EnteringContext) { +    return getTypeName(II, NameLoc, S, &SS, false, ObjectType); +  } + +    /// ActOnCXXNamedCast - Parse {dynamic,static,reinterpret,const}_cast's.    virtual OwningExprResult ActOnCXXNamedCast(SourceLocation OpLoc,                                               tok::TokenKind Kind, diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index f4d3d3e54d5..f491fa895b2 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1387,8 +1387,7 @@ private:    //===--------------------------------------------------------------------===//    // C++ 9: classes [class] and C structs/unions.    TypeResult ParseClassName(SourceLocation &EndLocation, -                            const CXXScopeSpec *SS = 0, -                            bool DestrExpected = false); +                            const CXXScopeSpec *SS = 0);    void ParseClassSpecifier(tok::TokenKind TagTokKind, SourceLocation TagLoc,                             DeclSpec &DS,                  const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(), diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 51ee6a44348..225ce256b11 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -464,8 +464,7 @@ void Parser::ParseDecltypeSpecifier(DeclSpec &DS) {  ///         simple-template-id  ///  Parser::TypeResult Parser::ParseClassName(SourceLocation &EndLocation, -                                          const CXXScopeSpec *SS, -                                          bool DestrExpected) { +                                          const CXXScopeSpec *SS) {    // Check whether we have a template-id that names a type.    if (Tok.is(tok::annot_template_id)) {      TemplateIdAnnotation *TemplateId @@ -536,8 +535,7 @@ Parser::TypeResult Parser::ParseClassName(SourceLocation &EndLocation,    // We have an identifier; check whether it is actually a type.    TypeTy *Type = Actions.getTypeName(*Id, IdLoc, CurScope, SS, true);    if (!Type) {     -    Diag(IdLoc, DestrExpected ? diag::err_destructor_class_name -                            : diag::err_expected_class_name); +    Diag(IdLoc, diag::err_expected_class_name);      return true;    } diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 0dbe1ea8389..932d6ebf97f 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -846,13 +846,8 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,                                     EnteringContext, Template);        if (TNK == TNK_Non_template && Id.DestructorName == 0) { -        // The identifier following the destructor did not refer to a template -        // or to a type. Complain. -        if (ObjectType) -          Diag(NameLoc, diag::err_ident_in_pseudo_dtor_not_a_type) -            << Name;         -        else -          Diag(NameLoc, diag::err_destructor_class_name); +        Diag(NameLoc, diag::err_destructor_template_id) +          << Name << SS.getRange();          return true;                }      } @@ -1258,7 +1253,7 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,      // Parse the class-name.      if (Tok.isNot(tok::identifier)) { -      Diag(Tok, diag::err_destructor_class_name); +      Diag(Tok, diag::err_destructor_tilde_identifier);        return true;      } @@ -1273,17 +1268,13 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,      }      // Note that this is a destructor name. -    Action::TypeTy *Ty = Actions.getTypeName(*ClassName, ClassNameLoc, -                                             CurScope, &SS, false, ObjectType); -    if (!Ty) { -      if (ObjectType) -        Diag(ClassNameLoc, diag::err_ident_in_pseudo_dtor_not_a_type) -          << ClassName;         -      else -        Diag(ClassNameLoc, diag::err_destructor_class_name); +    Action::TypeTy *Ty = Actions.getDestructorName(TildeLoc, *ClassName,  +                                                   ClassNameLoc, CurScope, +                                                   SS, ObjectType, +                                                   EnteringContext); +    if (!Ty)        return true; -    } -     +      Result.setDestructorName(TildeLoc, Ty, ClassNameLoc);      return false;    } diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 3c0adbe6b0d..25ca3d1144c 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -2056,6 +2056,12 @@ public:                                 SourceLocation Loc,                                                            ASTOwningVector<&ActionBase::DeleteExpr> &ConvertedArgs); +  virtual TypeTy *getDestructorName(SourceLocation TildeLoc, +                                    IdentifierInfo &II, SourceLocation NameLoc, +                                    Scope *S, const CXXScopeSpec &SS, +                                    TypeTy *ObjectType, +                                    bool EnteringContext); +    /// ActOnCXXNamedCast - Parse {dynamic,static,reinterpret,const}_cast's.    virtual OwningExprResult ActOnCXXNamedCast(SourceLocation OpLoc,                                               tok::TokenKind Kind, diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 9eeda54299a..4098d0f2cf2 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -24,6 +24,232 @@  #include "llvm/ADT/STLExtras.h"  using namespace clang; +Action::TypeTy *Sema::getDestructorName(SourceLocation TildeLoc, +                                        IdentifierInfo &II,  +                                        SourceLocation NameLoc, +                                        Scope *S, const CXXScopeSpec &SS, +                                        TypeTy *ObjectTypePtr, +                                        bool EnteringContext) { +  // Determine where to perform name lookup. + +  // FIXME: This area of the standard is very messy, and the current +  // wording is rather unclear about which scopes we search for the +  // destructor name; see core issues 399 and 555. Issue 399 in +  // particular shows where the current description of destructor name +  // lookup is completely out of line with existing practice, e.g., +  // this appears to be ill-formed: +  // +  //   namespace N { +  //     template <typename T> struct S { +  //       ~S(); +  //     }; +  //   } +  // +  //   void f(N::S<int>* s) { +  //     s->N::S<int>::~S(); +  //   } +  // +  //  +  QualType SearchType; +  DeclContext *LookupCtx = 0; +  bool isDependent = false; +  bool LookInScope = false; + +  // If we have an object type, it's because we are in a +  // pseudo-destructor-expression or a member access expression, and +  // we know what type we're looking for. +  if (ObjectTypePtr) +    SearchType = GetTypeFromParser(ObjectTypePtr); + +  if (SS.isSet()) { +    // C++ [basic.lookup.qual]p6: +    //   If a pseudo-destructor-name (5.2.4) contains a +    //   nested-name-specifier, the type-names are looked up as types +    //   in the scope designated by the nested-name-specifier. Similarly, in  +    //   a qualified-id of theform: +    // +    //     :: [opt] nested-name-specifier[opt] class-name :: ~class-name  +    // +    //   the second class-name is looked up in the same scope as the first. +    // +    // To implement this, we look at the prefix of the +    // nested-name-specifier we were given, and determine the lookup +    // context from that. +    NestedNameSpecifier *NNS = (NestedNameSpecifier *)SS.getScopeRep(); +    if (NestedNameSpecifier *Prefix = NNS->getPrefix()) { +      CXXScopeSpec PrefixSS; +      PrefixSS.setScopeRep(Prefix); +      LookupCtx = computeDeclContext(PrefixSS, EnteringContext); +      isDependent = isDependentScopeSpecifier(PrefixSS); +    } else if (ObjectTypePtr) { +      LookupCtx = computeDeclContext(SearchType); +      isDependent = SearchType->isDependentType(); +    } else { +      LookupCtx = computeDeclContext(SS, EnteringContext); +      if (LookupCtx && !LookupCtx->isTranslationUnit()) { +        LookupCtx = LookupCtx->getParent(); +        isDependent = LookupCtx->isDependentContext(); +      } else { +        isDependent = false; +      } +    } + +    LookInScope = false; +  } else if (ObjectTypePtr) { +    // C++ [basic.lookup.classref]p3: +    //   If the unqualified-id is ~type-name, the type-name is looked up +    //   in the context of the entire postfix-expression. If the type T +    //   of the object expression is of a class type C, the type-name is +    //   also looked up in the scope of class C. At least one of the +    //   lookups shall find a name that refers to (possibly +    //   cv-qualified) T. +    LookupCtx = computeDeclContext(SearchType); +    isDependent = SearchType->isDependentType(); +    assert((isDependent || !SearchType->isIncompleteType()) &&  +           "Caller should have completed object type"); + +    LookInScope = true; +  } else { +    // Perform lookup into the current scope (only). +    LookInScope = true; +  } + +  LookupResult Found(*this, &II, NameLoc, LookupOrdinaryName); +  for (unsigned Step = 0; Step != 2; ++Step) { +    // Look for the name first in the computed lookup context (if we +    // have one) and, if that fails to find a match, in the sope (if +    // we're allowed to look there). +    Found.clear(); +    if (Step == 0 && LookupCtx) +      LookupQualifiedName(Found, LookupCtx); +    else if (Step == 1 && LookInScope) +      LookupName(Found, S); +    else +      continue; + +    // FIXME: Should we be suppressing ambiguities here? +    if (Found.isAmbiguous()) +      return 0; + +    if (TypeDecl *Type = Found.getAsSingle<TypeDecl>()) { +      QualType T = Context.getTypeDeclType(Type); +      // If we found the injected-class-name of a class template, retrieve the +      // type of that template. +      // FIXME: We really shouldn't need to do this. +      if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Type)) +        if (Record->isInjectedClassName()) +          if (Record->getDescribedClassTemplate()) +            T = Record->getDescribedClassTemplate() +                                           ->getInjectedClassNameType(Context); + +      if (SearchType.isNull() || SearchType->isDependentType() || +          Context.hasSameUnqualifiedType(T, SearchType)) { +        // We found our type! + +        return T.getAsOpaquePtr(); +      } +    } + +    // If the name that we found is a class template name, and it is +    // the same name as the template name in the last part of the +    // nested-name-specifier (if present) or the object type, then +    // this is the destructor for that class. +    // FIXME: This is a workaround until we get real drafting for core +    // issue 399, for which there isn't even an obvious direction.  +    if (ClassTemplateDecl *Template = Found.getAsSingle<ClassTemplateDecl>()) { +      QualType MemberOfType; +      if (SS.isSet()) { +        if (DeclContext *Ctx = computeDeclContext(SS, EnteringContext)) { +          // Figure out the type of the context, if it has one. +          if (ClassTemplateSpecializationDecl *Spec +                          = dyn_cast<ClassTemplateSpecializationDecl>(Ctx)) +            MemberOfType = Context.getTypeDeclType(Spec); +          else if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Ctx)) { +            if (Record->getDescribedClassTemplate()) +              MemberOfType = Record->getDescribedClassTemplate() +                                          ->getInjectedClassNameType(Context); +            else +              MemberOfType = Context.getTypeDeclType(Record); +          } +        } +      } +      if (MemberOfType.isNull()) +        MemberOfType = SearchType; +       +      if (MemberOfType.isNull()) +        continue; + +      // We're referring into a class template specialization. If the +      // class template we found is the same as the template being +      // specialized, we found what we are looking for. +      if (const RecordType *Record = MemberOfType->getAs<RecordType>()) { +        if (ClassTemplateSpecializationDecl *Spec +              = dyn_cast<ClassTemplateSpecializationDecl>(Record->getDecl())) { +          if (Spec->getSpecializedTemplate()->getCanonicalDecl() == +                Template->getCanonicalDecl()) +            return MemberOfType.getAsOpaquePtr(); +        } + +        continue; +      } +       +      // We're referring to an unresolved class template +      // specialization. Determine whether we class template we found +      // is the same as the template being specialized or, if we don't +      // know which template is being specialized, that it at least +      // has the same name. +      if (const TemplateSpecializationType *SpecType +            = MemberOfType->getAs<TemplateSpecializationType>()) { +        TemplateName SpecName = SpecType->getTemplateName(); + +        // The class template we found is the same template being +        // specialized. +        if (TemplateDecl *SpecTemplate = SpecName.getAsTemplateDecl()) { +          if (SpecTemplate->getCanonicalDecl() == Template->getCanonicalDecl()) +            return MemberOfType.getAsOpaquePtr(); + +          continue; +        } + +        // The class template we found has the same name as the +        // (dependent) template name being specialized. +        if (DependentTemplateName *DepTemplate  +                                    = SpecName.getAsDependentTemplateName()) { +          if (DepTemplate->isIdentifier() && +              DepTemplate->getIdentifier() == Template->getIdentifier()) +            return MemberOfType.getAsOpaquePtr(); + +          continue; +        } +      } +    } +  } + +  if (isDependent) { +    // We didn't find our type, but that's okay: it's dependent +    // anyway. +    NestedNameSpecifier *NNS = 0; +    SourceRange Range; +    if (SS.isSet()) { +      NNS = (NestedNameSpecifier *)SS.getScopeRep(); +      Range = SourceRange(SS.getRange().getBegin(), NameLoc); +    } else { +      NNS = NestedNameSpecifier::Create(Context, &II); +      Range = SourceRange(NameLoc); +    } + +    return CheckTypenameType(NNS, II, Range).getAsOpaquePtr(); +  } + +  if (ObjectTypePtr) +    Diag(NameLoc, diag::err_ident_in_pseudo_dtor_not_a_type) +      << &II;         +  else +    Diag(NameLoc, diag::err_destructor_class_name); + +  return 0; +} +  /// ActOnCXXTypeidOfType - Parse typeid( type-id ).  Action::OwningExprResult  Sema::ActOnCXXTypeid(SourceLocation OpLoc, SourceLocation LParenLoc, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 10e411f5825..c98f88f86aa 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4911,18 +4911,21 @@ namespace {      /// \brief Transforms a typename type by determining whether the type now      /// refers to a member of the current instantiation, and then      /// type-checking and building a QualifiedNameType (when possible). -    QualType TransformTypenameType(TypeLocBuilder &TLB, TypenameTypeLoc TL); +    QualType TransformTypenameType(TypeLocBuilder &TLB, TypenameTypeLoc TL,  +                                   QualType ObjectType);    };  }  QualType  CurrentInstantiationRebuilder::TransformTypenameType(TypeLocBuilder &TLB, -                                                     TypenameTypeLoc TL) { +                                                     TypenameTypeLoc TL,  +                                                     QualType ObjectType) {    TypenameType *T = TL.getTypePtr();    NestedNameSpecifier *NNS      = TransformNestedNameSpecifier(T->getQualifier(), -                              /*FIXME:*/SourceRange(getBaseLocation())); +                                   /*FIXME:*/SourceRange(getBaseLocation()), +                                   ObjectType);    if (!NNS)      return QualType(); diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 51e17fe472e..f12c559ec16 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -570,7 +570,8 @@ namespace {      /// \brief Transforms a template type parameter type by performing      /// substitution of the corresponding template type argument.      QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB, -                                           TemplateTypeParmTypeLoc TL); +                                           TemplateTypeParmTypeLoc TL, +                                           QualType ObjectType);    };  } @@ -859,7 +860,8 @@ Sema::OwningExprResult TemplateInstantiator::TransformCXXDefaultArgExpr(  QualType  TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB, -                                                TemplateTypeParmTypeLoc TL) { +                                                TemplateTypeParmTypeLoc TL,  +                                                    QualType ObjectType) {    TemplateTypeParmType *T = TL.getTypePtr();    if (T->getDepth() < TemplateArgs.getNumLevels()) {      // Replace the template type parameter with its corresponding diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index fc069f7ba38..a59608661c4 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -191,7 +191,7 @@ public:    /// switched to storing TypeSourceInfos.    ///    /// \returns the transformed type. -  QualType TransformType(QualType T); +  QualType TransformType(QualType T, QualType ObjectType = QualType());    /// \brief Transforms the given type-with-location into a new    /// type-with-location. @@ -201,13 +201,15 @@ public:    /// may override this function (to take over all type    /// transformations) or some set of the TransformXXXType functions    /// to alter the transformation. -  TypeSourceInfo *TransformType(TypeSourceInfo *DI); +  TypeSourceInfo *TransformType(TypeSourceInfo *DI,  +                                QualType ObjectType = QualType());    /// \brief Transform the given type-with-location into a new    /// type, collecting location information in the given builder    /// as necessary.    /// -  QualType TransformType(TypeLocBuilder &TLB, TypeLoc TL); +  QualType TransformType(TypeLocBuilder &TLB, TypeLoc TL,  +                         QualType ObjectType = QualType());    /// \brief Transform the given statement.    /// @@ -307,20 +309,17 @@ public:  #define ABSTRACT_TYPELOC(CLASS, PARENT)  #define TYPELOC(CLASS, PARENT)                                   \ -  QualType Transform##CLASS##Type(TypeLocBuilder &TLB, CLASS##TypeLoc T); +  QualType Transform##CLASS##Type(TypeLocBuilder &TLB, CLASS##TypeLoc T, \ +                                  QualType ObjectType = QualType());  #include "clang/AST/TypeLocNodes.def" -  QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL); +  QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL,  +                                  QualType ObjectType);    QualType     TransformTemplateSpecializationType(const TemplateSpecializationType *T,                                        QualType ObjectType); -  QualType -  TransformTemplateSpecializationType(TypeLocBuilder &TLB, -                                      TemplateSpecializationTypeLoc TL, -                                      QualType ObjectType); -      OwningStmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr);  #define STMT(Node, Parent)                        \ @@ -1779,7 +1778,8 @@ TreeTransform<Derived>::TransformNestedNameSpecifier(NestedNameSpecifier *NNS,    case NestedNameSpecifier::TypeSpecWithTemplate:    case NestedNameSpecifier::TypeSpec: {      TemporaryBase Rebase(*this, Range.getBegin(), DeclarationName()); -    QualType T = getDerived().TransformType(QualType(NNS->getAsType(), 0)); +    QualType T = getDerived().TransformType(QualType(NNS->getAsType(), 0), +                                            ObjectType);      if (T.isNull())        return 0; @@ -1820,14 +1820,8 @@ TreeTransform<Derived>::TransformDeclarationName(DeclarationName Name,    case DeclarationName::CXXDestructorName:    case DeclarationName::CXXConversionFunctionName: {      TemporaryBase Rebase(*this, Loc, Name); -    QualType T; -    if (!ObjectType.isNull() &&  -        isa<TemplateSpecializationType>(Name.getCXXNameType())) { -      TemplateSpecializationType *SpecType -        = cast<TemplateSpecializationType>(Name.getCXXNameType()); -      T = TransformTemplateSpecializationType(SpecType, ObjectType); -    } else -      T = getDerived().TransformType(Name.getCXXNameType()); +    QualType T = getDerived().TransformType(Name.getCXXNameType(),  +                                            ObjectType);      if (T.isNull())        return DeclarationName(); @@ -1847,7 +1841,8 @@ TreeTransform<Derived>::TransformTemplateName(TemplateName Name,    if (QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName()) {      NestedNameSpecifier *NNS        = getDerived().TransformNestedNameSpecifier(QTN->getQualifier(), -                      /*FIXME:*/SourceRange(getDerived().getBaseLocation())); +                        /*FIXME:*/SourceRange(getDerived().getBaseLocation()), +                                                  ObjectType);      if (!NNS)        return TemplateName(); @@ -1873,7 +1868,8 @@ TreeTransform<Derived>::TransformTemplateName(TemplateName Name,    if (DependentTemplateName *DTN = Name.getAsDependentTemplateName()) {      NestedNameSpecifier *NNS        = getDerived().TransformNestedNameSpecifier(DTN->getQualifier(), -                        /*FIXME:*/SourceRange(getDerived().getBaseLocation())); +                        /*FIXME:*/SourceRange(getDerived().getBaseLocation()), +                                                  ObjectType);      if (!NNS && DTN->getQualifier())        return TemplateName(); @@ -2055,7 +2051,8 @@ bool TreeTransform<Derived>::TransformTemplateArgument(  //===----------------------------------------------------------------------===//  template<typename Derived> -QualType TreeTransform<Derived>::TransformType(QualType T) { +QualType TreeTransform<Derived>::TransformType(QualType T,  +                                               QualType ObjectType) {    if (getDerived().AlreadyTransformed(T))      return T; @@ -2064,7 +2061,7 @@ QualType TreeTransform<Derived>::TransformType(QualType T) {    TypeSourceInfo *DI = getSema().Context.CreateTypeSourceInfo(T);    DI->getTypeLoc().initialize(getDerived().getBaseLocation()); -  TypeSourceInfo *NewDI = getDerived().TransformType(DI); +  TypeSourceInfo *NewDI = getDerived().TransformType(DI, ObjectType);    if (!NewDI)      return QualType(); @@ -2073,7 +2070,8 @@ QualType TreeTransform<Derived>::TransformType(QualType T) {  }  template<typename Derived> -TypeSourceInfo *TreeTransform<Derived>::TransformType(TypeSourceInfo *DI) { +TypeSourceInfo *TreeTransform<Derived>::TransformType(TypeSourceInfo *DI, +                                                      QualType ObjectType) {    if (getDerived().AlreadyTransformed(DI->getType()))      return DI; @@ -2082,7 +2080,7 @@ TypeSourceInfo *TreeTransform<Derived>::TransformType(TypeSourceInfo *DI) {    TypeLoc TL = DI->getTypeLoc();    TLB.reserve(TL.getFullDataSize()); -  QualType Result = getDerived().TransformType(TLB, TL); +  QualType Result = getDerived().TransformType(TLB, TL, ObjectType);    if (Result.isNull())      return 0; @@ -2091,12 +2089,14 @@ TypeSourceInfo *TreeTransform<Derived>::TransformType(TypeSourceInfo *DI) {  template<typename Derived>  QualType -TreeTransform<Derived>::TransformType(TypeLocBuilder &TLB, TypeLoc T) { +TreeTransform<Derived>::TransformType(TypeLocBuilder &TLB, TypeLoc T, +                                      QualType ObjectType) {    switch (T.getTypeLocClass()) {  #define ABSTRACT_TYPELOC(CLASS, PARENT)  #define TYPELOC(CLASS, PARENT) \    case TypeLoc::CLASS: \ -    return getDerived().Transform##CLASS##Type(TLB, cast<CLASS##TypeLoc>(T)); +    return getDerived().Transform##CLASS##Type(TLB, cast<CLASS##TypeLoc>(T), \ +                                               ObjectType);  #include "clang/AST/TypeLocNodes.def"    } @@ -2112,10 +2112,12 @@ TreeTransform<Derived>::TransformType(TypeLocBuilder &TLB, TypeLoc T) {  template<typename Derived>  QualType  TreeTransform<Derived>::TransformQualifiedType(TypeLocBuilder &TLB, -                                               QualifiedTypeLoc T) { +                                               QualifiedTypeLoc T, +                                               QualType ObjectType) {    Qualifiers Quals = T.getType().getLocalQualifiers(); -  QualType Result = getDerived().TransformType(TLB, T.getUnqualifiedLoc()); +  QualType Result = getDerived().TransformType(TLB, T.getUnqualifiedLoc(), +                                               ObjectType);    if (Result.isNull())      return QualType(); @@ -2166,7 +2168,8 @@ QualType TransformTypeSpecType(TypeLocBuilder &TLB, TyLoc T) {  template<typename Derived>  QualType TreeTransform<Derived>::TransformBuiltinType(TypeLocBuilder &TLB, -                                                      BuiltinTypeLoc T) { +                                                      BuiltinTypeLoc T, +                                                      QualType ObjectType) {    BuiltinTypeLoc NewT = TLB.push<BuiltinTypeLoc>(T.getType());    NewT.setBuiltinLoc(T.getBuiltinLoc());    if (T.needsExtraLocalData()) @@ -2176,21 +2179,24 @@ QualType TreeTransform<Derived>::TransformBuiltinType(TypeLocBuilder &TLB,  template<typename Derived>  QualType TreeTransform<Derived>::TransformComplexType(TypeLocBuilder &TLB, -                                                      ComplexTypeLoc T) { +                                                      ComplexTypeLoc T, +                                                      QualType ObjectType) {    // FIXME: recurse?    return TransformTypeSpecType(TLB, T);  }  template<typename Derived>  QualType TreeTransform<Derived>::TransformPointerType(TypeLocBuilder &TLB, -                                                      PointerTypeLoc TL) { +                                                      PointerTypeLoc TL,  +                                                      QualType ObjectType) {    TransformPointerLikeType(PointerType);  }  template<typename Derived>  QualType  TreeTransform<Derived>::TransformBlockPointerType(TypeLocBuilder &TLB, -                                                  BlockPointerTypeLoc TL) { +                                                  BlockPointerTypeLoc TL, +                                                  QualType ObjectType) {    TransformPointerLikeType(BlockPointerType);  } @@ -2201,7 +2207,8 @@ TreeTransform<Derived>::TransformBlockPointerType(TypeLocBuilder &TLB,  template<typename Derived>  QualType  TreeTransform<Derived>::TransformReferenceType(TypeLocBuilder &TLB, -                                               ReferenceTypeLoc TL) { +                                               ReferenceTypeLoc TL, +                                               QualType ObjectType) {    const ReferenceType *T = TL.getTypePtr();    // Note that this works with the pointee-as-written. @@ -2233,21 +2240,24 @@ TreeTransform<Derived>::TransformReferenceType(TypeLocBuilder &TLB,  template<typename Derived>  QualType  TreeTransform<Derived>::TransformLValueReferenceType(TypeLocBuilder &TLB, -                                                 LValueReferenceTypeLoc TL) { -  return TransformReferenceType(TLB, TL); +                                                 LValueReferenceTypeLoc TL, +                                                     QualType ObjectType) { +  return TransformReferenceType(TLB, TL, ObjectType);  }  template<typename Derived>  QualType  TreeTransform<Derived>::TransformRValueReferenceType(TypeLocBuilder &TLB, -                                                 RValueReferenceTypeLoc TL) { -  return TransformReferenceType(TLB, TL); +                                                 RValueReferenceTypeLoc TL, +                                                     QualType ObjectType) { +  return TransformReferenceType(TLB, TL, ObjectType);  }  template<typename Derived>  QualType  TreeTransform<Derived>::TransformMemberPointerType(TypeLocBuilder &TLB, -                                                   MemberPointerTypeLoc TL) { +                                                   MemberPointerTypeLoc TL, +                                                   QualType ObjectType) {    MemberPointerType *T = TL.getTypePtr();    QualType PointeeType = getDerived().TransformType(TLB, TL.getPointeeLoc()); @@ -2279,7 +2289,8 @@ TreeTransform<Derived>::TransformMemberPointerType(TypeLocBuilder &TLB,  template<typename Derived>  QualType  TreeTransform<Derived>::TransformConstantArrayType(TypeLocBuilder &TLB, -                                                   ConstantArrayTypeLoc TL) { +                                                   ConstantArrayTypeLoc TL, +                                                   QualType ObjectType) {    ConstantArrayType *T = TL.getTypePtr();    QualType ElementType = getDerived().TransformType(TLB, TL.getElementLoc());    if (ElementType.isNull()) @@ -2314,7 +2325,8 @@ TreeTransform<Derived>::TransformConstantArrayType(TypeLocBuilder &TLB,  template<typename Derived>  QualType TreeTransform<Derived>::TransformIncompleteArrayType(                                                TypeLocBuilder &TLB, -                                              IncompleteArrayTypeLoc TL) { +                                              IncompleteArrayTypeLoc TL, +                                              QualType ObjectType) {    IncompleteArrayType *T = TL.getTypePtr();    QualType ElementType = getDerived().TransformType(TLB, TL.getElementLoc());    if (ElementType.isNull()) @@ -2342,7 +2354,8 @@ QualType TreeTransform<Derived>::TransformIncompleteArrayType(  template<typename Derived>  QualType  TreeTransform<Derived>::TransformVariableArrayType(TypeLocBuilder &TLB, -                                                   VariableArrayTypeLoc TL) { +                                                   VariableArrayTypeLoc TL, +                                                   QualType ObjectType) {    VariableArrayType *T = TL.getTypePtr();    QualType ElementType = getDerived().TransformType(TLB, TL.getElementLoc());    if (ElementType.isNull()) @@ -2383,7 +2396,8 @@ TreeTransform<Derived>::TransformVariableArrayType(TypeLocBuilder &TLB,  template<typename Derived>  QualType  TreeTransform<Derived>::TransformDependentSizedArrayType(TypeLocBuilder &TLB, -                                             DependentSizedArrayTypeLoc TL) { +                                             DependentSizedArrayTypeLoc TL, +                                                        QualType ObjectType) {    DependentSizedArrayType *T = TL.getTypePtr();    QualType ElementType = getDerived().TransformType(TLB, TL.getElementLoc());    if (ElementType.isNull()) @@ -2426,7 +2440,8 @@ TreeTransform<Derived>::TransformDependentSizedArrayType(TypeLocBuilder &TLB,  template<typename Derived>  QualType TreeTransform<Derived>::TransformDependentSizedExtVectorType(                                        TypeLocBuilder &TLB, -                                      DependentSizedExtVectorTypeLoc TL) { +                                      DependentSizedExtVectorTypeLoc TL, +                                      QualType ObjectType) {    DependentSizedExtVectorType *T = TL.getTypePtr();    // FIXME: ext vector locs should be nested @@ -2468,7 +2483,8 @@ QualType TreeTransform<Derived>::TransformDependentSizedExtVectorType(  template<typename Derived>  QualType TreeTransform<Derived>::TransformVectorType(TypeLocBuilder &TLB, -                                                     VectorTypeLoc TL) { +                                                     VectorTypeLoc TL, +                                                     QualType ObjectType) {    VectorType *T = TL.getTypePtr();    QualType ElementType = getDerived().TransformType(T->getElementType());    if (ElementType.isNull()) @@ -2491,7 +2507,8 @@ QualType TreeTransform<Derived>::TransformVectorType(TypeLocBuilder &TLB,  template<typename Derived>  QualType TreeTransform<Derived>::TransformExtVectorType(TypeLocBuilder &TLB, -                                                        ExtVectorTypeLoc TL) { +                                                        ExtVectorTypeLoc TL, +                                                        QualType ObjectType) {    VectorType *T = TL.getTypePtr();    QualType ElementType = getDerived().TransformType(T->getElementType());    if (ElementType.isNull()) @@ -2516,7 +2533,8 @@ QualType TreeTransform<Derived>::TransformExtVectorType(TypeLocBuilder &TLB,  template<typename Derived>  QualType  TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB, -                                                   FunctionProtoTypeLoc TL) { +                                                   FunctionProtoTypeLoc TL, +                                                   QualType ObjectType) {    FunctionProtoType *T = TL.getTypePtr();    QualType ResultType = getDerived().TransformType(TLB, TL.getResultLoc());    if (ResultType.isNull()) @@ -2592,7 +2610,8 @@ TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB,  template<typename Derived>  QualType TreeTransform<Derived>::TransformFunctionNoProtoType(                                                   TypeLocBuilder &TLB, -                                                 FunctionNoProtoTypeLoc TL) { +                                                 FunctionNoProtoTypeLoc TL, +                                                 QualType ObjectType) {    FunctionNoProtoType *T = TL.getTypePtr();    QualType ResultType = getDerived().TransformType(TLB, TL.getResultLoc());    if (ResultType.isNull()) @@ -2612,7 +2631,8 @@ QualType TreeTransform<Derived>::TransformFunctionNoProtoType(  template<typename Derived> QualType  TreeTransform<Derived>::TransformUnresolvedUsingType(TypeLocBuilder &TLB, -                                                 UnresolvedUsingTypeLoc TL) { +                                                 UnresolvedUsingTypeLoc TL, +                                                     QualType ObjectType) {    UnresolvedUsingType *T = TL.getTypePtr();    Decl *D = getDerived().TransformDecl(T->getDecl());    if (!D) @@ -2635,7 +2655,8 @@ TreeTransform<Derived>::TransformUnresolvedUsingType(TypeLocBuilder &TLB,  template<typename Derived>  QualType TreeTransform<Derived>::TransformTypedefType(TypeLocBuilder &TLB, -                                                      TypedefTypeLoc TL) { +                                                      TypedefTypeLoc TL, +                                                      QualType ObjectType) {    TypedefType *T = TL.getTypePtr();    TypedefDecl *Typedef      = cast_or_null<TypedefDecl>(getDerived().TransformDecl(T->getDecl())); @@ -2658,7 +2679,8 @@ QualType TreeTransform<Derived>::TransformTypedefType(TypeLocBuilder &TLB,  template<typename Derived>  QualType TreeTransform<Derived>::TransformTypeOfExprType(TypeLocBuilder &TLB, -                                                      TypeOfExprTypeLoc TL) { +                                                      TypeOfExprTypeLoc TL, +                                                       QualType ObjectType) {    // typeof expressions are not potentially evaluated contexts    EnterExpressionEvaluationContext Unevaluated(SemaRef, Action::Unevaluated); @@ -2685,7 +2707,8 @@ QualType TreeTransform<Derived>::TransformTypeOfExprType(TypeLocBuilder &TLB,  template<typename Derived>  QualType TreeTransform<Derived>::TransformTypeOfType(TypeLocBuilder &TLB, -                                                     TypeOfTypeLoc TL) { +                                                     TypeOfTypeLoc TL, +                                                     QualType ObjectType) {    TypeSourceInfo* Old_Under_TI = TL.getUnderlyingTInfo();    TypeSourceInfo* New_Under_TI = getDerived().TransformType(Old_Under_TI);    if (!New_Under_TI) @@ -2709,7 +2732,8 @@ QualType TreeTransform<Derived>::TransformTypeOfType(TypeLocBuilder &TLB,  template<typename Derived>  QualType TreeTransform<Derived>::TransformDecltypeType(TypeLocBuilder &TLB, -                                                       DecltypeTypeLoc TL) { +                                                       DecltypeTypeLoc TL, +                                                       QualType ObjectType) {    DecltypeType *T = TL.getTypePtr();    // decltype expressions are not potentially evaluated contexts @@ -2736,7 +2760,8 @@ QualType TreeTransform<Derived>::TransformDecltypeType(TypeLocBuilder &TLB,  template<typename Derived>  QualType TreeTransform<Derived>::TransformRecordType(TypeLocBuilder &TLB, -                                                     RecordTypeLoc TL) { +                                                     RecordTypeLoc TL, +                                                     QualType ObjectType) {    RecordType *T = TL.getTypePtr();    RecordDecl *Record      = cast_or_null<RecordDecl>(getDerived().TransformDecl(T->getDecl())); @@ -2759,7 +2784,8 @@ QualType TreeTransform<Derived>::TransformRecordType(TypeLocBuilder &TLB,  template<typename Derived>  QualType TreeTransform<Derived>::TransformEnumType(TypeLocBuilder &TLB, -                                                   EnumTypeLoc TL) { +                                                   EnumTypeLoc TL, +                                                   QualType ObjectType) {    EnumType *T = TL.getTypePtr();    EnumDecl *Enum      = cast_or_null<EnumDecl>(getDerived().TransformDecl(T->getDecl())); @@ -2782,7 +2808,8 @@ QualType TreeTransform<Derived>::TransformEnumType(TypeLocBuilder &TLB,  template <typename Derived>  QualType TreeTransform<Derived>::TransformElaboratedType(TypeLocBuilder &TLB, -                                                      ElaboratedTypeLoc TL) { +                                                         ElaboratedTypeLoc TL, +                                                       QualType ObjectType) {    ElaboratedType *T = TL.getTypePtr();    // FIXME: this should be a nested type. @@ -2808,26 +2835,20 @@ QualType TreeTransform<Derived>::TransformElaboratedType(TypeLocBuilder &TLB,  template<typename Derived>  QualType TreeTransform<Derived>::TransformTemplateTypeParmType(                                                  TypeLocBuilder &TLB, -                                                TemplateTypeParmTypeLoc TL) { +                                                TemplateTypeParmTypeLoc TL, +                                                QualType ObjectType) {    return TransformTypeSpecType(TLB, TL);  }  template<typename Derived>  QualType TreeTransform<Derived>::TransformSubstTemplateTypeParmType(                                           TypeLocBuilder &TLB, -                                         SubstTemplateTypeParmTypeLoc TL) { +                                         SubstTemplateTypeParmTypeLoc TL, +                                         QualType ObjectType) {    return TransformTypeSpecType(TLB, TL);  }  template<typename Derived> -inline QualType  -TreeTransform<Derived>::TransformTemplateSpecializationType( -                                          TypeLocBuilder &TLB, -                                          TemplateSpecializationTypeLoc TL) { -  return TransformTemplateSpecializationType(TLB, TL, QualType()); -} - -template<typename Derived>  QualType TreeTransform<Derived>::TransformTemplateSpecializationType(                                        const TemplateSpecializationType *TST,                                                          QualType ObjectType) { @@ -2901,11 +2922,13 @@ QualType TreeTransform<Derived>::TransformTemplateSpecializationType(  template<typename Derived>  QualType  TreeTransform<Derived>::TransformQualifiedNameType(TypeLocBuilder &TLB, -                                                   QualifiedNameTypeLoc TL) { +                                                   QualifiedNameTypeLoc TL, +                                                   QualType ObjectType) {    QualifiedNameType *T = TL.getTypePtr();    NestedNameSpecifier *NNS      = getDerived().TransformNestedNameSpecifier(T->getQualifier(), -                                                SourceRange()); +                                                SourceRange(), +                                                ObjectType);    if (!NNS)      return QualType(); @@ -2930,14 +2953,16 @@ TreeTransform<Derived>::TransformQualifiedNameType(TypeLocBuilder &TLB,  template<typename Derived>  QualType TreeTransform<Derived>::TransformTypenameType(TypeLocBuilder &TLB, -                                                       TypenameTypeLoc TL) { +                                                       TypenameTypeLoc TL, +                                                       QualType ObjectType) {    TypenameType *T = TL.getTypePtr();    /* FIXME: preserve source information better than this */    SourceRange SR(TL.getNameLoc());    NestedNameSpecifier *NNS -    = getDerived().TransformNestedNameSpecifier(T->getQualifier(), SR); +    = getDerived().TransformNestedNameSpecifier(T->getQualifier(), SR, +                                                ObjectType);    if (!NNS)      return QualType(); @@ -2970,7 +2995,8 @@ QualType TreeTransform<Derived>::TransformTypenameType(TypeLocBuilder &TLB,  template<typename Derived>  QualType  TreeTransform<Derived>::TransformObjCInterfaceType(TypeLocBuilder &TLB, -                                                   ObjCInterfaceTypeLoc TL) { +                                                   ObjCInterfaceTypeLoc TL, +                                                   QualType ObjectType) {    assert(false && "TransformObjCInterfaceType unimplemented");    return QualType();  } @@ -2978,7 +3004,8 @@ TreeTransform<Derived>::TransformObjCInterfaceType(TypeLocBuilder &TLB,  template<typename Derived>  QualType  TreeTransform<Derived>::TransformObjCObjectPointerType(TypeLocBuilder &TLB, -                                               ObjCObjectPointerTypeLoc TL) { +                                               ObjCObjectPointerTypeLoc TL, +                                                       QualType ObjectType) {    assert(false && "TransformObjCObjectPointerType unimplemented");    return QualType();  } diff --git a/clang/test/SemaCXX/destructor.cpp b/clang/test/SemaCXX/destructor.cpp index 6837cd40157..a0c2c1e037e 100644 --- a/clang/test/SemaCXX/destructor.cpp +++ b/clang/test/SemaCXX/destructor.cpp @@ -40,9 +40,9 @@ struct F {    ~F(); // expected-error {{destructor cannot be redeclared}}  }; -~; // expected-error {{expected the class name after '~' to name a destructor}} +~; // expected-error {{expected a class name after '~' to name a destructor}}  ~undef(); // expected-error {{expected the class name after '~' to name a destructor}} -~operator+(int, int);  // expected-error {{expected the class name after '~' to name a destructor}} +~operator+(int, int);  // expected-error {{expected a class name after '~' to name a destructor}}  ~F(){} // expected-error {{destructor must be a non-static member function}}  struct G { diff --git a/clang/test/SemaCXX/invalid-member-expr.cpp b/clang/test/SemaCXX/invalid-member-expr.cpp index 666595c84f0..7b17afbf818 100644 --- a/clang/test/SemaCXX/invalid-member-expr.cpp +++ b/clang/test/SemaCXX/invalid-member-expr.cpp @@ -6,7 +6,7 @@ void test() {    X x;    x.int; // expected-error{{expected unqualified-id}} -  x.~int(); // expected-error{{expected the class name}} +  x.~int(); // expected-error{{expected a class name}}    x.operator; // expected-error{{missing type specifier after 'operator'}}    x.operator typedef; // expected-error{{missing type specifier after 'operator'}}  } @@ -15,7 +15,7 @@ void test2() {    X *x;    x->int; // expected-error{{expected unqualified-id}} -  x->~int(); // expected-error{{expected the class name}} +  x->~int(); // expected-error{{expected a class name}}    x->operator; // expected-error{{missing type specifier after 'operator'}}    x->operator typedef; // expected-error{{missing type specifier after 'operator'}}  } diff --git a/clang/test/SemaCXX/pseudo-destructors.cpp b/clang/test/SemaCXX/pseudo-destructors.cpp index 15e37c58820..13ffe1b2005 100644 --- a/clang/test/SemaCXX/pseudo-destructors.cpp +++ b/clang/test/SemaCXX/pseudo-destructors.cpp @@ -18,8 +18,8 @@ void f(A* a, Foo *f, int *i) {    a->~foo(); // expected-error{{identifier 'foo' in pseudo-destructor expression does not name a type}} -  // FIXME: the type printed below isn't wonderful -  a->~Bar(); // expected-error{{no member named}} +  // FIXME: the diagnostic below isn't wonderful +  a->~Bar(); // expected-error{{does not name a type}}    f->~Bar();    f->~Foo(); @@ -28,7 +28,7 @@ void f(A* a, Foo *f, int *i) {    g().~Bar(); // expected-error{{non-scalar}}    f->::~Bar(); -  f->N::~Wibble(); +  f->N::~Wibble(); // expected-error{{expected the class name after '~' to name a destructor}}    f->::~Bar(17, 42); // expected-error{{cannot have any arguments}}  } diff --git a/clang/test/SemaTemplate/destructor-template.cpp b/clang/test/SemaTemplate/destructor-template.cpp index b5ad967d69d..7dd429b796f 100644 --- a/clang/test/SemaTemplate/destructor-template.cpp +++ b/clang/test/SemaTemplate/destructor-template.cpp @@ -17,3 +17,16 @@ void destroy_me(T me) {  }  template void destroy_me(Incomplete*); + +namespace PR6152 { +  template<typename T> struct X { void f(); }; +  template<typename T> struct Y { }; +  template<typename T> +  void X<T>::f() { +    Y<T> *y; +    y->template Y<T>::~Y(); +  } +   +  template struct X<int>; +} +  | 

