Add support for member references (E1.E2, E1->E2) with C++ semantics,
which can refer to static data members, enumerators, and member functions as well as to non-static data members. Implement correct lvalue computation for member references in C++. Compute the result type of non-static data members of reference type properly. llvm-svn: 61294
This commit is contained in:
parent
a0befc0a6f
commit
2eedc3aa1c
|
@ -126,7 +126,8 @@ public:
|
|||
LV_NotObjectType,
|
||||
LV_IncompleteVoidType,
|
||||
LV_DuplicateVectorComponents,
|
||||
LV_InvalidExpression
|
||||
LV_InvalidExpression,
|
||||
LV_MemberFunction
|
||||
};
|
||||
isLvalueResult isLvalue(ASTContext &Ctx) const;
|
||||
|
||||
|
@ -147,7 +148,8 @@ public:
|
|||
MLV_ArrayType,
|
||||
MLV_NotBlockQualified,
|
||||
MLV_ReadonlyProperty,
|
||||
MLV_NoSetterProperty
|
||||
MLV_NoSetterProperty,
|
||||
MLV_MemberFunction
|
||||
};
|
||||
isModifiableLvalueResult isModifiableLvalue(ASTContext &Ctx) const;
|
||||
|
||||
|
@ -830,17 +832,17 @@ public:
|
|||
///
|
||||
class MemberExpr : public Expr {
|
||||
Stmt *Base;
|
||||
FieldDecl *MemberDecl;
|
||||
NamedDecl *MemberDecl;
|
||||
SourceLocation MemberLoc;
|
||||
bool IsArrow; // True if this is "X->F", false if this is "X.F".
|
||||
public:
|
||||
MemberExpr(Expr *base, bool isarrow, FieldDecl *memberdecl, SourceLocation l,
|
||||
MemberExpr(Expr *base, bool isarrow, NamedDecl *memberdecl, SourceLocation l,
|
||||
QualType ty)
|
||||
: Expr(MemberExprClass, ty),
|
||||
Base(base), MemberDecl(memberdecl), MemberLoc(l), IsArrow(isarrow) {}
|
||||
|
||||
Expr *getBase() const { return cast<Expr>(Base); }
|
||||
FieldDecl *getMemberDecl() const { return MemberDecl; }
|
||||
NamedDecl *getMemberDecl() const { return MemberDecl; }
|
||||
bool isArrow() const { return IsArrow; }
|
||||
|
||||
virtual SourceRange getSourceRange() const {
|
||||
|
|
|
@ -1171,6 +1171,10 @@ DIAG(err_typecheck_member_reference_ivar, ERROR,
|
|||
"%0 does not have a member named %1")
|
||||
DIAG(err_typecheck_member_reference_arrow, ERROR,
|
||||
"member reference type %0 is not a pointer")
|
||||
DIAG(err_typecheck_member_reference_type, ERROR,
|
||||
"cannot refer to type member %0 with '%select{.|->}1'")
|
||||
DIAG(err_typecheck_member_reference_unknown, ERROR,
|
||||
"cannot refer to member %0 with '%select{.|->}1'")
|
||||
DIAG(err_typecheck_incomplete_tag, ERROR,
|
||||
"incomplete definition of type %0")
|
||||
DIAG(err_typecheck_no_member, ERROR,
|
||||
|
|
|
@ -407,8 +407,43 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
|
|||
return LV_Valid;
|
||||
break;
|
||||
}
|
||||
case MemberExprClass: { // C99 6.5.2.3p4
|
||||
case MemberExprClass: {
|
||||
const MemberExpr *m = cast<MemberExpr>(this);
|
||||
if (Ctx.getLangOptions().CPlusPlus) { // C++ [expr.ref]p4:
|
||||
NamedDecl *Member = m->getMemberDecl();
|
||||
// C++ [expr.ref]p4:
|
||||
// If E2 is declared to have type "reference to T", then E1.E2
|
||||
// is an lvalue.
|
||||
if (ValueDecl *Value = dyn_cast<ValueDecl>(Member))
|
||||
if (Value->getType()->isReferenceType())
|
||||
return LV_Valid;
|
||||
|
||||
// -- If E2 is a static data member [...] then E1.E2 is an lvalue.
|
||||
if (isa<CXXClassVarDecl>(Member))
|
||||
return LV_Valid;
|
||||
|
||||
// -- If E2 is a non-static data member [...]. If E1 is an
|
||||
// lvalue, then E1.E2 is an lvalue.
|
||||
if (isa<FieldDecl>(Member))
|
||||
return m->isArrow() ? LV_Valid : m->getBase()->isLvalue(Ctx);
|
||||
|
||||
// -- If it refers to a static member function [...], then
|
||||
// E1.E2 is an lvalue.
|
||||
// -- Otherwise, if E1.E2 refers to a non-static member
|
||||
// function [...], then E1.E2 is not an lvalue.
|
||||
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Member))
|
||||
return Method->isStatic()? LV_Valid : LV_MemberFunction;
|
||||
|
||||
// -- If E2 is a member enumerator [...], the expression E1.E2
|
||||
// is not an lvalue.
|
||||
if (isa<EnumConstantDecl>(Member))
|
||||
return LV_InvalidExpression;
|
||||
|
||||
// Not an lvalue.
|
||||
return LV_InvalidExpression;
|
||||
}
|
||||
|
||||
// C99 6.5.2.3p4
|
||||
return m->isArrow() ? LV_Valid : m->getBase()->isLvalue(Ctx);
|
||||
}
|
||||
case UnaryOperatorClass:
|
||||
|
@ -542,6 +577,7 @@ Expr::isModifiableLvalueResult Expr::isModifiableLvalue(ASTContext &Ctx) const {
|
|||
if (CE->getSubExpr()->isLvalue(Ctx) == LV_Valid)
|
||||
return MLV_LValueCast;
|
||||
return MLV_InvalidExpression;
|
||||
case LV_MemberFunction: return MLV_MemberFunction;
|
||||
}
|
||||
|
||||
QualType CT = Ctx.getCanonicalType(getType());
|
||||
|
@ -1113,7 +1149,8 @@ bool Expr::isNullPointerConstant(ASTContext &Ctx) const
|
|||
bool Expr::isBitField() {
|
||||
Expr *E = this->IgnoreParenCasts();
|
||||
if (MemberExpr *MemRef = dyn_cast<MemberExpr>(E))
|
||||
return MemRef->getMemberDecl()->isBitField();
|
||||
if (FieldDecl *Field = dyn_cast<FieldDecl>(MemRef->getMemberDecl()))
|
||||
return Field->isBitField();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1245,21 +1282,21 @@ static int64_t evaluateOffsetOf(ASTContext& C, const Expr *E) {
|
|||
|
||||
RecordDecl *RD = Ty->getAsRecordType()->getDecl();
|
||||
const ASTRecordLayout &RL = C.getASTRecordLayout(RD);
|
||||
FieldDecl *FD = ME->getMemberDecl();
|
||||
|
||||
// FIXME: This is linear time. And the fact that we're indexing
|
||||
// into the layout by position in the record means that we're
|
||||
// either stuck numbering the fields in the AST or we have to keep
|
||||
// the linear search (yuck and yuck).
|
||||
unsigned i = 0;
|
||||
for (RecordDecl::field_iterator Field = RD->field_begin(),
|
||||
FieldEnd = RD->field_end();
|
||||
Field != FieldEnd; (void)++Field, ++i) {
|
||||
if (*Field == FD)
|
||||
break;
|
||||
if (FieldDecl *FD = dyn_cast<FieldDecl>(ME->getMemberDecl())) {
|
||||
// FIXME: This is linear time. And the fact that we're indexing
|
||||
// into the layout by position in the record means that we're
|
||||
// either stuck numbering the fields in the AST or we have to keep
|
||||
// the linear search (yuck and yuck).
|
||||
unsigned i = 0;
|
||||
for (RecordDecl::field_iterator Field = RD->field_begin(),
|
||||
FieldEnd = RD->field_end();
|
||||
Field != FieldEnd; (void)++Field, ++i) {
|
||||
if (*Field == FD)
|
||||
break;
|
||||
}
|
||||
|
||||
return RL.getFieldOffset(i) + evaluateOffsetOf(C, ME->getBase());
|
||||
}
|
||||
|
||||
return RL.getFieldOffset(i) + evaluateOffsetOf(C, ME->getBase());
|
||||
} else if (const ArraySubscriptExpr *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
|
||||
const Expr *Base = ASE->getBase();
|
||||
|
||||
|
|
|
@ -153,7 +153,10 @@ APValue LValueExprEvaluator::VisitMemberExpr(MemberExpr *E) {
|
|||
|
||||
RecordDecl *RD = Ty->getAsRecordType()->getDecl();
|
||||
const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD);
|
||||
FieldDecl *FD = E->getMemberDecl();
|
||||
|
||||
FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl());
|
||||
if (!FD) // FIXME: deal with other kinds of member expressions
|
||||
return APValue();
|
||||
|
||||
// FIXME: This is linear time.
|
||||
unsigned i = 0;
|
||||
|
|
|
@ -702,10 +702,7 @@ void StmtPrinter::VisitCallExpr(CallExpr *Call) {
|
|||
void StmtPrinter::VisitMemberExpr(MemberExpr *Node) {
|
||||
PrintExpr(Node->getBase());
|
||||
OS << (Node->isArrow() ? "->" : ".");
|
||||
|
||||
FieldDecl *Field = Node->getMemberDecl();
|
||||
assert(Field && "MemberExpr should alway reference a field!");
|
||||
OS << Field->getNameAsString();
|
||||
OS << Node->getMemberDecl()->getNameAsString();
|
||||
}
|
||||
void StmtPrinter::VisitExtVectorElementExpr(ExtVectorElementExpr *Node) {
|
||||
PrintExpr(Node->getBase());
|
||||
|
|
|
@ -761,7 +761,7 @@ void MemberExpr::EmitImpl(Serializer& S) const {
|
|||
|
||||
MemberExpr* MemberExpr::CreateImpl(Deserializer& D, ASTContext& C) {
|
||||
SourceLocation L = SourceLocation::ReadVal(D);
|
||||
FieldDecl* MemberDecl = cast<FieldDecl>(D.ReadPtr<Decl>());
|
||||
NamedDecl* MemberDecl = cast<NamedDecl>(D.ReadPtr<Decl>());
|
||||
bool IsArrow = D.ReadBool();
|
||||
QualType T = QualType::ReadVal(D);
|
||||
Expr* base = D.ReadOwnedPtr<Expr>(C);
|
||||
|
|
|
@ -930,12 +930,16 @@ void GRExprEngine::VisitMemberExpr(MemberExpr* M, NodeTy* Pred,
|
|||
else
|
||||
VisitLValue(Base, Pred, Tmp); // x.f = ... or ... = x.f
|
||||
|
||||
FieldDecl *Field = dyn_cast<FieldDecl>(M->getMemberDecl());
|
||||
if (!Field) // FIXME: skipping member expressions for non-fields
|
||||
return;
|
||||
|
||||
for (NodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) {
|
||||
const GRState* St = GetState(*I);
|
||||
// FIXME: Should we insert some assumption logic in here to determine
|
||||
// if "Base" is a valid piece of memory? Before we put this assumption
|
||||
// later when using FieldOffset lvals (which we no longer have).
|
||||
SVal L = StateMgr.GetLValue(St, GetSVal(St, Base), M->getMemberDecl());
|
||||
// later when using FieldOffset lvals (which we no longer have).
|
||||
SVal L = StateMgr.GetLValue(St, GetSVal(St, Base), Field);
|
||||
|
||||
if (asLValue)
|
||||
MakeNode(Dst, M, *I, BindExpr(St, M, L));
|
||||
|
|
|
@ -782,7 +782,9 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) {
|
|||
CVRQualifiers = BaseExpr->getType().getCVRQualifiers();
|
||||
}
|
||||
|
||||
FieldDecl *Field = E->getMemberDecl();
|
||||
FieldDecl *Field = dyn_cast<FieldDecl>(E->getMemberDecl());
|
||||
// FIXME: Handle non-field member expressions
|
||||
assert(Field && "No code generation for non-field member references");
|
||||
LValue MemExpLV = EmitLValueForField(BaseValue, Field, isUnion, CVRQualifiers);
|
||||
LValue::SetObjCIvar(MemExpLV, isIvar);
|
||||
return MemExpLV;
|
||||
|
|
|
@ -564,7 +564,10 @@ public:
|
|||
else
|
||||
Base = EmitLValue(ME->getBase());
|
||||
|
||||
unsigned FieldNumber = CGM.getTypes().getLLVMFieldNo(ME->getMemberDecl());
|
||||
FieldDecl *Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
|
||||
// FIXME: Handle other kinds of member expressions.
|
||||
assert(Field && "No code generation for non-field member expressions");
|
||||
unsigned FieldNumber = CGM.getTypes().getLLVMFieldNo(Field);
|
||||
llvm::Constant *Zero = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0);
|
||||
llvm::Constant *Idx = llvm::ConstantInt::get(llvm::Type::Int32Ty,
|
||||
FieldNumber);
|
||||
|
|
|
@ -1244,24 +1244,44 @@ ActOnMemberReferenceExpr(ExprTy *Base, SourceLocation OpLoc,
|
|||
<< &Member << BaseExpr->getSourceRange();
|
||||
}
|
||||
|
||||
FieldDecl *MemberDecl = dyn_cast<FieldDecl>(*Lookup.first);
|
||||
if (!MemberDecl) {
|
||||
unsigned DiagID = PP.getDiagnostics().getCustomDiagID(Diagnostic::Error,
|
||||
"Clang only supports references to members");
|
||||
return Diag(MemberLoc, DiagID);
|
||||
}
|
||||
if (FieldDecl *MemberDecl = dyn_cast<FieldDecl>(*Lookup.first)) {
|
||||
// Figure out the type of the member; see C99 6.5.2.3p3, C++ [expr.ref]
|
||||
// FIXME: Handle address space modifiers
|
||||
QualType MemberType = MemberDecl->getType();
|
||||
if (const ReferenceType *Ref = MemberType->getAsReferenceType())
|
||||
MemberType = Ref->getPointeeType();
|
||||
else {
|
||||
unsigned combinedQualifiers =
|
||||
MemberType.getCVRQualifiers() | BaseType.getCVRQualifiers();
|
||||
if (MemberDecl->isMutable())
|
||||
combinedQualifiers &= ~QualType::Const;
|
||||
MemberType = MemberType.getQualifiedType(combinedQualifiers);
|
||||
}
|
||||
|
||||
// Figure out the type of the member; see C99 6.5.2.3p3
|
||||
// FIXME: Handle address space modifiers
|
||||
QualType MemberType = MemberDecl->getType();
|
||||
unsigned combinedQualifiers =
|
||||
MemberType.getCVRQualifiers() | BaseType.getCVRQualifiers();
|
||||
if (MemberDecl->isMutable())
|
||||
combinedQualifiers &= ~QualType::Const;
|
||||
MemberType = MemberType.getQualifiedType(combinedQualifiers);
|
||||
return new MemberExpr(BaseExpr, OpKind == tok::arrow, MemberDecl,
|
||||
MemberLoc, MemberType);
|
||||
} else if (CXXClassVarDecl *Var = dyn_cast<CXXClassVarDecl>(*Lookup.first))
|
||||
return new MemberExpr(BaseExpr, OpKind == tok::arrow, Var, MemberLoc,
|
||||
Var->getType().getNonReferenceType());
|
||||
else if (FunctionDecl *MemberFn = dyn_cast<FunctionDecl>(*Lookup.first))
|
||||
return new MemberExpr(BaseExpr, OpKind == tok::arrow, MemberFn, MemberLoc,
|
||||
MemberFn->getType());
|
||||
else if (OverloadedFunctionDecl *Ovl
|
||||
= dyn_cast<OverloadedFunctionDecl>(*Lookup.first))
|
||||
return new MemberExpr(BaseExpr, OpKind == tok::arrow, Ovl, MemberLoc,
|
||||
Context.OverloadTy);
|
||||
else if (EnumConstantDecl *Enum = dyn_cast<EnumConstantDecl>(*Lookup.first))
|
||||
return new MemberExpr(BaseExpr, OpKind == tok::arrow, Enum, MemberLoc,
|
||||
Enum->getType());
|
||||
else if (isa<TypeDecl>(*Lookup.first))
|
||||
return Diag(MemberLoc, diag::err_typecheck_member_reference_type)
|
||||
<< DeclarationName(&Member) << int(OpKind == tok::arrow);
|
||||
|
||||
return new MemberExpr(BaseExpr, OpKind == tok::arrow, MemberDecl,
|
||||
MemberLoc, MemberType);
|
||||
// We found a declaration kind that we didn't expect. This is a
|
||||
// generic error message that tells the user that she can't refer
|
||||
// to this member with '.' or '->'.
|
||||
return Diag(MemberLoc, diag::err_typecheck_member_reference_unknown)
|
||||
<< DeclarationName(&Member) << int(OpKind == tok::arrow);
|
||||
}
|
||||
|
||||
// Handle access to Objective-C instance variables, such as "Obj->ivar" and
|
||||
|
@ -2920,10 +2940,12 @@ QualType Sema::CheckAddressOfOperand(Expr *op, SourceLocation OpLoc) {
|
|||
return QualType();
|
||||
}
|
||||
} else if (MemberExpr *MemExpr = dyn_cast<MemberExpr>(op)) { // C99 6.5.3.2p1
|
||||
if (MemExpr->getMemberDecl()->isBitField()) {
|
||||
Diag(OpLoc, diag::err_typecheck_address_of)
|
||||
<< "bit-field" << op->getSourceRange();
|
||||
return QualType();
|
||||
if (FieldDecl *Field = dyn_cast<FieldDecl>(MemExpr->getMemberDecl())) {
|
||||
if (Field->isBitField()) {
|
||||
Diag(OpLoc, diag::err_typecheck_address_of)
|
||||
<< "bit-field" << op->getSourceRange();
|
||||
return QualType();
|
||||
}
|
||||
}
|
||||
// Check for Apple extension for accessing vector components.
|
||||
} else if (isa<ArraySubscriptExpr>(op) &&
|
||||
|
|
|
@ -666,26 +666,27 @@ bool Sema::IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType)
|
|||
// other value of that type for promotion purposes (C++ 4.5p3).
|
||||
if (MemberExpr *MemRef = dyn_cast<MemberExpr>(From)) {
|
||||
using llvm::APSInt;
|
||||
FieldDecl *MemberDecl = MemRef->getMemberDecl();
|
||||
APSInt BitWidth;
|
||||
if (MemberDecl->isBitField() &&
|
||||
FromType->isIntegralType() && !FromType->isEnumeralType() &&
|
||||
From->isIntegerConstantExpr(BitWidth, Context)) {
|
||||
APSInt ToSize(Context.getTypeSize(ToType));
|
||||
|
||||
// Are we promoting to an int from a bitfield that fits in an int?
|
||||
if (BitWidth < ToSize ||
|
||||
(FromType->isSignedIntegerType() && BitWidth <= ToSize)) {
|
||||
return To->getKind() == BuiltinType::Int;
|
||||
if (FieldDecl *MemberDecl = dyn_cast<FieldDecl>(MemRef->getMemberDecl())) {
|
||||
APSInt BitWidth;
|
||||
if (MemberDecl->isBitField() &&
|
||||
FromType->isIntegralType() && !FromType->isEnumeralType() &&
|
||||
From->isIntegerConstantExpr(BitWidth, Context)) {
|
||||
APSInt ToSize(Context.getTypeSize(ToType));
|
||||
|
||||
// Are we promoting to an int from a bitfield that fits in an int?
|
||||
if (BitWidth < ToSize ||
|
||||
(FromType->isSignedIntegerType() && BitWidth <= ToSize)) {
|
||||
return To->getKind() == BuiltinType::Int;
|
||||
}
|
||||
|
||||
// Are we promoting to an unsigned int from an unsigned bitfield
|
||||
// that fits into an unsigned int?
|
||||
if (FromType->isUnsignedIntegerType() && BitWidth <= ToSize) {
|
||||
return To->getKind() == BuiltinType::UInt;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Are we promoting to an unsigned int from an unsigned bitfield
|
||||
// that fits into an unsigned int?
|
||||
if (FromType->isUnsignedIntegerType() && BitWidth <= ToSize) {
|
||||
return To->getKind() == BuiltinType::UInt;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// RUN: clang -fsyntax-only -verify %s
|
||||
|
||||
class X{
|
||||
public:
|
||||
enum E {Enumerator};
|
||||
int f();
|
||||
static int mem;
|
||||
static float g();
|
||||
};
|
||||
|
||||
void test(X* xp, X x) {
|
||||
int i1 = x.f();
|
||||
int i2 = xp->f();
|
||||
x.E; // expected-error{{cannot refer to type member 'E' with '.'}}
|
||||
xp->E; // expected-error{{cannot refer to type member 'E' with '->'}}
|
||||
// FIXME: lookup needs to find enumerators int i3 = x.Enumerator;
|
||||
// FIXME: int i4 = xp->Enumerator;
|
||||
x.mem = 1;
|
||||
xp->mem = 2;
|
||||
float f1 = x.g();
|
||||
float f2 = xp->g();
|
||||
}
|
Loading…
Reference in New Issue