george.burgess.iv updated this revision to Diff 52167.
george.burgess.iv marked an inline comment as done.
george.burgess.iv added a comment.
Addressed all feedback.
http://reviews.llvm.org/D18540
Files:
lib/AST/ExprConstant.cpp
test/SemaCXX/builtin-object-size-cxx14.cpp
test/SemaCXX/constant-expression-cxx1y.cpp
Index: test/SemaCXX/constant-expression-cxx1y.cpp
===================================================================
--- test/SemaCXX/constant-expression-cxx1y.cpp
+++ test/SemaCXX/constant-expression-cxx1y.cpp
@@ -179,12 +179,10 @@
static_assert(!test1(100), "");
static_assert(!test1(101), ""); // expected-error {{constant expression}} expected-note {{in call to 'test1(101)'}}
- // FIXME: We should be able to reject this before it's called
- constexpr void f() {
+ constexpr void f() { // expected-error{{constexpr function never produces a constant expression}} expected-note@+2{{assignment to dereferenced one-past-the-end pointer is not allowed in a constant expression}}
char foo[10] = { "z" }; // expected-note {{here}}
- foo[10] = 'x'; // expected-warning {{past the end}} expected-note {{assignment to dereferenced one-past-the-end pointer}}
+ foo[10] = 'x'; // expected-warning {{past the end}}
}
- constexpr int k = (f(), 0); // expected-error {{constant expression}} expected-note {{in call}}
}
namespace array_resize {
Index: test/SemaCXX/builtin-object-size-cxx14.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/builtin-object-size-cxx14.cpp
@@ -0,0 +1,99 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s
+
+namespace basic {
+// Ensuring that __bos can be used in constexpr functions without anything
+// sketchy going on...
+constexpr int bos0() {
+ int k = 5;
+ char cs[10] = {};
+ return __builtin_object_size(&cs[k], 0);
+}
+
+constexpr int bos1() {
+ int k = 5;
+ char cs[10] = {};
+ return __builtin_object_size(&cs[k], 1);
+}
+
+constexpr int bos2() {
+ int k = 5;
+ char cs[10] = {};
+ return __builtin_object_size(&cs[k], 2);
+}
+
+constexpr int bos3() {
+ int k = 5;
+ char cs[10] = {};
+ return __builtin_object_size(&cs[k], 3);
+}
+
+static_assert(bos0() == sizeof(char) * 5, "");
+static_assert(bos1() == sizeof(char) * 5, "");
+static_assert(bos2() == sizeof(char) * 5, "");
+static_assert(bos3() == sizeof(char) * 5, "");
+}
+
+namespace in_enable_if {
+// The code that prompted these changes was __bos in enable_if
+
+void copy5CharsInto(char *buf) // expected-note{{candidate}}
+ __attribute__((enable_if(__builtin_object_size(buf, 0) != -1 &&
+ __builtin_object_size(buf, 0) > 5,
+ "")));
+
+// We use different EvalModes for __bos with type 0 versus 1. Ensure 1 works,
+// too...
+void copy5CharsIntoStrict(char *buf) // expected-note{{candidate}}
+ __attribute__((enable_if(__builtin_object_size(buf, 1) != -1 &&
+ __builtin_object_size(buf, 1) > 5,
+ "")));
+
+struct LargeStruct {
+ int pad;
+ char buf[6];
+ int pad2;
+};
+
+struct SmallStruct {
+ int pad;
+ char buf[5];
+ int pad2;
+};
+
+void noWriteToBuf() {
+ char buf[6];
+ copy5CharsInto(buf);
+
+ LargeStruct large;
+ copy5CharsIntoStrict(large.buf);
+}
+
+void initTheBuf() {
+ char buf[6] = {};
+ copy5CharsInto(buf);
+
+ LargeStruct large = {0, {}, 0};
+ copy5CharsIntoStrict(large.buf);
+}
+
+int getI();
+void initTheBufWithALoop() {
+ char buf[6] = {};
+ for (unsigned I = getI(); I != sizeof(buf); ++I)
+ buf[I] = I;
+ copy5CharsInto(buf);
+
+ LargeStruct large;
+ for (unsigned I = getI(); I != sizeof(buf); ++I)
+ large.buf[I] = I;
+ copy5CharsIntoStrict(large.buf);
+}
+
+void tooSmallBuf() {
+ char buf[5];
+ copy5CharsInto(buf); // expected-error{{no matching function for call}}
+
+ SmallStruct small;
+ copy5CharsIntoStrict(small.buf); // expected-error{{no matching function for call}}
+}
+}
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -478,6 +478,9 @@
/// fold (not just why it's not strictly a constant expression)?
bool HasFoldFailureDiagnostic;
+ /// \brief Whether or not we're currently speculatively evaluating.
+ bool IsSpeculativelyEvaluating;
+
enum EvaluationMode {
/// Evaluate as a constant expression. Stop if we find that the expression
/// is not a constant expression.
@@ -542,7 +545,8 @@
BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr),
EvaluatingDecl((const ValueDecl *)nullptr),
EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false),
- HasFoldFailureDiagnostic(false), EvalMode(Mode) {}
+ HasFoldFailureDiagnostic(false), IsSpeculativelyEvaluating(false),
+ EvalMode(Mode) {}
void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
EvaluatingDecl = Base;
@@ -764,6 +768,27 @@
llvm_unreachable("Missed EvalMode case");
}
+ /// Notes that we failed to evaluate an expression that other expressions
+ /// directly depend on, and determine if we should keep evaluating. This
+ /// should only be called if we actually intend to keep evaluating.
+ ///
+ /// Call noteSideEffect() instead if we may be able to ignore the value that
+ /// we failed to evaluate, e.g. if we failed to evaluate Foo() in:
+ ///
+ /// (Foo(), 1) // use noteSideEffect
+ /// (Foo() || true) // use noteSideEffect
+ /// Foo() + 1 // use noteUnrecoverableFailure
+ LLVM_ATTRIBUTE_UNUSED_RESULT bool noteUnrecoverableFailure() {
+ // This is a little bit icky. We leave HasSideEffects unchange if we
+ // aren't going to keep running, because eval modes that don't allow
+ // evaluation after side-effects/failure sometimes depend on the value of
+ // HasSideEffects. This is why this function should only be called if we
+ // intend to keep evaluating.
+ bool KeepGoing = keepEvaluatingAfterFailure();
+ EvalStatus.HasSideEffects |= KeepGoing;
+ return KeepGoing;
+ }
+
bool allowInvalidBaseExpr() const {
return EvalMode == EM_DesignatorFold;
}
@@ -817,18 +842,20 @@
class SpeculativeEvaluationRAII {
EvalInfo &Info;
Expr::EvalStatus Old;
+ bool OldSpecEval;
public:
- SpeculativeEvaluationRAII(EvalInfo &Info,
- SmallVectorImpl<PartialDiagnosticAt> *NewDiag = nullptr)
- : Info(Info), Old(Info.EvalStatus) {
+ SpeculativeEvaluationRAII(
+ EvalInfo &Info, SmallVectorImpl<PartialDiagnosticAt> *NewDiag = nullptr)
+ : Info(Info), Old(Info.EvalStatus),
+ OldSpecEval(Info.IsSpeculativelyEvaluating) {
Info.EvalStatus.Diag = NewDiag;
// If we're speculatively evaluating, we may have skipped over some
// evaluations and missed out a side effect.
- Info.EvalStatus.HasSideEffects = true;
}
~SpeculativeEvaluationRAII() {
Info.EvalStatus = Old;
+ Info.IsSpeculativelyEvaluating = OldSpecEval;
}
};
@@ -2612,6 +2639,10 @@
return CompleteObject();
}
+ // Don't allow the speculative evaluation of writes.
+ if (AK != AK_Read && Info.IsSpeculativelyEvaluating)
+ return CompleteObject();
+
CallStackFrame *Frame = nullptr;
if (LVal.CallIndex) {
Frame = Info.getCallFrame(LVal.CallIndex);
@@ -2785,12 +2816,11 @@
}
// In C++1y, we can't safely access any mutable state when we might be
- // evaluating after an unmodeled side effect or an evaluation failure.
+ // evaluating after an unmodeled side effect.
//
// FIXME: Not all local state is mutable. Allow local constant subobjects
// to be read here (but take care with 'mutable' fields).
- if (Frame && Info.getLangOpts().CPlusPlus14 &&
- (Info.EvalStatus.HasSideEffects || Info.keepEvaluatingAfterFailure()))
+ if (Frame && Info.getLangOpts().CPlusPlus14 && Info.EvalStatus.HasSideEffects)
return CompleteObject();
return CompleteObject(BaseVal, BaseType);
@@ -3247,7 +3277,7 @@
assert(BO->getOpcode() == BO_PtrMemD || BO->getOpcode() == BO_PtrMemI);
if (!EvaluateObjectArgument(Info, BO->getLHS(), LV)) {
- if (Info.keepEvaluatingAfterFailure()) {
+ if (Info.noteUnrecoverableFailure()) {
MemberPtr MemPtr;
EvaluateMemberPointer(BO->getRHS(), MemPtr, Info);
}
@@ -3541,7 +3571,7 @@
// FIXME: This isn't quite right; if we're performing aggregate
// initialization, each braced subexpression is its own full-expression.
FullExpressionRAII Scope(Info);
- if (!EvaluateDecl(Info, DclIt) && !Info.keepEvaluatingAfterFailure())
+ if (!EvaluateDecl(Info, DclIt) && !Info.noteUnrecoverableFailure())
return ESR_Failed;
}
return ESR_Succeeded;
@@ -3816,7 +3846,7 @@
if (!Evaluate(ArgValues[I - Args.begin()], Info, *I)) {
// If we're checking for a potential constant expression, evaluate all
// initializers even if some of them fail.
- if (!Info.keepEvaluatingAfterFailure())
+ if (!Info.noteUnrecoverableFailure())
return false;
Success = false;
}
@@ -4008,7 +4038,7 @@
*Value, FD))) {
// If we're checking for a potential constant expression, evaluate all
// initializers even if some of them fail.
- if (!Info.keepEvaluatingAfterFailure())
+ if (!Info.noteUnrecoverableFailure())
return false;
Success = false;
}
@@ -4041,6 +4071,9 @@
template<typename ConditionalOperator>
void CheckPotentialConstantConditional(const ConditionalOperator *E) {
assert(Info.checkingPotentialConstantExpression());
+ assert(
+ Info.EvalStatus.HasSideEffects &&
+ "Need multiple speculative eval RAIIs below if we haven't failed yet.");
// Speculatively evaluate both arms.
{
@@ -4065,8 +4098,13 @@
bool HandleConditionalOperator(const ConditionalOperator *E) {
bool BoolResult;
if (!EvaluateAsBooleanCondition(E->getCond(), BoolResult, Info)) {
- if (Info.checkingPotentialConstantExpression())
+ if (Info.checkingPotentialConstantExpression()) {
+ bool KeepGoing = Info.noteUnrecoverableFailure();
+ (void)KeepGoing;
+ assert(KeepGoing && "We can't evaluate after a failure in potential "
+ "constant expressions?");
CheckPotentialConstantConditional(E);
+ }
return false;
}
@@ -4853,7 +4891,7 @@
// The overall lvalue result is the result of evaluating the LHS.
if (!this->Visit(CAO->getLHS())) {
- if (Info.keepEvaluatingAfterFailure())
+ if (Info.noteUnrecoverableFailure())
Evaluate(RHS, this->Info, CAO->getRHS());
return false;
}
@@ -4874,7 +4912,7 @@
APValue NewVal;
if (!this->Visit(E->getLHS())) {
- if (Info.keepEvaluatingAfterFailure())
+ if (Info.noteUnrecoverableFailure())
Evaluate(NewVal, this->Info, E->getRHS());
return false;
}
@@ -4962,7 +5000,7 @@
std::swap(PExp, IExp);
bool EvalPtrOK = EvaluatePointer(PExp, Result, Info);
- if (!EvalPtrOK && !Info.keepEvaluatingAfterFailure())
+ if (!EvalPtrOK && !Info.noteUnrecoverableFailure())
return false;
llvm::APSInt Offset;
@@ -5465,7 +5503,7 @@
APValue &FieldVal = Result.getStructBase(ElementNo);
if (!EvaluateInPlace(FieldVal, Info, Subobject, Init)) {
- if (!Info.keepEvaluatingAfterFailure())
+ if (!Info.noteUnrecoverableFailure())
return false;
Success = false;
}
@@ -5503,7 +5541,7 @@
if (!EvaluateInPlace(FieldVal, Info, Subobject, Init) ||
(Field->isBitField() && !truncateBitfieldValue(Info, Init,
FieldVal, Field))) {
- if (!Info.keepEvaluatingAfterFailure())
+ if (!Info.noteUnrecoverableFailure())
return false;
Success = false;
}
@@ -5953,7 +5991,7 @@
Info, Subobject, Init) ||
!HandleLValueArrayAdjustment(Info, Init, Subobject,
CAT->getElementType(), 1)) {
- if (!Info.keepEvaluatingAfterFailure())
+ if (!Info.noteUnrecoverableFailure())
return false;
Success = false;
}
@@ -7164,7 +7202,7 @@
LHSResult.Failed = true;
// Since we weren't able to evaluate the left hand side, it
- // must have had side effects.
+ // might have had side effects.
if (!Info.noteSideEffect())
return false;
@@ -7180,7 +7218,7 @@
assert(E->getLHS()->getType()->isIntegralOrEnumerationType() &&
E->getRHS()->getType()->isIntegralOrEnumerationType());
- if (LHSResult.Failed && !Info.keepEvaluatingAfterFailure())
+ if (LHSResult.Failed && !Info.noteUnrecoverableFailure())
return false; // Ignore RHS;
return true;
@@ -7333,7 +7371,7 @@
}
bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
- if (!Info.keepEvaluatingAfterFailure() && E->isAssignmentOp())
+ if (E->isAssignmentOp() && !Info.noteUnrecoverableFailure())
return Error(E);
if (DataRecursiveIntBinOpEvaluator::shouldEnqueue(E))
@@ -7358,7 +7396,7 @@
} else {
LHSOK = EvaluateComplex(E->getLHS(), LHS, Info);
}
- if (!LHSOK && !Info.keepEvaluatingAfterFailure())
+ if (!LHSOK && !Info.noteUnrecoverableFailure())
return false;
if (E->getRHS()->getType()->isRealFloatingType()) {
@@ -7406,7 +7444,7 @@
APFloat RHS(0.0), LHS(0.0);
bool LHSOK = EvaluateFloat(E->getRHS(), RHS, Info);
- if (!LHSOK && !Info.keepEvaluatingAfterFailure())
+ if (!LHSOK && !Info.noteUnrecoverableFailure())
return false;
if (!EvaluateFloat(E->getLHS(), LHS, Info) || !LHSOK)
@@ -7440,7 +7478,7 @@
LValue LHSValue, RHSValue;
bool LHSOK = EvaluatePointer(E->getLHS(), LHSValue, Info);
- if (!LHSOK && !Info.keepEvaluatingAfterFailure())
+ if (!LHSOK && !Info.noteUnrecoverableFailure())
return false;
if (!EvaluatePointer(E->getRHS(), RHSValue, Info) || !LHSOK)
@@ -7657,7 +7695,7 @@
MemberPtr LHSValue, RHSValue;
bool LHSOK = EvaluateMemberPointer(E->getLHS(), LHSValue, Info);
- if (!LHSOK && Info.keepEvaluatingAfterFailure())
+ if (!LHSOK && !Info.noteUnrecoverableFailure())
return false;
if (!EvaluateMemberPointer(E->getRHS(), RHSValue, Info) || !LHSOK)
@@ -8229,7 +8267,7 @@
APFloat RHS(0.0);
bool LHSOK = EvaluateFloat(E->getLHS(), Result, Info);
- if (!LHSOK && !Info.keepEvaluatingAfterFailure())
+ if (!LHSOK && !Info.noteUnrecoverableFailure())
return false;
return EvaluateFloat(E->getRHS(), RHS, Info) && LHSOK &&
handleFloatFloatBinOp(Info, E, Result, E->getOpcode(), RHS);
@@ -8506,7 +8544,7 @@
} else {
LHSOK = Visit(E->getLHS());
}
- if (!LHSOK && !Info.keepEvaluatingAfterFailure())
+ if (!LHSOK && !Info.noteUnrecoverableFailure())
return false;
ComplexValue RHS;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits