[cfe-commits] r90289 - in /cfe/trunk: lib/Sema/Sema.h lib/Sema/SemaExpr.cpp lib/Sema/SemaTemplate.cpp test/CXX/class/class.mfct/class.mfct.non-static/ test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp

John McCall rjmccall at apple.com
Tue Dec 1 19:53:29 PST 2009


Author: rjmccall
Date: Tue Dec  1 21:53:29 2009
New Revision: 90289

URL: http://llvm.org/viewvc/llvm-project?rev=90289&view=rev
Log:
Stop trying to analyze class-hierarchies for dependently-scoped id-expressions;
there's nothing interesting we can say now that we're correctly not requiring
the qualifier to name a known base class in dependent contexts.

Require scope specifiers on member access expressions to name complete types
if they're not dependent;  delay lookup when they are dependent.

Use more appropriate diagnostics when qualified implicit member access
expressions find declarations from unrelated classes.

Added:
    cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp
Modified:
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/   (props changed)

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

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Tue Dec  1 21:53:29 2009
@@ -1438,7 +1438,7 @@
   OwningExprResult ActOnDependentIdExpression(const CXXScopeSpec &SS,
                                               DeclarationName Name,
                                               SourceLocation NameLoc,
-                                              bool CheckForImplicitMember,
+                                              bool isAddressOfOperand,
                                 const TemplateArgumentListInfo *TemplateArgs);
   
   OwningExprResult BuildDeclRefExpr(NamedDecl *D, QualType Ty,
@@ -1549,8 +1549,7 @@
                                     DeclPtrTy ObjCImpDecl);
 
   bool CheckQualifiedMemberReference(Expr *BaseExpr, QualType BaseType,
-                                     NestedNameSpecifier *Qualifier,
-                                     SourceRange QualifierRange,
+                                     const CXXScopeSpec &SS,
                                      const LookupResult &R);
 
   OwningExprResult ActOnDependentMemberExpr(ExprArg Base,

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Tue Dec  1 21:53:29 2009
@@ -901,10 +901,8 @@
   // Determine whether this is a member of an unknown specialization;
   // we need to handle these differently.
   if (SS.isSet() && IsDependentIdExpression(*this, SS)) {
-    bool CheckForImplicitMember = !isAddressOfOperand;
-
     return ActOnDependentIdExpression(SS, Name, NameLoc,
-                                      CheckForImplicitMember,
+                                      isAddressOfOperand,
                                       TemplateArgs);
   }
 
@@ -2140,15 +2138,18 @@
 static void DiagnoseQualifiedMemberReference(Sema &SemaRef,
                                              Expr *BaseExpr,
                                              QualType BaseType,
-                                             NestedNameSpecifier *Qualifier,
-                                             SourceRange QualifierRange,
+                                             const CXXScopeSpec &SS,
                                              const LookupResult &R) {
-  DeclContext *DC = R.getRepresentativeDecl()->getDeclContext();
+  // If this is an implicit member access, use a different set of
+  // diagnostics.
+  if (!BaseExpr)
+    return DiagnoseInstanceReference(SemaRef, SS, R);
 
   // FIXME: this is an exceedingly lame diagnostic for some of the more
   // complicated cases here.
+  DeclContext *DC = R.getRepresentativeDecl()->getDeclContext();
   SemaRef.Diag(R.getNameLoc(), diag::err_not_direct_base_or_virtual)
-    << QualifierRange << DC << BaseType;
+    << SS.getRange() << DC << BaseType;
 }
 
 // Check whether the declarations we found through a nested-name
@@ -2165,8 +2166,7 @@
 // we actually pick through overload resolution is from a superclass.
 bool Sema::CheckQualifiedMemberReference(Expr *BaseExpr,
                                          QualType BaseType,
-                                         NestedNameSpecifier *Qualifier,
-                                         SourceRange QualifierRange,
+                                         const CXXScopeSpec &SS,
                                          const LookupResult &R) {
   const RecordType *BaseRT = BaseType->getAs<RecordType>();
   if (!BaseRT) {
@@ -2195,8 +2195,7 @@
       return false;
   }
 
-  DiagnoseQualifiedMemberReference(*this, BaseExpr, BaseType,
-                                     Qualifier, QualifierRange, R);
+  DiagnoseQualifiedMemberReference(*this, BaseExpr, BaseType, SS, R);
   return true;
 }
 
@@ -2216,6 +2215,12 @@
     // nested-name-specifier.
     DC = SemaRef.computeDeclContext(SS, false);
 
+    if (SemaRef.RequireCompleteDeclContext(SS)) {
+      SemaRef.Diag(SS.getRange().getEnd(), diag::err_typecheck_incomplete_tag)
+        << SS.getRange() << DC;
+      return true;
+    }
+
     assert(DC && "Cannot handle non-computable dependent contexts in lookup");
       
     if (!isa<TypeDecl>(DC)) {
@@ -2240,7 +2245,8 @@
                                const TemplateArgumentListInfo *TemplateArgs) {
   Expr *Base = BaseArg.takeAs<Expr>();
 
-  if (BaseType->isDependentType())
+  if (BaseType->isDependentType() ||
+      (SS.isSet() && isDependentScopeSpecifier(SS)))
     return ActOnDependentMemberExpr(ExprArg(*this, Base), BaseType,
                                     IsArrow, OpLoc,
                                     SS, FirstQualifierInScope,
@@ -2311,11 +2317,11 @@
     return ExprError();
   }
 
-  // We can't always diagnose the problem yet: it's permitted for
-  // lookup to find things from an invalid context as long as they
-  // don't get picked by overload resolution.
-  if (SS.isSet() && CheckQualifiedMemberReference(BaseExpr, BaseType,
-                                                  Qualifier, SS.getRange(), R))
+  // Diagnose qualified lookups that find only declarations from a
+  // non-base type.  Note that it's okay for lookup to find
+  // declarations from a non-base type as long as those aren't the
+  // ones picked by overload resolution.
+  if (SS.isSet() && CheckQualifiedMemberReference(BaseExpr, BaseType, SS, R))
     return ExprError();
 
   // Construct an unresolved result if we in fact got an unresolved

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Tue Dec  1 21:53:29 2009
@@ -248,119 +248,23 @@
   }
 }
 
-/// Constructs a full type for the given nested-name-specifier.
-static QualType GetTypeForQualifier(ASTContext &Context,
-                                    NestedNameSpecifier *Qualifier) {
-  // Three possibilities:
-
-  // 1.  A namespace (global or not).
-  assert(!Qualifier->getAsNamespace() && "can't construct type for namespace");
-
-  // 2.  A type (templated or not).
-  Type *Ty = Qualifier->getAsType();
-  if (Ty) return QualType(Ty, 0);
-
-  // 3.  A dependent identifier.
-  assert(Qualifier->getAsIdentifier());
-  return Context.getTypenameType(Qualifier->getPrefix(),
-                                 Qualifier->getAsIdentifier());
-}
-
-static bool HasDependentTypeAsBase(ASTContext &Context,
-                                   CXXRecordDecl *Record,
-                                   CanQualType T) {
-  for (CXXRecordDecl::base_class_iterator I = Record->bases_begin(),
-         E = Record->bases_end(); I != E; ++I) {
-    CanQualType BaseT = Context.getCanonicalType((*I).getType());
-    if (BaseT == T)
-      return true;
-
-    // We have to recurse here to cover some really bizarre cases.
-    // Obviously, we can only have the dependent type as an indirect
-    // base class through a dependent base class, and usually it's
-    // impossible to know which instantiation a dependent base class
-    // will have.  But!  If we're actually *inside* the dependent base
-    // class, then we know its instantiation and can therefore be
-    // reasonably expected to look into it.
-
-    // template <class T> class A : Base<T> {
-    //   class Inner : A<T> {
-    //     void foo() {
-    //       Base<T>::foo(); // statically known to be an implicit member
-    //                          reference
-    //     }
-    //   };
-    // };
-
-    CanQual<RecordType> RT = BaseT->getAs<RecordType>();
-
-    // Base might be a dependent member type, in which case we
-    // obviously can't look into it.
-    if (!RT) continue;
-
-    CXXRecordDecl *BaseRecord = cast<CXXRecordDecl>(RT->getDecl());
-    if (BaseRecord->isDefinition() &&
-        HasDependentTypeAsBase(Context, BaseRecord, T))
-      return true;
-  }
-
-  return false;
-}
-
-/// Checks whether the given dependent nested-name specifier
-/// introduces an implicit member reference.  This is only true if the
-/// nested-name specifier names a type identical to one of the current
-/// instance method's context's (possibly indirect) base classes.
-static bool IsImplicitDependentMemberReference(Sema &SemaRef,
-                                               NestedNameSpecifier *Qualifier,
-                                               QualType &ThisType) {
-  // If the context isn't a C++ method, then it isn't an implicit
-  // member reference.
-  CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(SemaRef.CurContext);
-  if (!MD || MD->isStatic())
-    return false;
-
-  ASTContext &Context = SemaRef.Context;
-
-  // We want to check whether the method's context is known to inherit
-  // from the type named by the nested name specifier.  The trivial
-  // case here is:
-  //   template <class T> class Base { ... };
-  //   template <class T> class Derived : Base<T> {
-  //     void foo() {
-  //       Base<T>::foo();
-  //     }
-  //   };
-
-  QualType QT = GetTypeForQualifier(Context, Qualifier);
-  CanQualType T = Context.getCanonicalType(QT);
-
-  // And now, just walk the non-dependent type hierarchy, trying to
-  // find the given type as a literal base class.
-  CXXRecordDecl *Record = cast<CXXRecordDecl>(MD->getParent());
-  if (Context.getCanonicalType(Context.getTypeDeclType(Record)) == T || 
-      HasDependentTypeAsBase(Context, Record, T)) {
-    ThisType = MD->getThisType(Context);
-    return true;
-  }
-
-  return false;
-}
-
-/// ActOnDependentIdExpression - Handle a dependent declaration name
-/// that was just parsed.
+/// ActOnDependentIdExpression - Handle a dependent id-expression that
+/// was just parsed.  This is only possible with an explicit scope
+/// specifier naming a dependent type.
 Sema::OwningExprResult
 Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS,
                                  DeclarationName Name,
                                  SourceLocation NameLoc,
-                                 bool CheckForImplicitMember,
+                                 bool isAddressOfOperand,
                            const TemplateArgumentListInfo *TemplateArgs) {
   NestedNameSpecifier *Qualifier
     = static_cast<NestedNameSpecifier*>(SS.getScopeRep());
     
-  QualType ThisType;
-  if (CheckForImplicitMember &&
-      IsImplicitDependentMemberReference(*this, Qualifier, ThisType)) {
+  if (!isAddressOfOperand &&
+      isa<CXXMethodDecl>(CurContext) &&
+      cast<CXXMethodDecl>(CurContext)->isInstance()) {
+    QualType ThisType = cast<CXXMethodDecl>(CurContext)->getThisType(Context);
+    
     // Since the 'this' expression is synthesized, we don't need to
     // perform the double-lookup check.
     NamedDecl *FirstQualifierInScope = 0;

Propchange: cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/

------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Tue Dec  1 21:53:29 2009
@@ -0,0 +1 @@
+Output

Added: cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp?rev=90289&view=auto

==============================================================================
--- cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp (added)
+++ cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp Tue Dec  1 21:53:29 2009
@@ -0,0 +1,93 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+// [class.mfct.non-static]p3:
+//   When an id-expression (5.1) that is not part of a class member
+//   access syntax (5.2.5) and not used to form a pointer to member
+//   (5.3.1) is used in the body of a non-static member function of
+//   class X, if name lookup (3.4.1) resolves the name in the
+//   id-expression to a non-static non-type member of some class C,
+//   the id-expression is transformed into a class member access
+//   expression (5.2.5) using (*this) (9.3.2) as the
+//   postfix-expression to the left of the . operator. [ Note: if C is
+//   not X or a base class of X, the class member access expression is
+//   ill-formed. --end note] Similarly during name lookup, when an
+//   unqualified-id (5.1) used in the definition of a member function
+//   for class X resolves to a static member, an enumerator or a
+//   nested type of class X or of a base class of X, the
+//   unqualified-id is transformed into a qualified-id (5.1) in which
+//   the nested-name-specifier names the class of the member function.
+
+namespace test0 {
+  class A {
+    int data_member;
+    int instance_method();
+    static int static_method();
+
+    bool test() {
+      return data_member + instance_method() < static_method();
+    }
+  };
+}
+
+namespace test1 {
+  struct Opaque1 {}; struct Opaque2 {}; struct Opaque3 {};
+
+  struct A {
+    void foo(Opaque1); // expected-note {{candidate}}
+    void foo(Opaque2); // expected-note {{candidate}}
+    void test();
+  };
+
+  struct B : A {
+    
+  };
+
+  void A::test() {
+    B::foo(Opaque1());
+    B::foo(Opaque2());
+    B::foo(Opaque3()); // expected-error {{no matching member function}}
+  }
+}
+
+namespace test2 {
+  class Unrelated {
+    void foo();
+  };
+
+  template <class T> struct B;
+  template <class T> struct C;
+
+  template <class T> struct A {
+    void foo();
+
+    void test0() {
+      Unrelated::foo(); // expected-error {{call to non-static member function without an object argument}}
+    }
+
+    void test1() {
+      B<T>::foo();
+    }
+
+    static void test2() {
+      B<T>::foo(); // expected-error {{call to non-static member function without an object argument}}
+    }
+
+    void test3() {
+      C<T>::foo(); // expected-error {{no member named 'foo'}}
+    }
+  };
+
+  template <class T> struct B : A<T> {
+  };
+
+  template <class T> struct C {
+  };
+
+  int test() {
+    A<int> a;
+    a.test0(); // no instantiation note here, decl is ill-formed
+    a.test1();
+    a.test2(); // expected-note {{in instantiation}}
+    a.test3(); // expected-note {{in instantiation}}
+  }
+}





More information about the cfe-commits mailing list