george.burgess.iv updated this revision to Diff 52834. george.burgess.iv marked 7 inline comments as done. george.burgess.iv added a comment.
Addressed 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 { @@ -938,3 +936,18 @@ constexpr int testb = f(e2, 3); // expected-error {{constant expression}} expected-note {{in call}} constexpr int testc = f(e3, 3); } + +namespace SpeculativeEvalWrites { + // Ensure that we don't try to speculatively evaluate writes. + constexpr int add(int a, int b) { return a + b; } + + constexpr int f() { + int i = 0; + int a = 0; + // __builtin_object_size speculatively evaluates its first argument. + __builtin_object_size((i = 1, &a), 0); + return i; + } + + static_assert(!f(), ""); +} 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,29 @@ 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 noteFailure + LLVM_ATTRIBUTE_UNUSED_RESULT bool noteFailure() { + // Failure when evaluating some expression often means there is some + // subexpression whose evaluation was skipped. Therefore, (because we + // don't track whether we skipped an expression when unwinding after an + // evaluation failure) every evaluation failure that bubbles up from a + // subexpression implies that a side-effect has potentially happened. We + // skip setting the HasSideEffects flag to true until we decide to + // continue evaluating after that point, which happens here. + bool KeepGoing = keepEvaluatingAfterFailure(); + EvalStatus.HasSideEffects |= KeepGoing; + return KeepGoing; + } + bool allowInvalidBaseExpr() const { return EvalMode == EM_DesignatorFold; } @@ -812,23 +839,24 @@ ~FoldOffsetRAII() { Info.EvalMode = OldMode; } }; - /// RAII object used to suppress diagnostics and side-effects from a - /// speculative evaluation. + /// RAII object used to potentially suppress diagnostics and side-effects from + /// a speculative evaluation. 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; + Info.IsSpeculativelyEvaluating = true; } + ~SpeculativeEvaluationRAII() { Info.EvalStatus = Old; + Info.IsSpeculativelyEvaluating = OldSpecEval; } }; @@ -2785,12 +2813,13 @@ } // 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) || + (AK != AK_Read && Info.IsSpeculativelyEvaluating)) return CompleteObject(); return CompleteObject(BaseVal, BaseType); @@ -3247,7 +3276,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); } @@ -3543,7 +3572,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; @@ -3818,7 +3847,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; } @@ -4010,7 +4039,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; } @@ -4045,14 +4074,16 @@ assert(Info.checkingPotentialConstantExpression()); // Speculatively evaluate both arms. + SmallVector<PartialDiagnosticAt, 8> Diag; { - SmallVector<PartialDiagnosticAt, 8> Diag; SpeculativeEvaluationRAII Speculate(Info, &Diag); - StmtVisitorTy::Visit(E->getFalseExpr()); if (Diag.empty()) return; + } + { + SpeculativeEvaluationRAII Speculate(Info, &Diag); Diag.clear(); StmtVisitorTy::Visit(E->getTrueExpr()); if (Diag.empty()) @@ -4067,7 +4098,7 @@ bool HandleConditionalOperator(const ConditionalOperator *E) { bool BoolResult; if (!EvaluateAsBooleanCondition(E->getCond(), BoolResult, Info)) { - if (Info.checkingPotentialConstantExpression()) + if (Info.checkingPotentialConstantExpression() && Info.noteFailure()) CheckPotentialConstantConditional(E); return false; } @@ -4855,7 +4886,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; } @@ -4876,7 +4907,7 @@ APValue NewVal; if (!this->Visit(E->getLHS())) { - if (Info.keepEvaluatingAfterFailure()) + if (Info.noteFailure()) Evaluate(NewVal, this->Info, E->getRHS()); return false; } @@ -4964,7 +4995,7 @@ std::swap(PExp, IExp); bool EvalPtrOK = EvaluatePointer(PExp, Result, Info); - if (!EvalPtrOK && !Info.keepEvaluatingAfterFailure()) + if (!EvalPtrOK && !Info.noteFailure()) return false; llvm::APSInt Offset; @@ -5467,7 +5498,7 @@ APValue &FieldVal = Result.getStructBase(ElementNo); if (!EvaluateInPlace(FieldVal, Info, Subobject, Init)) { - if (!Info.keepEvaluatingAfterFailure()) + if (!Info.noteFailure()) return false; Success = false; } @@ -5505,7 +5536,7 @@ if (!EvaluateInPlace(FieldVal, Info, Subobject, Init) || (Field->isBitField() && !truncateBitfieldValue(Info, Init, FieldVal, Field))) { - if (!Info.keepEvaluatingAfterFailure()) + if (!Info.noteFailure()) return false; Success = false; } @@ -5955,7 +5986,7 @@ Info, Subobject, Init) || !HandleLValueArrayAdjustment(Info, Init, Subobject, CAT->getElementType(), 1)) { - if (!Info.keepEvaluatingAfterFailure()) + if (!Info.noteFailure()) return false; Success = false; } @@ -7048,23 +7079,28 @@ Job() = default; Job(Job &&J) : E(J.E), LHSResult(J.LHSResult), Kind(J.Kind), - StoredInfo(J.StoredInfo), OldEvalStatus(J.OldEvalStatus) { + StoredInfo(J.StoredInfo), OldEvalStatus(J.OldEvalStatus), + WasSpecEval(J.WasSpecEval) { J.StoredInfo = nullptr; } void startSpeculativeEval(EvalInfo &Info) { OldEvalStatus = Info.EvalStatus; + WasSpecEval = Info.IsSpeculativelyEvaluating; Info.EvalStatus.Diag = nullptr; + Info.IsSpeculativelyEvaluating = true; StoredInfo = &Info; } ~Job() { if (StoredInfo) { StoredInfo->EvalStatus = OldEvalStatus; + StoredInfo->IsSpeculativelyEvaluating = WasSpecEval; } } private: EvalInfo *StoredInfo = nullptr; // non-null if status changed. Expr::EvalStatus OldEvalStatus; + bool WasSpecEval; }; SmallVector<Job, 16> Queue; @@ -7166,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; @@ -7182,7 +7218,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; @@ -7334,10 +7370,34 @@ llvm_unreachable("Invalid Job::Kind!"); } +namespace { +/// Used when we determine that we should fail, but can keep evaluating prior to +/// noting that we had a failure. +class DelayedNoteFailureRAII { + EvalInfo &Info; + bool NoteFailure; + +public: + DelayedNoteFailureRAII(EvalInfo &Info, bool NoteFailure = true) + : Info(Info), NoteFailure(NoteFailure) {} + ~DelayedNoteFailureRAII() { + if (NoteFailure) { + bool ContinueAfterFailure = Info.noteFailure(); + (void)ContinueAfterFailure; + assert(ContinueAfterFailure && + "Shouldn't have kept evaluating on failure."); + } + } +}; +} + bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { + // We don't call noteFailure immediately because the assignment happens after + // we evaluate LHS and RHS. if (!Info.keepEvaluatingAfterFailure() && E->isAssignmentOp()) return Error(E); + DelayedNoteFailureRAII MaybeNoteFailureLater(Info, E->isAssignmentOp()); if (DataRecursiveIntBinOpEvaluator::shouldEnqueue(E)) return DataRecursiveIntBinOpEvaluator(*this, Result).Traverse(E); @@ -7360,7 +7420,7 @@ } else { LHSOK = EvaluateComplex(E->getLHS(), LHS, Info); } - if (!LHSOK && !Info.keepEvaluatingAfterFailure()) + if (!LHSOK && !Info.noteFailure()) return false; if (E->getRHS()->getType()->isRealFloatingType()) { @@ -7408,7 +7468,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) @@ -7442,7 +7502,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) @@ -7659,7 +7719,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) @@ -8231,7 +8291,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); @@ -8508,7 +8568,7 @@ } else { LHSOK = Visit(E->getLHS()); } - if (!LHSOK && !Info.keepEvaluatingAfterFailure()) + if (!LHSOK && !Info.noteFailure()) return false; ComplexValue RHS;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits