]> granicus.if.org Git - clang/commitdiff
When a member reference expression includes a qualifier on the member
authorDouglas Gregor <dgregor@apple.com>
Wed, 26 Aug 2009 22:36:53 +0000 (22:36 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 26 Aug 2009 22:36:53 +0000 (22:36 +0000)
name, e.g.,

  x->Base::f()

retain the qualifier (and its source range information) in a new
subclass of MemberExpr called CXXQualifiedMemberExpr. Provide
construction, transformation, profiling, printing, etc., for this new
expression type.

When a virtual function is called via a qualified name, don't emit a
virtual call. Instead, call that function directly. Mike, could you
add a CodeGen test for this, too?

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

13 files changed:
include/clang/AST/Expr.h
include/clang/AST/ExprCXX.h
include/clang/AST/StmtNodes.def
lib/AST/Expr.cpp
lib/AST/StmtPrinter.cpp
lib/AST/StmtProfile.cpp
lib/Analysis/GRExprEngine.cpp
lib/CodeGen/CGCXX.cpp
lib/CodeGen/CGExpr.cpp
lib/Sema/SemaChecking.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/TreeTransform.h
test/CXX/class.derived/class.virtual/p12.cpp [new file with mode: 0644]

index ff16480c059be37889a2fb97644b661e6f1a8b20..827877e79807d9e981974f4c7b4efa2682992b0d 100644 (file)
@@ -33,6 +33,7 @@ namespace clang {
   class BlockDecl;
   class CXXOperatorCallExpr;
   class CXXMemberCallExpr;
+  class CXXQualifiedMemberExpr;
 
 /// Expr - This represents one expression.  Note that Expr's are subclasses of
 /// Stmt.  This allows an expression to be transparently used any place a Stmt
@@ -1054,6 +1055,14 @@ class MemberExpr : public Expr {
   
   /// IsArrow - True if this is "X->F", false if this is "X.F".
   bool IsArrow;
+  
+protected:
+  MemberExpr(StmtClass SC, Expr *base, bool isarrow, NamedDecl *memberdecl, 
+             SourceLocation l, QualType ty) 
+    : Expr(SC, ty, 
+           base->isTypeDependent(), base->isValueDependent()),
+  Base(base), MemberDecl(memberdecl), MemberLoc(l), IsArrow(isarrow) {}
+  
 public:
   MemberExpr(Expr *base, bool isarrow, NamedDecl *memberdecl, SourceLocation l,
              QualType ty) 
@@ -1094,9 +1103,11 @@ public:
   virtual SourceLocation getExprLoc() const { return MemberLoc; }
 
   static bool classof(const Stmt *T) { 
-    return T->getStmtClass() == MemberExprClass; 
+    return T->getStmtClass() == MemberExprClass ||
+      T->getStmtClass() == CXXQualifiedMemberExprClass;
   }
   static bool classof(const MemberExpr *) { return true; }
+  static bool classof(const CXXQualifiedMemberExpr *) { return true; }
   
   // Iterators
   virtual child_iterator child_begin();
index dfc32c7f97ca808f7e2a50291d08d2ee3d76896d..7e21a73cc28654c0135a67fa6d8ed7dba86ec8ab 100644 (file)
@@ -1285,7 +1285,41 @@ public:
   virtual child_iterator child_end();
 };
 
-/// \brief 
+/// \brief Represents a C++ member access expression that was written using
+/// a qualified name, e.g., "x->Base::f()".
+class CXXQualifiedMemberExpr : public MemberExpr {
+  /// QualifierRange - The source range that covers the
+  /// nested-name-specifier.
+  SourceRange QualifierRange;
+  
+  /// \brief The nested-name-specifier that qualifies this declaration
+  /// name.
+  NestedNameSpecifier *Qualifier;
+  
+public:
+  CXXQualifiedMemberExpr(Expr *base, bool isarrow, NestedNameSpecifier *Qual,
+                         SourceRange QualRange, NamedDecl *memberdecl, 
+                         SourceLocation l, QualType ty) 
+    : MemberExpr(CXXQualifiedMemberExprClass, base, isarrow, memberdecl, l, ty),
+      QualifierRange(QualRange), Qualifier(Qual) { }
+
+  /// \brief Retrieve the source range of the nested-name-specifier that 
+  /// qualifies the member name.
+  SourceRange getQualifierRange() const { return QualifierRange; }
+  
+  /// \brief Retrieve the nested-name-specifier that qualifies the
+  /// member reference expression.
+  NestedNameSpecifier *getQualifier() const { return Qualifier; }
+  
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == CXXQualifiedMemberExprClass;
+  }
+  static bool classof(const CXXQualifiedMemberExpr *) { return true; }  
+};
+  
+/// \brief Represents a C++ member access expression where the actual member
+/// referenced could not be resolved, e.g., because the base expression or the
+/// member name was dependent.
 class CXXUnresolvedMemberExpr : public Expr {
   /// \brief The expression for the base pointer or class reference,
   /// e.g., the \c x in x.f.
index af345991f1e69ddc406ffd4579616366ecac4d92..72dfa612c02c0d764575fed49ea122b9899ad3bb 100644 (file)
@@ -134,6 +134,7 @@ EXPR(CXXBindTemporaryExpr   , Expr)
 EXPR(CXXExprWithTemporaries , Expr)
 EXPR(CXXTemporaryObjectExpr , CXXConstructExpr)
 EXPR(CXXUnresolvedConstructExpr, Expr)
+EXPR(CXXQualifiedMemberExpr, MemberExpr)
 EXPR(CXXUnresolvedMemberExpr, Expr)
 
 // Obj-C Expressions.
index 7cc436c7a901669b2d2f9394695485dde9b82da9..46532cafae64f1952204f71dbddcf13e454e25b3 100644 (file)
@@ -497,6 +497,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
   }
 
   case MemberExprClass:
+  case CXXQualifiedMemberExprClass:
     // If the base pointer or element is to a volatile pointer/field, accessing
     // it is a side effect.
     if (getType().isVolatileQualified())
@@ -684,7 +685,8 @@ Expr::isLvalueResult Expr::isLvalueInternal(ASTContext &Ctx) const {
       return LV_Valid;
     break;
   }
-  case MemberExprClass: { 
+  case MemberExprClass: 
+  case CXXQualifiedMemberExprClass: { 
     const MemberExpr *m = cast<MemberExpr>(this);
     if (Ctx.getLangOptions().CPlusPlus) { // C++ [expr.ref]p4:
       NamedDecl *Member = m->getMemberDecl();
@@ -948,7 +950,8 @@ bool Expr::hasGlobalStorage() const {
       return true;
     return false;
   }
-  case MemberExprClass: {
+  case MemberExprClass:
+  case CXXQualifiedMemberExprClass: {
     const MemberExpr *M = cast<MemberExpr>(this);
     return !M->isArrow() && M->getBase()->hasGlobalStorage();
   }
@@ -992,7 +995,8 @@ bool Expr::isOBJCGCCandidate(ASTContext &Ctx) const {
     }
     return false;
   }
-  case MemberExprClass: {
+  case MemberExprClass: 
+  case CXXQualifiedMemberExprClass: {
     const MemberExpr *M = cast<MemberExpr>(this);
     return M->getBase()->isOBJCGCCandidate(Ctx);
   }
index 1c3041c8cdb6685ab93c9f2881934f99b1f430f0..c9614e178c46eb7dbfc561570829f5e3ae5c483d 100644 (file)
@@ -1126,6 +1126,16 @@ StmtPrinter::VisitCXXUnresolvedConstructExpr(
   OS << ")";
 }
 
+void StmtPrinter::VisitCXXQualifiedMemberExpr(CXXQualifiedMemberExpr *Node) {
+  // FIXME: Suppress printing implicit bases (like "this")
+  PrintExpr(Node->getBase());
+  OS << (Node->isArrow() ? "->" : ".");
+  // FIXME: Suppress printing references to unnamed objects
+  // representing anonymous unions/structs
+  Node->getQualifier()->print(OS, Policy);
+  OS << Node->getMemberDecl()->getNameAsString();
+}
+
 void StmtPrinter::VisitCXXUnresolvedMemberExpr(CXXUnresolvedMemberExpr *Node) {
   PrintExpr(Node->getBase());
   OS << (Node->isArrow() ? "->" : ".");
index ff20299168434ea2cacec8aaae9010218feb825e..0ffe5e17c63d1da1e961ff5c6ccdcc686feae918 100644 (file)
@@ -546,6 +546,11 @@ StmtProfiler::VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr *S) {
   VisitType(S->getTypeAsWritten());
 }
 
+void StmtProfiler::VisitCXXQualifiedMemberExpr(CXXQualifiedMemberExpr *S) {
+  VisitMemberExpr(S);
+  VisitNestedNameSpecifier(S->getQualifier());
+}
+
 void StmtProfiler::VisitCXXUnresolvedMemberExpr(CXXUnresolvedMemberExpr *S) {
   VisitExpr(S);
   ID.AddBoolean(S->isArrow());
index 3abaf552d8ec07fb9830359d33dc5038f92f98ef..600c52c5b09e934643b52a3692ffdc479baf97d9 100644 (file)
@@ -408,6 +408,7 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) {
       break;
       
     case Stmt::MemberExprClass:
+    case Stmt::CXXQualifiedMemberExprClass:
       VisitMemberExpr(cast<MemberExpr>(S), Pred, Dst, false);
       break;
       
@@ -515,6 +516,7 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred,
       return;
       
     case Stmt::MemberExprClass:
+    case Stmt::CXXQualifiedMemberExprClass:
       VisitMemberExpr(cast<MemberExpr>(Ex), Pred, Dst, true);
       return;
       
index 5405ac07d9e0267f1b3614228e5d6964de9bf82f..680d7357b9a434c2a112e4214ab756e349a4998c 100644 (file)
@@ -212,11 +212,13 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE) {
     This = BaseLV.getAddress();
   }
 
+  // C++ [class.virtual]p12:
+  //   Explicit qualification with the scope operator (5.1) suppresses the 
+  //   virtual call mechanism.
   llvm::Value *Callee;
-  // FIXME: Someone needs to keep track of the qualifications.
-  if (MD->isVirtual() /* && !ME->NotQualified() */)
+  if (MD->isVirtual() && !isa<CXXQualifiedMemberExpr>(CE)) {
     Callee = BuildVirtualCall(MD, This, Ty);
-  else
+  else
     Callee = CGM.GetAddrOfFunction(GlobalDecl(MD), Ty);
   
   return EmitCXXMemberCall(MD, Callee, This, 
index 310fae03c632c9fdbbab58c0ddfe034e2ba89c7b..75fc107d20e6d1ce76ac63c1891d08e6fd707624 100644 (file)
@@ -240,7 +240,9 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) {
     return EmitArraySubscriptExpr(cast<ArraySubscriptExpr>(E));
   case Expr::ExtVectorElementExprClass:
     return EmitExtVectorElementExpr(cast<ExtVectorElementExpr>(E));
-  case Expr::MemberExprClass: return EmitMemberExpr(cast<MemberExpr>(E));
+  case Expr::MemberExprClass: 
+  case Stmt::CXXQualifiedMemberExprClass:
+    return EmitMemberExpr(cast<MemberExpr>(E));
   case Expr::CompoundLiteralExprClass:
     return EmitCompoundLiteralLValue(cast<CompoundLiteralExpr>(E));
   case Expr::ConditionalOperatorClass:
index 27f03bd98f49aed94d19aabbcc5515d975a3ba12..e6fa3727369e9d275c98337b6cb2ff253523d17a 100644 (file)
@@ -1421,7 +1421,8 @@ static DeclRefExpr* EvalVal(Expr *E) {
   }
   
   // Accesses to members are potential references to data on the stack.
-  case Stmt::MemberExprClass: {
+  case Stmt::MemberExprClass: 
+  case Stmt::CXXQualifiedMemberExprClass: {
     MemberExpr *M = cast<MemberExpr>(E);
       
     // Check for indirect access.  We only want direct field accesses.
index 7a8189835fbb0c9d75643705c395437f02475114..e73708981891909ced354075ad64906ef61e88b2 100644 (file)
@@ -631,6 +631,7 @@ Sema::BuildAnonymousStructUnionMemberReference(SourceLocation Loc,
     if (BaseAddrSpace != MemberType.getAddressSpace())
       MemberType = Context.getAddrSpaceQualType(MemberType, BaseAddrSpace);
     MarkDeclarationReferenced(Loc, *FI);
+    // FIXME: Might this end up being a qualified name?
     Result = new (Context) MemberExpr(Result, BaseObjectIsPointer, *FI,
                                       OpLoc, MemberType);
     BaseObjectIsPointer = false;
@@ -874,6 +875,19 @@ Sema::PerformObjectMemberConversion(Expr *&From, NamedDecl *Member) {
   return false;
 }
 
+/// \brief Build a MemberExpr or CXXQualifiedMemberExpr, as appropriate.
+static MemberExpr *BuildMemberExpr(ASTContext &C, Expr *Base, bool isArrow, 
+                                   const CXXScopeSpec *SS, NamedDecl *Member, 
+                                   SourceLocation Loc, QualType Ty) {
+  if (SS && SS->isSet())
+    return new (C) CXXQualifiedMemberExpr(Base, isArrow, 
+                                          (NestedNameSpecifier *)SS->getScopeRep(),
+                                          SS->getRange(),
+                                          Member, Loc, Ty);
+  
+  return new (C) MemberExpr(Base, isArrow, Member, Loc, Ty);
+}
+
 /// \brief Complete semantic analysis for a reference to the given declaration.
 Sema::OwningExprResult
 Sema::BuildDeclarationNameExpr(SourceLocation Loc, NamedDecl *D,
@@ -985,8 +999,8 @@ Sema::BuildDeclarationNameExpr(SourceLocation Loc, NamedDecl *D,
             return ExprError();
           if (DiagnoseUseOfDecl(D, Loc))
             return ExprError();
-          return Owned(new (Context) MemberExpr(This, true, D,
-                                                Loc, MemberType));
+          return Owned(BuildMemberExpr(Context, This, true, SS, D,
+                                       Loc, MemberType));
         }
       }
     }
@@ -1953,7 +1967,7 @@ ObjCMethodDecl *Sema::FindMethodInNestedImplementations(
     return FindMethodInNestedImplementations(IFace->getSuperClass(), Sel);
   return Method;
 }
-
+                       
 Action::OwningExprResult 
 Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
                                tok::TokenKind OpKind, SourceLocation MemberLoc,
@@ -2112,37 +2126,37 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
       MarkDeclarationReferenced(MemberLoc, FD);
       if (PerformObjectMemberConversion(BaseExpr, FD))
         return ExprError();
-      return Owned(new (Context) MemberExpr(BaseExpr, OpKind == tok::arrow, FD,
-                                            MemberLoc, MemberType));
+      return Owned(BuildMemberExpr(Context, BaseExpr, OpKind == tok::arrow, SS, 
+                                   FD, MemberLoc, MemberType));
     }
     
     if (VarDecl *Var = dyn_cast<VarDecl>(MemberDecl)) {
       MarkDeclarationReferenced(MemberLoc, MemberDecl);
-      return Owned(new (Context) MemberExpr(BaseExpr, OpKind == tok::arrow,
-                                            Var, MemberLoc,
-                                         Var->getType().getNonReferenceType()));
+      return Owned(BuildMemberExpr(Context, BaseExpr, OpKind == tok::arrow, SS,
+                                   Var, MemberLoc,
+                                   Var->getType().getNonReferenceType()));
     }
     if (FunctionDecl *MemberFn = dyn_cast<FunctionDecl>(MemberDecl)) {
       MarkDeclarationReferenced(MemberLoc, MemberDecl);
-      return Owned(new (Context) MemberExpr(BaseExpr, OpKind == tok::arrow,
-                                            MemberFn, MemberLoc,
-                                            MemberFn->getType()));
+      return Owned(BuildMemberExpr(Context, BaseExpr, OpKind == tok::arrow, SS,
+                                   MemberFn, MemberLoc,
+                                   MemberFn->getType()));
     }
     if (FunctionTemplateDecl *FunTmpl 
           = dyn_cast<FunctionTemplateDecl>(MemberDecl)) {
       MarkDeclarationReferenced(MemberLoc, MemberDecl);
-      return Owned(new (Context) MemberExpr(BaseExpr, OpKind == tok::arrow,
-                                            FunTmpl, MemberLoc,
-                                            Context.OverloadTy));
+      return Owned(BuildMemberExpr(Context, BaseExpr, OpKind == tok::arrow, SS,
+                                   FunTmpl, MemberLoc,
+                                   Context.OverloadTy));
     }
     if (OverloadedFunctionDecl *Ovl
           = dyn_cast<OverloadedFunctionDecl>(MemberDecl))
-      return Owned(new (Context) MemberExpr(BaseExpr, OpKind == tok::arrow, Ovl,
-                                            MemberLoc, Context.OverloadTy));
+      return Owned(BuildMemberExpr(Context, BaseExpr, OpKind == tok::arrow, SS,
+                                   Ovl, MemberLoc, Context.OverloadTy));
     if (EnumConstantDecl *Enum = dyn_cast<EnumConstantDecl>(MemberDecl)) {
       MarkDeclarationReferenced(MemberLoc, MemberDecl);
-      return Owned(new (Context) MemberExpr(BaseExpr, OpKind == tok::arrow,
-                                            Enum, MemberLoc, Enum->getType()));
+      return Owned(BuildMemberExpr(Context, BaseExpr, OpKind == tok::arrow, SS,
+                                   Enum, MemberLoc, Enum->getType()));
     }
     if (isa<TypeDecl>(MemberDecl))
       return ExprError(Diag(MemberLoc,diag::err_typecheck_member_reference_type)
@@ -4802,6 +4816,7 @@ static NamedDecl *getPrimaryDecl(Expr *E) {
   case Stmt::QualifiedDeclRefExprClass:
     return cast<DeclRefExpr>(E)->getDecl();
   case Stmt::MemberExprClass:
+  case Stmt::CXXQualifiedMemberExprClass:
     // If this is an arrow operator, the address is an offset from
     // the base's value, so the object the base refers to is
     // irrelevant.
index 77d5220abd8b5182d8d57d31342812141b0b37bd..8aabad575a2c3794517dd944f20aa640da246cdf 100644 (file)
@@ -1421,6 +1421,28 @@ public:
                                                RParenLoc);
   }
   
+  /// \brief Build a new qualified member access expression.
+  /// 
+  /// By default, performs semantic analysis to build the new expression.
+  /// Subclasses may override this routine to provide different behavior.
+  OwningExprResult RebuildCXXQualifiedMemberExpr(ExprArg Base, 
+                                                 SourceLocation OpLoc,
+                                                 bool isArrow, 
+                                                NestedNameSpecifier *Qualifier,
+                                                 SourceRange QualifierRange,
+                                                 SourceLocation MemberLoc,
+                                                 NamedDecl *Member) {
+    CXXScopeSpec SS;
+    SS.setRange(QualifierRange);
+    SS.setScopeRep(Qualifier);
+    return getSema().ActOnMemberReferenceExpr(/*Scope=*/0, move(Base), OpLoc,
+                                              isArrow? tok::arrow : tok::period,
+                                              MemberLoc,
+                                              /*FIXME*/*Member->getIdentifier(),
+                                      /*FIXME?*/Sema::DeclPtrTy::make((Decl*)0),
+                                              &SS);
+  }
+  
   /// \brief Build a new member reference expression.
   ///
   /// By default, performs semantic analysis to build the new expression.
@@ -3992,6 +4014,44 @@ TreeTransform<Derived>::TransformCXXUnresolvedConstructExpr(
                                                         E->getRParenLoc());
 }
 
+template<typename Derived> 
+Sema::OwningExprResult 
+TreeTransform<Derived>::TransformCXXQualifiedMemberExpr(
+                                                  CXXQualifiedMemberExpr *E) { 
+  OwningExprResult Base = getDerived().TransformExpr(E->getBase());
+  if (Base.isInvalid())
+    return SemaRef.ExprError();
+  
+  NamedDecl *Member 
+    = cast_or_null<NamedDecl>(getDerived().TransformDecl(E->getMemberDecl()));
+  if (!Member)
+    return SemaRef.ExprError();
+  NestedNameSpecifier *Qualifier
+    = getDerived().TransformNestedNameSpecifier(E->getQualifier(),
+                                                E->getQualifierRange());
+  if (Qualifier == 0)
+    return SemaRef.ExprError();
+
+  if (!getDerived().AlwaysRebuild() &&
+      Base.get() == E->getBase() &&
+      Member == E->getMemberDecl() &&
+      Qualifier == E->getQualifier())
+    return SemaRef.Owned(E->Retain()); 
+  // FIXME: Bogus source location for the operator
+  SourceLocation FakeOperatorLoc
+    = SemaRef.PP.getLocForEndOfToken(E->getBase()->getSourceRange().getEnd());
+  
+  return getDerived().RebuildCXXQualifiedMemberExpr(move(Base), 
+                                                     FakeOperatorLoc,
+                                                     E->isArrow(),
+                                                     Qualifier,
+                                                     E->getQualifierRange(),
+                                                     E->getMemberLoc(),
+                                                     Member);
+}
+  
 template<typename Derived>
 Sema::OwningExprResult
 TreeTransform<Derived>::TransformCXXUnresolvedMemberExpr(
diff --git a/test/CXX/class.derived/class.virtual/p12.cpp b/test/CXX/class.derived/class.virtual/p12.cpp
new file mode 100644 (file)
index 0000000..b5974a0
--- /dev/null
@@ -0,0 +1,19 @@
+// RUN: clang-cc -ast-print %s | FileCheck %s
+
+// CHECK: test12_A::foo()
+struct test12_A {
+  virtual void foo();
+  
+  void bar() {
+    test12_A::foo();
+  }
+};
+
+// CHECK: xp->test24_B::wibble()
+struct test24_B {
+  virtual void wibble();
+};
+
+void foo(test24_B *xp) {
+  xp->test24_B::wibble();
+}