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:
Douglas Gregor 2009-08-26 22:36:53 +00:00
parent b60d87c517
commit c190523d7a
13 changed files with 194 additions and 28 deletions

View 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();

View 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.

View 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.

View 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);
}

View 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() ? "->" : ".");

View 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());

View 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;

View 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,

View 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:

View 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.

View 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.

View 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(

View File

@ -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();
}