george.burgess.iv updated this revision to Diff 33104.
george.burgess.iv added a comment.
Talked with Richard, and we both agree that adding 4 EvaluationModes is too
much for the use case. So, we decided to add a flag to LValue to denote that
the LValueBase is invalid. This allows us to get by with just 2 new
EvaluationModes, which is much more acceptable.
LValueBases can only be invalid if you're using one of the shiny new
EvaluationModes.
http://reviews.llvm.org/D12169
Files:
lib/AST/ExprConstant.cpp
test/CodeGen/object-size.c
Index: test/CodeGen/object-size.c
===================================================================
--- test/CodeGen/object-size.c
+++ test/CodeGen/object-size.c
@@ -161,6 +161,15 @@
gi = __builtin_object_size(&foo.a, 2);
// CHECK: store i32 4
gi = __builtin_object_size(&foo.a, 3);
+
+ // CHECK: store i32 4
+ gi = __builtin_object_size(&foo.b, 0);
+ // CHECK: store i32 4
+ gi = __builtin_object_size(&foo.b, 1);
+ // CHECK: store i32 4
+ gi = __builtin_object_size(&foo.b, 2);
+ // CHECK: store i32 4
+ gi = __builtin_object_size(&foo.b, 3);
}
// CHECK: @test20
@@ -221,25 +230,59 @@
gi = __builtin_object_size(&t[9].t[10], 2);
// CHECK: store i32 0
gi = __builtin_object_size(&t[9].t[10], 3);
+
+ // CHECK: store i32 0
+ gi = __builtin_object_size((char*)&t[0] + sizeof(t), 0);
+ // CHECK: store i32 0
+ gi = __builtin_object_size((char*)&t[0] + sizeof(t), 1);
+ // CHECK: store i32 0
+ gi = __builtin_object_size((char*)&t[0] + sizeof(t), 2);
+ // CHECK: store i32 0
+ gi = __builtin_object_size((char*)&t[0] + sizeof(t), 3);
+
+ // CHECK: store i32 0
+ gi = __builtin_object_size((char*)&t[9].t[0] + 10*sizeof(t[0].t), 0);
+ // CHECK: store i32 0
+ gi = __builtin_object_size((char*)&t[9].t[0] + 10*sizeof(t[0].t), 1);
+ // CHECK: store i32 0
+ gi = __builtin_object_size((char*)&t[9].t[0] + 10*sizeof(t[0].t), 2);
+ // CHECK: store i32 0
+ gi = __builtin_object_size((char*)&t[9].t[0] + 10*sizeof(t[0].t), 3);
}
-struct Test23Ty { int t[10]; };
+struct Test23Ty { int a; int t[10]; };
// CHECK: @test23
-void test23(struct Test22Ty *p) {
+void test23(struct Test23Ty *p) {
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
gi = __builtin_object_size(p, 0);
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
gi = __builtin_object_size(p, 1);
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)
gi = __builtin_object_size(p, 2);
-
// Note: this is currently fixed at 0 because LLVM doesn't have sufficient
// data to correctly handle type=3
// CHECK: store i32 0
gi = __builtin_object_size(p, 3);
-}
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+ gi = __builtin_object_size(&p->a, 0);
+ // CHECK: store i32 4
+ gi = __builtin_object_size(&p->a, 1);
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)
+ gi = __builtin_object_size(&p->a, 2);
+ // CHECK: store i32 4
+ gi = __builtin_object_size(&p->a, 3);
+
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+ gi = __builtin_object_size(&p->t[5], 0);
+ // CHECK: store i32 20
+ gi = __builtin_object_size(&p->t[5], 1);
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)
+ gi = __builtin_object_size(&p->t[5], 2);
+ // CHECK: store i32 20
+ gi = __builtin_object_size(&p->t[5], 3);
+}
// PR24493 -- ICE if __builtin_object_size called with NULL and (Type & 1) != 0
// CHECK @test24
@@ -280,3 +323,72 @@
// CHECK: store i32 0
gi = __builtin_object_size((void*)0 + 0x1000, 3);
}
+
+// CHECK: @test26
+void test26(struct Test23Ty *p) {
+ struct { int t[10]; } t[10];
+
+ // CHECK: store i32 356
+ gi = __builtin_object_size((char*)&t[1].t[1], 0);
+ // CHECK: store i32 36
+ gi = __builtin_object_size((char*)&t[1].t[1], 1);
+ // CHECK: store i32 356
+ gi = __builtin_object_size((char*)&t[1].t[1], 2);
+ // CHECK: store i32 36
+ gi = __builtin_object_size((char*)&t[1].t[1], 3);
+}
+
+// CHECK: @test27
+void test27() {
+ struct { int t[10]; } t[10];
+
+ // CHECK: store i32 359
+ gi = __builtin_object_size((char*)&t[1].t[0]+1, 0);
+ // CHECK: store i32 39
+ gi = __builtin_object_size((char*)&t[1].t[0]+1, 1);
+ // CHECK: store i32 359
+ gi = __builtin_object_size((char*)&t[1].t[0]+1, 2);
+ // CHECK: store i32 39
+ gi = __builtin_object_size((char*)&t[1].t[0]+1, 3);
+}
+
+// CHECK: @test28
+void test28() {
+ struct { int v[10]; } t[10];
+
+ // CHECK: store i32 356
+ gi = __builtin_object_size(&t[0].v[11], 0);
+ // CHECK: store i32 0
+ gi = __builtin_object_size(&t[0].v[12], 1);
+ // CHECK: store i32 348
+ gi = __builtin_object_size(&t[0].v[13], 2);
+ // CHECK: store i32 0
+ gi = __builtin_object_size(&t[0].v[14], 3);
+}
+
+struct Test29IncompleteTy;
+
+// CHECK: @test29
+void test29(struct Test29IncompleteTy *t) {
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+ gi = __builtin_object_size(t, 0);
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+ gi = __builtin_object_size(t, 1);
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)
+ gi = __builtin_object_size(t, 2);
+ // Note: this is currently fixed at 0 because LLVM doesn't have sufficient
+ // data to correctly handle type=3
+ // CHECK: store i32 0
+ gi = __builtin_object_size(t, 3);
+
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* {{.*}}, i1 false)
+ gi = __builtin_object_size(&test29, 0);
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* {{.*}}, i1 false)
+ gi = __builtin_object_size(&test29, 1);
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* {{.*}}, i1 true)
+ gi = __builtin_object_size(&test29, 2);
+ // Note: this is currently fixed at 0 because LLVM doesn't have sufficient
+ // data to correctly handle type=3
+ // CHECK: store i32 0
+ gi = __builtin_object_size(&test29, 3);
+}
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -259,29 +259,9 @@
MostDerivedPathLength = Entries.size();
}
void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E, uint64_t N);
- /// Add N to the address of this subobject.
- void adjustIndex(EvalInfo &Info, const Expr *E, uint64_t N) {
- if (Invalid) return;
- if (MostDerivedPathLength == Entries.size() && MostDerivedArraySize) {
- Entries.back().ArrayIndex += N;
- if (Entries.back().ArrayIndex > MostDerivedArraySize) {
- diagnosePointerArithmetic(Info, E, Entries.back().ArrayIndex);
- setInvalid();
- }
- return;
- }
- // [expr.add]p4: For the purposes of these operators, a pointer to a
- // nonarray object behaves the same as a pointer to the first element of
- // an array of length one with the type of the object as its element type.
- if (IsOnePastTheEnd && N == (uint64_t)-1)
- IsOnePastTheEnd = false;
- else if (!IsOnePastTheEnd && N == 1)
- IsOnePastTheEnd = true;
- else if (N != 0) {
- diagnosePointerArithmetic(Info, E, uint64_t(IsOnePastTheEnd) + N);
- setInvalid();
- }
- }
+
+ /// Add N to the index of this subobject.
+ void adjustIndex(EvalInfo &Info, const Expr *E, uint64_t N);
};
/// A stack frame in the constexpr call stack.
@@ -492,14 +472,33 @@
/// optimizer if we don't constant fold them here, but in an unevaluated
/// context we try to fold them immediately since the optimizer never
/// gets a chance to look at it.
- EM_PotentialConstantExpressionUnevaluated
+ EM_PotentialConstantExpressionUnevaluated,
+
+ /// Evaluate as a potential constant expression, ignoring any side-effects
+ /// that may occur. The intent of this mode is to determine an LValue's
+ /// Offset, so things not ordinarily allowed in constexprs
+ /// (reinterpret_casts, OOB array indices, etc.) are allowed. As such, the
+ /// Offset of any given LValue may not be a multiple of the LValue's
+ /// type's size (e.g. in
+ /// short s[2];
+ /// char *p = (char*)s + 1;)
+ ///
+ /// Additionally, this mode is allowed to continue evaluating if an LValue
+ /// base is determined to be invalid, but the members of the base can be
+ /// determined.
+ EM_OffsetFold,
+
+ /// Identical to EM_OffsetFold, but we're evaluating as though the
+ /// expression is a constant expression.
+ EM_ConstantExpressionOffsetFold,
} EvalMode;
/// Are we checking whether the expression is a potential constant
/// expression?
bool checkingPotentialConstantExpression() const {
return EvalMode == EM_PotentialConstantExpression ||
- EvalMode == EM_PotentialConstantExpressionUnevaluated;
+ EvalMode == EM_PotentialConstantExpressionUnevaluated ||
+ EvalMode == EM_OffsetFold;
}
/// Are we checking an expression for overflow?
@@ -595,6 +594,8 @@
case EM_PotentialConstantExpression:
case EM_ConstantExpressionUnevaluated:
case EM_PotentialConstantExpressionUnevaluated:
+ case EM_OffsetFold:
+ case EM_ConstantExpressionOffsetFold:
HasActiveDiagnostic = false;
return OptionalDiagnostic();
}
@@ -669,11 +670,13 @@
case EM_PotentialConstantExpressionUnevaluated:
case EM_EvaluateForOverflow:
case EM_IgnoreSideEffects:
+ case EM_OffsetFold:
return true;
case EM_ConstantExpression:
case EM_ConstantExpressionUnevaluated:
case EM_ConstantFold:
+ case EM_ConstantExpressionOffsetFold:
return false;
}
llvm_unreachable("Missed EvalMode case");
@@ -696,16 +699,38 @@
case EM_PotentialConstantExpression:
case EM_PotentialConstantExpressionUnevaluated:
case EM_EvaluateForOverflow:
+ case EM_OffsetFold:
return true;
case EM_ConstantExpression:
case EM_ConstantExpressionUnevaluated:
case EM_ConstantFold:
case EM_IgnoreSideEffects:
+ case EM_ConstantExpressionOffsetFold:
return false;
}
llvm_unreachable("Missed EvalMode case");
}
+
+ bool allowReinterpretCasts() const {
+ return EvalMode == EM_OffsetFold ||
+ EvalMode == EM_ConstantExpressionOffsetFold;
+ }
+
+ bool allowOutOfBoundsIndices() const {
+ return EvalMode == EM_OffsetFold ||
+ EvalMode == EM_ConstantExpressionOffsetFold;
+ }
+
+ bool allowNonObjectBoundaryOffsets() const {
+ return EvalMode == EM_OffsetFold ||
+ EvalMode == EM_ConstantExpressionOffsetFold;
+ }
+
+ bool allowInvalidBaseExpr() const {
+ return EvalMode == EM_OffsetFold ||
+ EvalMode == EM_ConstantExpressionOffsetFold;
+ }
};
/// Object used to treat all foldable expressions as constant expressions.
@@ -736,6 +761,22 @@
}
};
+ /// RAII object used to treat the current evaluation as the correct pointer
+ /// offset fold for the current EvalMode
+ struct FoldOffsetRAII {
+ EvalInfo &Info;
+ EvalInfo::EvaluationMode OldMode;
+ explicit FoldOffsetRAII(EvalInfo &Info)
+ : Info(Info), OldMode(Info.EvalMode) {
+ if (Info.checkingPotentialConstantExpression())
+ Info.EvalMode = EvalInfo::EM_OffsetFold;
+ else
+ Info.EvalMode = EvalInfo::EM_ConstantExpressionOffsetFold;
+ }
+
+ ~FoldOffsetRAII() { Info.EvalMode = OldMode; }
+ };
+
/// RAII object used to suppress diagnostics and side-effects from a
/// speculative evaluation.
class SpeculativeEvaluationRAII {
@@ -818,6 +859,30 @@
setInvalid();
}
+void SubobjectDesignator::adjustIndex(EvalInfo &Info, const Expr *E, uint64_t N) {
+ if (Invalid) return;
+ if (MostDerivedPathLength == Entries.size() && MostDerivedArraySize) {
+ Entries.back().ArrayIndex += N;
+ if (!Info.allowOutOfBoundsIndices() &&
+ Entries.back().ArrayIndex > MostDerivedArraySize) {
+ diagnosePointerArithmetic(Info, E, Entries.back().ArrayIndex);
+ setInvalid();
+ }
+ return;
+ }
+ // [expr.add]p4: For the purposes of these operators, a pointer to a
+ // nonarray object behaves the same as a pointer to the first element of
+ // an array of length one with the type of the object as its element type.
+ if (IsOnePastTheEnd && N == (uint64_t)-1)
+ IsOnePastTheEnd = false;
+ else if (!IsOnePastTheEnd && N == 1)
+ IsOnePastTheEnd = true;
+ else if (N != 0) {
+ diagnosePointerArithmetic(Info, E, uint64_t(IsOnePastTheEnd) + N);
+ setInvalid();
+ }
+}
+
CallStackFrame::CallStackFrame(EvalInfo &Info, SourceLocation CallLoc,
const FunctionDecl *Callee, const LValue *This,
APValue *Arguments)
@@ -917,7 +982,8 @@
struct LValue {
APValue::LValueBase Base;
CharUnits Offset;
- unsigned CallIndex;
+ bool InvalidBase : 1;
+ unsigned CallIndex : 31;
SubobjectDesignator Designator;
const APValue::LValueBase getLValueBase() const { return Base; }
@@ -938,17 +1004,23 @@
assert(V.isLValue());
Base = V.getLValueBase();
Offset = V.getLValueOffset();
+ InvalidBase = false;
CallIndex = V.getLValueCallIndex();
Designator = SubobjectDesignator(Ctx, V);
}
- void set(APValue::LValueBase B, unsigned I = 0) {
+ void set(APValue::LValueBase B, unsigned I = 0, bool BInvalid = false) {
Base = B;
Offset = CharUnits::Zero();
+ InvalidBase = BInvalid;
CallIndex = I;
Designator = SubobjectDesignator(getType(B));
}
+ void setInvalid(APValue::LValueBase B, unsigned I = 0) {
+ set(B, I, true);
+ }
+
// Check that this LValue is not based on a null pointer. If it is, produce
// a diagnostic and mark the designator as invalid.
bool checkNullPointer(EvalInfo &Info, const Expr *E,
@@ -1905,8 +1977,33 @@
return false;
// Compute the new offset in the appropriate width.
- LVal.Offset += Adjustment * SizeOfPointee;
- LVal.adjustIndex(Info, E, Adjustment);
+ CharUnits AddedOffset = Adjustment * SizeOfPointee;
+ int64_t IndexAdjustment = Adjustment;
+
+ // If we allow offsets that aren't on object boundaries, we need to take
+ // into account any additional offsets that aren't fully accounted for
+ // (already) by indices
+ if (Info.allowNonObjectBoundaryOffsets()) {
+ QualType ArrayType = LVal.Designator.MostDerivedType;
+ // If ArrayType is null, we're offsetting a constant, not an array index.
+ if (!ArrayType.isNull() && !ArrayType->isIncompleteType() &&
+ ArrayType != EltTy) {
+ CharUnits SizeOfArray;
+ if (!HandleSizeof(Info, E->getExprLoc(), ArrayType, SizeOfArray))
+ return false;
+
+ if (SizeOfArray != SizeOfPointee) {
+ CharUnits OffBoundary =
+ CharUnits::fromQuantity(LVal.Offset % SizeOfArray);
+ AddedOffset += OffBoundary;
+ LVal.Offset -= OffBoundary;
+ IndexAdjustment = AddedOffset / SizeOfArray;
+ }
+ }
+ }
+
+ LVal.Offset += AddedOffset;
+ LVal.adjustIndex(Info, E, IndexAdjustment);
return true;
}
@@ -3903,6 +4000,12 @@
bool DerivedZeroInitialization(const Expr *E) {
return static_cast<Derived*>(this)->ZeroInitialization(E);
}
+ // Called when we couldn't evaluate the LValue Base of a member expression,
+ // but the members could be visited properly.
+ bool DerivedInvalidBase(const APValue &V, const Expr *E) {
+ assert(Info.allowInvalidBaseExpr() && "This shouldn't be allowed");
+ return static_cast<Derived*>(this)->InvalidBase(V, E);
+ }
// Check whether a conditional operator with a non-constant condition is a
// potential constant expression. If neither arm is a potential constant
@@ -3952,6 +4055,11 @@
return Info.CCEDiag(E, D);
}
+ // Some expr evaluators can't cleanly handle an invalid LValue base
+ bool InvalidBase(const APValue &V, const Expr *E) {
+ return DerivedZeroInitialization(E);
+ }
+
bool ZeroInitialization(const Expr *E) { return Error(E); }
public:
@@ -4189,10 +4297,16 @@
/// A member expression where the object is a prvalue is itself a prvalue.
bool VisitMemberExpr(const MemberExpr *E) {
assert(!E->isArrow() && "missing call to bound member function?");
-
APValue Val;
- if (!Evaluate(Val, Info, E->getBase()))
- return false;
+ bool BaseInvalid = false;
+ if (!Evaluate(Val, Info, E->getBase())) {
+ if (!Info.allowInvalidBaseExpr())
+ return false;
+ CharUnits Offset = CharUnits::Zero();
+ APValue::NoLValuePath NoPath;
+ Val.setLValue(E->getBase(), Offset, NoPath, 0);
+ BaseInvalid = true;
+ }
QualType BaseTy = E->getBase()->getType();
@@ -4207,8 +4321,13 @@
Designator.addDeclUnchecked(FD);
APValue Result;
- return extractSubobject(Info, E, Obj, Designator, Result) &&
- DerivedSuccess(Result, E);
+ if (!extractSubobject(Info, E, Obj, Designator, Result))
+ return false;
+
+ if (BaseInvalid)
+ return DerivedInvalidBase(Result, E);
+ else
+ return DerivedSuccess(Result, E);
}
bool VisitCastExpr(const CastExpr *E) {
@@ -4337,23 +4456,31 @@
return true;
}
+ bool InvalidBase(const APValue &V, const Expr *E) {
+ Result.setInvalid(E);
+ return true;
+ }
+
bool VisitMemberExpr(const MemberExpr *E) {
// Handle non-static data members.
QualType BaseTy;
+ bool EvalOK;
if (E->isArrow()) {
- if (!EvaluatePointer(E->getBase(), Result, this->Info))
- return false;
+ EvalOK = EvaluatePointer(E->getBase(), Result, this->Info);
BaseTy = E->getBase()->getType()->castAs<PointerType>()->getPointeeType();
} else if (E->getBase()->isRValue()) {
assert(E->getBase()->getType()->isRecordType());
- if (!EvaluateTemporary(E->getBase(), Result, this->Info))
- return false;
+ EvalOK = EvaluateTemporary(E->getBase(), Result, this->Info);
BaseTy = E->getBase()->getType();
} else {
- if (!this->Visit(E->getBase()))
- return false;
+ EvalOK = this->Visit(E->getBase());
BaseTy = E->getBase()->getType();
}
+ if (!EvalOK) {
+ if (!this->Info.allowInvalidBaseExpr())
+ return false;
+ Result.setInvalid(E->getBase());
+ }
const ValueDecl *MD = E->getMemberDecl();
if (const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl())) {
@@ -4623,13 +4750,11 @@
}
bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) {
- // Handle static data members.
if (const VarDecl *VD = dyn_cast<VarDecl>(E->getMemberDecl())) {
VisitIgnoredValue(E->getBase());
return VisitVarDecl(E, VD);
}
- // Handle static member functions.
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(E->getMemberDecl())) {
if (MD->isStatic()) {
VisitIgnoredValue(E->getBase());
@@ -4755,6 +4880,12 @@
Result.setFrom(Info.Ctx, V);
return true;
}
+
+ bool InvalidBase(const APValue &V, const Expr *E) {
+ Result.setInvalid(E);
+ return true;
+ }
+
bool ZeroInitialization(const Expr *E) {
return Success((Expr*)nullptr);
}
@@ -4765,7 +4896,7 @@
bool VisitObjCStringLiteral(const ObjCStringLiteral *E)
{ return Success(E); }
bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E)
- { return Success(E); }
+ { return Success(E); }
bool VisitAddrLabelExpr(const AddrLabelExpr *E)
{ return Success(E); }
bool VisitCallExpr(const CallExpr *E);
@@ -4846,7 +4977,7 @@
// Bitcasts to cv void* are static_casts, not reinterpret_casts, so are
// permitted in constant expressions in C++11. Bitcasts from cv void* are
// also static_casts, but we disallow them as a resolution to DR1312.
- if (!E->getType()->isVoidPointerType()) {
+ if (!Info.allowReinterpretCasts() && !E->getType()->isVoidPointerType()) {
Result.Designator.setInvalid();
if (SubExpr->getType()->isVoidPointerType())
CCEDiag(E, diag::note_constexpr_invalid_cast)
@@ -6165,7 +6296,7 @@
/// Retrieves the "underlying object type" of the given expression,
/// as used by __builtin_object_size.
-static QualType getObjectType(APValue::LValueBase B) {
+static QualType GetObjectType(APValue::LValueBase B) {
if (const ValueDecl *D = B.dyn_cast<const ValueDecl*>()) {
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
return VD->getType();
@@ -6186,16 +6317,15 @@
// If there are any, but we can determine the pointed-to object anyway, then
// ignore the side-effects.
SpeculativeEvaluationRAII SpeculativeEval(Info);
- FoldConstant Fold(Info, true);
+ FoldOffsetRAII Fold(Info);
if (!EvaluatePointer(E->getArg(0), Base, Info))
return false;
}
CharUnits BaseOffset = Base.getLValueOffset();
-
- // If we point to before the start of the object, there are no
- // accessible bytes.
- if (BaseOffset < CharUnits::Zero())
+ // If we point to before the start of the object, there are no accessible
+ // bytes.
+ if (BaseOffset.isNegative())
return Success(0, E);
// MostDerivedType is null if we're dealing with a literal such as nullptr or
@@ -6205,32 +6335,36 @@
if (Base.Designator.MostDerivedType.isNull())
return Error(E);
+ // If Type & 1 is 0, we need to be able to statically guarantee that the bytes
+ // exist. If we can't verify the base, then we can't do that.
+ if ((Type & 1) == 0 && Base.InvalidBase)
+ return Error(E);
+
// If Type & 1 is 0, the object in question is the complete object; reset to
// a complete object designator in that case.
//
// If Type is 1 and we've lost track of the subobject, just find the complete
// object instead. (If Type is 3, that's not correct behavior and we should
// return 0 instead.)
LValue End = Base;
- if (((Type & 1) == 0) || (End.Designator.Invalid && Type == 1)) {
- QualType T = getObjectType(End.getLValueBase());
+ if ((Type & 1) == 0 || (End.Designator.Invalid && Type == 1)) {
+ QualType T = GetObjectType(End.getLValueBase());
if (T.isNull())
End.Designator.setInvalid();
else {
End.Designator = SubobjectDesignator(T);
End.Offset = CharUnits::Zero();
}
}
- // FIXME: We should produce a valid object size for an unknown object with a
- // known designator, if Type & 1 is 1. For instance:
+ // We produce a valid object size for an unknown object with a known
+ // designator, if Type & 1 is 1. For instance:
//
// extern struct X { char buff[32]; int a, b, c; } *p;
// int a = __builtin_object_size(p->buff + 4, 3); // returns 28
// int b = __builtin_object_size(p->buff + 4, 2); // returns 0, not 40
//
- // This is GCC's behavior. We currently don't do this, but (hopefully) will in
- // the near future.
+ // This matches GCC's behavior.
// If it is not possible to determine which objects ptr points to at compile
// time, __builtin_object_size should return (size_t) -1 for type 0 or 1
@@ -6244,23 +6378,29 @@
int64_t AmountToAdd = 1;
if (End.Designator.MostDerivedArraySize &&
End.Designator.Entries.size() == End.Designator.MostDerivedPathLength) {
- // We got a pointer to an array. Step to its end.
+ // We got a pointer to an array. Step to its end. Note that this can be
+ // negative (because array indices can be negative/OOB).
AmountToAdd = End.Designator.MostDerivedArraySize -
- End.Designator.Entries.back().ArrayIndex;
- } else if (End.Designator.IsOnePastTheEnd) {
+ End.Designator.Entries.back().ArrayIndex;
+ } else if (End.Designator.isOnePastTheEnd()) {
// We're already pointing at the end of the object.
AmountToAdd = 0;
}
- if (End.Designator.MostDerivedType->isIncompleteType() ||
- End.Designator.MostDerivedType->isFunctionType())
+ QualType PointeeType = End.Designator.MostDerivedType;
+ assert(!PointeeType.isNull());
+ if (PointeeType->isIncompleteType() || PointeeType->isFunctionType())
return Error(E);
- if (!HandleLValueArrayAdjustment(Info, E, End, End.Designator.MostDerivedType,
- AmountToAdd))
+ CharUnits SizeOfPointee;
+ if (!HandleSizeof(Info, E->getExprLoc(), PointeeType, SizeOfPointee))
return false;
- auto EndOffset = End.getLValueOffset();
+ CharUnits EndOffset = End.getLValueOffset();
+ // If Start's Designator is offset from an object boundary, End's is, as well.
+ // We need to account for this.
+ EndOffset += SizeOfPointee * AmountToAdd;
+ EndOffset -= CharUnits::fromQuantity(EndOffset % SizeOfPointee);
if (BaseOffset > EndOffset)
return Success(0, E);
@@ -6297,6 +6437,8 @@
case EvalInfo::EM_ConstantFold:
case EvalInfo::EM_EvaluateForOverflow:
case EvalInfo::EM_IgnoreSideEffects:
+ case EvalInfo::EM_OffsetFold:
+ case EvalInfo::EM_ConstantExpressionOffsetFold:
// Leave it to IR generation.
return Error(E);
case EvalInfo::EM_ConstantExpressionUnevaluated:
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits