[cfe-commits] r80843 - in /cfe/trunk: include/clang/Basic/ include/clang/Parse/ lib/Parse/ lib/Sema/ test/SemaCXX/ test/SemaTemplate/

Douglas Gregor dgregor at apple.com
Wed Sep 2 15:59:36 PDT 2009


Author: dgregor
Date: Wed Sep  2 17:59:36 2009
New Revision: 80843

URL: http://llvm.org/viewvc/llvm-project?rev=80843&view=rev
Log:
Rewrite of our handling of name lookup in C++ member access expressions, e.g.,

  x->Base::f

We no longer try to "enter" the context of the type that "x" points
to. Instead, we drag that object type through the parser and pass it
into the Sema routines that need to know how to perform lookup within
member access expressions.

We now implement most of the crazy name lookup rules in C++
[basic.lookup.classref] for non-templated code, including performing
lookup both in the context of the type referred to by the member
access and in the scope of the member access itself and then detecting
ambiguities when the two lookups collide (p1 and p4; p3 and p7 are
still TODO). This change also corrects our handling of name lookup
within template arguments of template-ids inside the
nested-name-specifier (p6; we used to look into the scope of the
object expression for them) and fixes PR4703.

I have disabled some tests that involve member access expressions
where the object expression has dependent type, because we don't yet
have the ability to describe dependent nested-name-specifiers starting
with an identifier.


Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Parse/Action.h
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/lib/Parse/MinimalAction.cpp
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Parse/ParseExpr.cpp
    cfe/trunk/lib/Parse/ParseExprCXX.cpp
    cfe/trunk/lib/Parse/Parser.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/lib/Sema/SemaLookup.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/lib/Sema/TreeTransform.h
    cfe/trunk/test/SemaCXX/nested-name-spec.cpp
    cfe/trunk/test/SemaCXX/qual-id-test.cpp
    cfe/trunk/test/SemaTemplate/member-function-template.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Sep  2 17:59:36 2009
@@ -366,6 +366,12 @@
 // C++ name lookup
 def err_incomplete_nested_name_spec : Error<
   "incomplete type %0 named in nested name specifier">;
+def err_nested_name_member_ref_lookup_ambiguous : Error<
+  "lookup of %0 in member access expression is ambiguous">;
+def note_ambig_member_ref_object_type : Note<
+  "lookup in the object type %0 refers here">;
+def note_ambig_member_ref_scope : Note<
+  "lookup from the current scope refers here">;
 
 // C++ class members
 def err_storageclass_invalid_for_member : Error<

Modified: cfe/trunk/include/clang/Parse/Action.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Action.h?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Wed Sep  2 17:59:36 2009
@@ -209,10 +209,12 @@
   /// \brief Determine whether the given identifier refers to the name of a
   /// template.
   ///
+  /// \param S the scope in which name lookup occurs
+  ///
   /// \param II the identifier that we are querying to determine whether it
   /// is a template.
   ///
-  /// \param S the scope in which name lookup occurs
+  /// \param IdLoc the source location of the identifier
   ///
   /// \param SS the C++ scope specifier that precedes the template name, if
   /// any.
@@ -224,8 +226,11 @@
   /// of the template that the name refers to.
   ///
   /// \returns the kind of template that this name refers to.
-  virtual TemplateNameKind isTemplateName(const IdentifierInfo &II, Scope *S,
+  virtual TemplateNameKind isTemplateName(Scope *S,
+                                          const IdentifierInfo &II, 
+                                          SourceLocation IdLoc,
                                           const CXXScopeSpec *SS,
+                                          TypeTy *ObjectType,
                                           bool EnteringContext,
                                           TemplateTy &Template) = 0;
 
@@ -236,17 +241,39 @@
     return 0;
   }
 
-  /// ActOnCXXNestedNameSpecifier - Called during parsing of a
-  /// nested-name-specifier. e.g. for "foo::bar::" we parsed "foo::" and now
-  /// we want to resolve "bar::". 'SS' is empty or the previously parsed
-  /// nested-name part ("foo::"), 'IdLoc' is the source location of 'bar',
-  /// 'CCLoc' is the location of '::' and 'II' is the identifier for 'bar'.
-  /// Returns a CXXScopeTy* object representing the C++ scope.
+  /// \brief Parsed an identifier followed by '::' in a C++
+  /// nested-name-specifier.
+  ///
+  /// \param S the scope in which the nested-name-specifier was parsed.
+  ///
+  /// \param SS the nested-name-specifier that precedes the identifier. For
+  /// example, if we are parsing "foo::bar::", \p SS will describe the "foo::"
+  /// that has already been parsed.
+  ///
+  /// \param IdLoc the location of the identifier we have just parsed (e.g.,
+  /// the "bar" in "foo::bar::".
+  ///
+  /// \param CCLoc the location of the '::' at the end of the 
+  /// nested-name-specifier.
+  ///
+  /// \param II the identifier that represents the scope that this 
+  /// nested-name-specifier refers to, e.g., the "bar" in "foo::bar::".
+  ///
+  /// \param ObjectType if this nested-name-specifier occurs as part of a 
+  /// C++ member access expression such as "x->Base::f", the type of the base
+  /// object (e.g., *x in the example, if "x" were a pointer).
+  /// 
+  /// \param EnteringContext if true, then we intend to immediately enter the
+  /// context of this nested-name-specifier, e.g., for an out-of-line 
+  /// definition of a class member.
+  ///
+  /// \returns a CXXScopeTy* object representing the C++ scope.
   virtual CXXScopeTy *ActOnCXXNestedNameSpecifier(Scope *S,
                                                   const CXXScopeSpec &SS,
                                                   SourceLocation IdLoc,
                                                   SourceLocation CCLoc,
                                                   IdentifierInfo &II,
+                                                  TypeTy *ObjectType,
                                                   bool EnteringContext) {
     return 0;
   }
@@ -267,27 +294,6 @@
     return 0; 
   }
 
-  /// ActOnCXXEnterMemberScope - Called when a C++ class member accessor ('.'
-  /// or '->') is parsed. After this method is called, according to
-  /// [C++ 3.4.5p4], qualified-ids should be looked up in the contexts of both
-  /// the entire postfix-expression and the scope of the class of the object
-  /// expression.
-  /// 'SS' should be an empty CXXScopeSpec to be filled with the class's scope.
-  virtual OwningExprResult ActOnCXXEnterMemberScope(Scope *S,
-                                                    CXXScopeSpec &SS,
-                                                    ExprArg Base,
-                                                    tok::TokenKind OpKind) {
-    return ExprEmpty();
-  }
-
-  /// ActOnCXXExitMemberScope - Called when a postfix-expression that previously
-  /// invoked ActOnCXXEnterMemberScope() is finished. 'SS' is the same
-  /// CXXScopeSpec that was passed to ActOnCXXEnterMemberScope. Used to
-  /// indicate that names should revert to being looked up in the defining
-  /// scope.
-  virtual void ActOnCXXExitMemberScope(Scope *S, const CXXScopeSpec &SS) {
-  }
-
   /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
   /// scope or nested-name-specifier) is parsed, part of a declarator-id.
   /// After this method is called, according to [C++ 3.4.3p3], names should be
@@ -1280,6 +1286,31 @@
     return ExprEmpty();
   }
 
+  /// \brief Invoked when the parser is starting to parse a C++ member access 
+  /// expression such as x.f or x->f.
+  ///
+  /// \param S the scope in which the member access expression occurs.
+  ///
+  /// \param Base the expression in which a member is being accessed, e.g., the
+  /// "x" in "x.f".
+  ///
+  /// \param OpLoc the location of the member access operator ("." or "->")
+  ///
+  /// \param OpKind the kind of member access operator ("." or "->")
+  ///
+  /// \param ObjectType originally NULL. The action should fill in this type
+  /// with the type into which name lookup should look to find the member in 
+  /// the member access expression.
+  ///
+  /// \returns the (possibly modified) \p Base expression
+  virtual OwningExprResult ActOnStartCXXMemberReference(Scope *S,
+                                                        ExprArg Base,
+                                                        SourceLocation OpLoc,
+                                                        tok::TokenKind OpKind,
+                                                        TypeTy *&ObjectType) {
+    return ExprEmpty();
+  }
+  
   /// ActOnDestructorReferenceExpr - Parsed a destructor reference, for example:
   ///
   /// t->~T();
@@ -1597,10 +1628,26 @@
   /// example, given "MetaFun::template apply", the scope specifier \p
   /// SS will be "MetaFun::", \p TemplateKWLoc contains the location
   /// of the "template" keyword, and "apply" is the \p Name.
+  ///
+  /// \param TemplateKWLoc the location of the "template" keyword (if any).
+  ///
+  /// \param Name the name of the template (an identifier)
+  ///
+  /// \param NameLoc the location of the identifier
+  ///
+  /// \param SS the nested-name-specifier that precedes the "template" keyword
+  /// or the template name. FIXME: If the dependent template name occurs in
+  /// a member access expression, e.g., "x.template f<T>", this 
+  /// nested-name-specifier will be empty.
+  ///
+  /// \param ObjectType if this dependent template name occurs in the 
+  /// context of a member access expression, the type of the object being
+  /// accessed.
   virtual TemplateTy ActOnDependentTemplateName(SourceLocation TemplateKWLoc,
                                                 const IdentifierInfo &Name,
                                                 SourceLocation NameLoc,
-                                                const CXXScopeSpec &SS) {
+                                                const CXXScopeSpec &SS,
+                                                TypeTy *ObjectType) {
     return TemplateTy();
   }
 
@@ -2128,26 +2175,11 @@
   virtual bool isCurrentClassName(const IdentifierInfo& II, Scope *S,
                                   const CXXScopeSpec *SS);
 
-  /// \brief Determine whether the given identifier refers to the name of a
-  /// template.
-  ///
-  /// \param II the identifier that we are querying to determine whether it
-  /// is a template.
-  ///
-  /// \param S the scope in which name lookup occurs
-  ///
-  /// \param SS the C++ scope specifier that precedes the template name, if
-  /// any.
-  ///
-  /// \param EnteringContext whether we are potentially entering the context
-  /// referred to by the scope specifier \p SS
-  ///
-  /// \param Template if the name does refer to a template, the declaration
-  /// of the template that the name refers to.
-  ///
-  /// \returns the kind of template that this name refers to.
-  virtual TemplateNameKind isTemplateName(const IdentifierInfo &II, Scope *S,
+  virtual TemplateNameKind isTemplateName(Scope *S,
+                                          const IdentifierInfo &II, 
+                                          SourceLocation IdLoc,
                                           const CXXScopeSpec *SS,
+                                          TypeTy *ObjectType,                              
                                           bool EnteringContext,
                                           TemplateTy &Template);
 

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Wed Sep  2 17:59:36 2009
@@ -770,13 +770,9 @@
   // C++ Expressions
   OwningExprResult ParseCXXIdExpression(bool isAddressOfOperand = false);
 
-  /// ParseOptionalCXXScopeSpecifier - Parse global scope or
-  /// nested-name-specifier if present.  Returns true if a nested-name-specifier
-  /// was parsed from the token stream.  Note that this routine will not parse
-  /// ::new or ::delete, it will just leave them in the token stream.
-  ///
   bool ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, 
-                                      bool EnteringContext = false);
+                                      TypeTy *ObjectType,
+                                      bool EnteringContext);
   
   //===--------------------------------------------------------------------===//
   // C++ 5.2p1: C++ Casts

Modified: cfe/trunk/lib/Parse/MinimalAction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/MinimalAction.cpp?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/MinimalAction.cpp (original)
+++ cfe/trunk/lib/Parse/MinimalAction.cpp Wed Sep  2 17:59:36 2009
@@ -160,8 +160,11 @@
 }
 
 TemplateNameKind 
-MinimalAction::isTemplateName(const IdentifierInfo &II, Scope *S,
+MinimalAction::isTemplateName(Scope *S,
+                              const IdentifierInfo &II,
+                              SourceLocation IdLoc,
                               const CXXScopeSpec *SS,
+                              TypeTy *ObjectType,                              
                               bool EnteringScope,
                               TemplateTy &TemplateDecl) {
   return TNK_Non_template;

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Wed Sep  2 17:59:36 2009
@@ -743,7 +743,7 @@
             ->Kind == TNK_Type_template) {
         // We have a qualified template-id, e.g., N::A<int>
         CXXScopeSpec SS;
-        ParseOptionalCXXScopeSpecifier(SS, true);
+        ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true);
         assert(Tok.is(tok::annot_template_id) && 
                "ParseOptionalCXXScopeSpecifier not working");
         AnnotateTemplateIdTokenAsType(&SS);
@@ -1596,7 +1596,7 @@
     Attr = ParseAttributes();
 
   CXXScopeSpec SS;
-  if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS)) {
+  if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS, 0, false)) {
     if (Tok.isNot(tok::identifier)) {
       Diag(Tok, diag::err_expected_ident);
       if (Tok.isNot(tok::l_brace)) {
@@ -2034,7 +2034,7 @@
       (Tok.is(tok::coloncolon) || Tok.is(tok::identifier) ||
        Tok.is(tok::annot_cxxscope))) {
     CXXScopeSpec SS;
-    if (ParseOptionalCXXScopeSpecifier(SS, true)) {
+    if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true)) {
       if(Tok.isNot(tok::star)) {
         // The scope spec really belongs to the direct-declarator.
         D.getCXXScopeSpec() = SS;
@@ -2191,7 +2191,8 @@
     if (D.mayHaveIdentifier()) {
       // ParseDeclaratorInternal might already have parsed the scope.
       bool afterCXXScope = D.getCXXScopeSpec().isSet() ||
-        ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), true);
+        ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), /*ObjectType=*/0, 
+                                       true);
       if (afterCXXScope) {
         // Change the declaration context for name lookup, until this function
         // is exited (and the declarator has been parsed).

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Wed Sep  2 17:59:36 2009
@@ -117,7 +117,7 @@
   
   CXXScopeSpec SS;
   // Parse (optional) nested-name-specifier.
-  ParseOptionalCXXScopeSpecifier(SS);
+  ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
 
   if (SS.isInvalid() || Tok.isNot(tok::identifier)) {
     Diag(Tok, diag::err_expected_namespace_name);
@@ -216,7 +216,7 @@
 
   CXXScopeSpec SS;
   // Parse (optional) nested-name-specifier.
-  ParseOptionalCXXScopeSpecifier(SS);
+  ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
 
   AttributeList *AttrList = 0;
   IdentifierInfo *NamespcName = 0;
@@ -273,7 +273,7 @@
     IsTypeName = false;
 
   // Parse nested-name-specifier.
-  ParseOptionalCXXScopeSpecifier(SS);
+  ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
 
   AttributeList *AttrList = 0;
 
@@ -538,7 +538,8 @@
   
   // Parse the (optional) nested-name-specifier.
   CXXScopeSpec SS;
-  if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS, true))
+  if (getLang().CPlusPlus && 
+      ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true))
     if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id))
       Diag(Tok, diag::err_expected_ident);
 
@@ -813,7 +814,7 @@
 
   // Parse optional '::' and optional nested-name-specifier.
   CXXScopeSpec SS;
-  ParseOptionalCXXScopeSpecifier(SS, true);
+  ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true);
 
   // The location of the base class itself.
   SourceLocation BaseLoc = Tok.getLocation();
@@ -1299,7 +1300,7 @@
 Parser::MemInitResult Parser::ParseMemInitializer(DeclPtrTy ConstructorDecl) {
   // parse '::'[opt] nested-name-specifier[opt]
   CXXScopeSpec SS;
-  ParseOptionalCXXScopeSpecifier(SS);
+  ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
   TypeTy *TemplateTypeTy = 0;
   if (Tok.is(tok::annot_template_id)) {
     TemplateIdAnnotation *TemplateId

Modified: cfe/trunk/lib/Parse/ParseExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExpr.cpp?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseExpr.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExpr.cpp Wed Sep  2 17:59:36 2009
@@ -923,14 +923,14 @@
       tok::TokenKind OpKind = Tok.getKind();
       SourceLocation OpLoc = ConsumeToken();  // Eat the "." or "->" token.
 
-      CXXScopeSpec MemberSS;
       CXXScopeSpec SS;
+      Action::TypeTy *ObjectType = 0;
       if (getLang().CPlusPlus && !LHS.isInvalid()) {
-        LHS = Actions.ActOnCXXEnterMemberScope(CurScope, MemberSS, move(LHS),
-                                               OpKind);
+        LHS = Actions.ActOnStartCXXMemberReference(CurScope, move(LHS),
+                                                   OpLoc, OpKind, ObjectType);
         if (LHS.isInvalid())
           break;
-        ParseOptionalCXXScopeSpecifier(SS);
+        ParseOptionalCXXScopeSpecifier(SS, ObjectType, false);
       }
 
       if (Tok.is(tok::identifier)) {
@@ -947,8 +947,6 @@
         ConsumeToken();
         
         if (!Tok.is(tok::identifier)) {
-          if (getLang().CPlusPlus)
-            Actions.ActOnCXXExitMemberScope(CurScope, MemberSS);
           Diag(Tok, diag::err_expected_ident);
           return ExprError();
         }
@@ -980,8 +978,6 @@
                                                            Tok.getLocation(),
                                                                ConvType, &SS);
         } else {
-          if (getLang().CPlusPlus)
-            Actions.ActOnCXXExitMemberScope(CurScope, MemberSS);
           // Don't emit a diagnostic; ParseConversionFunctionId does it for us
           return ExprError();
         }
@@ -1007,14 +1003,9 @@
         }
         ConsumeToken();
       } else {
-        if (getLang().CPlusPlus)
-          Actions.ActOnCXXExitMemberScope(CurScope, MemberSS);
         Diag(Tok, diag::err_expected_ident);
         return ExprError();
       }
-
-      if (getLang().CPlusPlus)
-        Actions.ActOnCXXExitMemberScope(CurScope, MemberSS);
       break;
     }
     case tok::plusplus:    // postfix-expression: postfix-expression '++'

Modified: cfe/trunk/lib/Parse/ParseExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExprCXX.cpp?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseExprCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExprCXX.cpp Wed Sep  2 17:59:36 2009
@@ -16,10 +16,11 @@
 #include "clang/Parse/DeclSpec.h"
 using namespace clang;
 
-/// ParseOptionalCXXScopeSpecifier - Parse global scope or
-/// nested-name-specifier if present.  Returns true if a nested-name-specifier
-/// was parsed from the token stream.  Note that this routine will not parse
-/// ::new or ::delete, it will just leave them in the token stream.
+/// \brief Parse global scope or nested-name-specifier if present. 
+///
+/// Parses a C++ global scope specifier ('::') or nested-name-specifier (which
+/// may be preceded by '::'). Note that this routine will not parse ::new or 
+/// ::delete; it will just leave them in the token stream.
 ///
 ///       '::'[opt] nested-name-specifier
 ///       '::'
@@ -28,9 +29,22 @@
 ///         type-name '::'
 ///         namespace-name '::'
 ///         nested-name-specifier identifier '::'
-///         nested-name-specifier 'template'[opt] simple-template-id '::' [TODO]
+///         nested-name-specifier 'template'[opt] simple-template-id '::'
+///
+///
+/// \param SS the scope specifier that will be set to the parsed 
+/// nested-name-specifier (or empty)
+///
+/// \param ObjectType if this nested-name-specifier is being parsed following 
+/// the "." or "->" of a member access expression, this parameter provides the
+/// type of the object whose members are being accessed.
 ///
+/// \param EnteringContext whether we will be entering into the context of
+/// the nested-name-specifier after parsing it.
+///
+/// \returns true if a scope specifier was parsed.
 bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
+                                            Action::TypeTy *ObjectType,
                                             bool EnteringContext) {
   assert(getLang().CPlusPlus &&
          "Call sites of this function should be guarded by checking for C++");
@@ -59,16 +73,28 @@
   }
 
   while (true) {
+    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.
+      //
+      // To implement this, we clear out the object type as soon as we've
+      // seen a leading '::' or part of a nested-name-specifier.
+      ObjectType = 0;
+    }
+    
     // nested-name-specifier:
     //   nested-name-specifier 'template'[opt] simple-template-id '::'
 
     // Parse the optional 'template' keyword, then make sure we have
     // 'identifier <' after it.
     if (Tok.is(tok::kw_template)) {
-      // If we don't have a scope specifier, this isn't a
+      // If we don't have a scope specifier or an object type, this isn't a
       // nested-name-specifier, since they aren't allowed to start with
       // 'template'.
-      if (!HasScopeSpecifier)
+      if (!HasScopeSpecifier && !ObjectType)
         break;
 
       SourceLocation TemplateKWLoc = ConsumeToken();
@@ -91,7 +117,8 @@
       TemplateTy Template 
         = Actions.ActOnDependentTemplateName(TemplateKWLoc,
                                              *Tok.getIdentifierInfo(),
-                                             Tok.getLocation(), SS);
+                                             Tok.getLocation(), SS,
+                                             ObjectType);
       if (!Template)
         break;
       if (AnnotateTemplateIdToken(Template, TNK_Dependent_template_name,
@@ -173,7 +200,7 @@
       
       SS.setScopeRep(
         Actions.ActOnCXXNestedNameSpecifier(CurScope, SS, IdLoc, CCLoc, II,
-                                            EnteringContext));
+                                            ObjectType, EnteringContext));
       SS.setEndLoc(CCLoc);
       continue;
     }
@@ -182,7 +209,10 @@
     //   type-name '<'
     if (Next.is(tok::less)) {
       TemplateTy Template;
-      if (TemplateNameKind TNK = Actions.isTemplateName(II, CurScope, &SS,
+      if (TemplateNameKind TNK = Actions.isTemplateName(CurScope, II,
+                                                        Tok.getLocation(),
+                                                        &SS,
+                                                        ObjectType,
                                                         EnteringContext,
                                                         Template)) {
         // We have found a template name, so annotate this this token
@@ -267,7 +297,7 @@
   //   '::' unqualified-id
   //
   CXXScopeSpec SS;
-  ParseOptionalCXXScopeSpecifier(SS);
+  ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
 
   // unqualified-id:
   //   identifier

Modified: cfe/trunk/lib/Parse/Parser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/Parser.cpp?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/Parser.cpp (original)
+++ cfe/trunk/lib/Parse/Parser.cpp Wed Sep  2 17:59:36 2009
@@ -884,7 +884,8 @@
     //            simple-template-id
     SourceLocation TypenameLoc = ConsumeToken();
     CXXScopeSpec SS;
-    bool HadNestedNameSpecifier = ParseOptionalCXXScopeSpecifier(SS, false);
+    bool HadNestedNameSpecifier 
+      = ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
     if (!HadNestedNameSpecifier) {
       Diag(Tok.getLocation(), diag::err_expected_qualified_after_typename);
       return false;
@@ -928,7 +929,7 @@
 
   CXXScopeSpec SS;
   if (getLang().CPlusPlus)
-    ParseOptionalCXXScopeSpecifier(SS, EnteringContext);
+    ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, EnteringContext);
 
   if (Tok.is(tok::identifier)) {
     // Determine whether the identifier is a type name.
@@ -959,8 +960,10 @@
     if (NextToken().is(tok::less)) {
       TemplateTy Template;
       if (TemplateNameKind TNK 
-            = Actions.isTemplateName(*Tok.getIdentifierInfo(),
-                                     CurScope, &SS, EnteringContext, Template))
+            = Actions.isTemplateName(CurScope, *Tok.getIdentifierInfo(), 
+                                     Tok.getLocation(), &SS, 
+                                     /*ObjectType=*/0, EnteringContext, 
+                                     Template))
         if (AnnotateTemplateIdToken(Template, TNK, &SS)) {
           // If an unrecoverable error occurred, we need to return true here,
           // because the token stream is in a damaged state.  We may not return
@@ -1022,7 +1025,7 @@
          "Cannot be a type or scope token!");
 
   CXXScopeSpec SS;
-  if (!ParseOptionalCXXScopeSpecifier(SS, EnteringContext))
+  if (!ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, EnteringContext))
     return Tok.is(tok::annot_template_id);
 
   // Push the current token back into the token stream (or revert it if it is

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Wed Sep  2 17:59:36 2009
@@ -1983,6 +1983,12 @@
                                                TypeTy *Ty,
                                                SourceLocation RParen);
   
+  virtual OwningExprResult ActOnStartCXXMemberReference(Scope *S,
+                                                        ExprArg Base,
+                                                        SourceLocation OpLoc,
+                                                        tok::TokenKind OpKind,
+                                                        TypeTy *&ObjectType);
+    
   virtual OwningExprResult
   ActOnDestructorReferenceExpr(Scope *S, ExprArg Base,
                                SourceLocation OpLoc,
@@ -2029,6 +2035,7 @@
 
   bool RequireCompleteDeclContext(const CXXScopeSpec &SS);
   
+  DeclContext *computeDeclContext(QualType T);
   DeclContext *computeDeclContext(const CXXScopeSpec &SS, 
                                   bool EnteringContext = false);
   bool isDependentScopeSpecifier(const CXXScopeSpec &SS);
@@ -2051,6 +2058,7 @@
                                                   SourceLocation IdLoc,
                                                   SourceLocation CCLoc,
                                                   IdentifierInfo &II,
+                                                  TypeTy *ObjectType,
                                                   bool EnteringContext);
 
   /// ActOnCXXNestedNameSpecifier - Called during parsing of a
@@ -2067,23 +2075,6 @@
                                                   SourceRange TypeRange,
                                                   SourceLocation CCLoc);
 
-  /// ActOnCXXEnterMemberScope - Called when a C++ class member accessor ('.'
-  /// or '->') is parsed. After this method is called, according to
-  /// [C++ 3.4.5p4], qualified-ids should be looked up in the contexts of both
-  /// the entire postfix-expression and the scope of the class of the object
-  /// expression.
-  /// 'SS' should be an empty CXXScopeSpec to be filled with the class's scope.
-  virtual OwningExprResult ActOnCXXEnterMemberScope(Scope *S, CXXScopeSpec &SS,
-                                                    ExprArg Base,
-                                                    tok::TokenKind OpKind);
-
-  /// ActOnCXXExitMemberScope - Called when a postfix-expression that previously
-  /// invoked ActOnCXXEnterMemberScope() is finished. 'SS' is the same
-  /// CXXScopeSpec that was passed to ActOnCXXEnterMemberScope. Used to
-  /// indicate that names should revert to being looked up in the defining
-  /// scope.
-  virtual void ActOnCXXExitMemberScope(Scope *S, const CXXScopeSpec &SS);
-
   /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
   /// scope or nested-name-specifier) is parsed, part of a declarator-id.
   /// After this method is called, according to [C++ 3.4.3p3], names should be
@@ -2311,8 +2302,11 @@
   //===--------------------------------------------------------------------===//
   // C++ Templates [C++ 14]
   //
-  virtual TemplateNameKind isTemplateName(const IdentifierInfo &II, Scope *S,
+  virtual TemplateNameKind isTemplateName(Scope *S,
+                                          const IdentifierInfo &II, 
+                                          SourceLocation IdLoc,
                                           const CXXScopeSpec *SS,
+                                          TypeTy *ObjectType,
                                           bool EnteringContext,
                                           TemplateTy &Template);
   bool DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl);
@@ -2400,7 +2394,8 @@
   virtual TemplateTy ActOnDependentTemplateName(SourceLocation TemplateKWLoc,
                                                 const IdentifierInfo &Name,
                                                 SourceLocation NameLoc,
-                                                const CXXScopeSpec &SS);
+                                                const CXXScopeSpec &SS,
+                                                TypeTy *ObjectType);
 
   bool CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
                                     ClassTemplateSpecializationDecl *PrevDecl,

Modified: cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp Wed Sep  2 17:59:36 2009
@@ -22,6 +22,20 @@
 #include "llvm/Support/raw_ostream.h"
 using namespace clang;
 
+/// \brief Compute the DeclContext that is associated with the given type.
+///
+/// \param T the type for which we are attempting to find a DeclContext.
+///
+/// \returns the declaration context represented by the type T, 
+/// or NULL if the declaration context cannot be computed (e.g., because it is
+/// dependent and not the current instantiation).
+DeclContext *Sema::computeDeclContext(QualType T) {
+  if (const TagType *Tag = T->getAs<TagType>())
+    return Tag->getDecl();
+  
+  return 0;
+}
+
 /// \brief Compute the DeclContext that is associated with the given
 /// scope specifier.
 ///
@@ -244,6 +258,36 @@
   return NestedNameSpecifier::GlobalSpecifier(Context);
 }
 
+/// \brief Determines whether the given declaration is an valid acceptable
+/// result for name lookup of a nested-name-specifier.
+bool isAcceptableNestedNameSpecifier(ASTContext &Context, NamedDecl *SD) {
+  if (!SD)
+    return false;
+  
+  // Namespace and namespace aliases are fine.
+  if (isa<NamespaceDecl>(SD) || isa<NamespaceAliasDecl>(SD))
+    return true;
+  
+  if (!isa<TypeDecl>(SD))
+    return false;
+  
+  // 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 = Context.getTypeDeclType(cast<TypeDecl>(SD));
+  if (T->isDependentType())
+    return true;
+  else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(SD)) {
+    if (TD->getUnderlyingType()->isRecordType() ||
+        (Context.getLangOptions().CPlusPlus0x && 
+         TD->getUnderlyingType()->isEnumeralType()))
+      return true;
+  } else if (isa<RecordDecl>(SD) || 
+             (Context.getLangOptions().CPlusPlus0x && isa<EnumDecl>(SD)))
+    return true;
+
+  return false;
+}
+
 /// ActOnCXXNestedNameSpecifier - Called during parsing of a
 /// nested-name-specifier. e.g. for "foo::bar::" we parsed "foo::" and now
 /// we want to resolve "bar::". 'SS' is empty or the previously parsed
@@ -255,58 +299,134 @@
                                                     SourceLocation IdLoc,
                                                     SourceLocation CCLoc,
                                                     IdentifierInfo &II,
+                                                    TypeTy *ObjectTypePtr,
                                                     bool EnteringContext) {
   NestedNameSpecifier *Prefix 
     = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
+  
+  // Determine where to perform name lookup
+  DeclContext *LookupCtx = 0;
+  bool isDependent = false;
+  if (ObjectTypePtr) {
+    // This nested-name-specifier occurs in a member access expression, e.g.,
+    // x->B::f, and we are looking into the type of the object.
+    assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist");
+    QualType ObjectType = QualType::getFromOpaquePtr(ObjectTypePtr);
+    LookupCtx = computeDeclContext(ObjectType);
+    isDependent = ObjectType->isDependentType();
+  } else if (SS.isSet()) {
+    // This nested-name-specifier occurs after another nested-name-specifier,
+    // so long into the context associated with the prior nested-name-specifier.
+    LookupCtx = computeDeclContext(SS, EnteringContext);
+    isDependent = isDependentScopeSpecifier(SS);
+  }
 
-  NamedDecl *SD = LookupParsedName(S, &SS, &II, LookupNestedNameSpecifierName,
-                                   false, false, SourceLocation(),
-                                   EnteringContext);
+  LookupResult Found;
+  bool ObjectTypeSearchedInScope = false;
+  if (LookupCtx) {
+    // Perform "qualified" name lookup into the declaration context we 
+    // computed, which is either the type of the base of a member access
+    // expression or the declaration context associated with a prior 
+    // nested-name-specifier.
+    
+    // The declaration context must be complete.
+    if (!LookupCtx->isDependentContext() && RequireCompleteDeclContext(SS))
+      return 0;
 
-  if (SD) {
+    Found = LookupQualifiedName(LookupCtx, &II, LookupNestedNameSpecifierName,
+                                false);
+    
+    if (ObjectTypePtr && Found.getKind() == LookupResult::NotFound && S) {
+      // C++ [basic.lookup.classref]p4:
+      //   If the id-expression in a class member access is a qualified-id of
+      //   the form 
+      //
+      //        class-name-or-namespace-name::...
+      //
+      //   the class-name-or-namespace-name following the . or -> operator is 
+      //   looked up both in the context of the entire postfix-expression and in 
+      //   the scope of the class of the object expression. If the name is found
+      //   only in the scope of the class of the object expression, the name 
+      //   shall refer to a class-name. If the name is found only in the 
+      //   context of the entire postfix-expression, the name shall refer to a
+      //   class-name or namespace-name. [...]
+      //
+      // Qualified name lookup into a class will not find a namespace-name,
+      // so we do not need to diagnoste that case specifically. However, 
+      // this qualified name lookup may find nothing. In that case, perform
+      // unqualified name lookup in the given scope.
+      
+      // FIXME: When we're instantiating a template, do we actually have to
+      // look in the scope of the template? Seems fishy...
+      Found = LookupName(S, &II, LookupNestedNameSpecifierName);
+      ObjectTypeSearchedInScope = true;
+    }
+  } else if (isDependent) {
+    // We were not able to compute the declaration context for a dependent
+    // base object type or prior nested-name-specifier, so this 
+    // nested-name-specifier refers to an unknown specialization. Just build
+    // a dependent nested-name-specifier.
+    return NestedNameSpecifier::Create(Context, Prefix, &II);
+  } else {
+    // Perform unqualified name lookup in the current scope.
+    Found = LookupName(S, &II, LookupNestedNameSpecifierName);
+  }
+  
+  // FIXME: Deal with ambiguities cleanly.
+  NamedDecl *SD = Found;
+  if (isAcceptableNestedNameSpecifier(Context, SD)) {
+    if (ObjectTypePtr && !ObjectTypeSearchedInScope && S) {
+      // C++ [basic.lookup.classref]p4:
+      //   [...] If the name is found in both contexts, the 
+      //   class-name-or-namespace-name shall refer to the same entity.
+      //
+      // We already found the name in the scope of the object. Now, look
+      // into the current scope (the scope of the postfix-expression) to
+      // see if we can find the same name there. 
+      LookupResult FoundOuter 
+        = LookupName(S, &II, LookupNestedNameSpecifierName);
+      
+      // FIXME: Handle ambiguities in FoundOuter!
+      NamedDecl *OuterDecl = FoundOuter;
+      if (isAcceptableNestedNameSpecifier(Context, OuterDecl) &&
+          OuterDecl->getCanonicalDecl() != SD->getCanonicalDecl() &&
+          (!isa<TypeDecl>(OuterDecl) || !isa<TypeDecl>(SD) ||
+           !Context.hasSameType(
+                          Context.getTypeDeclType(cast<TypeDecl>(OuterDecl)),
+                               Context.getTypeDeclType(cast<TypeDecl>(SD))))) {
+        Diag(IdLoc, diag::err_nested_name_member_ref_lookup_ambiguous)
+          << &II;
+        Diag(SD->getLocation(), diag::note_ambig_member_ref_object_type)
+          << QualType::getFromOpaquePtr(ObjectTypePtr);
+        Diag(OuterDecl->getLocation(), diag::note_ambig_member_ref_scope);
+             
+        // Fall through so that we'll pick the name we found in the object type,
+        // since that's probably what the user wanted anyway.
+      }
+    }
+    
     if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(SD))
       return NestedNameSpecifier::Create(Context, Prefix, Namespace);
 
-    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 = 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()))
-          AcceptableType = true;
-      } else if (isa<RecordDecl>(Type) || 
-                 (getLangOptions().CPlusPlus0x && isa<EnumDecl>(Type)))
-        AcceptableType = true;
-
-      if (AcceptableType)
-        return NestedNameSpecifier::Create(Context, Prefix, false, 
-                                           T.getTypePtr());
-    }
-    
     // FIXME: It would be nice to maintain the namespace alias name, then
     // see through that alias when resolving the nested-name-specifier down to
     // a declaration context.
     if (NamespaceAliasDecl *Alias = dyn_cast<NamespaceAliasDecl>(SD))
       return NestedNameSpecifier::Create(Context, Prefix,
-                                         Alias->getNamespace());
 
-    // Fall through to produce an error: we found something that isn't
-    // a class or a namespace.
-  } else if (SS.isSet() && isDependentScopeSpecifier(SS))
-    return NestedNameSpecifier::Create(Context, Prefix, &II);
+                                         Alias->getNamespace());
+    
+    QualType T = Context.getTypeDeclType(cast<TypeDecl>(SD));
+    return NestedNameSpecifier::Create(Context, Prefix, false,
+                                       T.getTypePtr());
+  }
 
   // If we didn't find anything during our lookup, try again with
   // ordinary name lookup, which can help us produce better error
   // messages.
   if (!SD)
-    SD = LookupParsedName(S, &SS, &II, LookupOrdinaryName,
-                          false, false, SourceLocation(),
-                          EnteringContext);
+    SD = LookupName(S, &II, LookupOrdinaryName);
+  
   unsigned DiagID;
   if (SD)
     DiagID = diag::err_expected_class_or_namespace;
@@ -338,58 +458,6 @@
                                      T.getTypePtr());
 }
 
-Action::OwningExprResult
-Sema::ActOnCXXEnterMemberScope(Scope *S, CXXScopeSpec &SS, ExprArg Base,
-                               tok::TokenKind OpKind) {
-  // Since this might be a postfix expression, get rid of ParenListExprs.
-  Base = MaybeConvertParenListExprToParenExpr(S, move(Base));
-
-  Expr *BaseExpr = (Expr*)Base.get();
-  assert(BaseExpr && "no record expansion");
-
-  QualType BaseType = BaseExpr->getType();
-  // FIXME: handle dependent types
-  if (BaseType->isDependentType())
-    return move(Base);
-
-  // C++ [over.match.oper]p8:
-  //   [...] When operator->returns, the operator-> is applied  to the value 
-  //   returned, with the original second operand.
-  if (OpKind == tok::arrow) {
-    while (BaseType->isRecordType()) {
-      Base = BuildOverloadedArrowExpr(S, move(Base), BaseExpr->getExprLoc());
-      BaseExpr = (Expr*)Base.get();
-      if (BaseExpr == NULL)
-          return ExprError();
-      BaseType = BaseExpr->getType();
-    }
-  }
-
-  if (BaseType->isPointerType())
-    BaseType = BaseType->getPointeeType();
-
-  // We could end up with various non-record types here, such as extended 
-  // vector types or Objective-C interfaces. Just return early and let
-  // ActOnMemberReferenceExpr do the work.
-  if (!BaseType->isRecordType())
-    return move(Base);
-
-  SS.setRange(BaseExpr->getSourceRange());
-  SS.setScopeRep(
-    NestedNameSpecifier::Create(Context, 0, false, BaseType.getTypePtr())
-    );
-
-  if (S)
-    ActOnCXXEnterDeclaratorScope(S,SS);
-  return move(Base);
-}
-
-void Sema::ActOnCXXExitMemberScope(Scope *S, const CXXScopeSpec &SS) {
-  if (S && SS.isSet())
-    ActOnCXXExitDeclaratorScope(S,SS);
-}
-
-
 /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
 /// scope or nested-name-specifier) is parsed, part of a declarator-id.
 /// After this method is called, according to [C++ 3.4.3p3], names should be

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Wed Sep  2 17:59:36 2009
@@ -1754,6 +1754,48 @@
   return E;
 }
 
+Sema::OwningExprResult 
+Sema::ActOnStartCXXMemberReference(Scope *S, ExprArg Base, SourceLocation OpLoc,
+                                   tok::TokenKind OpKind, TypeTy *&ObjectType) {
+  // Since this might be a postfix expression, get rid of ParenListExprs.
+  Base = MaybeConvertParenListExprToParenExpr(S, move(Base));
+  
+  Expr *BaseExpr = (Expr*)Base.get();
+  assert(BaseExpr && "no record expansion");
+  
+  QualType BaseType = BaseExpr->getType();
+  if (BaseType->isDependentType()) {
+    // FIXME: member of the current instantiation
+    ObjectType = BaseType.getAsOpaquePtr();
+    return move(Base);
+  }
+  
+  // C++ [over.match.oper]p8:
+  //   [...] When operator->returns, the operator-> is applied  to the value 
+  //   returned, with the original second operand.
+  if (OpKind == tok::arrow) {
+    while (BaseType->isRecordType()) {
+      Base = BuildOverloadedArrowExpr(S, move(Base), BaseExpr->getExprLoc());
+      BaseExpr = (Expr*)Base.get();
+      if (BaseExpr == NULL)
+        return ExprError();
+      BaseType = BaseExpr->getType();
+    }
+  }
+  
+  if (BaseType->isPointerType())
+    BaseType = BaseType->getPointeeType();
+  
+  // We could end up with various non-record types here, such as extended 
+  // vector types or Objective-C interfaces. Just return early and let
+  // ActOnMemberReferenceExpr do the work.
+  if (!BaseType->isRecordType())
+    return move(Base);
+    
+  ObjectType = BaseType.getAsOpaquePtr();
+  return move(Base);  
+}
+
 Sema::OwningExprResult
 Sema::ActOnDestructorReferenceExpr(Scope *S, ExprArg Base,
                                    SourceLocation OpLoc,

Modified: cfe/trunk/lib/Sema/SemaLookup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLookup.cpp?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaLookup.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLookup.cpp Wed Sep  2 17:59:36 2009
@@ -1004,8 +1004,9 @@
                           LookupNameKind NameKind, bool RedeclarationOnly) {
   assert(LookupCtx && "Sema::LookupQualifiedName requires a lookup context");
   
-  if (!Name) return LookupResult::CreateLookupResult(Context, 0);
-
+  if (!Name) 
+    return LookupResult::CreateLookupResult(Context, 0);
+  
   // If we're performing qualified name lookup (e.g., lookup into a
   // struct), find fields as part of ordinary name lookup.
   unsigned IDNS
@@ -1013,16 +1014,25 @@
                                                 getLangOptions().CPlusPlus);
   if (NameKind == LookupOrdinaryName)
     IDNS |= Decl::IDNS_Member;
-
+  
+  // Make sure that the declaration context is complete.
+  assert((!isa<TagDecl>(LookupCtx) ||
+          LookupCtx->isDependentContext() ||
+          cast<TagDecl>(LookupCtx)->isDefinition() ||
+          Context.getTypeDeclType(cast<TagDecl>(LookupCtx))->getAs<TagType>()
+            ->isBeingDefined()) &&
+         "Declaration context must already be complete!");
+    
   // Perform qualified name lookup into the LookupCtx.
   DeclContext::lookup_iterator I, E;
   for (llvm::tie(I, E) = LookupCtx->lookup(Name); I != E; ++I)
     if (isAcceptableLookupResult(*I, NameKind, IDNS))
       return LookupResult::CreateLookupResult(Context, I, E);
 
-  // If this isn't a C++ class or we aren't allowed to look into base
-  // classes, we're done.
-  if (RedeclarationOnly || !isa<CXXRecordDecl>(LookupCtx))
+  // If this isn't a C++ class, we aren't allowed to look into base
+  // classes, we're done, or the lookup context is dependent, we're done.
+  if (RedeclarationOnly || !isa<CXXRecordDecl>(LookupCtx) || 
+      LookupCtx->isDependentContext())
     return LookupResult::CreateLookupResult(Context, 0);
 
   // Perform lookup into our base classes.
@@ -1152,24 +1162,9 @@
     if (DeclContext *DC = computeDeclContext(*SS, EnteringContext)) {
       // We have resolved the scope specifier to a particular declaration 
       // contex, and will perform name lookup in that context.
-      
-      if (DC->isDependentContext()) {
-        // If this is a dependent context, then we are looking for a member of
-        // the current instantiation. This is a narrow search that looks into
-        // just the described declaration context (C++0x [temp.dep.type]).
-        unsigned IDNS = getIdentifierNamespacesFromLookupNameKind(NameKind, 
-                                                                  true);
-        DeclContext::lookup_iterator I, E;
-        for (llvm::tie(I, E) = DC->lookup(Name); I != E; ++I)
-          if (isAcceptableLookupResult(*I, NameKind, IDNS))
-            return LookupResult::CreateLookupResult(Context, I, E);
-      }
-      
-      // Qualified name lookup into the named declaration context.
-      // The declaration context must be complete.
-      if (RequireCompleteDeclContext(*SS))
+      if (!DC->isDependentContext() && RequireCompleteDeclContext(*SS))
         return LookupResult::CreateLookupResult(Context, 0);
-            
+                                    
       return LookupQualifiedName(DC, Name, NameKind, RedeclarationOnly);
     }
 

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Wed Sep  2 17:59:36 2009
@@ -21,122 +21,215 @@
 
 using namespace clang;
 
-/// isTemplateName - Determines whether the identifier II is a
-/// template name in the current scope, and returns the template
-/// declaration if II names a template. An optional CXXScope can be
-/// passed to indicate the C++ scope in which the identifier will be
-/// found. 
-TemplateNameKind Sema::isTemplateName(const IdentifierInfo &II, Scope *S,
-                                      const CXXScopeSpec *SS,
-                                      bool EnteringContext,
-                                      TemplateTy &TemplateResult) {                                      
-  LookupResult Found = LookupParsedName(S, SS, &II, LookupOrdinaryName, 
-                                        false, false, SourceLocation(),
-                                        EnteringContext);
+/// \brief Determine whether the declaration found is acceptable as the name
+/// of a template and, if so, return that template declaration. Otherwise,
+/// returns NULL.
+static NamedDecl *isAcceptableTemplateName(ASTContext &Context, NamedDecl *D) {
+  if (!D)
+    return 0;
   
-  // FIXME: Cope with ambiguous name-lookup results.
-  assert(!Found.isAmbiguous() && 
-         "Cannot handle template name-lookup ambiguities");
+  if (isa<TemplateDecl>(D))
+    return D;
   
-  NamedDecl *IIDecl = Found;
+  if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(D)) {
+    // C++ [temp.local]p1:
+    //   Like normal (non-template) classes, class templates have an
+    //   injected-class-name (Clause 9). The injected-class-name
+    //   can be used with or without a template-argument-list. When
+    //   it is used without a template-argument-list, it is
+    //   equivalent to the injected-class-name followed by the
+    //   template-parameters of the class template enclosed in
+    //   <>. When it is used with a template-argument-list, it
+    //   refers to the specified class template specialization,
+    //   which could be the current specialization or another
+    //   specialization.
+    if (Record->isInjectedClassName()) {
+      Record = cast<CXXRecordDecl>(Record->getCanonicalDecl());
+      if (Record->getDescribedClassTemplate())
+        return Record->getDescribedClassTemplate();
+
+      if (ClassTemplateSpecializationDecl *Spec
+            = dyn_cast<ClassTemplateSpecializationDecl>(Record))
+        return Spec->getSpecializedTemplate();
+    }
+    
+    return 0;
+  }
   
-  TemplateNameKind TNK = TNK_Non_template;
-  TemplateDecl *Template = 0;
-
-  if (IIDecl) {
-    if ((Template = dyn_cast<TemplateDecl>(IIDecl))) {
-      if (isa<FunctionTemplateDecl>(IIDecl))
-        TNK = TNK_Function_template;
-      else if (isa<ClassTemplateDecl>(IIDecl) || 
-               isa<TemplateTemplateParmDecl>(IIDecl))
-        TNK = TNK_Type_template;
-      else
-        assert(false && "Unknown template declaration kind");
-    } else if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(IIDecl)) {
-      // C++ [temp.local]p1:
-      //   Like normal (non-template) classes, class templates have an
-      //   injected-class-name (Clause 9). The injected-class-name
-      //   can be used with or without a template-argument-list. When
-      //   it is used without a template-argument-list, it is
-      //   equivalent to the injected-class-name followed by the
-      //   template-parameters of the class template enclosed in
-      //   <>. When it is used with a template-argument-list, it
-      //   refers to the specified class template specialization,
-      //   which could be the current specialization or another
-      //   specialization.
-      if (Record->isInjectedClassName()) {
-        Record = cast<CXXRecordDecl>(Record->getCanonicalDecl());
-        if ((Template = Record->getDescribedClassTemplate()))
-          TNK = TNK_Type_template;
-        else if (ClassTemplateSpecializationDecl *Spec
-                   = dyn_cast<ClassTemplateSpecializationDecl>(Record)) {
-          Template = Spec->getSpecializedTemplate();
-          TNK = TNK_Type_template;
-        }
+  OverloadedFunctionDecl *Ovl = dyn_cast<OverloadedFunctionDecl>(D);
+  if (!Ovl)
+    return 0;
+  
+  for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(),
+                                              FEnd = Ovl->function_end();
+       F != FEnd; ++F) {
+    if (FunctionTemplateDecl *FuncTmpl = dyn_cast<FunctionTemplateDecl>(*F)) {
+      // We've found a function template. Determine whether there are
+      // any other function templates we need to bundle together in an
+      // OverloadedFunctionDecl
+      for (++F; F != FEnd; ++F) {
+        if (isa<FunctionTemplateDecl>(*F))
+          break;
       }
-    } else if (OverloadedFunctionDecl *Ovl 
-                 = dyn_cast<OverloadedFunctionDecl>(IIDecl)) {
-      for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(),
-                                                  FEnd = Ovl->function_end();
-           F != FEnd; ++F) {
-        if (FunctionTemplateDecl *FuncTmpl 
-              = dyn_cast<FunctionTemplateDecl>(*F)) {
-          // We've found a function template. Determine whether there are
-          // any other function templates we need to bundle together in an
-          // OverloadedFunctionDecl
-          for (++F; F != FEnd; ++F) {
-            if (isa<FunctionTemplateDecl>(*F))
-              break;
-          }
-          
-          if (F != FEnd) {
-            // Build an overloaded function decl containing only the
-            // function templates in Ovl.
-            OverloadedFunctionDecl *OvlTemplate 
-              = OverloadedFunctionDecl::Create(Context,
-                                               Ovl->getDeclContext(),
-                                               Ovl->getDeclName());
-            OvlTemplate->addOverload(FuncTmpl);
+      
+      if (F != FEnd) {
+        // Build an overloaded function decl containing only the
+        // function templates in Ovl.
+        OverloadedFunctionDecl *OvlTemplate 
+          = OverloadedFunctionDecl::Create(Context,
+                                           Ovl->getDeclContext(),
+                                           Ovl->getDeclName());
+        OvlTemplate->addOverload(FuncTmpl);
+        OvlTemplate->addOverload(*F);
+        for (++F; F != FEnd; ++F) {
+          if (isa<FunctionTemplateDecl>(*F))
             OvlTemplate->addOverload(*F);
-            for (++F; F != FEnd; ++F) {
-              if (isa<FunctionTemplateDecl>(*F))
-                OvlTemplate->addOverload(*F);
-            }
-
-            // Form the resulting TemplateName
-            if (SS && SS->isSet() && !SS->isInvalid()) {
-              NestedNameSpecifier *Qualifier 
-                = static_cast<NestedNameSpecifier *>(SS->getScopeRep());
-              TemplateResult 
-                = TemplateTy::make(Context.getQualifiedTemplateName(Qualifier, 
-                                                                    false,
-                                                                  OvlTemplate));              
-            } else {
-              TemplateResult = TemplateTy::make(TemplateName(OvlTemplate));
-            }
-            return TNK_Function_template;
-          }
-          
-          TNK = TNK_Function_template;
-          Template = FuncTmpl;
-          break;
         }
+        
+        return OvlTemplate;
       }
-    }
 
-    if (TNK != TNK_Non_template) {
-      if (SS && SS->isSet() && !SS->isInvalid()) {
-        NestedNameSpecifier *Qualifier 
-          = static_cast<NestedNameSpecifier *>(SS->getScopeRep());
-        TemplateResult 
-          = TemplateTy::make(Context.getQualifiedTemplateName(Qualifier, 
-                                                              false,
-                                                              Template));
-      } else
-        TemplateResult = TemplateTy::make(TemplateName(Template));
+      return FuncTmpl;
     }
   }
-  return TNK;
+  
+  return 0;
+}
+
+TemplateNameKind Sema::isTemplateName(Scope *S,
+                                      const IdentifierInfo &II, 
+                                      SourceLocation IdLoc,
+                                      const CXXScopeSpec *SS,
+                                      TypeTy *ObjectTypePtr,
+                                      bool EnteringContext,
+                                      TemplateTy &TemplateResult) {
+  // Determine where to perform name lookup
+  DeclContext *LookupCtx = 0;
+  bool isDependent = false;
+  if (ObjectTypePtr) {
+    // This nested-name-specifier occurs in a member access expression, e.g.,
+    // x->B::f, and we are looking into the type of the object.
+    assert((!SS || !SS->isSet()) && 
+           "ObjectType and scope specifier cannot coexist");
+    QualType ObjectType = QualType::getFromOpaquePtr(ObjectTypePtr);
+    LookupCtx = computeDeclContext(ObjectType);
+    isDependent = ObjectType->isDependentType();
+  } else if (SS && SS->isSet()) {
+    // This nested-name-specifier occurs after another nested-name-specifier,
+    // so long into the context associated with the prior nested-name-specifier.
+
+    LookupCtx = computeDeclContext(*SS, EnteringContext);
+    isDependent = isDependentScopeSpecifier(*SS);
+  }
+  
+  LookupResult Found;
+  bool ObjectTypeSearchedInScope = false;
+  if (LookupCtx) {
+    // Perform "qualified" name lookup into the declaration context we
+    // computed, which is either the type of the base of a member access
+    // expression or the declaration context associated with a prior 
+    // nested-name-specifier.
+
+    // The declaration context must be complete.
+    if (!LookupCtx->isDependentContext() && RequireCompleteDeclContext(*SS))
+      return TNK_Non_template;
+    
+    Found = LookupQualifiedName(LookupCtx, &II, LookupOrdinaryName);
+    
+    if (ObjectTypePtr && Found.getKind() == LookupResult::NotFound) {
+      // C++ [basic.lookup.classref]p1:
+      //   In a class member access expression (5.2.5), if the . or -> token is
+      //   immediately followed by an identifier followed by a <, the 
+      //   identifier must be looked up to determine whether the < is the 
+      //   beginning of a template argument list (14.2) or a less-than operator.
+      //   The identifier is first looked up in the class of the object 
+      //   expression. If the identifier is not found, it is then looked up in 
+      //   the context of the entire postfix-expression and shall name a class
+      //   or function template.
+      //
+      // FIXME: When we're instantiating a template, do we actually have to
+      // look in the scope of the template? Seems fishy...
+      Found = LookupName(S, &II, LookupOrdinaryName);
+      ObjectTypeSearchedInScope = true;
+    }
+  } else if (isDependent) {
+    // We cannot look into a dependent object type or 
+    return TNK_Non_template;
+  } else {
+    // Perform unqualified name lookup in the current scope.
+    Found = LookupName(S, &II, LookupOrdinaryName);
+  }
+  
+  // FIXME: Cope with ambiguous name-lookup results.
+  assert(!Found.isAmbiguous() && 
+         "Cannot handle template name-lookup ambiguities");
+
+  NamedDecl *Template = isAcceptableTemplateName(Context, Found);
+  if (!Template)
+    return TNK_Non_template;
+
+  if (ObjectTypePtr && !ObjectTypeSearchedInScope) {
+    // C++ [basic.lookup.classref]p1:
+    //   [...] If the lookup in the class of the object expression finds a 
+    //   template, the name is also looked up in the context of the entire
+    //   postfix-expression and [...]
+    //
+    LookupResult FoundOuter = LookupName(S, &II, LookupOrdinaryName);
+    // FIXME: Handle ambiguities in this lookup better
+    NamedDecl *OuterTemplate = isAcceptableTemplateName(Context, FoundOuter);
+    
+    if (!OuterTemplate) {
+      //   - if the name is not found, the name found in the class of the 
+      //     object expression is used, otherwise
+    } else if (!isa<ClassTemplateDecl>(OuterTemplate)) {
+      //   - if the name is found in the context of the entire 
+      //     postfix-expression and does not name a class template, the name 
+      //     found in the class of the object expression is used, otherwise
+    } else {
+      //   - if the name found is a class template, it must refer to the same
+      //     entity as the one found in the class of the object expression, 
+      //     otherwise the program is ill-formed.
+      if (OuterTemplate->getCanonicalDecl() != Template->getCanonicalDecl()) {
+        Diag(IdLoc, diag::err_nested_name_member_ref_lookup_ambiguous)
+          << &II;
+        Diag(Template->getLocation(), diag::note_ambig_member_ref_object_type)
+          << QualType::getFromOpaquePtr(ObjectTypePtr);
+        Diag(OuterTemplate->getLocation(), diag::note_ambig_member_ref_scope);
+        
+        // Recover by taking the template that we found in the object 
+        // expression's type.
+      }
+    }      
+  }
+  
+  if (SS && SS->isSet() && !SS->isInvalid()) {
+    NestedNameSpecifier *Qualifier 
+      = static_cast<NestedNameSpecifier *>(SS->getScopeRep());
+    if (OverloadedFunctionDecl *Ovl 
+          = dyn_cast<OverloadedFunctionDecl>(Template))
+      TemplateResult 
+        = TemplateTy::make(Context.getQualifiedTemplateName(Qualifier, false,
+                                                            Ovl));
+    else
+      TemplateResult 
+        = TemplateTy::make(Context.getQualifiedTemplateName(Qualifier, false,
+                                                 cast<TemplateDecl>(Template)));    
+  } else if (OverloadedFunctionDecl *Ovl 
+               = dyn_cast<OverloadedFunctionDecl>(Template)) {
+    TemplateResult = TemplateTy::make(TemplateName(Ovl));
+  } else {
+    TemplateResult = TemplateTy::make(
+                                  TemplateName(cast<TemplateDecl>(Template)));
+  }
+  
+  if (isa<ClassTemplateDecl>(Template) || 
+      isa<TemplateTemplateParmDecl>(Template))
+    return TNK_Type_template;
+  
+  assert((isa<FunctionTemplateDecl>(Template) || 
+          isa<OverloadedFunctionDecl>(Template)) &&
+         "Unhandled template kind in Sema::isTemplateName");
+  return TNK_Function_template;
 }
 
 /// DiagnoseTemplateParameterShadow - Produce a diagnostic complaining
@@ -1130,14 +1223,11 @@
 Sema::ActOnDependentTemplateName(SourceLocation TemplateKWLoc,
                                  const IdentifierInfo &Name,
                                  SourceLocation NameLoc,
-                                 const CXXScopeSpec &SS) {
-  if (!SS.isSet() || SS.isInvalid())
-    return TemplateTy();
-
-  NestedNameSpecifier *Qualifier 
-    = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
-
-  if (computeDeclContext(SS, false)) {
+                                 const CXXScopeSpec &SS,
+                                 TypeTy *ObjectType) {
+  if ((ObjectType && 
+       computeDeclContext(QualType::getFromOpaquePtr(ObjectType))) ||
+      (SS.isSet() && computeDeclContext(SS, false))) {
     // C++0x [temp.names]p5:
     //   If a name prefixed by the keyword template is not the name of
     //   a template, the program is ill-formed. [Note: the keyword
@@ -1155,7 +1245,8 @@
     // "template" keyword is now permitted). We follow the C++0x
     // rules, even in C++03 mode, retroactively applying the DR.
     TemplateTy Template;
-    TemplateNameKind TNK = isTemplateName(Name, 0, &SS, false, Template);
+    TemplateNameKind TNK = isTemplateName(0, Name, NameLoc, &SS, ObjectType, 
+                                          false, Template);
     if (TNK == TNK_Non_template) {
       Diag(NameLoc, diag::err_template_kw_refers_to_non_template)
         << &Name;
@@ -1165,6 +1256,13 @@
     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));
 }
 

Modified: cfe/trunk/lib/Sema/TreeTransform.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/TreeTransform.h (original)
+++ cfe/trunk/lib/Sema/TreeTransform.h Wed Sep  2 17:59:36 2009
@@ -1454,7 +1454,10 @@
     OwningExprResult Base = move(BaseE);
     tok::TokenKind OpKind = IsArrow? tok::arrow : tok::period;
     CXXScopeSpec SS;
-    Base = SemaRef.ActOnCXXEnterMemberScope(0, SS, move(Base), OpKind);
+    Sema::TypeTy *ObjectType = 0;
+    
+    Base = SemaRef.ActOnStartCXXMemberReference(0, move(Base), OperatorLoc,
+                                                OpKind, ObjectType);
     if (Base.isInvalid())
       return SemaRef.ExprError();
     
@@ -1463,7 +1466,6 @@
                                             MemberLoc,
                                             Name,
                                     /*FIXME?*/Sema::DeclPtrTy::make((Decl*)0));
-    SemaRef.ActOnCXXExitMemberScope(0, SS);
     return move(Base);
   }
 
@@ -4410,6 +4412,7 @@
   return static_cast<NestedNameSpecifier *>(
                     SemaRef.ActOnCXXNestedNameSpecifier(0, SS, Range.getEnd(), 
                                                         Range.getEnd(), II,
+                                                        /*FIXME:ObjectType=*/0,
                                                         false));
 }
 
@@ -4467,7 +4470,11 @@
   SS.setRange(SourceRange(getDerived().getBaseLocation()));
   SS.setScopeRep(Qualifier);
   Sema::TemplateTy Template;
-  TemplateNameKind TNK = SemaRef.isTemplateName(II, 0, &SS, false, 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)

Modified: cfe/trunk/test/SemaCXX/nested-name-spec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/nested-name-spec.cpp?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/test/SemaCXX/nested-name-spec.cpp (original)
+++ cfe/trunk/test/SemaCXX/nested-name-spec.cpp Wed Sep  2 17:59:36 2009
@@ -13,7 +13,7 @@
 }
 
 A:: ; // expected-error {{expected unqualified-id}}
-::A::ax::undef ex3; // expected-error {{expected a class or namespace}} expected-error {{unknown type name 'undef'}}
+::A::ax::undef ex3; // expected-error {{no member named}} expected-error {{unknown type name 'undef'}}
 A::undef1::undef2 ex4; // expected-error {{no member named 'undef1'}} expected-error {{unknown type name 'undef2'}}
 
 int A::C::Ag1() { return 0; }

Modified: cfe/trunk/test/SemaCXX/qual-id-test.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/qual-id-test.cpp?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/test/SemaCXX/qual-id-test.cpp (original)
+++ cfe/trunk/test/SemaCXX/qual-id-test.cpp Wed Sep  2 17:59:36 2009
@@ -3,7 +3,7 @@
 {
     namespace B
     {
-        struct base
+        struct base // expected-note{{object type}}
         {
             void x() {}
             void y() {}
@@ -82,13 +82,31 @@
         i.foo(); // expected-error{{member reference base type 'int' is not a structure or union}}
     }
 
+    void fun4a() {
+      A::sub *a;
+      
+      typedef A::member base; // expected-note{{current scope}}
+      a->base::x(); // expected-error{{ambiguous}}      
+    }
+
+    void fun4b() {
+      A::sub *a;
+      
+      typedef A::B::base base;
+      a->base::x();
+    }
+  
     template<typename T>
-    void fun4()
+    void fun5()
     {
         T a;
         a.x();
         a->foo();
 
+#if 0
+        // FIXME: We need the notion of identifiers as dependent 
+        // nested-name-specifiers without a prefix for this code to work.
+      
         // Things that work for the wrong reason
         a.A::sub::x();
         a.A::B::base::x();
@@ -98,9 +116,20 @@
         a.bad::x();
 
         // Things that fail, but shouldn't
-        a.sub::x(); // expected-error{{use of undeclared identifier 'sub'}}
-        a.base::x(); // expected-error{{use of undeclared identifier 'base'}}
-        a.B::base::x(); // expected-error{{use of undeclared identifier 'B'}}
-        a->member::foo(); // expected-error{{use of undeclared identifier 'member'}}
+        a.sub::x(); // xpected-error{{use of undeclared identifier 'sub'}}
+        a.base::x(); // xpected-error{{use of undeclared identifier 'base'}}
+        a.B::base::x(); // xpected-error{{use of undeclared identifier 'B'}}
+        a->member::foo(); // xpected-error{{use of undeclared identifier 'member'}}
+#endif
     }
 }
+
+// PR4703
+struct a {
+  int a;
+  static int sa;
+};
+
+a a;
+
+int a::sa = a.a;

Modified: cfe/trunk/test/SemaTemplate/member-function-template.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/member-function-template.cpp?rev=80843&r1=80842&r2=80843&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/member-function-template.cpp (original)
+++ cfe/trunk/test/SemaTemplate/member-function-template.cpp Wed Sep  2 17:59:36 2009
@@ -48,3 +48,4 @@
 
 // PR4608
 class A { template <class x> x a(x z) { return z+y; } int y; };
+





More information about the cfe-commits mailing list