forked from OSchip/llvm-project
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
This commit is contained in:
parent
b60d87c517
commit
c190523d7a
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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() ? "->" : ".");
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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();
|
||||
}
|
Loading…
Reference in New Issue