From: Douglas Gregor Date: Wed, 31 Mar 2010 22:19:08 +0000 (+0000) Subject: Change the representation of dependent elaborated-type-specifiers X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4033642464e8ba0982f88f34cffad808d247b393;p=clang Change the representation of dependent elaborated-type-specifiers (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 --- diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 578382a130..b500b6cb00 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -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 diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index d50e1d1480..27a277ddbc 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -760,6 +760,27 @@ bool Type::isSpecifierType() const { } } +bool Type::isElaboratedTypeSpecifier() const { + if (getTypeClass() == Elaborated) + return true; + + if (const DependentNameType *Dependent = dyn_cast(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!"); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 4c101ad2b8..832a75848e 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -5408,7 +5408,7 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, // // FIXME: handle "template <> friend class A;", which // is possibly well-formed? Who even knows? - if (TempParams.size() && !isa(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(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()) { diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index e3533905fd..8e93fd876e 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -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 diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index a2ace07576..b08447e480 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -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(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(); + 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 diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp index 392888e71b..bfb4780cde 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp @@ -25,8 +25,7 @@ template <> class B { }; template <> struct B { - // 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::Member); void e4(enum B::Member); // expected-error {{use of 'Member' with tag type that does not match previous declaration}} template struct C { - void foo(class B::Member); // expected-error{{no type named 'Member' in 'B'}} + void foo(class B::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 f1; diff --git a/test/SemaTemplate/instantiate-elab-type-specifier.cpp b/test/SemaTemplate/instantiate-elab-type-specifier.cpp new file mode 100644 index 0000000000..e5e10a85cf --- /dev/null +++ b/test/SemaTemplate/instantiate-elab-type-specifier.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// PR5681 +template struct Base { + struct foo {}; + int foo; +}; + +template struct Derived : Base { + typedef struct Base::foo type; +}; + +template struct Derived;