]> granicus.if.org Git - clang/commitdiff
Implement the semantics of the injected-class-name within a class
authorDouglas Gregor <dgregor@apple.com>
Sun, 10 May 2009 22:57:19 +0000 (22:57 +0000)
committerDouglas Gregor <dgregor@apple.com>
Sun, 10 May 2009 22:57:19 +0000 (22:57 +0000)
template. The injected-class-name is either a type or a template,
depending on whether a '<' follows it. As a type, the
injected-class-name's template argument list contains its template
parameters in declaration order.

As part of this, add logic for canonicalizing declarations, and be
sure to canonicalize declarations used in template names and template
arguments.

A TagType is dependent if the declaration it references is dependent.

I'm not happy about the rather complicated protocol needed to use
ASTContext::getTemplateSpecializationType.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@71408 91177308-0d34-0410-b5e6-96231b3b80d8

12 files changed:
include/clang/AST/ASTContext.h
include/clang/AST/Decl.h
include/clang/AST/DeclTemplate.h
include/clang/AST/Type.h
lib/AST/ASTContext.cpp
lib/AST/Decl.cpp
lib/AST/DeclTemplate.cpp
lib/AST/Type.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
test/SemaTemplate/injected-class-name.cpp

index 1f24dee0f3e568f8b4218aea7458bbeb05ab06d3..8b4c89b6e8b14a71dd9d1b0a8e1d546754340e5f 100644 (file)
@@ -564,6 +564,9 @@ public:
     return T1.getUnqualifiedType() == T2.getUnqualifiedType();
   }
 
+  /// \brief Retrieves the "canonical" declaration of the given declaration.
+  Decl *getCanonicalDecl(Decl *D);
+
   /// \brief Retrieves the "canonical" declaration of the given tag
   /// declaration.
   ///
@@ -571,11 +574,11 @@ public:
   /// either the definition of the tag (if it is a complete type) or
   /// the first declaration of that tag.
   TagDecl *getCanonicalDecl(TagDecl *Tag) {
-    QualType T = getTagDeclType(Tag);
-    return cast<TagDecl>(cast<TagType>(T.getTypePtr()->CanonicalType)
-                           ->getDecl());
+    return cast<TagDecl>(getCanonicalDecl((Decl *)Tag));
   }
 
+  /// \brief Retrieves the "canonical" declaration of 
+
   /// \brief Retrieves the "canonical" nested name specifier for a
   /// given nested name specifier.
   ///
index 90d1328c3d8b26565da77e8d34d949ed41e56969..79d04b338e04d8c26d1694cfe7c91446be862587 100644 (file)
@@ -902,7 +902,12 @@ public:
   bool isDefinition() const {
     return IsDefinition;
   }
-  
+
+  /// \brief Whether this declaration declares a type that is
+  /// dependent, i.e., a type that somehow depends on template
+  /// parameters.
+  bool isDependentType() const;
+
   /// @brief Starts the definition of this tag declaration.
   /// 
   /// This method should be invoked at the beginning of the definition
index 5036127d3fffb7b87e6523d2ef2eb890bb5745e0..0853b29ab194bd2cf9589c25c10a1356af3911d5 100644 (file)
@@ -674,6 +674,9 @@ protected:
     /// \brief The class template specializations for this class
     /// template, including explicit specializations and instantiations.
     llvm::FoldingSet<ClassTemplateSpecializationDecl> Specializations;
+
+    /// \brief The injected-class-name type for this class template.
+    QualType InjectedClassNameType;
   };
 
   /// \brief Previous declaration of this class template.
@@ -700,6 +703,11 @@ public:
     return static_cast<CXXRecordDecl *>(TemplatedDecl);
   }
 
+  /// \brief Retrieve the previous declaration of this template.
+  ClassTemplateDecl *getPreviousDeclaration() const {
+    return PreviousDeclaration;
+  }
+
   /// Create a class template node.
   static ClassTemplateDecl *Create(ASTContext &C, DeclContext *DC,
                                    SourceLocation L,
@@ -713,6 +721,22 @@ public:
     return CommonPtr->Specializations;
   }
 
+  /// \brief Retrieve the type of the injected-class-name for this
+  /// class template.
+  ///
+  /// The injected-class-name for a class template \c X is \c
+  /// X<template-args>, where \c template-args is formed from the
+  /// template arguments that correspond to the template parameters of
+  /// \c X. For example:
+  ///
+  /// \code
+  /// template<typename T, int N>
+  /// struct array {
+  ///   typedef array this_type; // "array" is equivalent to "array<T, N>"
+  /// };
+  /// \endcode
+  QualType getInjectedClassNameType(ASTContext &Context);
+
   // Implement isa/cast/dyncast support
   static bool classof(const Decl *D)
   { return D->getKind() == ClassTemplate; }
index 22e36a82155c9287f38f0486ef8611a6d171e405..ff3d89c354b00bafaad5c3a8a9ef539b8e5c4d35 100644 (file)
@@ -1281,11 +1281,7 @@ class TagType : public Type {
   friend class TagDecl;
 
 protected:
-  // FIXME: We'll need the user to pass in information about whether
-  // this type is dependent or not, because we don't have enough
-  // information to compute it here.
-  TagType(TypeClass TC, TagDecl *D, QualType can) 
-    : Type(TC, can, /*Dependent=*/false), decl(D, 0) {}
+  TagType(TypeClass TC, TagDecl *D, QualType can);
 
 public:   
   TagDecl *getDecl() const { return decl.getPointer(); }
index 133e8c20b885928355467126a3ed272487eb809b..f9d648b816b392f6328a49f07c3245887bfb4b0c 100644 (file)
@@ -1703,11 +1703,39 @@ QualType ASTContext::getCanonicalType(QualType T) {
                               VAT->getIndexTypeQualifier());
 }
 
