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

Talked with Richard, and we both agree that adding 4 EvaluationModes is too 
much for the use case. So, we decided to add a flag to LValue to denote that 
the LValueBase is invalid. This allows us to get by with just 2 new 
EvaluationModes, which is much more acceptable.

LValueBases can only be invalid if you're using one of the shiny new 
EvaluationModes.


http://reviews.llvm.org/D12169

Files:
  lib/AST/ExprConstant.cpp
  test/CodeGen/object-size.c

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

Reply via email to