]> granicus.if.org Git - clang/commitdiff
Initial stab at implement dependent member references to member
authorDouglas Gregor <dgregor@apple.com>
Wed, 9 Sep 2009 00:23:06 +0000 (00:23 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 9 Sep 2009 00:23:06 +0000 (00:23 +0000)
templates, e.g.,

  x.template get<T>

We can now parse these, represent them within an UnresolvedMemberExpr
expression, then instantiate that expression node in simple cases.

This allows us to stumble through parsing LLVM's Casting.h.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@81300 91177308-0d34-0410-b5e6-96231b3b80d8

13 files changed:
include/clang/AST/ExprCXX.h
include/clang/AST/TemplateName.h
lib/AST/ASTContext.cpp
lib/AST/ExprCXX.cpp
lib/AST/NestedNameSpecifier.cpp
lib/AST/StmtPrinter.cpp
lib/AST/TemplateName.cpp
lib/Parse/ParseExpr.cpp
lib/Parse/ParseExprCXX.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/TreeTransform.h
test/SemaTemplate/member-template-access-expr.cpp [new file with mode: 0644]

index b10191002f68b8290186da792fe7ea6e906add8f..0d9b4cef547c0fed630b514bcba00194f23fc183 100644 (file)
@@ -1407,8 +1407,12 @@ class CXXUnresolvedMemberExpr : public Expr {
   
   /// \brief Whether this member expression used the '->' operator or
   /// the '.' operator.
-  bool IsArrow;
+  bool IsArrow : 1;
 
+  /// \brief Whether this member expression has explicitly-specified template
+  /// arguments.
+  bool HasExplicitTemplateArgumentList : 1;
+  
   /// \brief The location of the '->' or '.' operator.
   SourceLocation OperatorLoc;
 
@@ -1435,6 +1439,36 @@ class CXXUnresolvedMemberExpr : public Expr {
   /// \brief The location of the member name.
   SourceLocation MemberLoc;
   
+  /// \brief Retrieve the explicit template argument list that followed the
+  /// member template name, if any.
+  ExplicitTemplateArgumentList *getExplicitTemplateArgumentList() {
+    if (!HasExplicitTemplateArgumentList)
+      return 0;
+    
+    return reinterpret_cast<ExplicitTemplateArgumentList *>(this + 1);
+  }
+  
+  /// \brief Retrieve the explicit template argument list that followed the
+  /// member template name, if any.
+  const ExplicitTemplateArgumentList *getExplicitTemplateArgumentList() const {
+    return const_cast<CXXUnresolvedMemberExpr *>(this)
+             ->getExplicitTemplateArgumentList();
+  }
+  
+  CXXUnresolvedMemberExpr(ASTContext &C, 
+                          Expr *Base, bool IsArrow, 
+                          SourceLocation OperatorLoc,
+                          NestedNameSpecifier *Qualifier,
+                          SourceRange QualifierRange,
+                          NamedDecl *FirstQualifierFoundInScope,
+                          DeclarationName Member,
+                          SourceLocation MemberLoc,
+                          bool HasExplicitTemplateArgs,
+                          SourceLocation LAngleLoc,
+                          const TemplateArgument *TemplateArgs,
+                          unsigned NumTemplateArgs,
+                          SourceLocation RAngleLoc);
+  
 public:
   CXXUnresolvedMemberExpr(ASTContext &C, 
                           Expr *Base, bool IsArrow, 
@@ -1444,12 +1478,28 @@ public:
                           NamedDecl *FirstQualifierFoundInScope,
                           DeclarationName Member,
                           SourceLocation MemberLoc)
-    : Expr(CXXUnresolvedMemberExprClass, C.DependentTy, true, true),
-      Base(Base), IsArrow(IsArrow), OperatorLoc(OperatorLoc),
-      Qualifier(Qualifier), QualifierRange(QualifierRange),
-      FirstQualifierFoundInScope(FirstQualifierFoundInScope),
-      Member(Member), MemberLoc(MemberLoc) { }
-
+  : Expr(CXXUnresolvedMemberExprClass, C.DependentTy, true, true),
+    Base(Base), IsArrow(IsArrow), HasExplicitTemplateArgumentList(false),
+    OperatorLoc(OperatorLoc),
+    Qualifier(Qualifier), QualifierRange(QualifierRange),
+    FirstQualifierFoundInScope(FirstQualifierFoundInScope),
+    Member(Member), MemberLoc(MemberLoc) { }
+  
+  static CXXUnresolvedMemberExpr *
+  Create(ASTContext &C, 
+         Expr *Base, bool IsArrow, 
+         SourceLocation OperatorLoc,
+         NestedNameSpecifier *Qualifier,
+         SourceRange QualifierRange,
+         NamedDecl *FirstQualifierFoundInScope,
+         DeclarationName Member,
+         SourceLocation MemberLoc,
+         bool HasExplicitTemplateArgs,
+         SourceLocation LAngleLoc,
+         const TemplateArgument *TemplateArgs,
+         unsigned NumTemplateArgs,
+         SourceLocation RAngleLoc);
+         
   /// \brief Retrieve the base object of this member expressions,
   /// e.g., the \c x in \c x.m.
   Expr *getBase() { return cast<Expr>(Base); }
@@ -1497,10 +1547,57 @@ public:
   SourceLocation getMemberLoc() const { return MemberLoc; }
   void setMemberLoc(SourceLocation L) { MemberLoc = L; }
 
+  /// \brief Determines whether this member expression actually had a C++
+  /// template argument list explicitly specified, e.g., x.f<int>.
+  bool hasExplicitTemplateArgumentList() { 
+    return HasExplicitTemplateArgumentList; 
+  }
+  
+  /// \brief Retrieve the location of the left angle bracket following the 
+  /// member name ('<'), if any.
+  SourceLocation getLAngleLoc() const { 
+    if (!HasExplicitTemplateArgumentList)
+      return SourceLocation();
+    
+    return getExplicitTemplateArgumentList()->LAngleLoc;
+  }
+  
+  /// \brief Retrieve the template arguments provided as part of this
+  /// template-id.
+  const TemplateArgument *getTemplateArgs() const { 
+    if (!HasExplicitTemplateArgumentList)
+      return 0;   
+    
+    return getExplicitTemplateArgumentList()->getTemplateArgs();
+  }
+  
+  /// \brief Retrieve the number of template arguments provided as part of this
+  /// template-id.
+  unsigned getNumTemplateArgs() const { 
+    if (!HasExplicitTemplateArgumentList)
+      return 0;   
+    
+    return getExplicitTemplateArgumentList()->NumTemplateArgs;
+  }
+  
+  /// \brief Retrieve the location of the right angle bracket following the 
+  /// template arguments ('>').
+  SourceLocation getRAngleLoc() const { 
+    if (!HasExplicitTemplateArgumentList)
+      return SourceLocation();
+    
+    return getExplicitTemplateArgumentList()->RAngleLoc;
+  }
+  
   virtual SourceRange getSourceRange() const {
+    if (HasExplicitTemplateArgumentList)
+      return SourceRange(Base->getSourceRange().getBegin(),
+                         getRAngleLoc());
+    
     return SourceRange(Base->getSourceRange().getBegin(),
                        MemberLoc);
   }
+  
   static bool classof(const Stmt *T) { 
     return T->getStmtClass() == CXXUnresolvedMemberExprClass;
   }
index c557133eacadcfa1d98803303241588fe9bbd97c..67d3f73c37a861eb288bb1effb48cee8684d638c 100644 (file)
@@ -217,7 +217,7 @@ public:
 /// resolved prior to template instantiation.
 ///
 /// This kind of template name refers to a dependent template name,
-/// including its nested name specifier. For example,
+/// including its nested name specifier (if any). For example,
 /// DependentTemplateName can refer to "MetaFun::template apply",
 /// where "MetaFun::" is the nested name specifier and "apply" is the
 /// template name referenced. The "template" keyword is implied.
index 780b1fdc42e02c0c8b64818fbe227e0ef7f95ef8..30e42349564df0b3f38756b55c65067ee3310439 100644 (file)
@@ -3319,7 +3319,8 @@ TemplateName ASTContext::getQualifiedTemplateName(NestedNameSpecifier *NNS,
 /// template name such as \c MetaFun::template apply.
 TemplateName ASTContext::getDependentTemplateName(NestedNameSpecifier *NNS, 
                                                   const IdentifierInfo *Name) {
-  assert(NNS->isDependent() && "Nested name specifier must be dependent");
+  assert((!NNS || NNS->isDependent()) && 
+         "Nested name specifier must be dependent");
 
   llvm::FoldingSetNodeID ID;
   DependentTemplateName::Profile(ID, NNS, Name);
index 86c522795e74f4b708131163ec736aa95eaf4aa4..7c36caad5851a873a6b6be940a53f7f064eb3d6e 100644 (file)
@@ -523,6 +523,77 @@ Stmt::child_iterator CXXUnresolvedConstructExpr::child_end() {
   return child_iterator(reinterpret_cast<Stmt **>(this + 1) + NumArgs);
 }
 
+CXXUnresolvedMemberExpr::CXXUnresolvedMemberExpr(ASTContext &C, 
+                                                 Expr *Base, bool IsArrow, 
+                                                 SourceLocation OperatorLoc,
+                                                 NestedNameSpecifier *Qualifier,
+                                                 SourceRange QualifierRange,
+                                          NamedDecl *FirstQualifierFoundInScope,
+                                                 DeclarationName Member,
+                                                 SourceLocation MemberLoc,
+                                                 bool HasExplicitTemplateArgs,
+                                                 SourceLocation LAngleLoc,
+                                           const TemplateArgument *TemplateArgs,
+                                                 unsigned NumTemplateArgs,
+                                                 SourceLocation RAngleLoc)
+  : Expr(CXXUnresolvedMemberExprClass, C.DependentTy, true, true),
+    Base(Base), IsArrow(IsArrow), 
+    HasExplicitTemplateArgumentList(HasExplicitTemplateArgs),
+    OperatorLoc(OperatorLoc),
+    Qualifier(Qualifier), QualifierRange(QualifierRange),
+    FirstQualifierFoundInScope(FirstQualifierFoundInScope),
+    Member(Member), MemberLoc(MemberLoc)
+{
+  if (HasExplicitTemplateArgumentList) {
+    ExplicitTemplateArgumentList *ETemplateArgs 
+      = getExplicitTemplateArgumentList();
+    ETemplateArgs->LAngleLoc = LAngleLoc;
+    ETemplateArgs->RAngleLoc = RAngleLoc;
+    ETemplateArgs->NumTemplateArgs = NumTemplateArgs;
+    
+    TemplateArgument *SavedTemplateArgs = ETemplateArgs->getTemplateArgs();
+    for (unsigned I = 0; I < NumTemplateArgs; ++I)
+      new (SavedTemplateArgs + I) TemplateArgument(TemplateArgs[I]);            
+  }
+}
+
+CXXUnresolvedMemberExpr *
+CXXUnresolvedMemberExpr::Create(ASTContext &C, 
+                                Expr *Base, bool IsArrow, 
+                                SourceLocation OperatorLoc,
+                                NestedNameSpecifier *Qualifier,
+                                SourceRange QualifierRange,
+                                NamedDecl *FirstQualifierFoundInScope,
+                                DeclarationName Member,
+                                SourceLocation MemberLoc,
+                                bool HasExplicitTemplateArgs,
+                                SourceLocation LAngleLoc,
+                                const TemplateArgument *TemplateArgs,
+                                unsigned NumTemplateArgs,
+                                SourceLocation RAngleLoc)
+{
+  if (!HasExplicitTemplateArgs)
+    return new (C) CXXUnresolvedMemberExpr(C, Base, IsArrow, OperatorLoc,
+                                           Qualifier, QualifierRange,
+                                           FirstQualifierFoundInScope,
+                                           Member, MemberLoc);
+  
+  void *Mem = C.Allocate(sizeof(CXXUnresolvedMemberExpr) +
+                         sizeof(ExplicitTemplateArgumentList) + 
+                         sizeof(TemplateArgument) * NumTemplateArgs,
+                         llvm::alignof<CXXUnresolvedMemberExpr>());
+  return new (Mem) CXXUnresolvedMemberExpr(C, Base, IsArrow, OperatorLoc,
+                                           Qualifier, QualifierRange,
+                                           FirstQualifierFoundInScope,
+                                           Member,
+                                           MemberLoc,
+                                           HasExplicitTemplateArgs,
+                                           LAngleLoc,
+                                           TemplateArgs,
+                                           NumTemplateArgs,
+                                           RAngleLoc);
+}
+
 Stmt::child_iterator CXXUnresolvedMemberExpr::child_begin() {
   return child_iterator(&Base);
 }
index 24fb75242042788131545d7c616864b82d723fef..2ed8eb00993d4b38efed9a4f84d733118f28ad4f 100644 (file)
@@ -42,7 +42,7 @@ NestedNameSpecifier *
 NestedNameSpecifier::Create(ASTContext &Context, NestedNameSpecifier *Prefix, 
                             IdentifierInfo *II) {
   assert(II && "Identifier cannot be NULL");
-  assert(Prefix && Prefix->isDependent() && "Prefix must be dependent");
+  assert((!Prefix || Prefix->isDependent()) && "Prefix must be dependent");
 
   NestedNameSpecifier Mockup;
   Mockup.Prefix.setPointer(Prefix);
index 317486cd71201d06afce20e3d1e1c00a746257cc..7a0d6d6349574673176e62357a0e0d13966a3ea0 100644 (file)
@@ -493,12 +493,10 @@ void StmtPrinter::VisitTemplateIdRefExpr(TemplateIdRefExpr *Node) {
   if (Node->getQualifier())
     Node->getQualifier()->print(OS, Policy);
   Node->getTemplateName().print(OS, Policy, true);
-  OS << '<';
   OS << TemplateSpecializationType::PrintTemplateArgumentList(
                                                       Node->getTemplateArgs(),
                                                    Node->getNumTemplateArgs(),
                                                               Policy);
-  OS << '>';
 }
 
 void StmtPrinter::VisitObjCIvarRefExpr(ObjCIvarRefExpr *Node) {
@@ -1154,7 +1152,18 @@ void StmtPrinter::VisitCXXUnresolvedMemberExpr(CXXUnresolvedMemberExpr *Node) {
   OS << (Node->isArrow() ? "->" : ".");
   if (NestedNameSpecifier *Qualifier = Node->getQualifier())
     Qualifier->print(OS, Policy);
+  else if (Node->hasExplicitTemplateArgumentList())
+    // FIXME: Track use of "template" keyword explicitly?
+    OS << "template ";
+  
   OS << Node->getMember().getAsString();
+  
+  if (Node->hasExplicitTemplateArgumentList()) {
+    OS << TemplateSpecializationType::PrintTemplateArgumentList(
+                                                    Node->getTemplateArgs(),
+                                                    Node->getNumTemplateArgs(),
+                                                    Policy);
+  }
 }
 
 static const char *getTypeTraitName(UnaryTypeTrait UTT) {
index 3913121b09e5268df64867007bc41ccc0e721dd2..92c741b35d6724a7fdd79bc9afc8bc858531dae0 100644 (file)
@@ -67,7 +67,7 @@ TemplateName::print(llvm::raw_ostream &OS, const PrintingPolicy &Policy,
       OS << "template ";
     OS << QTN->getDecl()->getNameAsString();
   } else if (DependentTemplateName *DTN = getAsDependentTemplateName()) {
-    if (!SuppressNNS)
+    if (!SuppressNNS && DTN->getQualifier())
       DTN->getQualifier()->print(OS, Policy);
     OS << "template ";
     // FIXME: Shouldn't we have a more general kind of name?
index da923c489d4c825a4a410791657a4519e24a020c..39496626febc2b7089ae9fd31ee621a70b4219ec 100644 (file)
@@ -918,8 +918,10 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) {
       ConsumeParen();
       break;
     }
-    case tok::arrow:       // postfix-expression: p-e '->' identifier
-    case tok::period: {    // postfix-expression: p-e '.' identifier
+    case tok::arrow:
+    case tok::period: {
+      // postfix-expression: p-e '->' template[opt] id-expression
+      // postfix-expression: p-e '.' template[opt] id-expression
       tok::TokenKind OpKind = Tok.getKind();
       SourceLocation OpLoc = ConsumeToken();  // Eat the "." or "->" token.
 
index 4cd952e393f11d203836c1b7bbcf8047ea5af21d..a248a5dbeef1ef16ab4fb83f61ad19877949896c 100644 (file)
@@ -76,7 +76,9 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
     if (HasScopeSpecifier) {
       // C++ [basic.lookup.classref]p5:
       //   If the qualified-id has the form
+      //
       //       ::class-name-or-namespace-name::...
+      //
       //   the class-name-or-namespace-name is looked up in global scope as a
       //   class-name or namespace-name.
       //
index 17708346ab8154ec48663d1d35b20c929d4f3f9f..2644c1d6f754629abba984daa13d9bb563f2c611 100644 (file)
@@ -2029,14 +2029,17 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
           FirstQualifierInScope = FindFirstQualifierInScope(S, Qualifier);
       }
       
-      return Owned(new (Context) CXXUnresolvedMemberExpr(Context,
-                                                         BaseExpr, true, 
-                                                         OpLoc,
-                                                         Qualifier,
+      return Owned(CXXUnresolvedMemberExpr::Create(Context, BaseExpr, true, 
+                                                   OpLoc, Qualifier,
                                             SS? SS->getRange() : SourceRange(),
-                                                         FirstQualifierInScope,
-                                                         MemberName,
-                                                         MemberLoc));
+                                                   FirstQualifierInScope,
+                                                   MemberName,
+                                                   MemberLoc,
+                                                   HasExplicitTemplateArgs,
+                                                   LAngleLoc,
+                                                   ExplicitTemplateArgs,
+                                                   NumExplicitTemplateArgs,
+                                                   RAngleLoc));
     }
     else if (const PointerType *PT = BaseType->getAs<PointerType>())
       BaseType = PT->getPointeeType();
@@ -2067,14 +2070,19 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
             FirstQualifierInScope = FindFirstQualifierInScope(S, Qualifier);
         }
         
-        return Owned(new (Context) CXXUnresolvedMemberExpr(Context,
-                                                           BaseExpr, false, 
-                                                           OpLoc, 
-                                                           Qualifier,
+        return Owned(CXXUnresolvedMemberExpr::Create(Context,
+                                                     BaseExpr, false, 
+                                                     OpLoc, 
+                                                     Qualifier,
                                             SS? SS->getRange() : SourceRange(),
-                                                         FirstQualifierInScope,
-                                                           MemberName,
-                                                           MemberLoc));
+                                                     FirstQualifierInScope,
+                                                     MemberName,
+                                                     MemberLoc,
+                                                     HasExplicitTemplateArgs,
+                                                     LAngleLoc,
+                                                     ExplicitTemplateArgs,
+                                                     NumExplicitTemplateArgs,
+                                                     RAngleLoc));
       }
     }
   }
index bc2c304ecc0994bf781b21db7d16d988bb6bd4d3..dc455185fecec12f456d60849f879cc3b4394e7f 100644 (file)
@@ -1229,7 +1229,7 @@ Sema::ActOnMemberTemplateIdReferenceExpr(Scope *S, ExprArg Base,
   else if (OverloadedFunctionDecl *Ovl = Template.getAsOverloadedFunctionDecl())
     Name = Ovl->getDeclName();
   else
-    assert(false && "Cannot support dependent template names yet");
+    Name = Template.getAsDependentTemplateName()->getName();
   
   // Translate the parser's template argument list in our AST format.
   llvm::SmallVector<TemplateArgument, 16> TemplateArgs;
@@ -1287,11 +1287,6 @@ Sema::ActOnDependentTemplateName(SourceLocation TemplateKWLoc,
     return Template;
   }
 
-  // FIXME: We need to be able to create a dependent template name with just
-  // an identifier, to handle the x->template f<T> case.
-  assert(!ObjectType && 
-      "Cannot handle dependent template names without a nested-name-specifier");
-  
   NestedNameSpecifier *Qualifier 
     = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
   return TemplateTy::make(Context.getDependentTemplateName(Qualifier, &Name));
index 02b402b77a2b01836e34f7744199b94bf52e0bc9..d19653d157c3b37fe0c1c52378832da5b512dd26 100644 (file)
@@ -261,7 +261,8 @@ public:
   /// By default, transforms the template name by transforming the declarations
   /// and nested-name-specifiers that occur within the template name. 
   /// Subclasses may override this function to provide alternate behavior.
-  TemplateName TransformTemplateName(TemplateName Name);
+  TemplateName TransformTemplateName(TemplateName Name,
+                                     QualType ObjectType = QualType());
   
   /// \brief Transform the given template argument.
   ///
@@ -567,7 +568,8 @@ public:
   /// template name. Subclasses may override this routine to provide different
   /// behavior.
   TemplateName RebuildTemplateName(NestedNameSpecifier *Qualifier,
-                                   const IdentifierInfo &II);
+                                   const IdentifierInfo &II,
+                                   QualType ObjectType);
   
   
   /// \brief Build a new compound statement.
@@ -1510,16 +1512,58 @@ public:
     SS.setRange(QualifierRange);
     SS.setScopeRep(Qualifier);
         
-    Base = SemaRef.BuildMemberReferenceExpr(/*Scope=*/0,
+    return SemaRef.BuildMemberReferenceExpr(/*Scope=*/0,
                                             move(Base), OperatorLoc, OpKind,
                                             MemberLoc,
                                             Name,
                                     /*FIXME?*/Sema::DeclPtrTy::make((Decl*)0),
                                             &SS,
                                             FirstQualifierInScope);
-    return move(Base);
   }
 
+  /// \brief Build a new member reference expression with explicit template
+  /// arguments.
+  ///
+  /// By default, performs semantic analysis to build the new expression.
+  /// Subclasses may override this routine to provide different behavior.
+  OwningExprResult RebuildCXXUnresolvedMemberExpr(ExprArg BaseE,
+                                                  bool IsArrow,
+                                                  SourceLocation OperatorLoc,
+                                                NestedNameSpecifier *Qualifier,
+                                                  SourceRange QualifierRange,
+                                                  TemplateName Template,
+                                                SourceLocation TemplateNameLoc,
+                                              NamedDecl *FirstQualifierInScope,
+                                                  SourceLocation LAngleLoc,
+                                          const TemplateArgument *TemplateArgs,
+                                                  unsigned NumTemplateArgs,
+                                                  SourceLocation RAngleLoc) {
+    OwningExprResult Base = move(BaseE);
+    tok::TokenKind OpKind = IsArrow? tok::arrow : tok::period;
+    
+    CXXScopeSpec SS;
+    SS.setRange(QualifierRange);
+    SS.setScopeRep(Qualifier);
+    
+    // FIXME: We're going to end up looking up the template based on its name,
+    // twice! Also, duplicates part of Sema::ActOnMemberTemplateIdReferenceExpr.
+    DeclarationName Name;
+    if (TemplateDecl *ActualTemplate = Template.getAsTemplateDecl())
+      Name = ActualTemplate->getDeclName();
+    else if (OverloadedFunctionDecl *Ovl 
+               = Template.getAsOverloadedFunctionDecl())
+      Name = Ovl->getDeclName();
+    else
+      Name = Template.getAsDependentTemplateName()->getName();
+          
+      return SemaRef.BuildMemberReferenceExpr(/*Scope=*/0, move(Base), 
+                                              OperatorLoc, OpKind,
+                                              TemplateNameLoc, Name, true,
+                                              LAngleLoc, TemplateArgs,
+                                              NumTemplateArgs, RAngleLoc,
+                                              Sema::DeclPtrTy(), &SS);
+  }
+  
   /// \brief Build a new Objective-C @encode expression.
   ///
   /// By default, performs semantic analysis to build the new expression.
@@ -1746,7 +1790,8 @@ TreeTransform<Derived>::TransformDeclarationName(DeclarationName Name,
 
 template<typename Derived>
 TemplateName 
-TreeTransform<Derived>::TransformTemplateName(TemplateName Name) {
+TreeTransform<Derived>::TransformTemplateName(TemplateName Name,
+                                              QualType ObjectType) {
   if (QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName()) {
     NestedNameSpecifier *NNS 
       = getDerived().TransformNestedNameSpecifier(QTN->getQualifier(),
@@ -1789,14 +1834,14 @@ TreeTransform<Derived>::TransformTemplateName(TemplateName Name) {
     NestedNameSpecifier *NNS 
       = getDerived().TransformNestedNameSpecifier(DTN->getQualifier(),
                         /*FIXME:*/SourceRange(getDerived().getBaseLocation()));
-    if (!NNS)
+    if (!NNS && DTN->getQualifier())
       return TemplateName();
     
     if (!getDerived().AlwaysRebuild() &&
         NNS == DTN->getQualifier())
       return Name;
     
-    return getDerived().RebuildTemplateName(NNS, *DTN->getName());
+    return getDerived().RebuildTemplateName(NNS, *DTN->getName(), ObjectType);
   }
   
   if (TemplateDecl *Template = Name.getAsTemplateDecl()) {
@@ -4195,6 +4240,9 @@ TreeTransform<Derived>::TransformCXXUnresolvedMemberExpr(
   if (Base.isInvalid())
     return SemaRef.ExprError();
   
+  // FIXME: The first qualifier found might be a template type parameter,
+  // in which case there is no transformed declaration to refer to (it might
+  // refer to a built-in type!).
   NamedDecl *FirstQualifierInScope
     = cast_or_null<NamedDecl>(
               getDerived().TransformDecl(E->getFirstQualifierFoundInScope()));
@@ -4214,21 +4262,60 @@ TreeTransform<Derived>::TransformCXXUnresolvedMemberExpr(
   if (!Name)
     return SemaRef.ExprError();
   
-  if (!getDerived().AlwaysRebuild() &&
-      Base.get() == E->getBase() &&
-      Qualifier == E->getQualifier() &&
-      Name == E->getMember() &&
-      FirstQualifierInScope == E->getFirstQualifierFoundInScope())
-    return SemaRef.Owned(E->Retain()); 
+  if (!E->hasExplicitTemplateArgumentList()) {
+    // This is a reference to a member without an explicitly-specified
+    // template argument list. Optimize for this common case.
+    if (!getDerived().AlwaysRebuild() &&
+        Base.get() == E->getBase() &&
+        Qualifier == E->getQualifier() &&
+        Name == E->getMember() &&
+        FirstQualifierInScope == E->getFirstQualifierFoundInScope())
+      return SemaRef.Owned(E->Retain()); 
+    
+    return getDerived().RebuildCXXUnresolvedMemberExpr(move(Base),
+                                                       E->isArrow(),
+                                                       E->getOperatorLoc(),
+                                                       Qualifier,
+                                                       E->getQualifierRange(),
+                                                       Name,
+                                                       E->getMemberLoc(),
+                                                       FirstQualifierInScope);
+  }
+
+  // FIXME: This is an ugly hack, which forces the same template name to
+  // be looked up multiple times. Yuck!
+  // FIXME: This also won't work for, e.g., x->template operator+<int>
+  TemplateName OrigTemplateName
+    = SemaRef.Context.getDependentTemplateName(0, Name.getAsIdentifierInfo());
+  
+  TemplateName Template 
+    = getDerived().TransformTemplateName(OrigTemplateName, 
+                                       QualType::getFromOpaquePtr(ObjectType));
+  if (Template.isNull())
+    return SemaRef.ExprError();
+  
+  llvm::SmallVector<TemplateArgument, 4> TransArgs;
+  for (unsigned I = 0, N = E->getNumTemplateArgs(); I != N; ++I) {
+    TemplateArgument TransArg 
+    = getDerived().TransformTemplateArgument(E->getTemplateArgs()[I]);
+    if (TransArg.isNull())
+      return SemaRef.ExprError();
+    
+    TransArgs.push_back(TransArg);
+  }
   
   return getDerived().RebuildCXXUnresolvedMemberExpr(move(Base),
                                                      E->isArrow(),
                                                      E->getOperatorLoc(),
                                                      Qualifier,
                                                      E->getQualifierRange(),
-                                                     Name,
+                                                     Template,
                                                      E->getMemberLoc(),
-                                                     FirstQualifierInScope);
+                                                     FirstQualifierInScope,
+                                                     E->getLAngleLoc(),
+                                                     TransArgs.data(),
+                                                     TransArgs.size(),
+                                                     E->getRAngleLoc());
 }
 
 template<typename Derived>
@@ -4643,33 +4730,18 @@ TreeTransform<Derived>::RebuildTemplateName(NestedNameSpecifier *Qualifier,
 template<typename Derived>
 TemplateName 
 TreeTransform<Derived>::RebuildTemplateName(NestedNameSpecifier *Qualifier,
-                                            const IdentifierInfo &II) {
-  if (Qualifier->isDependent())
-    return SemaRef.Context.getDependentTemplateName(Qualifier, &II);
-  
-  // Somewhat redundant with ActOnDependentTemplateName.
+                                            const IdentifierInfo &II,
+                                            QualType ObjectType) {
   CXXScopeSpec SS;
   SS.setRange(SourceRange(getDerived().getBaseLocation()));
-  SS.setScopeRep(Qualifier);
-  Sema::TemplateTy Template;
-  TemplateNameKind TNK = SemaRef.isTemplateName(0, II,
-                                     /*FIXME:*/getDerived().getBaseLocation(),
-                                                &SS, 
-                                                /*FIXME:ObjectType=*/0, false,
-                                                Template);
-  if (TNK == TNK_Non_template) {
-    SemaRef.Diag(getDerived().getBaseLocation(), 
-                 diag::err_template_kw_refers_to_non_template)
-      << &II;
-    return TemplateName();
-  } else if (TNK == TNK_Function_template) {
-    SemaRef.Diag(getDerived().getBaseLocation(), 
-                 diag::err_template_kw_refers_to_non_template)
-      << &II;
-    return TemplateName();
-  }
-  
-  return Template.getAsVal<TemplateName>();  
+  SS.setScopeRep(Qualifier);  
+  return getSema().ActOnDependentTemplateName(
+                                      /*FIXME:*/getDerived().getBaseLocation(),
+                                              II,
+                                      /*FIXME:*/getDerived().getBaseLocation(),
+                                              SS,
+                                              ObjectType.getAsOpaquePtr())
+           .template getAsVal<TemplateName>();
 }
  
 template<typename Derived>
diff --git a/test/SemaTemplate/member-template-access-expr.cpp b/test/SemaTemplate/member-template-access-expr.cpp
new file mode 100644 (file)
index 0000000..20437ae
--- /dev/null
@@ -0,0 +1,30 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+template<typename U, typename T>
+U f0(T t) {
+  return t.template get<U>();
+}
+
+template<typename U, typename T>
+int &f1(T t) {
+  // FIXME: When we pretty-print this, we lose the "template" keyword.
+  return t.U::template get<int&>();
+}
+
+struct X {
+  template<typename T> T get();
+};
+
+void test_f0(X x) {
+  int i = f0<int>(x);
+  int &ir = f0<int&>(x);
+}
+
+struct XDerived : public X {
+};
+
+void test_f1(XDerived xd) {
+  // FIXME: Not quite functional yet.
+//  int &ir = f1<X>(xd);
+}
+