From: Douglas Gregor Date: Fri, 27 Mar 2009 23:10:48 +0000 (+0000) Subject: Initial implementation of parsing, semantic analysis, and template X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d57959af02b4af695276f4204443afe6e5d86bd8;p=clang Initial implementation of parsing, semantic analysis, and template instantiation for C++ typename-specifiers such as typename T::type The parsing of typename-specifiers is relatively easy thanks to annotation tokens. When we see the "typename", we parse the typename-specifier and produce a typename annotation token. There are only a few places where we need to handle this. We currently parse the typename-specifier form that terminates in an identifier, but not the simple-template-id form, e.g., typename T::template apply Parsing of nested-name-specifiers has a similar problem, since at this point we don't have any representation of a class template specialization whose template-name is unknown. Semantic analysis is only partially complete, with some support for template instantiation that works for simple examples. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@67875 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 3cf48b4994..308ea5f3fa 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -73,6 +73,7 @@ class ASTContext { llvm::FoldingSet ClassTemplateSpecializationTypes; llvm::FoldingSet QualifiedNameTypes; + llvm::FoldingSet TypenameTypes; llvm::FoldingSet ObjCQualifiedInterfaceTypes; llvm::FoldingSet ObjCQualifiedIdTypes; @@ -295,6 +296,9 @@ public: QualType getQualifiedNameType(NestedNameSpecifier *NNS, QualType NamedType); + QualType getTypenameType(NestedNameSpecifier *NNS, + const IdentifierInfo *Name, + QualType Canon = QualType()); /// getObjCQualifiedInterfaceType - Return a /// ObjCQualifiedInterfaceType type for the given interface decl and @@ -526,6 +530,32 @@ public: ->getDecl()); } + /// \brief Retrieves the "canonical" nested name specifier for a + /// given nested name specifier. + /// + /// The canonical nested name specifier is a nested name specifier + /// that uniquely identifies a type or namespace within the type + /// system. For example, given: + /// + /// \code + /// namespace N { + /// struct S { + /// template struct X { typename T* type; }; + /// }; + /// } + /// + /// template struct Y { + /// typename N::S::X::type member; + /// }; + /// \endcode + /// + /// Here, the nested-name-specifier for N::S::X:: will be + /// S::X, since 'S' and 'X' are uniquely defined + /// by declarations in the type system and the canonical type for + /// the template type parameter 'T' is template-param-0-0. + NestedNameSpecifier * + getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS); + /// Type Query functions. If the type is an instance of the specified class, /// return the Type pointer for the underlying maximally pretty type. This /// is a member of ASTContext because this may need to do some amount of diff --git a/include/clang/AST/DeclarationName.h b/include/clang/AST/DeclarationName.h index 42c1640b56..a3c1e1faa6 100644 --- a/include/clang/AST/DeclarationName.h +++ b/include/clang/AST/DeclarationName.h @@ -149,7 +149,8 @@ public: DeclarationName() : Ptr(0) { } // Construct a declaration name from an IdentifierInfo *. - DeclarationName(IdentifierInfo *II) : Ptr(reinterpret_cast(II)) { + DeclarationName(const IdentifierInfo *II) + : Ptr(reinterpret_cast(II)) { assert((Ptr & PtrMask) == 0 && "Improperly aligned IdentifierInfo"); } diff --git a/include/clang/AST/NestedNameSpecifier.h b/include/clang/AST/NestedNameSpecifier.h index a740efbe09..fb35a0809e 100644 --- a/include/clang/AST/NestedNameSpecifier.h +++ b/include/clang/AST/NestedNameSpecifier.h @@ -169,6 +169,10 @@ public: } void Destroy(ASTContext &Context); + + /// \brief Dump the nested name specifier to standard output to aid + /// in debugging. + void Dump(); }; } diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index b05f478681..0b455aed29 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -1609,6 +1609,65 @@ protected: friend class Type; }; +/// \brief Represents a 'typename' specifier that names a type within +/// a dependent type, e.g., "typename T::type". +/// +/// TypenameType has a very similar structure to QualifiedNameType, +/// which also involves a nested-name-specifier following by a type, +/// and (FIXME!) both can even be prefixed by the 'typename' +/// keyword. However, the two types serve very different roles: +/// QualifiedNameType is a non-semantic type that serves only as sugar +/// to show how a particular type was written in the source +/// code. TypenameType, on the other hand, only occurs when the +/// nested-name-specifier is dependent, such that we cannot resolve +/// the actual type until after instantiation. +class TypenameType : public Type, public llvm::FoldingSetNode { + /// \brief The nested name specifier containing the qualifier. + NestedNameSpecifier *NNS; + + /// \brief The type that this typename specifier refers to. + /// FIXME: Also need to represent the "template simple-template-id" case. + const IdentifierInfo *Name; + + TypenameType(NestedNameSpecifier *NNS, const IdentifierInfo *Name, + QualType CanonType) + : Type(Typename, CanonType, true), NNS(NNS), Name(Name) { + assert(NNS->isDependent() && + "TypenameType requires a dependent nested-name-specifier"); + } + + friend class ASTContext; // ASTContext creates these + +public: + /// \brief Retrieve the qualification on this type. + NestedNameSpecifier *getQualifier() const { return NNS; } + + /// \brief Retrieve the type named by the typename specifier. + const IdentifierInfo *getName() const { return Name; } + + virtual void getAsStringInternal(std::string &InnerString) const; + + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, NNS, Name); + } + + static void Profile(llvm::FoldingSetNodeID &ID, NestedNameSpecifier *NNS, + const IdentifierInfo *Name) { + ID.AddPointer(NNS); + ID.AddPointer(Name); + } + + static bool classof(const Type *T) { + return T->getTypeClass() == Typename; + } + static bool classof(const TypenameType *T) { return true; } + +protected: + virtual void EmitImpl(llvm::Serializer& S) const; + static Type* CreateImpl(ASTContext& Context, llvm::Deserializer& D); + friend class Type; +}; + /// ObjCInterfaceType - Interfaces are the core concept in Objective-C for /// object oriented design. They basically correspond to C++ classes. There /// are two kinds of interface types, normal interfaces like "NSString" and diff --git a/include/clang/AST/TypeNodes.def b/include/clang/AST/TypeNodes.def index e1e79aab53..42057139f8 100644 --- a/include/clang/AST/TypeNodes.def +++ b/include/clang/AST/TypeNodes.def @@ -74,6 +74,7 @@ TYPE(Enum, TagType) DEPENDENT_TYPE(TemplateTypeParm, Type) NON_CANONICAL_TYPE(ClassTemplateSpecialization, Type) NON_CANONICAL_TYPE(QualifiedName, Type) +DEPENDENT_TYPE(Typename, Type) TYPE(ObjCInterface, Type) TYPE(ObjCQualifiedInterface, ObjCInterfaceType) TYPE(ObjCQualifiedId, Type) diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 3352f0a1b5..4f1b1abe48 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -232,6 +232,8 @@ def warn_cxx0x_right_shift_in_template_arg : Warning< "use of right-shift operator ('>>') in template argument will require " "parentheses in C++0x">; +def err_expected_qualified_after_typename : Error< + "expected a qualified name after 'typename'">; // Language specific pragmas // - Generic warnings diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 552bb9a305..f668ecd6d9 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -687,6 +687,17 @@ def note_default_arg_instantiation_here : Note< "in instantiation of default argument for '%0' required here">; def err_field_instantiates_to_function : Error< "data member instantiated with function type %0">; +def err_nested_name_spec_non_tag : Error< + "type %0 cannot be used prior to '::' because it has no members">; + +// C++ typename-specifiers +def err_typename_nested_not_found : Error<"no type named %0 in %1">; +def err_typename_nested_not_found_global : Error< + "no type named %0 in the global namespace">; +def err_typename_nested_not_type : Error< + "typename specifier refers to non-type member %0">; +def note_typename_refers_here : Note< + "referenced member %0 is declared here">; def err_unexpected_typedef : Error< "unexpected type name %0: expected expression">; diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index c75a209d33..b602934cc3 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -1264,6 +1264,19 @@ public: return 0; } + /// \brief Called when the parser has parsed a C++ typename + /// specifier, e.g., "typename T::type". + /// + /// \param TypenameLoc the location of the 'typename' keyword + /// \param SS the nested-name-specifier following the typename (e.g., 'T::'). + /// \param II the identifier we're retrieving (e.g., 'type' in the example). + /// \param IdLoc the location of the identifier. + virtual TypeResult + ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS, + const IdentifierInfo &II, SourceLocation IdLoc) { + return 0; + } + //===----------------------- Obj-C Declarations -------------------------===// // ActOnStartClassInterface - this action is called immediately after parsing diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index e433769a9c..2956460684 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1411,6 +1411,32 @@ ASTContext::getQualifiedNameType(NestedNameSpecifier *NNS, return QualType(T, 0); } +QualType ASTContext::getTypenameType(NestedNameSpecifier *NNS, + const IdentifierInfo *Name, + QualType Canon) { + assert(NNS->isDependent() && "nested-name-specifier must be dependent"); + + if (Canon.isNull()) { + NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS); + if (CanonNNS != NNS) + Canon = getTypenameType(CanonNNS, Name); + } + + llvm::FoldingSetNodeID ID; + TypenameType::Profile(ID, NNS, Name); + + void *InsertPos = 0; + TypenameType *T + = TypenameTypes.FindNodeOrInsertPos(ID, InsertPos); + if (T) + return QualType(T, 0); + + T = new (*this) TypenameType(NNS, Name, Canon); + Types.push_back(T); + TypenameTypes.InsertNode(T, InsertPos); + return QualType(T, 0); +} + /// CmpProtocolNames - Comparison predicate for sorting protocols /// alphabetically. static bool CmpProtocolNames(const ObjCProtocolDecl *LHS, @@ -1582,6 +1608,46 @@ QualType ASTContext::getCanonicalType(QualType T) { VAT->getIndexTypeQualifier()); } +NestedNameSpecifier * +ASTContext::getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS) { + if (!NNS) + return 0; + + switch (NNS->getKind()) { + case NestedNameSpecifier::Identifier: + // Canonicalize the prefix but keep the identifier the same. + return NestedNameSpecifier::Create(*this, + getCanonicalNestedNameSpecifier(NNS->getPrefix()), + NNS->getAsIdentifier()); + + case NestedNameSpecifier::Namespace: + // A namespace is canonical; build a nested-name-specifier with + // this namespace and no prefix. + return NestedNameSpecifier::Create(*this, 0, NNS->getAsNamespace()); + + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: { + QualType T = getCanonicalType(QualType(NNS->getAsType(), 0)); + NestedNameSpecifier *Prefix = 0; + + // FIXME: This isn't the right check! + if (T->isDependentType()) + Prefix = getCanonicalNestedNameSpecifier(NNS->getPrefix()); + + return NestedNameSpecifier::Create(*this, Prefix, + NNS->getKind() == NestedNameSpecifier::TypeSpecWithTemplate, + T.getTypePtr()); + } + + case NestedNameSpecifier::Global: + // The global specifier is canonical and unique. + return NNS; + } + + // Required to silence a GCC warning + return 0; +} + const ArrayType *ASTContext::getAsArrayType(QualType T) { // Handle the non-qualified case efficiently. diff --git a/lib/AST/NestedNameSpecifier.cpp b/lib/AST/NestedNameSpecifier.cpp index 62e972efd5..40efe2a169 100644 --- a/lib/AST/NestedNameSpecifier.cpp +++ b/lib/AST/NestedNameSpecifier.cpp @@ -17,6 +17,7 @@ #include "clang/AST/Type.h" #include "llvm/Support/raw_ostream.h" #include +#include using namespace clang; @@ -150,3 +151,12 @@ void NestedNameSpecifier::Destroy(ASTContext &Context) { this->~NestedNameSpecifier(); Context.Deallocate((void *)this); } + +void NestedNameSpecifier::Dump() { + std::string Result; + { + llvm::raw_string_ostream OS(Result); + Print(OS); + } + fprintf(stderr, "%s", Result.c_str()); +} diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 103c0a9134..0a3e8f91f0 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1435,6 +1435,22 @@ void QualifiedNameType::getAsStringInternal(std::string &InnerString) const { InnerString = MyString + ' ' + InnerString; } +void TypenameType::getAsStringInternal(std::string &InnerString) const { + std::string MyString; + + { + llvm::raw_string_ostream OS(MyString); + OS << "typename "; + NNS->Print(OS); + OS << Name->getName(); + } + + if (InnerString.empty()) + InnerString.swap(MyString); + else + InnerString = MyString + ' ' + InnerString; +} + void ObjCInterfaceType::getAsStringInternal(std::string &InnerString) const { if (!InnerString.empty()) // Prefix the basic type, e.g. 'typedefname X'. InnerString = ' ' + InnerString; diff --git a/lib/AST/TypeSerialization.cpp b/lib/AST/TypeSerialization.cpp index 4490847a56..4f0334f47a 100644 --- a/lib/AST/TypeSerialization.cpp +++ b/lib/AST/TypeSerialization.cpp @@ -430,6 +430,19 @@ QualifiedNameType::CreateImpl(ASTContext& Context, llvm::Deserializer& D) { return 0; } +//===----------------------------------------------------------------------===// +// TypenameType +//===----------------------------------------------------------------------===// +void TypenameType::EmitImpl(llvm::Serializer& S) const { + // FIXME: Serialize the actual components +} + +Type* +TypenameType::CreateImpl(ASTContext& Context, llvm::Deserializer& D) { + // FIXME: Implement de-serialization + return 0; +} + //===----------------------------------------------------------------------===// // VariableArrayType //===----------------------------------------------------------------------===// diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 2c26b13dc1..b1cbc3cceb 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -788,6 +788,12 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, getLang())*2; break; + // C++ typename-specifier: + case tok::kw_typename: + if (TryAnnotateTypeOrScopeToken()) + continue; + break; + // GNU typeof support. case tok::kw_typeof: ParseTypeofSpecifier(DS); @@ -876,6 +882,7 @@ bool Parser::ParseOptionalTypeSpecifier(DeclSpec &DS, int& isInvalid, switch (Tok.getKind()) { case tok::identifier: // foo::bar + case tok::kw_typename: // typename foo::bar // Annotate typenames and C++ scope specifiers. If we get one, just // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) @@ -1387,12 +1394,14 @@ bool Parser::isTypeSpecifierQualifier() { default: return false; case tok::identifier: // foo::bar + case tok::kw_typename: // typename T::type // Annotate typenames and C++ scope specifiers. If we get one, just // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) return isTypeSpecifierQualifier(); // Otherwise, not a type specifier. return false; + case tok::coloncolon: // ::foo::bar if (NextToken().is(tok::kw_new) || // ::new NextToken().is(tok::kw_delete)) // ::delete @@ -1466,7 +1475,9 @@ bool Parser::isDeclarationSpecifier() { // Unfortunate hack to support "Class.factoryMethod" notation. if (getLang().ObjC1 && NextToken().is(tok::period)) return false; + // Fall through + case tok::kw_typename: // typename T::type // Annotate typenames and C++ scope specifiers. If we get one, just // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 913f9baff1..f10cb383dd 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -712,6 +712,7 @@ Parser::OwningExprResult Parser::ParseCastExpression(bool isUnaryExpression, case tok::kw_float: case tok::kw_double: case tok::kw_void: + case tok::kw_typename: case tok::kw_typeof: case tok::annot_typename: { if (!getLang().CPlusPlus) { diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index 2cca2cdb1e..335a455acf 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -516,11 +516,11 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, /// class-specifier /// enum-specifier /// elaborated-type-specifier -/// typename-specifier [TODO] +/// typename-specifier /// cv-qualifier /// /// simple-type-specifier: -/// '::'[opt] nested-name-specifier[opt] type-name [TODO] +/// '::'[opt] nested-name-specifier[opt] type-name /// '::'[opt] nested-name-specifier 'template' /// simple-template-id [TODO] /// 'char' @@ -578,6 +578,7 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, Parser::TPResult Parser::isCXXDeclarationSpecifier() { switch (Tok.getKind()) { case tok::identifier: // foo::bar + case tok::kw_typename: // typename T::type // Annotate typenames and C++ scope specifiers. If we get one, just // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 5ce8b3dbcd..7f2c5d307b 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -795,10 +795,42 @@ Parser::OwningExprResult Parser::ParseSimpleAsm(SourceLocation *EndLoc) { /// Note that this routine emits an error if you call it with ::new or ::delete /// as the current tokens, so only call it in contexts where these are invalid. bool Parser::TryAnnotateTypeOrScopeToken() { - assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon)) && + assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) + || Tok.is(tok::kw_typename)) && "Cannot be a type or scope token!"); - // FIXME: Implement template-ids + if (Tok.is(tok::kw_typename)) { + // Parse a C++ typename-specifier, e.g., "typename T::type". + // + // typename-specifier: + // 'typename' '::' [opt] nested-name-specifier identifier + // 'typename' '::' [opt] nested-name-specifier template [opt] + // simple-template-id [TODO] + SourceLocation TypenameLoc = ConsumeToken(); + CXXScopeSpec SS; + bool HadNestedNameSpecifier = ParseOptionalCXXScopeSpecifier(SS); + if (!HadNestedNameSpecifier) { + Diag(Tok.getLocation(), diag::err_expected_qualified_after_typename); + return false; + } + + TypeResult Ty; + if (Tok.is(tok::identifier)) { + // FIXME: check whether the next token is '<', first! + Ty = Actions.ActOnTypenameType(TypenameLoc, SS, *Tok.getIdentifierInfo(), + Tok.getLocation()); + // FIXME: better error recovery! + Tok.setKind(tok::annot_typename); + Tok.setAnnotationValue(Ty.get()); + Tok.setAnnotationEndLoc(Tok.getLocation()); + Tok.setLocation(TypenameLoc); + PP.AnnotateCachedTokens(Tok); + return true; + } + + return false; + } + CXXScopeSpec SS; if (getLang().CPlusPlus) ParseOptionalCXXScopeSpecifier(SS); @@ -841,7 +873,7 @@ bool Parser::TryAnnotateTypeOrScopeToken() { // template-id, is not part of the annotation. Fall through to // push that token back into the stream and complete the C++ scope // specifier annotation. - } + } if (Tok.is(tok::annot_template_id)) { TemplateIdAnnotation *TemplateId diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index ac260c2e20..8dee4f19a2 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1790,6 +1790,20 @@ public: bool CheckTemplateDeclScope(Scope *S, MultiTemplateParamsArg &TemplateParameterLists); + /// \brief Called when the parser has parsed a C++ typename + /// specifier, e.g., "typename T::type". + /// + /// \param TypenameLoc the location of the 'typename' keyword + /// \param SS the nested-name-specifier following the typename (e.g., 'T::'). + /// \param II the identifier we're retrieving (e.g., 'type' in the example). + /// \param IdLoc the location of the identifier. + virtual TypeResult + ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS, + const IdentifierInfo &II, SourceLocation IdLoc); + QualType CheckTypenameType(NestedNameSpecifier *NNS, + const IdentifierInfo &II, + SourceRange Range); + //===--------------------------------------------------------------------===// // C++ Template Instantiation // diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index 8fb2811ce5..a879989b33 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -127,17 +127,20 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, if (TypeDecl *Type = dyn_cast(SD)) { // Determine whether we have a class (or, in C++0x, an enum) or // a typedef thereof. If so, build the nested-name-specifier. - QualType T; - if (TypedefDecl *TD = dyn_cast(SD)) { + QualType T = Context.getTypeDeclType(Type); + bool AcceptableType = false; + if (T->isDependentType()) + AcceptableType = true; + else if (TypedefDecl *TD = dyn_cast(SD)) { if (TD->getUnderlyingType()->isRecordType() || (getLangOptions().CPlusPlus0x && TD->getUnderlyingType()->isEnumeralType())) - T = Context.getTypeDeclType(TD); + AcceptableType = true; } else if (isa(Type) || (getLangOptions().CPlusPlus0x && isa(Type))) - T = Context.getTypeDeclType(Type); + AcceptableType = true; - if (!T.isNull()) + if (AcceptableType) return NestedNameSpecifier::Create(Context, Prefix, false, T.getTypePtr()); } diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 697582c82c..69946975cd 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1964,3 +1964,84 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK, CurContext->addDecl(Specialization); return Specialization; } + +Sema::TypeResult +Sema::ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS, + const IdentifierInfo &II, SourceLocation IdLoc) { + NestedNameSpecifier *NNS + = static_cast(SS.getScopeRep()); + if (!NNS) + return true; + + QualType T = CheckTypenameType(NNS, II, SourceRange(TypenameLoc, IdLoc)); + if (T.isNull()) + return 0; + + return T.getAsOpaquePtr(); +} + +/// \brief Build the type that describes a C++ typename specifier, +/// e.g., "typename T::type". +QualType +Sema::CheckTypenameType(NestedNameSpecifier *NNS, const IdentifierInfo &II, + SourceRange Range) { + if (NNS->isDependent()) // FIXME: member of the current instantiation! + return Context.getTypenameType(NNS, &II); + + CXXScopeSpec SS; + SS.setScopeRep(NNS); + SS.setRange(Range); + if (RequireCompleteDeclContext(SS)) + return QualType(); + + DeclContext *Ctx = computeDeclContext(SS); + assert(Ctx && "No declaration context?"); + + DeclarationName Name(&II); + LookupResult Result = LookupQualifiedName(Ctx, Name, LookupOrdinaryName, + false); + unsigned DiagID = 0; + Decl *Referenced = 0; + switch (Result.getKind()) { + case LookupResult::NotFound: + if (Ctx->isTranslationUnit()) + DiagID = diag::err_typename_nested_not_found_global; + else + DiagID = diag::err_typename_nested_not_found; + break; + + case LookupResult::Found: + if (TypeDecl *Type = dyn_cast(Result.getAsDecl())) { + // We found a type. Build a QualifiedNameType, since the + // typename-specifier was just sugar. FIXME: Tell + // QualifiedNameType that it has a "typename" prefix. + return Context.getQualifiedNameType(NNS, Context.getTypeDeclType(Type)); + } + + DiagID = diag::err_typename_nested_not_type; + Referenced = Result.getAsDecl(); + break; + + case LookupResult::FoundOverloaded: + DiagID = diag::err_typename_nested_not_type; + Referenced = *Result.begin(); + break; + + case LookupResult::AmbiguousBaseSubobjectTypes: + case LookupResult::AmbiguousBaseSubobjects: + case LookupResult::AmbiguousReference: + DiagnoseAmbiguousLookup(Result, Name, Range.getEnd(), Range); + return QualType(); + } + + // If we get here, it's because name lookup did not find a + // type. Emit an appropriate diagnostic and return an error. + if (NamedDecl *NamedCtx = dyn_cast(Ctx)) + Diag(Range.getEnd(), DiagID) << Range << Name << NamedCtx; + else + Diag(Range.getEnd(), DiagID) << Range << Name; + if (Referenced) + Diag(Referenced->getLocation(), diag::note_typename_refers_here) + << Name; + return QualType(); +} diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 52f87b93f7..b274f87025 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -485,8 +485,23 @@ QualType TemplateTypeInstantiator:: InstantiateQualifiedNameType(const QualifiedNameType *T, unsigned Quals) const { - assert(false && "Cannot have dependent qualified name types (yet)"); - return QualType(); + // When we instantiated a qualified name type, there's no point in + // keeping the qualification around in the instantiated result. So, + // just instantiate the named type. + return (*this)(T->getNamedType()); +} + +QualType +TemplateTypeInstantiator:: +InstantiateTypenameType(const TypenameType *T, unsigned Quals) const { + NestedNameSpecifier *NNS + = SemaRef.InstantiateNestedNameSpecifier(T->getQualifier(), + SourceRange(Loc), + TemplateArgs, NumTemplateArgs); + if (!NNS) + return QualType(); + + return SemaRef.CheckTypenameType(NNS, *T->getName(), SourceRange(Loc)); } QualType @@ -799,21 +814,29 @@ Sema::InstantiateNestedNameSpecifier(NestedNameSpecifier *NNS, if (!T->isDependentType()) return NNS; + // FIXME: We won't be able to perform the instantiation here when + // the template-name is dependent, e.g., we have something like + // "T::template apply::type". T = InstantiateType(T, TemplateArgs, NumTemplateArgs, Range.getBegin(), DeclarationName()); if (T.isNull()) return 0; - // Note that T.getTypePtr(), below, strips cv-qualifiers. This is - // perfectly reasonable, since cv-qualified types in - // nested-name-specifiers don't matter. - // FIXME: we need to perform more checking on this type. - return NestedNameSpecifier::Create(Context, Prefix, + if (T->isRecordType() || + (getLangOptions().CPlusPlus0x && T->isEnumeralType())) { + // Note that T.getTypePtr(), below, strips cv-qualifiers. This is + // perfectly reasonable, since cv-qualified types in + // nested-name-specifiers don't matter. + return NestedNameSpecifier::Create(Context, Prefix, NNS->getKind() == NestedNameSpecifier::TypeSpecWithTemplate, - T.getTypePtr()); + T.getTypePtr()); + } + + Diag(Range.getBegin(), diag::err_nested_name_spec_non_tag) << T; + return 0; } } - // Required to silence GCC warning. + // Required to silence a GCC warning return 0; } diff --git a/test/SemaTemplate/typename-specifier.cpp b/test/SemaTemplate/typename-specifier.cpp new file mode 100644 index 0000000000..d15dbbc6e9 --- /dev/null +++ b/test/SemaTemplate/typename-specifier.cpp @@ -0,0 +1,74 @@ +// RUN: clang-cc -fsyntax-only -verify %s +namespace N { + struct A { + typedef int type; + }; + + struct B { + }; + + struct C { + struct type { }; + int type; // expected-note 2{{referenced member 'type' is declared here}} + }; +} + +int i; + +typename N::A::type *ip1 = &i; +typename N::B::type *ip2 = &i; // expected-error{{ no type named 'type' in 'B'}} +typename N::C::type *ip3 = &i; // expected-error{{typename specifier refers to non-type member 'type'}} + +void test(double d) { + typename N::A::type f(typename N::A::type(a)); // expected-warning{{parentheses were disambiguated as a function declarator}} + int five = f(5); + + using namespace N; + for (typename A::type i = 0; i < 10; ++i) + five += 1; + + const typename N::A::type f2(d); +} + +namespace N { + template + struct X { + typedef typename T::type type; // expected-error 2{{no type named 'type' in 'B'}} \ + // FIXME: location info for error above isn't very good \ + // expected-error 2{{typename specifier refers to non-type member 'type'}} \ + // expected-error{{type 'int' cannot be used prior to '::' because it has no members}} + }; +} + +N::X::type *ip4 = &i; +N::X::type *ip5 = &i; // expected-note{{in instantiation of template class 'struct N::X' requested here}} \ +// FIXME: expected-error{{invalid token after top level declarator}} +N::X::type *ip6 = &i; // expected-note{{in instantiation of template class 'struct N::X' requested here}} \ +// FIXME: expected-error{{invalid token after top level declarator}} + +N::X::type fail1; // expected-note{{in instantiation of template class 'struct N::X' requested here}} \ +// FIXME: expected-error{{invalid token after top level declarator}} + +template +struct Y { + typedef typename N::X::type *type; // expected-note{{in instantiation of template class 'struct N::X' requested here}} \ + // expected-note{{in instantiation of template class 'struct N::X' requested here}} +}; + +struct A { + typedef int type; +}; + +struct B { +}; + +struct C { + struct type { }; + int type; // expected-note{{referenced member 'type' is declared here}} +}; + +::Y::type ip7 = &i; +::Y::type ip8 = &i; // expected-note{{in instantiation of template class 'struct Y' requested here}} \ +// FIXME: expected-error{{invalid token after top level declarator}} +::Y::type ip9 = &i; // expected-note{{in instantiation of template class 'struct Y' requested here}} \ +// FIXME: expected-error{{invalid token after top level declarator}}