george.burgess.iv created this revision.
george.burgess.iv added a reviewer: rsmith.
george.burgess.iv added a subscriber: cfe-commits.
(Hoping the formatting works as one would expect)
Motivating examples:
Pre-patch:
```
__builtin_object_size((char*)&foo, 0) != __builtin_object_size(&foo, 0) // if
__builtin_object_size(&foo, 0) != -1
__builtin_object_size(&foo[1].bar[-1].baz, 1) == -1. // Always.
```
Post-patch:
Both act as one would expect. This was accomplished by making three changes:
- Adding a flag to PointerExprEvaluator that makes it more accepting of
reinterpret_casts.
- Making array index/pointer offset less coupled in PointerExprEvaluator (we
now carry around an extra Offset field that denotes how far we are away from an
object boundary).
- Adding an OutermostMemberEvaluator that ignores `foo[1].bar[-1]` in
`foo[1].bar[-1].baz`, and is more relaxed with casts/pointer arithmetic/etc.
(Not 100% sold on the name)
http://reviews.llvm.org/D12169
Files:
lib/AST/ExprConstant.cpp
test/CXX/expr/expr.const/p2-0x.cpp
test/CodeGen/object-size.c
Index: test/CodeGen/object-size.c
===================================================================
--- test/CodeGen/object-size.c
+++ test/CodeGen/object-size.c
@@ -221,12 +221,30 @@
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]; };
// 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)
@@ -240,7 +258,6 @@
gi = __builtin_object_size(p, 3);
}
-
// PR24493 -- ICE if __builtin_object_size called with NULL and (Type & 1) != 0
// CHECK @test24
void test24() {
@@ -280,3 +297,81 @@
// 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 t[10]; } t[10];
+
+ // CHECK: store i32 356
+ gi = __builtin_object_size(&t[0].t[11], 0);
+ // CHECK: store i32 0
+ gi = __builtin_object_size(&t[0].t[12], 1);
+ // CHECK: store i32 348
+ gi = __builtin_object_size(&t[0].t[13], 2);
+ // CHECK: store i32 0
+ gi = __builtin_object_size(&t[0].t[14], 3);
+
+ // CHECK: store i32 364
+ gi = __builtin_object_size(&t[1].t[-1], 0);
+ // CHECK: store i32 0
+ gi = __builtin_object_size(&t[1].t[-2], 1);
+ // CHECK: store i32 372
+ gi = __builtin_object_size(&t[1].t[-3], 2);
+ // CHECK: store i32 0
+ gi = __builtin_object_size(&t[1].t[-4], 3);
+}
+
+struct Test30IncompleteTy;
+
+// CHECK: @test29
+void test29(struct Test30IncompleteTy *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: test/CXX/expr/expr.const/p2-0x.cpp
===================================================================
--- test/CXX/expr/expr.const/p2-0x.cpp
+++ test/CXX/expr/expr.const/p2-0x.cpp
@@ -191,7 +191,7 @@
constexpr B *p = a[0] + 4; // expected-error {{constant expression}} expected-note {{element 4 of array of 3 elements}}
B b = {};
constexpr A *pa = &b + 1; // expected-error {{constant expression}} expected-note {{base class of pointer past the end}}
- constexpr B *pb = (B*)((A*)&b + 1); // expected-error {{constant expression}} expected-note {{derived class of pointer past the end}}
+ constexpr B *pb = (B*)((A*)&b + 4); // expected-error {{constant expression}} expected-note {{derived class of pointer past the end}}
constexpr const int *pn = &(&b + 1)->n; // expected-error {{constant expression}} expected-note {{field of pointer past the end}}
constexpr B *parr = &a[3][0]; // expected-error {{constant expression}} expected-note {{array element of pointer past the end}}
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -48,6 +48,8 @@
#include <cstring>
#include <functional>
+#include <iostream>
+
using namespace clang;
using llvm::APSInt;
using llvm::APFloat;
@@ -259,7 +261,8 @@
MostDerivedPathLength = Entries.size();
}
void diagnosePointerArithmetic(EvalInfo &Info, const Expr *E, uint64_t N);
- /// Add N to the address of this subobject.
+
+ /// Add N to the index of this subobject.
void adjustIndex(EvalInfo &Info, const Expr *E, uint64_t N) {
if (Invalid) return;
if (MostDerivedPathLength == Entries.size() && MostDerivedArraySize) {
@@ -1094,7 +1097,14 @@
const LValue &This, const Expr *E,
bool AllowNonLiteralTypes = false);
static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info);
-static bool EvaluatePointer(const Expr *E, LValue &Result, EvalInfo &Info);
+static bool EvaluateOutermostMember(const Expr *E, EvalInfo &Info,
+ QualType &Result, CharUnits &Offset,
+ bool &IsInnermost);
+static bool EvaluatePointerWithOffset(const Expr *E, LValue &Result,
+ CharUnits &Offset, EvalInfo &Info,
+ bool UseStrictCastingRules = true);
+static bool EvaluatePointer(const Expr *E, LValue &Result, EvalInfo &Info,
+ bool UseStrictCastingRules = true);
static bool EvaluateMemberPointer(const Expr *E, MemberPtr &Result,
EvalInfo &Info);
static bool EvaluateTemporary(const Expr *E, LValue &Result, EvalInfo &Info);
@@ -4742,14 +4752,26 @@
: public ExprEvaluatorBase<PointerExprEvaluator> {
LValue &Result;
+ /// Any additional offset from Result that can't be accurately recorded as an
+ /// index in Result.Entries.back()
+ CharUnits &AddedOffset;
+
+ /// Though reinterpret_cast is not allowed in constexpr evaluation, we
+ /// sometimes use PointerExprEvaluator for non-constexpr things (intrinsics),
+ /// in which we can allow BitCasts to char*/etc. purely for pointer
+ /// arithmetic.
+ bool UseStrictCastingRules;
+
bool Success(const Expr *E) {
Result.set(E);
return true;
}
-public:
- PointerExprEvaluator(EvalInfo &info, LValue &Result)
- : ExprEvaluatorBaseTy(info), Result(Result) {}
+public:
+ PointerExprEvaluator(EvalInfo &info, LValue &Result, CharUnits &AddedOffset,
+ bool UseStrictCastingRules)
+ : ExprEvaluatorBaseTy(info), Result(Result), AddedOffset(AddedOffset),
+ UseStrictCastingRules(UseStrictCastingRules) {}
bool Success(const APValue &V, const Expr *E) {
Result.setFrom(Info.Ctx, V);
@@ -4765,7 +4787,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);
@@ -4793,9 +4815,39 @@
};
} // end anonymous namespace
-static bool EvaluatePointer(const Expr* E, LValue& Result, EvalInfo &Info) {
+/// Evaluates a pointer expression that may have offsets that are not a
+/// multiple of sizeof(*ptr), while trying to fold these offsets into an index.
+/// e.g.
+/// short s[2];
+/// // AddedOffset == 1, Result.Designator.Entries.back().Index == 0
+/// constexpr short *sp1 = (char*)&s[0] + 1;
+/// // AddedOffset == 0, Result.Designator.Entries.back().Index == 1
+/// constexpr short *sp2 = (char*)&s[0] + sizeof(short);
+///
+/// The additional offset **is included** in Result.Offset. AddedOffset should
+/// only be used if knowledge of just this added offset is necessary.
+static bool EvaluatePointerWithOffset(const Expr *E, LValue &Result,
+ CharUnits &AddedOffset, EvalInfo &Info,
+ bool StrictCasts) {
assert(E->isRValue() && E->getType()->hasPointerRepresentation());
- return PointerExprEvaluator(Info, Result).Visit(E);
+ AddedOffset = CharUnits::Zero();
+ return PointerExprEvaluator(Info, Result, AddedOffset, StrictCasts).Visit(E);
+}
+
+/// Attempts to evaluate a pointer expression. Fails and invalidates Result's
+/// Designator if there's any offset that can't be represented as an array
+/// index. (e.g. Evaluate(&I32Array+1) is okay, Evaluate((char*)&I32Array+1)
+/// is not.)
+static bool EvaluatePointer(const Expr *E, LValue &Result, EvalInfo &Info,
+ bool StrictCasts) {
+ CharUnits Offset;
+ if (!EvaluatePointerWithOffset(E, Result, Offset, Info, StrictCasts))
+ return false;
+ if (!Offset.isZero()) {
+ Result.Designator.setInvalid();
+ return false;
+ }
+ return true;
}
bool PointerExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
@@ -4808,21 +4860,53 @@
if (IExp->getType()->isPointerType())
std::swap(PExp, IExp);
- bool EvalPtrOK = EvaluatePointer(PExp, Result, Info);
+ bool EvalPtrOK = EvaluatePointer(PExp, Result, Info, UseStrictCastingRules);
if (!EvalPtrOK && !Info.keepEvaluatingAfterFailure())
return false;
llvm::APSInt Offset;
if (!EvaluateInteger(IExp, Offset, Info) || !EvalPtrOK)
return false;
- int64_t AdditionalOffset = getExtValue(Offset);
+ int64_t IndexOffset = getExtValue(Offset);
if (E->getOpcode() == BO_Sub)
- AdditionalOffset = -AdditionalOffset;
+ IndexOffset = -IndexOffset;
QualType Pointee = PExp->getType()->castAs<PointerType>()->getPointeeType();
- return HandleLValueArrayAdjustment(Info, E, Result, Pointee,
- AdditionalOffset);
+
+ // Special case: (char*)Foo.Bar[2] + sizeof(Foo.Bar[2]).
+ // We need to adjust the array index we're handing off in that case.
+ QualType ArrayTy = Result.Designator.MostDerivedType;
+ if (!ArrayTy.isNull() && !ArrayTy->isIncompleteType() && ArrayTy != Pointee) {
+ CharUnits PointeeEltSize;
+ if (!HandleSizeof(Info, PExp->getExprLoc(), Pointee, PointeeEltSize))
+ return false;
+
+ CharUnits ArrayEltSize;
+ if (!HandleSizeof(Info, PExp->getExprLoc(), ArrayTy, ArrayEltSize))
+ return false;
+
+ // Additionally, because LValuePathEntry wasn't made to really handle byte
+ // offsets, we need to keep the LValue Offset up-to-date (so casting e.g.
+ // BaseToDerived works as intended), but we also need to lie a bit about the
+ // array index.
+ //
+ // Example:
+ // (char*)Foo.Bar[1] + 2 // (assuming sizeof(Foo.Bar[1]) > 2)
+ // Will have an index of 1 and an offset of offsetof(Foo.Bar[1]) + 2.
+ if (PointeeEltSize != ArrayEltSize) {
+ CharUnits ByteOffset = IndexOffset * PointeeEltSize + AddedOffset;
+ CharUnits Rem = CharUnits::fromQuantity(ByteOffset % ArrayEltSize);
+ Result.Offset += Rem - AddedOffset;
+ AddedOffset = Rem;
+ IndexOffset = ByteOffset / ArrayEltSize;
+ }
+
+ // HandleLValueArrayAdjustment takes the type of the array
+ Pointee = ArrayTy;
+ }
+
+ return HandleLValueArrayAdjustment(Info, E, Result, Pointee, IndexOffset);
}
bool PointerExprEvaluator::VisitUnaryAddrOf(const UnaryOperator *E) {
@@ -4846,7 +4930,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 (UseStrictCastingRules && !E->getType()->isVoidPointerType()) {
Result.Designator.setInvalid();
if (SubExpr->getType()->isVoidPointerType())
CCEDiag(E, diag::note_constexpr_invalid_cast)
@@ -6165,7 +6249,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();
@@ -6177,93 +6261,182 @@
return QualType();
}
+namespace {
+/// We need an overall substantially more relaxed evaluator for
+/// __builtin_object_size than we need for constexprs. Some examples of GCC's
+/// behavior...
+/// struct A { struct { int is[10]; } b[10]; } a[10];
+/// __builtin_object_size(&a[-1].b[-1].is[0], 1) == 40
+/// __builtin_object_size(&a[-1].b[-1].is[-1], 1) == 0
+/// __builtin_object_size(&a[10].b[10].is[0], 1) == 40
+/// __builtin_object_size((char*)&a[0].b[0] + sizeof(a[0].b[0]), 1) == 40
+/// __builtin_object_size(&((struct A *)nullptr)->b[0], 1) == 40
+class OutermostMemberEvaluator
+ : public ConstStmtVisitor<OutermostMemberEvaluator, bool> {
+ EvalInfo &Info;
+ QualType &Result;
+
+ // Identical to Offset in PointerExprEvaluator
+ CharUnits &Offset;
+
+ // Is the outermost field also the innermost field? (e.g. in &foo)
+ bool &IsInnermost;
+
+ bool handleOffsetValue(const Expr *PExp, int64_t Offset);
+ bool handleOffsetExpr(const Expr *PExp, const Expr *IExp, bool Negative);
+
+public:
+ OutermostMemberEvaluator(EvalInfo &Info, QualType &Result, CharUnits &Offset,
+ bool &IsInnermost)
+ : Info(Info), Result(Result), Offset(Offset), IsInnermost(IsInnermost) {}
+
+ bool VisitBinaryOperator(const BinaryOperator *E);
+ bool VisitDeclRefExpr(const DeclRefExpr *E);
+ bool VisitMemberExpr(const MemberExpr *E);
+
+ bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+ return handleOffsetExpr(E->getBase(), E->getIdx(), false);
+ }
+ bool VisitCastExpr(const CastExpr *E) { return Visit(E->getSubExpr()); }
+ bool VisitParenExpr(const ParenExpr *E) { return Visit(E->getSubExpr()); }
+ bool VisitParenListExpr(const ParenListExpr *E) {
+ return Visit(E->getExpr(E->getNumExprs() - 1));
+ }
+ bool VisitUnaryAddrOf(const UnaryOperator *E) {
+ return Visit(E->getSubExpr());
+ }
+};
+} // anonymous namespace
+
+bool OutermostMemberEvaluator::handleOffsetExpr(const Expr *PExp,
+ const Expr *IExp,
+ bool Negative) {
+ APSInt Additional;
+ if (!EvaluateInteger(IExp, Additional, Info))
+ return false;
+
+ int64_t AdditionalUnits = getExtValue(Additional);
+ if (Negative)
+ AdditionalUnits = -AdditionalUnits;
+
+ return handleOffsetValue(PExp, AdditionalUnits);
+}
+
+bool OutermostMemberEvaluator::handleOffsetValue(const Expr *PExp,
+ int64_t Amount) {
+ QualType PtrType = PExp->getType();
+ assert(PtrType->isPointerType());
+
+ // We need to be able to handle cases where we're not given selectors. The
+ // most natural type to take from those is the type most local to the
+ // pointer.
+ Result = PtrType;
+
+ if (!EvaluateOutermostMember(PExp, Info, Result, Offset, IsInnermost))
+ return false;
+
+ QualType PointeeType = PtrType->castAs<PointerType>()->getPointeeType();
+ CharUnits PointeeSize;
+ if (!HandleSizeof(Info, PExp->getExprLoc(), PointeeType, PointeeSize))
+ return false;
+
+ Offset += PointeeSize * Amount;
+ return true;
+}
+
+static bool EvaluateOutermostMember(const Expr *E, EvalInfo &Info,
+ QualType &Result, CharUnits &Offset,
+ bool &IsInnermost) {
+ IsInnermost = true;
+ return OutermostMemberEvaluator(Info, Result, Offset, IsInnermost).Visit(E);
+}
+
+bool OutermostMemberEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
+ Result = E->getType();
+ return true;
+}
+
+bool OutermostMemberEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
+ auto Opcode = E->getOpcode();
+ if (Opcode == BO_Comma)
+ return Visit(E->getRHS());
+ if (Opcode != BO_Add && Opcode != BO_Sub)
+ return ConstStmtVisitor::VisitBinaryOperator(E);
+
+ bool Negative = Opcode == BO_Sub;
+ const Expr *PExp = E->getLHS();
+ const Expr *IExp = E->getRHS();
+ if (IExp->getType()->isPointerType())
+ std::swap(PExp, IExp);
+
+ return handleOffsetExpr(PExp, IExp, Negative);
+}
+
+bool OutermostMemberEvaluator::VisitMemberExpr(const MemberExpr *E) {
+ IsInnermost = false;
+ Result = E->getType();
+ return true;
+}
+
+/// Evaluates __builtin_object_size
bool IntExprEvaluator::TryEvaluateBuiltinObjectSize(const CallExpr *E,
unsigned Type) {
- // Determine the denoted object.
- LValue Base;
+ QualType FieldType;
+ CharUnits BaseOffset;
{
// The operand of __builtin_object_size is never evaluated for side-effects.
// 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);
- if (!EvaluatePointer(E->getArg(0), Base, Info))
- return false;
- }
- CharUnits BaseOffset = Base.getLValueOffset();
+ bool RequireFullEvaluation = true;
+ if (Type & 1) {
+ bool IsInnermost;
+ if (!EvaluateOutermostMember(E->getArg(0), Info, FieldType, BaseOffset,
+ IsInnermost))
+ return Error(E);
- // If we point to before the start of the object, there are no
- // accessible bytes.
- if (BaseOffset < CharUnits::Zero())
- return Success(0, E);
+ // If (Type & 1), we assume that any container(s) of the subobject are
+ // valid. However, if we're not dealing with a subobject, we need to
+ // act more or less as though the LSB of Type was unset to begin with.
+ RequireFullEvaluation = IsInnermost;
+ }
- // MostDerivedType is null if we're dealing with a literal such as nullptr or
- // (char*)0x100000. Lower it to LLVM in either case so it can figure out what
- // to do with it.
- // FIXME(gbiv): Try to do a better job with this in clang.
- if (Base.Designator.MostDerivedType.isNull())
- return Error(E);
+ if (RequireFullEvaluation) {
+ LValue Base;
+ CharUnits Discard;
+ if (!EvaluatePointerWithOffset(E->getArg(0), Base, Discard, Info, false))
+ 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 (T.isNull())
- End.Designator.setInvalid();
- else {
- End.Designator = SubobjectDesignator(T);
- End.Offset = CharUnits::Zero();
+ // MostDerivedType is null if we're dealing with a literal such as nullptr
+ // or (char*)0x100000. Lower it to LLVM in either case so it can figure
+ // out what to do with it.
+ // FIXME(gbiv): Try to do a better job with this in clang.
+ if (Base.Designator.MostDerivedType.isNull())
+ return Error(E);
+
+ FieldType = GetObjectType(Base.getLValueBase());
+ BaseOffset = Base.getLValueOffset();
}
}
- // FIXME: We should 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.
-
- // 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
- // and (size_t) 0 for type 2 or 3.
- if (End.Designator.Invalid)
- return false;
+ if (BaseOffset.isNegative())
+ return Success(0, E);
- // According to the GCC documentation, we want the size of the subobject
- // denoted by the pointer. But that's not quite right -- what we actually
- // want is the size of the immediately-enclosing array, if there is one.
- 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.
- AmountToAdd = End.Designator.MostDerivedArraySize -
- 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())
+ if (FieldType.isNull() || FieldType->isIncompleteType() ||
+ FieldType->isFunctionType())
return Error(E);
- if (!HandleLValueArrayAdjustment(Info, E, End, End.Designator.MostDerivedType,
- AmountToAdd))
- return false;
+ CharUnits EndOffset;
+ if (!HandleSizeof(Info, E->getExprLoc(), FieldType, EndOffset))
+ return Error(E);
- auto EndOffset = End.getLValueOffset();
if (BaseOffset > EndOffset)
return Success(0, E);
+ // Note: When this unifies N types at some point in the future, Type=2|3 needs
+ // to be min_element(sizes), Type=0|1 needs to be max_element(sizes)
return Success(EndOffset - BaseOffset, E);
}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits