From c190523d7a3a93d37c4a5bb612a928cfe3e6957a Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 26 Aug 2009 22:36:53 +0000 Subject: [PATCH] When a member reference expression includes a qualifier on the member 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? llvm-svn: 80167 --- clang/include/clang/AST/Expr.h | 13 +++- clang/include/clang/AST/ExprCXX.h | 36 ++++++++++- clang/include/clang/AST/StmtNodes.def | 1 + clang/lib/AST/Expr.cpp | 10 +++- clang/lib/AST/StmtPrinter.cpp | 10 ++++ clang/lib/AST/StmtProfile.cpp | 5 ++ clang/lib/Analysis/GRExprEngine.cpp | 2 + clang/lib/CodeGen/CGCXX.cpp | 8 ++- clang/lib/CodeGen/CGExpr.cpp | 4 +- clang/lib/Sema/SemaChecking.cpp | 3 +- clang/lib/Sema/SemaExpr.cpp | 51 ++++++++++------ clang/lib/Sema/TreeTransform.h | 60 +++++++++++++++++++ .../CXX/class.derived/class.virtual/p12.cpp | 19 ++++++ 13 files changed, 194 insertions(+), 28 deletions(-) create mode 100644 clang/test/CXX/class.derived/class.virtual/p12.cpp diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index ff16480c059b..827877e79807 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -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(); diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index dfc32c7f97ca..7e21a73cc286 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -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. diff --git a/clang/include/clang/AST/StmtNodes.def b/clang/include/clang/AST/StmtNodes.def index af345991f1e6..72dfa612c02c 100644 --- a/clang/include/clang/AST/StmtNodes.def +++ b/clang/include/clang/AST/StmtNodes.def @@ -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. diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 7cc436c7a901..46532cafae64 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -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(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(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(this); return M->getBase()->isOBJCGCCandidate(Ctx); } diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 1c3041c8cdb6..c9614e178c46 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -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() ? "->" : "."); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index ff2029916843..0ffe5e17c63d 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -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()); diff --git a/clang/lib/Analysis/GRExprEngine.cpp b/clang/lib/Analysis/GRExprEngine.cpp index 3abaf552d8ec..600c52c5b09e 100644 --- a/clang/lib/Analysis/GRExprEngine.cpp +++ b/clang/lib/Analysis/GRExprEngine.cpp @@ -408,6 +408,7 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { break; case Stmt::MemberExprClass: + case Stmt::CXXQualifiedMemberExprClass: VisitMemberExpr(cast(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(Ex), Pred, Dst, true); return; diff --git a/clang/lib/CodeGen/CGCXX.cpp b/clang/lib/CodeGen/CGCXX.cpp index 5405ac07d9e0..680d7357b9a4 100644 --- a/clang/lib/CodeGen/CGCXX.cpp +++ b/clang/lib/CodeGen/CGCXX.cpp @@ -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(CE)) { Callee = BuildVirtualCall(MD, This, Ty); - else + } else Callee = CGM.GetAddrOfFunction(GlobalDecl(MD), Ty); return EmitCXXMemberCall(MD, Callee, This, diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 310fae03c632..75fc107d20e6 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -240,7 +240,9 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) { return EmitArraySubscriptExpr(cast(E)); case Expr::ExtVectorElementExprClass: return EmitExtVectorElementExpr(cast(E)); - case Expr::MemberExprClass: return EmitMemberExpr(cast(E)); + case Expr::MemberExprClass: + case Stmt::CXXQualifiedMemberExprClass: + return EmitMemberExpr(cast(E)); case Expr::CompoundLiteralExprClass: return EmitCompoundLiteralLValue(cast(E)); case Expr::ConditionalOperatorClass: diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 27f03bd98f49..e6fa3727369e 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -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(E); // Check for indirect access. We only want direct field accesses. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 7a8189835fbb..e73708981891 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -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(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(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(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(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(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(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(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. diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 77d5220abd8b..8aabad575a2c 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -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::TransformCXXUnresolvedConstructExpr( E->getRParenLoc()); } +template +Sema::OwningExprResult +TreeTransform::TransformCXXQualifiedMemberExpr( + CXXQualifiedMemberExpr *E) { + OwningExprResult Base = getDerived().TransformExpr(E->getBase()); + if (Base.isInvalid()) + return SemaRef.ExprError(); + + NamedDecl *Member + = cast_or_null(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 Sema::OwningExprResult TreeTransform::TransformCXXUnresolvedMemberExpr( diff --git a/clang/test/CXX/class.derived/class.virtual/p12.cpp b/clang/test/CXX/class.derived/class.virtual/p12.cpp new file mode 100644 index 000000000000..b5974a021180 --- /dev/null +++ b/clang/test/CXX/class.derived/class.virtual/p12.cpp @@ -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(); +}