Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -538,6 +538,13 @@ /// Likewise, INT_MAX + 1 can be folded to INT_MIN, but has UB. bool HasUndefinedBehavior; + /// \brief Whether the evaluation was unable to reduce a construct to a + /// value. + /// + /// For example, in foo(bar, baz, qux), if evaluating bar fails, we may + /// try to evaluate baz and qux anyway. + bool HasFailure; + /// Diag - If this is non-null, it will be filled in with a stack of notes /// indicating why evaluation failed (or why it failed to produce a constant /// expression). @@ -548,7 +555,8 @@ SmallVectorImpl *Diag; EvalStatus() - : HasSideEffects(false), HasUndefinedBehavior(false), Diag(nullptr) {} + : HasSideEffects(false), HasUndefinedBehavior(false), HasFailure(false), + Diag(nullptr) {} // hasSideEffects - Return true if the evaluated expression has // side effects. 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,29 @@ llvm_unreachable("Missed EvalMode case"); } + /// Notes that we failed to evaluate an expression, and determine if we + /// should keep evaluating. + /// + /// Currently, this is used as a fancy way of noting that there *may* have + /// been a side-effect that we didn't see (and can't model). + /// + /// Note that this is sometimes *not* called when a failure happens. The + /// only time we really care about this case is when we're going to try to + /// evaluate things after the failure. It's therefore the responsibility of + /// the caller that wants to continue evaluating after a failure to call + /// this. + bool noteFailure() { + EvalStatus.HasFailure = true; + return keepEvaluatingAfterFailure(); + } + + /// Returns whether a side-effect that we can't model might have occurred. + /// Moreover, whether we've seen a side-effect, or if we failed to evaluate + /// something that might produce a side-effect. + bool sideEffectsMightHaveOccurred() const { + return EvalStatus.HasSideEffects || EvalStatus.HasFailure; + } + bool allowInvalidBaseExpr() const { return EvalMode == EM_DesignatorFold; } @@ -817,18 +844,20 @@ class SpeculativeEvaluationRAII { EvalInfo &Info; Expr::EvalStatus Old; + bool OldSpecEval; public: - SpeculativeEvaluationRAII(EvalInfo &Info, - SmallVectorImpl *NewDiag = nullptr) - : Info(Info), Old(Info.EvalStatus) { + SpeculativeEvaluationRAII( + EvalInfo &Info, SmallVectorImpl *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 +2641,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 +2818,12 @@ } // 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())) + Info.sideEffectsMightHaveOccurred()) return CompleteObject(); return CompleteObject(BaseVal, BaseType); @@ -3247,7 +3280,7 @@ assert(BO->getOpcode() == BO_PtrMemD || BO->getOpcode() == BO_PtrMemI); if (!EvaluateObjectArgument(Info, BO->getLHS(), LV)) { - if (Info.keepEvaluatingAfterFailure()) { + if (Info.noteFailure()) { MemberPtr MemPtr; EvaluateMemberPointer(BO->getRHS(), MemPtr, Info); } @@ -3541,7 +3574,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.noteFailure()) return ESR_Failed; } return ESR_Succeeded; @@ -3816,7 +3849,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.noteFailure()) return false; Success = false; } @@ -4008,7 +4041,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.noteFailure()) return false; Success = false; } @@ -4041,6 +4074,9 @@ template void CheckPotentialConstantConditional(const ConditionalOperator *E) { assert(Info.checkingPotentialConstantExpression()); + assert( + Info.EvalStatus.HasFailure && + "Need multiple speculative eval RAIIs below if we haven't failed yet."); // Speculatively evaluate both arms. { @@ -4065,6 +4101,7 @@ bool HandleConditionalOperator(const ConditionalOperator *E) { bool BoolResult; if (!EvaluateAsBooleanCondition(E->getCond(), BoolResult, Info)) { + Info.noteFailure(); if (Info.checkingPotentialConstantExpression()) CheckPotentialConstantConditional(E); return false; @@ -4853,7 +4890,7 @@ // The overall lvalue result is the result of evaluating the LHS. if (!this->Visit(CAO->getLHS())) { - if (Info.keepEvaluatingAfterFailure()) + if (Info.noteFailure()) Evaluate(RHS, this->Info, CAO->getRHS()); return false; } @@ -4874,7 +4911,7 @@ APValue NewVal; if (!this->Visit(E->getLHS())) { - if (Info.keepEvaluatingAfterFailure()) + if (Info.noteFailure()) Evaluate(NewVal, this->Info, E->getRHS()); return false; } @@ -4962,7 +4999,7 @@ std::swap(PExp, IExp); bool EvalPtrOK = EvaluatePointer(PExp, Result, Info); - if (!EvalPtrOK && !Info.keepEvaluatingAfterFailure()) + if (!EvalPtrOK && !Info.noteFailure()) return false; llvm::APSInt Offset; @@ -5465,7 +5502,7 @@ APValue &FieldVal = Result.getStructBase(ElementNo); if (!EvaluateInPlace(FieldVal, Info, Subobject, Init)) { - if (!Info.keepEvaluatingAfterFailure()) + if (!Info.noteFailure()) return false; Success = false; } @@ -5503,7 +5540,7 @@ if (!EvaluateInPlace(FieldVal, Info, Subobject, Init) || (Field->isBitField() && !truncateBitfieldValue(Info, Init, FieldVal, Field))) { - if (!Info.keepEvaluatingAfterFailure()) + if (!Info.noteFailure()) return false; Success = false; } @@ -5953,7 +5990,7 @@ Info, Subobject, Init) || !HandleLValueArrayAdjustment(Info, Init, Subobject, CAT->getElementType(), 1)) { - if (!Info.keepEvaluatingAfterFailure()) + if (!Info.noteFailure()) return false; Success = false; } @@ -7180,7 +7217,7 @@ assert(E->getLHS()->getType()->isIntegralOrEnumerationType() && E->getRHS()->getType()->isIntegralOrEnumerationType()); - if (LHSResult.Failed && !Info.keepEvaluatingAfterFailure()) + if (LHSResult.Failed && !Info.noteFailure()) return false; // Ignore RHS; return true; @@ -7333,7 +7370,7 @@ } bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { - if (!Info.keepEvaluatingAfterFailure() && E->isAssignmentOp()) + if (E->isAssignmentOp() && !Info.noteFailure()) return Error(E); if (DataRecursiveIntBinOpEvaluator::shouldEnqueue(E)) @@ -7358,7 +7395,7 @@ } else { LHSOK = EvaluateComplex(E->getLHS(), LHS, Info); } - if (!LHSOK && !Info.keepEvaluatingAfterFailure()) + if (!LHSOK && !Info.noteFailure()) return false; if (E->getRHS()->getType()->isRealFloatingType()) { @@ -7406,7 +7443,7 @@ APFloat RHS(0.0), LHS(0.0); bool LHSOK = EvaluateFloat(E->getRHS(), RHS, Info); - if (!LHSOK && !Info.keepEvaluatingAfterFailure()) + if (!LHSOK && !Info.noteFailure()) return false; if (!EvaluateFloat(E->getLHS(), LHS, Info) || !LHSOK) @@ -7440,7 +7477,7 @@ LValue LHSValue, RHSValue; bool LHSOK = EvaluatePointer(E->getLHS(), LHSValue, Info); - if (!LHSOK && !Info.keepEvaluatingAfterFailure()) + if (!LHSOK && !Info.noteFailure()) return false; if (!EvaluatePointer(E->getRHS(), RHSValue, Info) || !LHSOK) @@ -7657,7 +7694,7 @@ MemberPtr LHSValue, RHSValue; bool LHSOK = EvaluateMemberPointer(E->getLHS(), LHSValue, Info); - if (!LHSOK && Info.keepEvaluatingAfterFailure()) + if (!LHSOK && !Info.noteFailure()) return false; if (!EvaluateMemberPointer(E->getRHS(), RHSValue, Info) || !LHSOK) @@ -8229,7 +8266,7 @@ APFloat RHS(0.0); bool LHSOK = EvaluateFloat(E->getLHS(), Result, Info); - if (!LHSOK && !Info.keepEvaluatingAfterFailure()) + if (!LHSOK && !Info.noteFailure()) return false; return EvaluateFloat(E->getRHS(), RHS, Info) && LHSOK && handleFloatFloatBinOp(Info, E, Result, E->getOpcode(), RHS); @@ -8506,7 +8543,7 @@ } else { LHSOK = Visit(E->getLHS()); } - if (!LHSOK && !Info.keepEvaluatingAfterFailure()) + if (!LHSOK && !Info.noteFailure()) return false; ComplexValue RHS; @@ -9643,9 +9680,12 @@ for (ArrayRef::iterator I = Args.begin(), E = Args.end(); I != E; ++I) { if ((*I)->isValueDependent() || - !Evaluate(ArgValues[I - Args.begin()], Info, *I)) - // If evaluation fails, throw away the argument entirely. + !Evaluate(ArgValues[I - Args.begin()], Info, *I)) { + // If evaluation fails, throw away the argument entirely, and note that + // something might have failed. ArgValues[I - Args.begin()] = APValue(); + Info.noteFailure(); + } if (Info.EvalStatus.HasSideEffects) return false; } 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: 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 {