Basic parsing and semantic analysis for out-of-line definitions of the
member functions of class templates, e.g.,

  template<typename T> 
  struct X {
    void f(T);
  };

  template<typename T> X<T>::f(T) { /* ... */ }



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@76692 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp
index aad6e6d..7db0da4 100644
--- a/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/lib/Sema/SemaCXXScopeSpec.cpp
@@ -21,7 +21,19 @@
 
 /// \brief Compute the DeclContext that is associated with the given
 /// scope specifier.
-DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS) {
+///
+/// \param SS the C++ scope specifier as it appears in the source
+///
+/// \param EnteringContext when true, we will be entering the context of
+/// this scope specifier, so we can retrieve the declaration context of a
+/// class template or class template partial specialization even if it is
+/// not the current instantiation.
+///
+/// \returns the declaration context represented by the scope specifier @p SS,
+/// or NULL if the declaration context cannot be computed (e.g., because it is
+/// dependent and not the current instantiation).
+DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
+                                      bool EnteringContext) {
   if (!SS.isSet() || SS.isInvalid())
     return 0;
 
@@ -32,8 +44,29 @@
     // instantiation, return its DeclContext.
     if (CXXRecordDecl *Record = getCurrentInstantiationOf(NNS))
       return Record;
-    else
-      return 0;
+    
+    if (EnteringContext) {
+      // We are entering the context of the nested name specifier, so try to
+      // match the nested name specifier to either a primary class template
+      // or a class template partial specialization
+      if (const TemplateSpecializationType *SpecType
+            = dyn_cast_or_null<TemplateSpecializationType>(NNS->getAsType())) {
+        if (ClassTemplateDecl *ClassTemplate 
+              = dyn_cast_or_null<ClassTemplateDecl>(
+                            SpecType->getTemplateName().getAsTemplateDecl())) {
+          // If the type of the nested name specifier is the same as the
+          // injected class name of the named class template, we're entering
+          // into that class template definition.
+          QualType Injected = ClassTemplate->getInjectedClassNameType(Context);
+          if (Context.hasSameType(Injected, QualType(SpecType, 0)))
+            return ClassTemplate->getTemplatedDecl();
+                
+          // FIXME: Class template partial specializations
+        }
+      }
+    }
+    
+    return 0;
   }
 
   switch (NNS->getKind()) {
@@ -89,7 +122,10 @@
   assert(getLangOptions().CPlusPlus && "Only callable in C++");
   assert(NNS->isDependent() && "Only dependent nested-name-specifier allowed");
 
-  QualType T = QualType(NNS->getAsType(), 0);
+  if (!NNS->getAsType())
+    return 0;
+  
+  QualType T = Context.getCanonicalType(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())
@@ -138,7 +174,7 @@
     // 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)
+    if (Context.getCanonicalType(Context.getTypeDeclType(Record)) == T)
       return Record;
     
     if (ClassTemplateDecl *Template = Record->getDescribedClassTemplate()) {
@@ -286,7 +322,7 @@
 /// The 'SS' should be a non-empty valid CXXScopeSpec.
 void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
   assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
-  if (DeclContext *DC = computeDeclContext(SS))
+  if (DeclContext *DC = computeDeclContext(SS, true))
     EnterDeclaratorContext(S, DC);
   else
     const_cast<CXXScopeSpec&>(SS).setScopeRep(0);
@@ -299,7 +335,7 @@
 /// defining scope.
 void Sema::ActOnCXXExitDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
   assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
-  assert((SS.isInvalid() || S->getEntity() == computeDeclContext(SS)) && 
+  assert((SS.isInvalid() || S->getEntity() == computeDeclContext(SS, true)) && 
          "Context imbalance!");
   if (!SS.isInvalid())
     ExitDeclaratorContext(S);