+Decl *ASTContext::getCanonicalDecl(Decl *D) {
+  if (TagDecl *Tag = dyn_cast<TagDecl>(D)) {
+    QualType T = getTagDeclType(Tag);
+    return cast<TagDecl>(cast<TagType>(T.getTypePtr()->CanonicalType)
+                         ->getDecl());
+  }
+
+  if (ClassTemplateDecl *Template = dyn_cast<ClassTemplateDecl>(D)) {
+    while (Template->getPreviousDeclaration())
+      Template = Template->getPreviousDeclaration();
+    return Template;
+  }
+
+  if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) {
+    while (Function->getPreviousDeclaration())
+      Function = Function->getPreviousDeclaration();
+    return const_cast<FunctionDecl *>(Function);
+  }
+
+  if (const VarDecl *Var = dyn_cast<VarDecl>(D)) {
+    while (Var->getPreviousDeclaration())
+      Var = Var->getPreviousDeclaration();
+    return const_cast<VarDecl *>(Var);
+  }
+
+  return D;
+}
+
 TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name) {
   // If this template name refers to a template, the canonical
   // template name merely stores the template itself.
   if (TemplateDecl *Template = Name.getAsTemplateDecl())
-    return TemplateName(Template);
+    return TemplateName(cast<TemplateDecl>(getCanonicalDecl(Template)));
 
   DependentTemplateName *DTN = Name.getAsDependentTemplateName();
   assert(DTN && "Non-dependent template names must refer to template decls.");
index 9aba33c943497d7f7cb51391399cf8ecaa6694c9..621145f1af9bb5991069ea9d8061ce37ce652e1b 100644 (file)
@@ -14,6 +14,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/Expr.h"
@@ -505,6 +506,18 @@ OverloadedOperatorKind FunctionDecl::getOverloadedOperator() const {
 // TagDecl Implementation
 //===----------------------------------------------------------------------===//
 
+bool TagDecl::isDependentType() const {
+  if (isa<TemplateDecl>(this))
+    return true;
+
+  if (const TagDecl *TD = dyn_cast_or_null<TagDecl>(getDeclContext()))
+    return TD->isDependentType();
+
+  // FIXME: Tag types declared function templates are dependent types.
+  // FIXME: Look through block scopes.
+  return false;
+}
+
 void TagDecl::startDefinition() {
   TagType *TagT = const_cast<TagType *>(TypeForDecl->getAsTagType());
   TagT->decl.setPointer(this);
index 7389b83d20d2c9f4e607fdbbd3a41645deef03e8..4f64f8bd50860630673988566fb98ee38f69941a 100644 (file)
@@ -120,6 +120,65 @@ void ClassTemplateDecl::Destroy(ASTContext& C) {
   C.Deallocate((void*)this);
 }
 
+QualType ClassTemplateDecl::getInjectedClassNameType(ASTContext &Context) {
+  if (!CommonPtr->InjectedClassNameType.isNull())
+    return CommonPtr->InjectedClassNameType;
+
+  // FIXME: n2800 14.6.1p1 should say how the template arguments
+  // corresponding to template parameter packs should be pack
+  // expansions. We already say that in 14.6.2.1p2, so it would be
+  // better to fix that redundancy.
+
+  TemplateParameterList *Params = getTemplateParameters();
+
+  llvm::SmallVector<TemplateArgument, 16> TemplateArgs;
+  llvm::SmallVector<TemplateArgument, 16> CanonTemplateArgs;
+  TemplateArgs.reserve(Params->size());
+  CanonTemplateArgs.reserve(Params->size());
+
+  for (TemplateParameterList::iterator 
+         Param = Params->begin(), ParamEnd = Params->end(); 
+       Param != ParamEnd; ++Param) {
+    if (isa<TemplateTypeParmDecl>(*Param)) {
+      QualType ParamType = Context.getTypeDeclType(cast<TypeDecl>(*Param));
+      TemplateArgs.push_back(TemplateArgument((*Param)->getLocation(), 
+                                              ParamType));
+      CanonTemplateArgs.push_back(
+                         TemplateArgument((*Param)->getLocation(),
+                                          Context.getCanonicalType(ParamType)));
+    } else if (NonTypeTemplateParmDecl *NTTP = 
+                 dyn_cast<NonTypeTemplateParmDecl>(*Param)) {
+      // FIXME: Build canonical expression, too!
+      Expr *E = new (Context) DeclRefExpr(NTTP, NTTP->getType(),
+                                          NTTP->getLocation(),
+                                          NTTP->getType()->isDependentType(),
+                                          /*Value-dependent=*/true);
+      TemplateArgs.push_back(TemplateArgument(E));
+      CanonTemplateArgs.push_back(TemplateArgument(E));
+    } else { 
+      TemplateTemplateParmDecl *TTP = cast<TemplateTemplateParmDecl>(*Param);
+      TemplateArgs.push_back(TemplateArgument(TTP->getLocation(), TTP));
+      CanonTemplateArgs.push_back(TemplateArgument(TTP->getLocation(), 
+                                              Context.getCanonicalDecl(TTP)));
+    }
+  }
+
+  // FIXME: I should really move the "build-the-canonical-type" logic
+  // into ASTContext::getTemplateSpecializationType.
+  TemplateName Name = TemplateName(this);
+  QualType CanonType = Context.getTemplateSpecializationType(
+                                       Context.getCanonicalTemplateName(Name),
+                                             &CanonTemplateArgs[0],
+                                             CanonTemplateArgs.size());
+
+  CommonPtr->InjectedClassNameType
+    = Context.getTemplateSpecializationType(Name,
+                                            &TemplateArgs[0],
+                                            TemplateArgs.size(),
+                                            CanonType);
+  return CommonPtr->InjectedClassNameType;
+}
+
 //===----------------------------------------------------------------------===//
 // TemplateTypeParm Allocation/Deallocation Method Implementations
 //===----------------------------------------------------------------------===//
index b5a884085b22a9efa421cc1128431c4b2eb629a6..bc439c8c89cb54bfb5a1660f980d34de013970cf 100644 (file)
@@ -1000,6 +1000,9 @@ TypeOfExprType::TypeOfExprType(Expr *E, QualType can)
   assert(!isa<TypedefType>(can) && "Invalid canonical type");
 }
 
+TagType::TagType(TypeClass TC, TagDecl *D, QualType can) 
+  : Type(TC, can, D->isDependentType()), decl(D, 0) {}
+
 bool RecordType::classof(const TagType *TT) {
   return isa<RecordDecl>(TT->getDecl());
 }
index ea6913769a80d49abdf897b962646c23d9c23241..d9e883f7a8f3bee4c49c5058eb9aa389aecec428 100644 (file)
@@ -123,8 +123,23 @@ Sema::TypeTy *Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc,
     if (TypeDecl *TD = dyn_cast<TypeDecl>(IIDecl)) {
       // Check whether we can use this type
       (void)DiagnoseUseOfDecl(IIDecl, NameLoc);
-      
-      T = Context.getTypeDeclType(TD);
+  
+      if (getLangOptions().CPlusPlus) {
+        // C++ [temp.local]p2:
+        //   Within the scope of a class template specialization or
+        //   partial specialization, when the injected-class-name is
+        //   not followed by a <, it is equivalent to the
+        //   injected-class-name followed by the template-argument s
+        //   of the class template specialization or partial
+        //   specialization enclosed in <>.
+        if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(TD))
+          if (RD->isInjectedClassName())
+            if (ClassTemplateDecl *Template = RD->getDescribedClassTemplate())
+              T = Template->getInjectedClassNameType(Context);
+      }
+
+      if (T.isNull())
+        T = Context.getTypeDeclType(TD);
     } else if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(IIDecl)) {
       // Check whether we can use this interface.
       (void)DiagnoseUseOfDecl(IIDecl, NameLoc);
index c8fdc220d06c713ccdb7e8f64b9525fe496d591e..0c91573180ad355161d8584755723a567258564f 100644 (file)
@@ -503,7 +503,7 @@ Sema::ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK,
             PrevClassTemplate? PrevClassTemplate->getTemplateParameters() : 0))
     Invalid = true;
     
