]> granicus.if.org Git - clang/commitdiff
Change the representation of dependent elaborated-type-specifiers
authorDouglas Gregor <dgregor@apple.com>
Wed, 31 Mar 2010 22:19:08 +0000 (22:19 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 31 Mar 2010 22:19:08 +0000 (22:19 +0000)
(such as "class T::foo") from an ElaboratedType of a TypenameType to a
DependentNameType, which more accurately models the underlying
concept.

Improve template instantiation for DependentNameType nodes that
represent nested-name-specifiers, by performing tag name lookup and
checking the resulting tag appropriately. Fixes PR5681.

There is still much testing and cleanup to do in this area.

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

include/clang/AST/Type.h
lib/AST/Type.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/TreeTransform.h
test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp
test/SemaTemplate/instantiate-elab-type-specifier.cpp [new file with mode: 0644]

index 578382a130e55ca001d6c28b1c292241da70796f..b500b6cb00be2ec55e964c7e28072f8564058170 100644 (file)
@@ -894,6 +894,9 @@ public:
   bool isDependentType() const { return Dependent; }
   bool isOverloadableType() const;
 
+  /// \brief Determine wither this type is a C++ elaborated-type-specifier.
+  bool isElaboratedTypeSpecifier() const;
+  
   /// hasPointerRepresentation - Whether this type is represented
   /// natively as a pointer; this includes pointers, references, block
   /// pointers, and Objective-C interface, qualified id, and qualified
index d50e1d148097b1c6728d1e449ceef61edf5c6687..27a277ddbcb00fc0d1a6228e783027ca7a2f92d7 100644 (file)
@@ -760,6 +760,27 @@ bool Type::isSpecifierType() const {
   }
 }
 
