зеркало из https://github.com/microsoft/clang-1.git
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:
Родитель
8173dba222
Коммит
9d293dfc0a
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче