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<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
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index e433769..2956460 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -1411,6 +1411,32 @@
   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 @@
                               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 62e972e..40efe2a 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 <cassert>
+#include <stdio.h>
 
 using namespace clang;
 
@@ -150,3 +151,12 @@
   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 103c0a9..0a3e8f9 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -1435,6 +1435,22 @@
     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 4490847..4f0334f 100644
--- a/lib/AST/TypeSerialization.cpp
+++ b/lib/AST/TypeSerialization.cpp
@@ -431,6 +431,19 @@
 }
 
 //===----------------------------------------------------------------------===//
+// 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 2c26b13..b1cbc3c 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -788,6 +788,12 @@
                                  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 @@
 
   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 @@
   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 @@
     // 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 913f9ba..f10cb38 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -712,6 +712,7 @@
   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 2cca2cd..335a455 100644
--- a/lib/Parse/ParseTentative.cpp
+++ b/lib/Parse/ParseTentative.cpp
@@ -516,11 +516,11 @@
 ///           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::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 5ce8b3d..7f2c5d3 100644
--- a/lib/Parse/Parser.cpp
+++ b/lib/Parse/Parser.cpp
@@ -795,10 +795,42 @@
 /// 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 @@
     // 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 ac260c2..8dee4f1 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -1790,6 +1790,20 @@
   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 8fb2811..a879989 100644
--- a/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/lib/Sema/SemaCXXScopeSpec.cpp
@@ -127,17 +127,20 @@
     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());
     }
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index 697582c..6994697 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -1964,3 +1964,84 @@
   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();
+}
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index 52f87b9..b274f87 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -485,8 +485,23 @@
 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 @@
     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;
 }