+bool Type::isElaboratedTypeSpecifier() const {
+  if (getTypeClass() == Elaborated)
+    return true;
+  
+  if (const DependentNameType *Dependent = dyn_cast<DependentNameType>(this)) {
+    switch (Dependent->getKeyword()) {
+    case ETK_None:
+    case ETK_Typename:
+      return false;
+        
+    case ETK_Class:
+    case ETK_Struct:
+    case ETK_Union:
+    case ETK_Enum:
+      return true;
+    }
+  }
+  
+  return false;
+}
+
 const char *Type::getTypeClassName() const {
   switch (TC) {
   default: assert(0 && "Type class not in TypeNodes.def!");
index 4c101ad2b85d70fbe5889d77b1d1d4acdfe0f447..832a75848e40a09afe19193b21c4aac38b40c268 100644 (file)
@@ -5408,7 +5408,7 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
   //
   // FIXME: handle "template <> friend class A<T>;", which
   // is possibly well-formed?  Who even knows?
-  if (TempParams.size() && !isa<ElaboratedType>(T)) {
+  if (TempParams.size() && !T->isElaboratedTypeSpecifier()) {
     Diag(Loc, diag::err_tagless_friend_type_template)
       << DS.getSourceRange();
     return DeclPtrTy();
@@ -5420,7 +5420,7 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
   //   * The class-key of the elaborated-type-specifier is required.
   // This is one of the rare places in Clang where it's legitimate to
   // ask about the "spelling" of the type.
-  if (!getLangOptions().CPlusPlus0x && !isa<ElaboratedType>(T)) {
+  if (!getLangOptions().CPlusPlus0x && !T->isElaboratedTypeSpecifier()) {
     // If we evaluated the type to a record type, suggest putting
     // a tag in front.
     if (const RecordType *RT = T->getAs<RecordType>()) {
index e3533905fd34de8cbf4adb69a035418f8245427d..8e93fd876edd609c41c50485f077121a0caad573 100644 (file)
@@ -4915,14 +4915,15 @@ Sema::ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
   if (!NNS)
     return true;
 
-  QualType T = CheckTypenameType(NNS, *Name, SourceRange(TagLoc, NameLoc));
-  if (T.isNull())
-    return true;
-
-  TagDecl::TagKind TagKind = TagDecl::getTagKindForTypeSpec(TagSpec);
-  QualType ElabType = Context.getElaboratedType(T, TagKind);
-
-  return ElabType.getAsOpaquePtr();
+  ElaboratedTypeKeyword Keyword;
+  switch (TagDecl::getTagKindForTypeSpec(TagSpec)) {
+  case TagDecl::TK_struct: Keyword = ETK_Struct; break;
+  case TagDecl::TK_class: Keyword = ETK_Class; break;
+  case TagDecl::TK_union: Keyword = ETK_Union; break;
+  case TagDecl::TK_enum: Keyword = ETK_Enum; break;
+  }
+  
+  return Context.getDependentNameType(Keyword, NNS, Name).getAsOpaquePtr();
 }
 
 Sema::TypeResult
index a2ace07576c1574f135956dd229dfca3c34b4cbc..b08447e4805f48e5f876e865800a56d294ad370a 100644 (file)
@@ -537,19 +537,21 @@ public:
 
   /// \brief Build a new typename type that refers to a template-id.
   ///
-  /// By default, builds a new DependentNameType type from the nested-name-specifier
+  /// By default, builds a new DependentNameType type from the 
+  /// nested-name-specifier
   /// and the given type. Subclasses may override this routine to provide
   /// different behavior.
   QualType RebuildDependentNameType(ElaboratedTypeKeyword Keyword,
                                     NestedNameSpecifier *NNS, QualType T) {
     if (NNS->isDependent()) {
+      // If the name is still dependent, just build a new dependent name type.
       CXXScopeSpec SS;
       SS.setScopeRep(NNS);
       if (!SemaRef.computeDeclContext(SS))
         return SemaRef.Context.getDependentNameType(Keyword, NNS,
                                           cast<TemplateSpecializationType>(T));
     }
-
+    
     // FIXME: Handle elaborated-type-specifiers separately.
     return SemaRef.Context.getQualifiedNameType(NNS, T);
   }
@@ -563,8 +565,76 @@ public:
                                     NestedNameSpecifier *NNS,
                                     const IdentifierInfo *Id,
                                     SourceRange SR) {
-    // FIXME: Handle elaborated-type-specifiers separately.
-    return SemaRef.CheckTypenameType(NNS, *Id, SR);
+    CXXScopeSpec SS;
+    SS.setScopeRep(NNS);
+    
+    if (NNS->isDependent()) {
+      // If the name is still dependent, just build a new dependent name type.
+      if (!SemaRef.computeDeclContext(SS))
+        return SemaRef.Context.getDependentNameType(Keyword, NNS, Id);
+    }
+
+    TagDecl::TagKind Kind = TagDecl::TK_enum;
+    switch (Keyword) {
+      case ETK_None:
+        // FIXME: Note the lack of the "typename" specifier!
+        // Fall through
+      case ETK_Typename:
+        return SemaRef.CheckTypenameType(NNS, *Id, SR);
+        
+      case ETK_Class: Kind = TagDecl::TK_class; break;
+      case ETK_Struct: Kind = TagDecl::TK_struct; break;
+      case ETK_Union: Kind = TagDecl::TK_union; break;
+      case ETK_Enum: Kind = TagDecl::TK_enum; break;
+    }
+    
+    // We had a dependent elaborated-type-specifier that as been transformed
+    // into a non-dependent elaborated-type-specifier. Find the tag we're
+    // referring to.
+    LookupResult Result(SemaRef, Id, SR.getEnd(), Sema::LookupTagName);
+    DeclContext *DC = SemaRef.computeDeclContext(SS, false);
+    if (!DC)
+      return QualType();
+
+    TagDecl *Tag = 0;
+    SemaRef.LookupQualifiedName(Result, DC);
+    switch (Result.getResultKind()) {
+      case LookupResult::NotFound:
+      case LookupResult::NotFoundInCurrentInstantiation:
+        break;
+        
+      case LookupResult::Found:
+        Tag = Result.getAsSingle<TagDecl>();
+        break;
+        
+      case LookupResult::FoundOverloaded:
+      case LookupResult::FoundUnresolvedValue:
+        llvm_unreachable("Tag lookup cannot find non-tags");
+        return QualType();
+        
+      case LookupResult::Ambiguous:
+        // Let the LookupResult structure handle ambiguities.
+        return QualType();
+    }
+
+    if (!Tag) {
+      // FIXME: Crummy diagnostic
+      SemaRef.Diag(SR.getEnd(), diag::err_not_tag_in_scope)
+        << Id << SR;
+      return QualType();
+    }
+    
+    // FIXME: Terrible location information
+    if (!SemaRef.isAcceptableTagRedeclaration(Tag, Kind, SR.getEnd(), *Id)) {
+      SemaRef.Diag(SR.getBegin(), diag::err_use_with_wrong_tag) << Id;
+      SemaRef.Diag(Tag->getLocation(), diag::note_previous_use);
+      return QualType();
+    }
+
+    // Build the elaborated-type-specifier type.
+    QualType T = SemaRef.Context.getTypeDeclType(Tag);
+    T = SemaRef.Context.getQualifiedNameType(NNS, T);
+    return SemaRef.Context.getElaboratedType(T, Kind);
   }
 
   /// \brief Build a new nested-name-specifier given the prefix and an
index 392888e71b45b97bee4e7c991e041b277a37635c..bfb4780cde8e2190c4127e2dc517d8ff982ba62c 100644 (file)
@@ -25,8 +25,7 @@ template <> class B<int> {
 };
 
 template <> struct B<A> {
-  // FIXME: the error here should be associated with the use at "void foo..."
-  union Member { // expected-note 4 {{previous use is here}} expected-error {{tag type that does not match previous declaration}}
+  union Member { // expected-note 4 {{previous use is here}}
     void* a;
   };
 };
@@ -52,7 +51,8 @@ void e3(union B<A>::Member);
 void e4(enum B<A>::Member); // expected-error {{use of 'Member' with tag type that does not match previous declaration}}
 
 template <class T> struct C {
-  void foo(class B<T>::Member); // expected-error{{no type named 'Member' in 'B<int>'}}
+  void foo(class B<T>::Member); // expected-error{{'Member' does not name a tag member in the specified scope}} \
+                                // expected-error{{use of 'Member' with tag type that does not match previous declaration}}
 };
 
 C<float> f1;
diff --git a/test/SemaTemplate/instantiate-elab-type-specifier.cpp b/test/SemaTemplate/instantiate-elab-type-specifier.cpp
new file mode 100644 (file)
index 0000000..e5e10a8
--- /dev/null
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// PR5681
+template <class T> struct Base {
+  struct foo {};
+  int foo;
+};
+
+template <class T> struct Derived : Base<T> {
+  typedef struct Base<T>::foo type;
+};
+
+template struct Derived<int>;