-  // If we had a scope specifier, we better have a previous template
+  // FIXME: If we had a scope specifier, we better have a previous template
   // declaration!
 
   CXXRecordDecl *NewClass = 
@@ -749,8 +749,9 @@ static void CanonicalizeTemplateArguments(const TemplateArgument *TemplateArgs,
       break;
 
     case TemplateArgument::Declaration:
-      Canonical.push_back(TemplateArgument(SourceLocation(),
-                                           TemplateArgs[Idx].getAsDecl()));
+      Canonical.push_back(
+                 TemplateArgument(SourceLocation(),
+                    Context.getCanonicalDecl(TemplateArgs[Idx].getAsDecl())));
       break;
 
     case TemplateArgument::Integral:
@@ -1124,10 +1125,9 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
             Invalid = true;
           
           // Add the converted template argument.
-          // FIXME: Need the "canonical" template declaration!
-          Converted.push_back(
-                    TemplateArgument(Arg.getLocation(),
-                                     cast<DeclRefExpr>(ArgExpr)->getDecl()));
+          Decl *D 
+            = Context.getCanonicalDecl(cast<DeclRefExpr>(ArgExpr)->getDecl());
+          Converted.push_back(TemplateArgument(Arg.getLocation(), D));
           continue;
         }
       }
@@ -1549,8 +1549,10 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
       if (CheckTemplateArgumentPointerToMember(Arg, Member))
         return true;
 
-      if (Converted)
+      if (Converted) {
+        Member = cast<NamedDecl>(Context.getCanonicalDecl(Member));
         Converted->push_back(TemplateArgument(StartLoc, Member));
+      }
 
       return false;
     }
@@ -1559,8 +1561,10 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
     if (CheckTemplateArgumentAddressOfObjectOrFunction(Arg, Entity))
       return true;
 
-    if (Converted)
+    if (Converted) {
+      Entity = cast<NamedDecl>(Context.getCanonicalDecl(Entity));
       Converted->push_back(TemplateArgument(StartLoc, Entity));
+    }
     return false;
   }
 
@@ -1598,8 +1602,10 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
     if (CheckTemplateArgumentAddressOfObjectOrFunction(Arg, Entity))
       return true;
 
-    if (Converted)
+    if (Converted) {
+      Entity = cast<NamedDecl>(Context.getCanonicalDecl(Entity));
       Converted->push_back(TemplateArgument(StartLoc, Entity));
+    }
 
     return false;
   }
@@ -1640,8 +1646,10 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
     if (CheckTemplateArgumentAddressOfObjectOrFunction(Arg, Entity))
       return true;
 
-    if (Converted)
+    if (Converted) {
+      Entity = cast<NamedDecl>(Context.getCanonicalDecl(Entity));
       Converted->push_back(TemplateArgument(StartLoc, Entity));
+    }
 
     return false;
   }
@@ -1670,9 +1678,11 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
   if (CheckTemplateArgumentPointerToMember(Arg, Member))
     return true;
   
-  if (Converted)
+  if (Converted) {
+    Member = cast<NamedDecl>(Context.getCanonicalDecl(Member));
     Converted->push_back(TemplateArgument(StartLoc, Member));
-  
+  }
+
   return false;
 }
 
index 62c717b6d3df94752b97c8e7f3110f5bde1aef2e..a075ea938d020bbae4bb2117841abfd7b72fa1d2 100644 (file)
@@ -273,11 +273,8 @@ Decl *TemplateDeclInstantiator::VisitCXXRecordDecl(CXXRecordDecl *D) {
                             D->getLocation(), D->getIdentifier(), PrevDecl);
   Record->setImplicit(D->isImplicit());
   Record->setAccess(D->getAccess());
-
   if (!D->isInjectedClassName())
     Record->setInstantiationOfMemberClass(D);
-  else
-    Record->setDescribedClassTemplate(D->getDescribedClassTemplate());
 
   Owner->addDecl(SemaRef.Context, Record);
   return Record;
index 43fb454bca52c5ff0a79dab38cdf3075c693d79e..c5f826d849ca350d0d0a3b137aafd4ebd79c17a3 100644 (file)
@@ -1,5 +1,4 @@
 // RUN: clang-cc -fsyntax-only -verify %s
-
 template<typename T>
 struct X {
   X<T*> *ptr;
@@ -13,5 +12,29 @@ struct X<int***> {
 };
 
 // FIXME: EDG rejects this in their strict-conformance mode, but I
-// don't see any wording making this ill-formed.
+// don't see any wording making this ill-formed.  Actually,
+// [temp.local]p2 might make it ill-formed. Are we "in the scope of
+// the class template specialization?"
 X<float>::X<int> xi = x;
+
+// [temp.local]p1:
+
+// FIXME: test non-type and template template parameters
+template<typename T, typename U>
+struct X0 {
+  typedef T type;
+  typedef U U_type;
+  typedef U_type U_type2;
+
+  void f0(const X0&); // expected-note{{here}}
+  void f0(X0&);
+  void f0(const X0<T, U>&); // expected-error{{redecl}}
+
+  void f1(const X0&); // expected-note{{here}}
+  void f1(X0&);
+  void f1(const X0<type, U_type2>&); // expected-error{{redecl}}
+
+  void f2(const X0&); // expected-note{{here}}
+  void f2(X0&);
+  void f2(const ::X0<type, U_type2>&); // expected-error{{redecl}}
+};