Implement the notions of the "current instantiation" and "unknown
specialization" within a C++ template, and permit name lookup into the
current instantiation. For example, given:

  template<typename T, typename U>
  struct X {
    typedef T type;

    X* x1;  // current instantiation
    X<T, U> *x2; // current instantiation
    X<U, T> *x3; // not current instantiation
    ::X<type, U> *x4; // current instantiation
    X<typename X<type, U>::type, U>: *x5; // current instantiation
  };



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@71471 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp
index 5395869..bbc1bc5 100644
--- a/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/lib/Sema/SemaCXXScopeSpec.cpp
@@ -13,6 +13,7 @@
 
 #include "Sema.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/Parse/DeclSpec.h"
 #include "llvm/ADT/STLExtras.h"
@@ -26,8 +27,14 @@
 
   NestedNameSpecifier *NNS 
     = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
-  if (NNS->isDependent())
-    return 0;
+  if (NNS->isDependent()) {
+    // If this nested-name-specifier refers to the current
+    // instantiation, return its DeclContext.
+    if (CXXRecordDecl *Record = getCurrentInstantiationOf(NNS))
+      return Record;
+    else
+      return 0;
+  }
 
   switch (NNS->getKind()) {
   case NestedNameSpecifier::Identifier:
@@ -61,6 +68,90 @@
   return NNS->isDependent();
 }
 
+// \brief Determine whether this C++ scope specifier refers to an
+// unknown specialization, i.e., a dependent type that is not the
+// current instantiation.
+bool Sema::isUnknownSpecialization(const CXXScopeSpec &SS) {
+  if (!isDependentScopeSpecifier(SS))
+    return false;
+
+  NestedNameSpecifier *NNS 
+    = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
+  return getCurrentInstantiationOf(NNS) == 0;
+}
+
+/// \brief If the given nested name specifier refers to the current
+/// instantiation, return the declaration that corresponds to that
+/// current instantiation (C++0x [temp.dep.type]p1).
+///
+/// \param NNS a dependent nested name specifier.
+CXXRecordDecl *Sema::getCurrentInstantiationOf(NestedNameSpecifier *NNS) {
+  assert(getLangOptions().CPlusPlus && "Only callable in C++");
+  assert(NNS->isDependent() && "Only dependent nested-name-specifier allowed");
+
+  QualType T = QualType(NNS->getAsType(), 0);
+  // If the nested name specifier does not refer to a type, then it
+  // does not refer to the current instantiation.
+  if (T.isNull())
+    return 0;
+
+  T = Context.getCanonicalType(T);
+
+  for (DeclContext *Ctx = CurContext; Ctx; Ctx = Ctx->getParent()) {
+    // If we've hit a namespace or the global scope, then the
+    // nested-name-specifier can't refer to the current instantiation.
+    if (Ctx->isFileContext())
+      return 0;
+
+    // Skip non-class contexts.
+    CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Ctx);
+    if (!Record)
+      continue;
+
+    // If this record type is not dependent, 
+    if (!Record->isDependentType())
+      return 0;
+
+    // C++ [temp.dep.type]p1:
+    //
+    //   In the definition of a class template, a nested class of a
+    //   class template, a member of a class template, or a member of a
+    //   nested class of a class template, a name refers to the current
+    //   instantiation if it is
+    //     -- the injected-class-name (9) of the class template or
+    //        nested class,
+    //     -- in the definition of a primary class template, the name
+    //        of the class template followed by the template argument
+    //        list of the primary template (as described below)
+    //        enclosed in <>,
+    //     -- in the definition of a nested class of a class template,
+    //        the name of the nested class referenced as a member of
+    //        the current instantiation, or 
+    //     -- in the definition of a partial specialization, the name
+    //        of the class template followed by the template argument
+    //        list of the partial specialization enclosed in <>. If
+    //        the nth template parameter is a parameter pack, the nth
+    //        template argument is a pack expansion (14.6.3) whose
+    //        pattern is the name of the parameter pack. (FIXME)
+    //
+    // All of these options come down to having the
+    // nested-name-specifier type that is equivalent to the
+    // injected-class-name of one of the types that is currently in
+    // our context.
+    if (Context.getTypeDeclType(Record) == T)
+      return Record;
+    
+    if (ClassTemplateDecl *Template = Record->getDescribedClassTemplate()) {
+      QualType InjectedClassName 
+        = Template->getInjectedClassNameType(Context);
+      if (T == Context.getCanonicalType(InjectedClassName))
+        return Template->getTemplatedDecl();
+    }
+  }
+
+  return 0;
+}
+
 /// \brief Require that the context specified by SS be complete.
 ///
 /// If SS refers to a type, this routine checks whether the type is
@@ -113,9 +204,10 @@
   NestedNameSpecifier *Prefix 
     = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
 
-  // If the prefix is already dependent, there is no name lookup to
-  // perform. Just build the resulting nested-name-specifier.
-  if (Prefix && Prefix->isDependent())
+  // If the prefix already refers to an unknown specialization, there
+  // is no name lookup to perform. Just build the resulting
+  // nested-name-specifier.
+  if (Prefix && isUnknownSpecialization(SS))
     return NestedNameSpecifier::Create(Context, Prefix, &II);
 
   NamedDecl *SD = LookupParsedName(S, &SS, &II, LookupNestedNameSpecifierName);