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

Reply via email to