Improve our handling of (C++) references within Clang. Specifically:

- Do not allow expressions to ever have reference type
  - Extend Expr::isLvalue to handle more cases where having written a
    reference into the source implies that the expression is an lvalue
    (e.g., function calls, C++ casts).
  - Make GRExprEngine::VisitCall treat the call arguments as lvalues when
    they are being bound to a reference parameter.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58306 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Douglas Gregor 2008-10-28 00:22:11 +00:00
Родитель 8173dba222
Коммит 9d293dfc0a
9 изменённых файлов: 114 добавлений и 68 удалений

Просмотреть файл

@ -38,7 +38,16 @@ namespace clang {
class Expr : public Stmt {
QualType TR;
protected:
Expr(StmtClass SC, QualType T) : Stmt(SC), TR(T) {}
Expr(StmtClass SC, QualType T) : Stmt(SC), TR(T) {
// In C++, the type of an expression is always adjusted so that it
// will not have reference type an expression will never have
// reference type (C++ [expr]p6). Use
// QualType::getNonReferenceType() to retrieve the non-reference
// type. Additionally, inspect Expr::isLvalue to determine whether
// an expression that is adjusted in this manner should be
// considered an lvalue.
assert((T.isNull() || !T->isReferenceType()) && "Expressions can't have reference type");
}
public:
QualType getType() const { return TR; }
void setType(QualType t) { TR = t; }
@ -786,20 +795,14 @@ public:
const Expr *getSubExpr() const { return cast<Expr>(Op); }
static bool classof(const Stmt *T) {
switch (T->getStmtClass()) {
case ImplicitCastExprClass:
case ExplicitCastExprClass:
case ExplicitCCastExprClass:
case CXXNamedCastExprClass:
case CXXStaticCastExprClass:
case CXXDynamicCastExprClass:
case CXXReinterpretCastExprClass:
case CXXConstCastExprClass:
case CXXFunctionalCastExprClass:
StmtClass SC = T->getStmtClass();
if (SC >= CXXNamedCastExprClass && SC <= CXXFunctionalCastExprClass)
return true;
default:
return false;
}
if (SC >= ImplicitCastExprClass && SC <= ExplicitCCastExprClass)
return true;
return false;
}
static bool classof(const CastExpr *) { return true; }
@ -862,18 +865,13 @@ public:
QualType getTypeAsWritten() const { return TypeAsWritten; }
static bool classof(const Stmt *T) {
switch (T->getStmtClass()) {
case ExplicitCastExprClass:
case ExplicitCCastExprClass:
case CXXFunctionalCastExprClass:
case CXXStaticCastExprClass:
case CXXDynamicCastExprClass:
case CXXReinterpretCastExprClass:
case CXXConstCastExprClass:
StmtClass SC = T->getStmtClass();
if (SC >= ExplicitCastExprClass && SC <= ExplicitCCastExprClass)
return true;
default:
return false;
}
if (SC >= CXXNamedCastExprClass && SC <= CXXFunctionalCastExprClass)
return true;
return false;
}
static bool classof(const ExplicitCastExpr *) { return true; }
};

Просмотреть файл

@ -301,7 +301,8 @@ class CXXConditionDeclExpr : public DeclRefExpr {
public:
CXXConditionDeclExpr(SourceLocation startLoc,
SourceLocation eqLoc, VarDecl *var)
: DeclRefExpr(CXXConditionDeclExprClass, var, var->getType(), startLoc) {}
: DeclRefExpr(CXXConditionDeclExprClass, var,
var->getType().getNonReferenceType(), startLoc) {}
virtual void Destroy(ASTContext& Ctx);

Просмотреть файл

@ -95,10 +95,10 @@ STMT(61, CXXStaticCastExpr , CXXNamedCastExpr)
STMT(62, CXXDynamicCastExpr , CXXNamedCastExpr)
STMT(63, CXXReinterpretCastExpr , CXXNamedCastExpr)
STMT(64, CXXConstCastExpr , CXXNamedCastExpr)
STMT(65, CXXBoolLiteralExpr , Expr)
STMT(66, CXXThrowExpr , Expr)
STMT(67, CXXDefaultArgExpr , Expr)
STMT(68, CXXFunctionalCastExpr , Expr)
STMT(65, CXXFunctionalCastExpr , Expr)
STMT(66, CXXBoolLiteralExpr , Expr)
STMT(67, CXXThrowExpr , Expr)
STMT(68, CXXDefaultArgExpr , Expr)
STMT(69, CXXZeroInitValueExpr , Expr)
STMT(70, CXXConditionDeclExpr , DeclRefExpr)

Просмотреть файл

@ -495,6 +495,10 @@ protected:
void VisitCall(CallExpr* CE, NodeTy* Pred,
CallExpr::arg_iterator AI, CallExpr::arg_iterator AE,
NodeSet& Dst);
void VisitCallRec(CallExpr* CE, NodeTy* Pred,
CallExpr::arg_iterator AI, CallExpr::arg_iterator AE,
NodeSet& Dst, const FunctionTypeProto *,
unsigned ParamIdx = 0);
/// VisitCast - Transfer function logic for all casts (implicit and explicit).
void VisitCast(Expr* CastE, Expr* Ex, NodeTy* Pred, NodeSet& Dst);

Просмотреть файл

@ -393,6 +393,20 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
break;
case ParenExprClass: // C99 6.5.1p5
return cast<ParenExpr>(this)->getSubExpr()->isLvalue(Ctx);
case CallExprClass: {
// C++ [expr.call]p10:
// A function call is an lvalue if and only if the result type
// is a reference.
QualType CalleeType
= dyn_cast<CallExpr>(this)->getCallee()->IgnoreParens()->getType();
if (const PointerType *FnTypePtr = CalleeType->getAsPointerType())
if (const FunctionType *FnType
= FnTypePtr->getPointeeType()->getAsFunctionType())
if (FnType->getResultType()->isReferenceType())
return LV_Valid;
break;
}
case CompoundLiteralExprClass: // C99 6.5.2.5p5
return LV_Valid;
case ExtVectorElementExprClass:
@ -407,10 +421,25 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
return (cast<PredefinedExpr>(this)->getIdentType()
== PredefinedExpr::CXXThis
? LV_InvalidExpression : LV_Valid);
case VAArgExprClass:
return LV_Valid;
case CXXDefaultArgExprClass:
return cast<CXXDefaultArgExpr>(this)->getExpr()->isLvalue(Ctx);
case CXXConditionDeclExprClass:
return LV_Valid;
case ExplicitCCastExprClass:
case CXXFunctionalCastExprClass:
case CXXStaticCastExprClass:
case CXXDynamicCastExprClass:
case CXXReinterpretCastExprClass:
case CXXConstCastExprClass:
// The result of an explicit cast is an lvalue if the type we are
// casting to is a reference type. See C++ [expr.cast]p1,
// C++ [expr.static.cast]p2, C++ [expr.dynamic.cast]p2,
// C++ [expr.reinterpret.cast]p1, C++ [expr.const.cast]p1.
if (cast<ExplicitCastExpr>(this)->getTypeAsWritten()->isReferenceType())
return LV_Valid;
break;
default:
break;
}

Просмотреть файл

@ -1063,22 +1063,43 @@ const GRState* GRExprEngine::EvalLocation(Expr* Ex, NodeTy* Pred,
//===----------------------------------------------------------------------===//
// Transfer function: Function calls.
//===----------------------------------------------------------------------===//
void GRExprEngine::VisitCall(CallExpr* CE, NodeTy* Pred,
CallExpr::arg_iterator AI,
CallExpr::arg_iterator AE,
NodeSet& Dst) {
NodeSet& Dst)
{
// Determine the type of function we're calling (if available).
const FunctionTypeProto *Proto = NULL;
QualType FnType = CE->getCallee()->IgnoreParens()->getType();
if (const PointerType *FnTypePtr = FnType->getAsPointerType())
Proto = FnTypePtr->getPointeeType()->getAsFunctionTypeProto();
VisitCallRec(CE, Pred, AI, AE, Dst, Proto, /*ParamIdx=*/0);
}
void GRExprEngine::VisitCallRec(CallExpr* CE, NodeTy* Pred,
CallExpr::arg_iterator AI,
CallExpr::arg_iterator AE,
NodeSet& Dst, const FunctionTypeProto *Proto,
unsigned ParamIdx) {
// Process the arguments.
if (AI != AE) {
NodeSet DstTmp;
Visit(*AI, Pred, DstTmp);
// If the call argument is being bound to a reference parameter,
// visit it as an lvalue, not an rvalue.
bool VisitAsLvalue = false;
if (Proto && ParamIdx < Proto->getNumArgs())
VisitAsLvalue = Proto->getArgType(ParamIdx)->isReferenceType();
NodeSet DstTmp;
if (VisitAsLvalue)
VisitLValue(*AI, Pred, DstTmp);
else
Visit(*AI, Pred, DstTmp);
++AI;
for (NodeSet::iterator DI=DstTmp.begin(), DE=DstTmp.end(); DI != DE; ++DI)
VisitCall(CE, *DI, AI, AE, Dst);
VisitCallRec(CE, *DI, AI, AE, Dst, Proto, ParamIdx + 1);
return;
}

Просмотреть файл

@ -1382,11 +1382,6 @@ bool Sema::CheckForConstantInitializer(Expr *Init, QualType DclT) {
if (CompoundLiteralExpr *e = dyn_cast<CompoundLiteralExpr>(Init))
return CheckForConstantInitializer(e->getInitializer(), DclT);
if (Init->getType()->isReferenceType()) {
// FIXME: Work out how the heck references work.
return false;
}
if (InitListExpr *Exp = dyn_cast<InitListExpr>(Init)) {
unsigned numInits = Exp->getNumInits();
for (unsigned i = 0; i < numInits; i++) {

Просмотреть файл

@ -35,10 +35,6 @@ void Sema::DefaultFunctionArrayConversion(Expr *&E) {
QualType Ty = E->getType();
assert(!Ty.isNull() && "DefaultFunctionArrayConversion - missing type");
if (const ReferenceType *ref = Ty->getAsReferenceType()) {
ImpCastExprToType(E, ref->getPointeeType()); // C++ [expr]
Ty = E->getType();
}
if (Ty->isFunctionType())
ImpCastExprToType(E, Context.getPointerType(Ty));
else if (Ty->isArrayType()) {
@ -68,10 +64,6 @@ Expr *Sema::UsualUnaryConversions(Expr *&Expr) {
QualType Ty = Expr->getType();
assert(!Ty.isNull() && "UsualUnaryConversions - missing type");
if (const ReferenceType *Ref = Ty->getAsReferenceType()) {
ImpCastExprToType(Expr, Ref->getPointeeType()); // C++ [expr]
Ty = Expr->getType();
}
if (Ty->isPromotableIntegerType()) // C99 6.3.1.1p2
ImpCastExprToType(Expr, Context.IntTy);
else
@ -442,11 +434,13 @@ Sema::ExprResult Sema::ActOnIdentifierExpr(Scope *S, SourceLocation Loc,
if (CurBlock && ShouldSnapshotBlockValueReference(CurBlock, VD)) {
// The BlocksAttr indicates the variable is bound by-reference.
if (VD->getAttr<BlocksAttr>())
return new BlockDeclRefExpr(VD, VD->getType(), Loc, true);
return new BlockDeclRefExpr(VD, VD->getType().getNonReferenceType(),
Loc, true);
// Variable will be bound by-copy, make it const within the closure.
VD->getType().addConst();
return new BlockDeclRefExpr(VD, VD->getType(), Loc, false);
return new BlockDeclRefExpr(VD, VD->getType().getNonReferenceType(),
Loc, false);
}
// If this reference is not in a block or if the referenced variable is
// within the block, create a normal DeclRefExpr.
@ -1674,8 +1668,15 @@ Sema::CheckAssignmentConstraints(QualType lhsType, QualType rhsType) {
if (lhsType == rhsType)
return Compatible; // Common case: fast path an exact match.
if (lhsType->isReferenceType() || rhsType->isReferenceType()) {
if (Context.typesAreCompatible(lhsType, rhsType))
// If the left-hand side is a reference type, then we are in a
// (rare!) case where we've allowed the use of references in C,
// e.g., as a parameter type in a built-in function. In this case,
// just make sure that the type referenced is compatible with the
// right-hand side type. The caller is responsible for adjusting
// lhsType so that the resulting expression does not have reference
// type.
if (const ReferenceType *lhsTypeRef = lhsType->getAsReferenceType()) {
if (Context.typesAreCompatible(lhsTypeRef->getPointeeType(), rhsType))
return Compatible;
return Incompatible;
}
@ -1808,8 +1809,7 @@ Sema::CheckSingleAssignmentConstraints(QualType lhsType, Expr *&rExpr) {
// DeclExpr's (created by ActOnIdentifierExpr), it would mess up the unary
// expressions that surpress this implicit conversion (&, sizeof).
//
// Suppress this for references: C++ 8.5.3p5. FIXME: revisit when references
// are better understood.
// Suppress this for references: C++ 8.5.3p5.
if (!lhsType->isReferenceType())
DefaultFunctionArrayConversion(rExpr);
@ -1818,8 +1818,12 @@ Sema::CheckSingleAssignmentConstraints(QualType lhsType, Expr *&rExpr) {
// C99 6.5.16.1p2: The value of the right operand is converted to the
// type of the assignment expression.
// CheckAssignmentConstraints allows the left-hand side to be a reference,
// so that we can use references in built-in functions even in C.
// The getNonReferenceType() call makes sure that the resulting expression
// does not have reference type.
if (rExpr->getType() != lhsType)
ImpCastExprToType(rExpr, lhsType);
ImpCastExprToType(rExpr, lhsType.getNonReferenceType());
return result;
}
@ -2909,7 +2913,8 @@ Sema::ExprResult Sema::ActOnBuiltinOffsetOf(SourceLocation BuiltinLoc,
// FIXME: Verify that MemberDecl isn't a bitfield.
// MemberDecl->getType() doesn't get the right qualifiers, but it doesn't
// matter here.
Res = new MemberExpr(Res, false, MemberDecl, OC.LocEnd, MemberDecl->getType());
Res = new MemberExpr(Res, false, MemberDecl, OC.LocEnd,
MemberDecl->getType().getNonReferenceType());
}
return new UnaryOperator(Res, UnaryOperator::OffsetOf, Context.getSizeType(),
@ -3121,7 +3126,8 @@ Sema::ExprResult Sema::ActOnOverloadExpr(ExprTy **args, unsigned NumArgs,
OE->getFn()->getSourceRange());
// Remember our match, and continue processing the remaining arguments
// to catch any errors.
OE = new OverloadExpr(Args, NumArgs, i, FnType->getResultType(),
OE = new OverloadExpr(Args, NumArgs, i,
FnType->getResultType().getNonReferenceType(),
BuiltinLoc, RParenLoc);
}
}
@ -3168,7 +3174,7 @@ Sema::ExprResult Sema::ActOnVAArg(SourceLocation BuiltinLoc,
// FIXME: Warn if a non-POD type is passed in.
return new VAArgExpr(BuiltinLoc, E, T, RPLoc);
return new VAArgExpr(BuiltinLoc, E, T.getNonReferenceType(), RPLoc);
}
bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy,

Просмотреть файл

@ -79,10 +79,6 @@ Sema::CheckConstCast(SourceLocation OpLoc, Expr *&SrcExpr, QualType DestType)
// C++ 5.2.11p4: An lvalue of type T1 can be [cast] to an lvalue of type T2
// [...] if a pointer to T1 can be [cast] to the type pointer to T2.
DestType = Context.getPointerType(DestTypeTmp->getPointeeType());
if (const ReferenceType *SrcTypeTmp = SrcType->getAsReferenceType()) {
// FIXME: This shouldn't actually be possible, but right now it is.
SrcType = SrcTypeTmp->getPointeeType();
}
SrcType = Context.getPointerType(SrcType);
} else {
// C++ 5.2.11p1: Otherwise, the result is an rvalue and the
@ -177,10 +173,6 @@ Sema::CheckReinterpretCast(SourceLocation OpLoc, Expr *&SrcExpr,
// built-in & and * operators.
// This code does this transformation for the checked types.
DestType = Context.getPointerType(DestTypeTmp->getPointeeType());
if (const ReferenceType *SrcTypeTmp = SrcType->getAsReferenceType()) {
// FIXME: This shouldn't actually be possible, but right now it is.
SrcType = SrcTypeTmp->getPointeeType();
}
SrcType = Context.getPointerType(SrcType);
} else {
// C++ 5.2.10p1: [...] the lvalue-to-rvalue, array-to-pointer, and