]> granicus.if.org Git - clang/commitdiff
Initial implementation of parsing, semantic analysis, and template
authorDouglas Gregor <dgregor@apple.com>
Fri, 27 Mar 2009 23:10:48 +0000 (23:10 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 27 Mar 2009 23:10:48 +0000 (23:10 +0000)
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<U, V>

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

21 files changed:
include/clang/AST/ASTContext.h
include/clang/AST/DeclarationName.h
include/clang/AST/NestedNameSpecifier.h
include/clang/AST/Type.h
include/clang/AST/TypeNodes.def
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Parse/Action.h
lib/AST/ASTContext.cpp
lib/AST/NestedNameSpecifier.cpp
lib/AST/Type.cpp
lib/AST/TypeSerialization.cpp
lib/Parse/ParseDecl.cpp
lib/Parse/ParseExpr.cpp
lib/Parse/ParseTentative.cpp
lib/Parse/Parser.cpp
lib/Sema/Sema.h
lib/Sema/SemaCXXScopeSpec.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateInstantiate.cpp
test/SemaTemplate/typename-specifier.cpp [new file with mode: 0644]

index 3cf48b4994c357eedb8aa93a5ebe4e82c38963bb..308ea5f3fa40c4ddcf6c9e81adade0274875df48 100644 (file)
@@ -73,6 +73,7 @@ class ASTContext {
   llvm::FoldingSet<ClassTemplateSpecializationType> 
     ClassTemplateSpecializationTypes;
   llvm::FoldingSet<QualifiedNameType> QualifiedNameTypes;
+  llvm::FoldingSet<TypenameType> TypenameTypes;
   llvm::FoldingSet<ObjCQualifiedInterfaceType> ObjCQualifiedInterfaceTypes;
   llvm::FoldingSet<ObjCQualifiedIdType> 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<typename T> struct X { typename T* type; };
+  ///   };
+  /// }
+  ///
+  /// template<typename T> struct Y {
+  ///   typename N::S::X<T>::type member;
+  /// };
+  /// \endcode
+  ///
+  /// Here, the nested-name-specifier for N::S::X<T>:: will be
+  /// S::X<template-param-0-0>, 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
index 42c1640b5649f7e1349ecf29dff3f8b50f48e14e..a3c1e1faa602df1c989c2d1b7ca997f09ef0f6db 100644 (file)
@@ -149,7 +149,8 @@ public:
   DeclarationName() : Ptr(0) { }
 
   // Construct a declaration name from an IdentifierInfo *.
-  DeclarationName(IdentifierInfo *II) : Ptr(reinterpret_cast<uintptr_t>(II)) { 
+  DeclarationName(const IdentifierInfo *II) 
+    : Ptr(reinterpret_cast<uintptr_t>(II)) { 
     assert((Ptr & PtrMask) == 0 && "Improperly aligned IdentifierInfo");
   }
 
index a740efbe09c01ba9d201b9b40a5ea33ff0f76ca4..fb35a0809e01d3cfcf216c40f767536fcb6e22aa 100644 (file)
@@ -169,6 +169,10 @@ public:
   }
 
   void Destroy(ASTContext &Context);
+
+  /// \brief Dump the nested name specifier to standard output to aid
+  /// in debugging.
+  void Dump();
 };
 
 }
index b05f478681f470d65ecfb6b8fa1e1bc0e6d0838f..0b455aed29f3a90b416e92ab1392949c68d9f9c1 100644 (file)
@@ -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
index e1e79aab53f07f857ac62987a7abe865a3543eef..42057139f872ea5a111202563411c11c2ee391da 100644 (file)
@@ -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)
index 3352f0a1b56adc18bc7dbe45afcd96a59705d0e3..4f1b1abe489c41441ef1be4bd2d02d98dbb6f68d 100644 (file)
@@ -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
index 552bb9a30542e376ade256c38a2663f688ac8307..f668ecd6d9f1295564cf705b1939c59799a657ae 100644 (file)
@@ -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">;
index c75a209d3312140bf8a01286497150949faf5640..b602934cc31ed0790f023f5f9b522e27d0000332 100644 (file)
@@ -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
index e433769a9ce882cff1c42e534384dccd719303f7..2956460684cf9306f7bea818a45f62ce0668f239 100644 (file)
@@ -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.
index 62e972efd5704a26ba416a97e86c80c821c148b4..40efe2a16989c62629f3112ce7da9af716cdcdf6 100644 (file)
@@ -17,6 +17,7 @@
 #include "clang/AST/Type.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cassert>
+#include <stdio.h>
 
 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());
+}
index 103c0a91345dc0993a3f0dc59aa14aaf5fb38e82..0a3e8f91f04c10ec24f192090cc729798c7ad1f2 100644 (file)
@@ -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;
index 4490847a56212239fcf7fd09a78ad088969853a4..4f0334f47a2d620d5c6a2a9f6352e7b0d28338fb 100644 (file)
@@ -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
 //===----------------------------------------------------------------------===//
index 2c26b13dc14fedfccdf83147c39b10073ede5aee..b1cbc3cceb6d3e7535bda8fee1048e9935febee1 100644 (file)
@@ -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())
index 913f9baff193672db3fba55e38628b7d43fefe78..f10cb383dda31c8f5369da9b1a6f76743113d0af 100644 (file)
@@ -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) {
index 2cca2cdb1e10d3933b82f1769b7aaaf8e22e54ce..335a455acfe0eb9387c46963a7c1c549b91c5518 100644 (file)
@@ -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())
index 5ce8b3dbcd8aa9be34959fdcf8be7aef21c3fd96..7f2c5d307b77a78a2ba3f384c185f3504b7439a9 100644 (file)
@@ -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 
index ac260c2e202dc6a0cd5bceb30f316257467f50f9..8dee4f19a21a8f699a01fdbfe6be4735a373384b 100644 (file)
@@ -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
   //
index 8fb2811ce51dc55494e1c656ebdb305c185b5227..a879989b33de4803a5d3f106335267202431f3c0 100644 (file)
@@ -127,17 +127,20 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
     if (TypeDecl *Type = dyn_cast<TypeDecl>(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<TypedefDecl>(SD)) {
+      QualType T = Context.getTypeDeclType(Type);
+      bool AcceptableType = false;
+      if (T->isDependentType())
+        AcceptableType = true;
+      else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(SD)) {
         if (TD->getUnderlyingType()->isRecordType() ||
             (getLangOptions().CPlusPlus0x && 
              TD->getUnderlyingType()->isEnumeralType()))
-          T = Context.getTypeDeclType(TD);
+          AcceptableType = true;
       } else if (isa<RecordDecl>(Type) || 
                  (getLangOptions().CPlusPlus0x && isa<EnumDecl>(Type)))
-        T = Context.getTypeDeclType(Type);
+        AcceptableType = true;
 
-      if (!T.isNull())
+      if (AcceptableType)
         return NestedNameSpecifier::Create(Context, Prefix, false, 
                                            T.getTypePtr());
     }
index 697582c82c39433b8e644ef7e12d0e4a1d7e448a..69946975cdc04c2364495ea1fd48380a116df6bb 100644 (file)
@@ -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<NestedNameSpecifier *>(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<TypeDecl>(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<NamedDecl>(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();
+}
index 52f87b93f7622decc0d9e6978597c83fb29dbbe9..b274f8702529a0ad9769c793ae8dda4f4bc62d8f 100644 (file)
@@ -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<U>::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 (file)
index 0000000..d15dbbc
--- /dev/null
@@ -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<typename T>
+  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<N::A>::type *ip4 = &i;
+N::X<N::B>::type *ip5 = &i; // expected-note{{in instantiation of template class 'struct N::X<struct N::B>' requested here}} \
+// FIXME: expected-error{{invalid token after top level declarator}}
+N::X<N::C>::type *ip6 = &i; // expected-note{{in instantiation of template class 'struct N::X<struct N::C>' requested here}} \
+// FIXME: expected-error{{invalid token after top level declarator}}
+
+N::X<int>::type fail1; // expected-note{{in instantiation of template class 'struct N::X<int>' requested here}} \
+// FIXME: expected-error{{invalid token after top level declarator}}
+
+template<typename T>
+struct Y {
+  typedef typename N::X<T>::type *type; // expected-note{{in instantiation of template class 'struct N::X<struct B>' requested here}} \
+  // expected-note{{in instantiation of template class 'struct N::X<struct C>' requested here}}
+};
+
+struct A {
+  typedef int type;
+};
+
+struct B {
+};
+
+struct C {
+  struct type { };
+  int type; // expected-note{{referenced member 'type' is declared here}}
+};
+
+::Y<A>::type ip7 = &i;
+::Y<B>::type ip8 = &i; // expected-note{{in instantiation of template class 'struct Y<struct B>' requested here}} \
+// FIXME: expected-error{{invalid token after top level declarator}}
+::Y<C>::type ip9 = &i; // expected-note{{in instantiation of template class 'struct Y<struct C>' requested here}} \
+// FIXME: expected-error{{invalid token after top level declarator}}