george.burgess.iv updated this revision to Diff 32985.
george.burgess.iv added a comment.

Mostly redid the patch; removed OutermostExprEvaluator, moved evaluation intent 
into `EvaluationMode`, made it so the more interesting rulebreaking that 
`__builtin_object_size` allows is only allowed when evaluating 
`__builtin_object_size`.

Not entirely sold on adding 4 EvaluationModes, but we need to know two bits of 
information, both of which impact how we evaluate things:

- Whether we're evaluating an actual ConstantExpression or a 
PotentialConstantExpression (if we always assume the former, we get memory 
corruption by reaching into `CallStackFrame::Arguments` sometimes; if we assume 
the latter, `attribute(enable_if)` breaks).
- If we're allowed to assume that the base of a `MemberExpr` is valid.

Also, I'm not 100% on calling `LValue.set(MaybeAnRValue)` if we are assuming 
that the base of a `MemberExpr` is valid. However, in such a case, I would 
think that it's safe to assume that the caller doesn't care about the base. 
Plus, it's In The Documentation(TM) that you shouldn't rely on 
`Result.getLValueBase()` being a valid base when the EvalMode would allow this 
assumption to be made.


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,42 @@
       /// 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;)
+      EM_OffsetFold,
+
+      /// Identical to EM_OffsetFold, but we're evaluating as though the
+      /// expression is a constant expression.
+      EM_ConstantExpressionOffsetFold,
+
+      /// Identical to OffsetFold, except we should only evaluate for
+      /// subobjects. Containers are assumed to be valid and are otherwise
+      /// ignored. NOTE: At the moment, this may result in LValues set from
+      /// prvalue Exprs.
+      EM_PartialOffsetFold,
+
+      /// Identical to ConstantExpressionOffsetFold, except we should only
+      /// evaluate for subobjects. Containers are assumed to be valid and are
+      /// otherwise ignored. NOTE: At the moment, this may result in LValues set
+      /// from prvalue Exprs.
+      EM_ConstantExpressionPartialOffsetFold,
     } 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 ||
+             EvalMode == EM_PartialOffsetFold;
     }
 
     /// Are we checking an expression for overflow?
@@ -595,6 +603,10 @@
           case EM_PotentialConstantExpression:
           case EM_ConstantExpressionUnevaluated:
           case EM_PotentialConstantExpressionUnevaluated:
+          case EM_OffsetFold:
+          case EM_PartialOffsetFold:
+          case EM_ConstantExpressionOffsetFold:
+          case EM_ConstantExpressionPartialOffsetFold:
             HasActiveDiagnostic = false;
             return OptionalDiagnostic();
           }
@@ -669,11 +681,15 @@
       case EM_PotentialConstantExpressionUnevaluated:
       case EM_EvaluateForOverflow:
       case EM_IgnoreSideEffects:
+      case EM_OffsetFold:
+      case EM_PartialOffsetFold:
         return true;
 
       case EM_ConstantExpression:
       case EM_ConstantExpressionUnevaluated:
       case EM_ConstantFold:
+      case EM_ConstantExpressionOffsetFold:
+      case EM_ConstantExpressionPartialOffsetFold:
         return false;
       }
       llvm_unreachable("Missed EvalMode case");
@@ -696,16 +712,46 @@
       case EM_PotentialConstantExpression:
       case EM_PotentialConstantExpressionUnevaluated:
       case EM_EvaluateForOverflow:
+      case EM_OffsetFold:
+      case EM_PartialOffsetFold:
         return true;
 
       case EM_ConstantExpression:
       case EM_ConstantExpressionUnevaluated:
       case EM_ConstantFold:
       case EM_IgnoreSideEffects:
+      case EM_ConstantExpressionOffsetFold:
+      case EM_ConstantExpressionPartialOffsetFold:
         return false;
       }
       llvm_unreachable("Missed EvalMode case");
     }
+
+    bool allowReinterpretCasts() const {
+      return EvalMode == EM_OffsetFold ||
+             EvalMode == EM_PartialOffsetFold ||
+             EvalMode == EM_ConstantExpressionOffsetFold ||
+             EvalMode == EM_ConstantExpressionPartialOffsetFold;
+    }
+
+    bool allowOutOfBoundsIndices() const {
+      return EvalMode == EM_OffsetFold ||
+             EvalMode == EM_PartialOffsetFold ||
+             EvalMode == EM_ConstantExpressionOffsetFold ||
+             EvalMode == EM_ConstantExpressionPartialOffsetFold;
+    }
+
+    bool allowNonObjectBoundaryOffsets() const {
+      return EvalMode == EM_OffsetFold ||
+             EvalMode == EM_PartialOffsetFold ||
+             EvalMode == EM_ConstantExpressionOffsetFold ||
+             EvalMode == EM_ConstantExpressionPartialOffsetFold;
+    }
+
+    bool assumeBaseExprIsValid() const {
+      return EvalMode == EM_PartialOffsetFold ||
+             EvalMode == EM_ConstantExpressionPartialOffsetFold;
+    }
   };
 
   /// Object used to treat all foldable expressions as constant expressions.
@@ -736,6 +782,29 @@
     }
   };
 
+  /// 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, bool PartialEval)
+        : Info(Info), OldMode(Info.EvalMode) {
+      if (Info.checkingPotentialConstantExpression()) {
+        if (PartialEval)
+          Info.EvalMode = EvalInfo::EM_PartialOffsetFold;
+        else
+          Info.EvalMode = EvalInfo::EM_OffsetFold;
+        return;
+      }
+      if (PartialEval)
+        Info.EvalMode = EvalInfo::EM_ConstantExpressionPartialOffsetFold;
+      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 +887,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)
@@ -1905,8 +1998,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;
 }
 
@@ -4189,10 +4307,14 @@
   /// 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;
+    if (!Evaluate(Val, Info, E->getBase())) {
+      if (!Info.assumeBaseExprIsValid())
+        return false;
+      CharUnits Offset = CharUnits::Zero();
+      APValue::NoLValuePath NoPath;
+      Val.setLValue(E->getBase(), Offset, NoPath, 0);
+    }
 
     QualType BaseTy = E->getBase()->getType();
 
@@ -4340,20 +4462,23 @@
   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.assumeBaseExprIsValid())
+        return false;
+      Result.set(E->getBase());
+    }
 
     const ValueDecl *MD = E->getMemberDecl();
     if (const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl())) {
@@ -4623,13 +4748,14 @@
 }
 
 bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) {
-  // Handle static data members.
+  if (Info.assumeBaseExprIsValid())
+    return Success(E);
+
   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());
@@ -4747,7 +4873,6 @@
     return true;
   }
 public:
-
   PointerExprEvaluator(EvalInfo &info, LValue &Result)
     : ExprEvaluatorBaseTy(info), Result(Result) {}
 
@@ -4765,7 +4890,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 +4971,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 +6290,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();
@@ -6185,17 +6310,17 @@
     // 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.
+    bool Subobject = Type & 1;
     SpeculativeEvaluationRAII SpeculativeEval(Info);
-    FoldConstant Fold(Info, true);
+    FoldOffsetRAII Fold(Info, Subobject);
     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
@@ -6208,29 +6333,28 @@
   // 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.)
+  // We can't pretend that Type is 0 if Type is 1 and the Designator is invalid, 
+  // because the LValue's Base may be invalid as well. This is because we
+  // executed a partial offset fold in EvaluatePointer.
   LValue End = Base;
-  if (((Type & 1) == 0) || (End.Designator.Invalid && Type == 1)) {
-    QualType T = getObjectType(End.getLValueBase());
+  if ((Type & 1) == 0) {
+    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 +6368,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 +6427,10 @@
     case EvalInfo::EM_ConstantFold:
     case EvalInfo::EM_EvaluateForOverflow:
     case EvalInfo::EM_IgnoreSideEffects:
+    case EvalInfo::EM_OffsetFold:
+    case EvalInfo::EM_PartialOffsetFold:
+    case EvalInfo::EM_ConstantExpressionOffsetFold:
+    case EvalInfo::EM_ConstantExpressionPartialOffsetFold:
       // Leave it to IR generation.
       return Error(E);
     case EvalInfo::EM_ConstantExpressionUnevaluated:
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to