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 cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits