From 83587db1bda97f45d2b5a4189e584e2a18be511a Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 15 Feb 2012 02:18:13 +0000 Subject: [PATCH] Implement DR1454. This allows all intermediate results in constant expressions to be core constant expressions (including pointers and references to temporaries), and makes constexpr calculations Turing-complete. A Turing machine simulator is included as a testcase. This opens up the possibilty of removing CCValue entirely, and removing some copies from the constant evaluator in the process, but that cleanup is not part of this change. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150557 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/APValue.h | 15 +- include/clang/Basic/DiagnosticASTKinds.td | 8 +- lib/AST/APValue.cpp | 19 +- lib/AST/ExprConstant.cpp | 414 +++++++++++++-------- test/CXX/expr/expr.const/p2-0x.cpp | 26 +- test/SemaCXX/constant-expression-cxx11.cpp | 39 +- test/SemaCXX/constexpr-turing.cpp | 55 +++ 7 files changed, 373 insertions(+), 203 deletions(-) create mode 100644 test/SemaCXX/constexpr-turing.cpp diff --git a/include/clang/AST/APValue.h b/include/clang/AST/APValue.h index f361f0f5c8..f687fb7beb 100644 --- a/include/clang/AST/APValue.h +++ b/include/clang/AST/APValue.h @@ -138,14 +138,14 @@ public: APValue(const APValue &RHS) : Kind(Uninitialized) { *this = RHS; } - APValue(LValueBase B, const CharUnits &O, NoLValuePath N) + APValue(LValueBase B, const CharUnits &O, NoLValuePath N, unsigned CallIndex) : Kind(Uninitialized) { - MakeLValue(); setLValue(B, O, N); + MakeLValue(); setLValue(B, O, N, CallIndex); } APValue(LValueBase B, const CharUnits &O, ArrayRef Path, - bool OnePastTheEnd) + bool OnePastTheEnd, unsigned CallIndex) : Kind(Uninitialized) { - MakeLValue(); setLValue(B, O, Path, OnePastTheEnd); + MakeLValue(); setLValue(B, O, Path, OnePastTheEnd, CallIndex); } APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(Uninitialized) { MakeArray(InitElts, Size); @@ -246,6 +246,7 @@ public: bool isLValueOnePastTheEnd() const; bool hasLValuePath() const; ArrayRef getLValuePath() const; + unsigned getLValueCallIndex() const; APValue &getVectorElt(unsigned I) { assert(isVector() && "Invalid accessor"); @@ -365,9 +366,11 @@ public: ((ComplexAPFloat*)(char*)Data)->Real = R; ((ComplexAPFloat*)(char*)Data)->Imag = I; } - void setLValue(LValueBase B, const CharUnits &O, NoLValuePath); + void setLValue(LValueBase B, const CharUnits &O, NoLValuePath, + unsigned CallIndex); void setLValue(LValueBase B, const CharUnits &O, - ArrayRef Path, bool OnePastTheEnd); + ArrayRef Path, bool OnePastTheEnd, + unsigned CallIndex); void setUnion(const FieldDecl *Field, const APValue &Value) { assert(isUnion() && "Invalid accessor"); ((UnionData*)(char*)Data)->Field = Field; diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index f2154fb4e9..2d6e498fbb 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -35,9 +35,7 @@ def note_constexpr_nonliteral : Note< "non-literal type %0 cannot be used in a constant expression">; def note_constexpr_non_global : Note< "%select{pointer|reference}0 to %select{|subobject of }1" - "%select{temporary|%4}2 %select{is not a constant expression|" - "cannot be returned from a constexpr function|" - "cannot be used to initialize a member in a constant expression}3">; + "%select{temporary|%3}2 is not a constant expression">; def note_constexpr_array_index : Note<"cannot refer to element %0 of " "%select{array of %2 elements|non-array object}1 in a constant expression">; def note_constexpr_float_arithmetic : Note< @@ -76,6 +74,10 @@ def note_constexpr_void_comparison : Note< def note_constexpr_temporary_here : Note<"temporary created here">; def note_constexpr_depth_limit_exceeded : Note< "constexpr evaluation exceeded maximum depth of %0 calls">; +def note_constexpr_call_limit_exceeded : Note< + "constexpr evaluation hit maximum call limit">; +def note_constexpr_lifetime_ended : Note< + "read of %select{temporary|variable}0 whose lifetime has ended">; def note_constexpr_ltor_volatile_type : Note< "read of volatile-qualified type %0 is not allowed in a constant expression">; def note_constexpr_ltor_volatile_obj : Note< diff --git a/lib/AST/APValue.cpp b/lib/AST/APValue.cpp index b8942c3310..4e17d3b69e 100644 --- a/lib/AST/APValue.cpp +++ b/lib/AST/APValue.cpp @@ -28,6 +28,7 @@ namespace { llvm::PointerIntPair BaseAndIsOnePastTheEnd; CharUnits Offset; unsigned PathLength; + unsigned CallIndex; }; } @@ -166,9 +167,10 @@ const APValue &APValue::operator=(const APValue &RHS) { else if (isLValue()) { if (RHS.hasLValuePath()) setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), RHS.getLValuePath(), - RHS.isLValueOnePastTheEnd()); + RHS.isLValueOnePastTheEnd(), RHS.getLValueCallIndex()); else - setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), NoLValuePath()); + setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), NoLValuePath(), + RHS.getLValueCallIndex()); } else if (isArray()) { for (unsigned I = 0, N = RHS.getArrayInitializedElts(); I != N; ++I) getArrayInitializedElt(I) = RHS.getArrayInitializedElt(I); @@ -525,22 +527,31 @@ ArrayRef APValue::getLValuePath() const { return ArrayRef(LVal.getPath(), LVal.PathLength); } -void APValue::setLValue(LValueBase B, const CharUnits &O, NoLValuePath) { +unsigned APValue::getLValueCallIndex() const { + assert(isLValue() && "Invalid accessor"); + return ((const LV*)(const char*)Data)->CallIndex; +} + +void APValue::setLValue(LValueBase B, const CharUnits &O, NoLValuePath, + unsigned CallIndex) { assert(isLValue() && "Invalid accessor"); LV &LVal = *((LV*)(char*)Data); LVal.BaseAndIsOnePastTheEnd.setPointer(B); LVal.BaseAndIsOnePastTheEnd.setInt(false); LVal.Offset = O; + LVal.CallIndex = CallIndex; LVal.resizePath((unsigned)-1); } void APValue::setLValue(LValueBase B, const CharUnits &O, - ArrayRef Path, bool IsOnePastTheEnd) { + ArrayRef Path, bool IsOnePastTheEnd, + unsigned CallIndex) { assert(isLValue() && "Invalid accessor"); LV &LVal = *((LV*)(char*)Data); LVal.BaseAndIsOnePastTheEnd.setPointer(B); LVal.BaseAndIsOnePastTheEnd.setInt(IsOnePastTheEnd); LVal.Offset = O; + LVal.CallIndex = CallIndex; LVal.resizePath(Path.size()); memcpy(LVal.getPath(), Path.data(), Path.size() * sizeof(LValuePathEntry)); } diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 6ad9938906..9454895c75 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -51,26 +51,14 @@ using namespace clang; using llvm::APSInt; using llvm::APFloat; -/// EvalInfo - This is a private struct used by the evaluator to capture -/// information about a subexpression as it is folded. It retains information -/// about the AST context, but also maintains information about the folded -/// expression. -/// -/// If an expression could be evaluated, it is still possible it is not a C -/// "integer constant expression" or constant expression. If not, this struct -/// captures information about how and why not. -/// -/// One bit of information passed *into* the request for constant folding -/// indicates whether the subexpression is "evaluated" or not according to C -/// rules. For example, the RHS of (0 && foo()) is not evaluated. We can -/// evaluate the expression regardless of what the RHS is, but C only allows -/// certain things in certain situations. +static bool IsGlobalLValue(APValue::LValueBase B); + namespace { struct LValue; struct CallStackFrame; struct EvalInfo; - QualType getType(APValue::LValueBase B) { + static QualType getType(APValue::LValueBase B) { if (!B) return QualType(); if (const ValueDecl *D = B.dyn_cast()) return D->getType(); @@ -79,6 +67,7 @@ namespace { /// Get an LValue path entry, which is known to not be an array index, as a /// field or base class. + static APValue::BaseOrMemberType getAsBaseOrMember(APValue::LValuePathEntry E) { APValue::BaseOrMemberType Value; Value.setFromOpaqueValue(E.BaseOrMember); @@ -87,17 +76,17 @@ namespace { /// Get an LValue path entry, which is known to not be an array index, as a /// field declaration. - const FieldDecl *getAsField(APValue::LValuePathEntry E) { + static const FieldDecl *getAsField(APValue::LValuePathEntry E) { return dyn_cast(getAsBaseOrMember(E).getPointer()); } /// Get an LValue path entry, which is known to not be an array index, as a /// base class declaration. - const CXXRecordDecl *getAsBaseClass(APValue::LValuePathEntry E) { + static const CXXRecordDecl *getAsBaseClass(APValue::LValuePathEntry E) { return dyn_cast(getAsBaseOrMember(E).getPointer()); } /// Determine whether this LValue path entry for a base class names a virtual /// base class. - bool isVirtualBaseClass(APValue::LValuePathEntry E) { + static bool isVirtualBaseClass(APValue::LValuePathEntry E) { return getAsBaseOrMember(E).getInt(); } @@ -267,9 +256,6 @@ namespace { class CCValue : public APValue { typedef llvm::APSInt APSInt; typedef llvm::APFloat APFloat; - /// If the value is a reference or pointer into a parameter or temporary, - /// this is the corresponding call stack frame. - CallStackFrame *CallFrame; /// If the value is a reference or pointer, this is a description of how the /// subobject was specified. SubobjectDesignator Designator; @@ -282,22 +268,19 @@ namespace { CCValue(const APValue *E, unsigned N) : APValue(E, N) {} CCValue(const APSInt &R, const APSInt &I) : APValue(R, I) {} CCValue(const APFloat &R, const APFloat &I) : APValue(R, I) {} - CCValue(const CCValue &V) : APValue(V), CallFrame(V.CallFrame) {} - CCValue(LValueBase B, const CharUnits &O, CallStackFrame *F, + CCValue(const CCValue &V) : APValue(V), Designator(V.Designator) {} + CCValue(LValueBase B, const CharUnits &O, unsigned I, const SubobjectDesignator &D) : - APValue(B, O, APValue::NoLValuePath()), CallFrame(F), Designator(D) {} + APValue(B, O, APValue::NoLValuePath(), I), Designator(D) {} CCValue(ASTContext &Ctx, const APValue &V, GlobalValue) : - APValue(V), CallFrame(0), Designator(Ctx, V) {} + APValue(V), Designator(Ctx, V) { + } CCValue(const ValueDecl *D, bool IsDerivedMember, ArrayRef Path) : APValue(D, IsDerivedMember, Path) {} CCValue(const AddrLabelExpr* LHSExpr, const AddrLabelExpr* RHSExpr) : APValue(LHSExpr, RHSExpr) {} - CallStackFrame *getLValueFrame() const { - assert(getKind() == LValue); - return CallFrame; - } SubobjectDesignator &getLValueDesignator() { assert(getKind() == LValue); return Designator; @@ -305,6 +288,21 @@ namespace { const SubobjectDesignator &getLValueDesignator() const { return const_cast(this)->getLValueDesignator(); } + APValue toAPValue() const { + if (!isLValue()) + return *this; + + if (Designator.Invalid) { + // This is not a core constant expression. An appropriate diagnostic + // will have already been produced. + return APValue(getLValueBase(), getLValueOffset(), + APValue::NoLValuePath(), getLValueCallIndex()); + } + + return APValue(getLValueBase(), getLValueOffset(), + Designator.Entries, Designator.IsOnePastTheEnd, + getLValueCallIndex()); + } }; /// A stack frame in the constexpr call stack. @@ -320,6 +318,9 @@ namespace { /// Callee - The function which was called. const FunctionDecl *Callee; + /// Index - The call index of this call. + unsigned Index; + /// This - The binding for the this pointer in this call, if any. const LValue *This; @@ -372,6 +373,20 @@ namespace { } }; + /// EvalInfo - This is a private struct used by the evaluator to capture + /// information about a subexpression as it is folded. It retains information + /// about the AST context, but also maintains information about the folded + /// expression. + /// + /// If an expression could be evaluated, it is still possible it is not a C + /// "integer constant expression" or constant expression. If not, this struct + /// captures information about how and why not. + /// + /// One bit of information passed *into* the request for constant folding + /// indicates whether the subexpression is "evaluated" or not according to C + /// rules. For example, the RHS of (0 && foo()) is not evaluated. We can + /// evaluate the expression regardless of what the RHS is, but C only allows + /// certain things in certain situations. struct EvalInfo { ASTContext &Ctx; @@ -384,6 +399,9 @@ namespace { /// CallStackDepth - The number of calls in the call stack right now. unsigned CallStackDepth; + /// NextCallIndex - The next call index to assign. + unsigned NextCallIndex; + typedef llvm::DenseMap MapTy; /// OpaqueValues - Values used as the common expression in a /// BinaryConditionalOperator. @@ -413,7 +431,8 @@ namespace { EvalInfo(const ASTContext &C, Expr::EvalStatus &S) : Ctx(const_cast(C)), EvalStatus(S), CurrentCall(0), - CallStackDepth(0), BottomFrame(*this, SourceLocation(), 0, 0, 0), + CallStackDepth(0), NextCallIndex(1), + BottomFrame(*this, SourceLocation(), 0, 0, 0), EvaluatingDecl(0), EvaluatingDeclValue(0), HasActiveDiagnostic(false), CheckingPotentialConstantExpression(false) {} @@ -435,6 +454,11 @@ namespace { // when checking a potential constant expression. if (CheckingPotentialConstantExpression && CallStackDepth > 1) return false; + if (NextCallIndex == 0) { + // NextCallIndex has wrapped around. + Diag(Loc, diag::note_constexpr_call_limit_exceeded); + return false; + } if (CallStackDepth <= getLangOpts().ConstexprCallDepth) return true; Diag(Loc, diag::note_constexpr_depth_limit_exceeded) @@ -442,6 +466,16 @@ namespace { return false; } + CallStackFrame *getCallFrame(unsigned CallIndex) { + assert(CallIndex && "no call index in getCallFrame"); + // We will eventually hit BottomFrame, which has Index 1, so Frame can't + // be null in this loop. + CallStackFrame *Frame = CurrentCall; + while (Frame->Index > CallIndex) + Frame = Frame->Caller; + return (Frame->Index == CallIndex) ? Frame : 0; + } + private: /// Add a diagnostic to the diagnostics list. PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId) { @@ -560,7 +594,7 @@ CallStackFrame::CallStackFrame(EvalInfo &Info, SourceLocation CallLoc, const FunctionDecl *Callee, const LValue *This, const CCValue *Arguments) : Info(Info), Caller(Info.CurrentCall), CallLoc(CallLoc), Callee(Callee), - This(This), Arguments(Arguments) { + Index(Info.NextCallIndex++), This(This), Arguments(Arguments) { Info.CurrentCall = this; ++Info.CallStackDepth; } @@ -591,10 +625,11 @@ static void describeCall(CallStackFrame *Frame, llvm::raw_ostream &Out) { if (!Arg.isLValue() || Arg.getLValueDesignator().Invalid) Arg.printPretty(Out, Frame->Info.Ctx, Param->getType()); else { - // Deliberately slice off the frame to form an APValue we can print. + // Convert the CCValue to an APValue without checking for constantness. APValue Value(Arg.getLValueBase(), Arg.getLValueOffset(), Arg.getLValueDesignator().Entries, - Arg.getLValueDesignator().IsOnePastTheEnd); + Arg.getLValueDesignator().IsOnePastTheEnd, + Arg.getLValueCallIndex()); Value.printPretty(Out, Frame->Info.Ctx, Param->getType()); } @@ -679,31 +714,31 @@ namespace { struct LValue { APValue::LValueBase Base; CharUnits Offset; - CallStackFrame *Frame; + unsigned CallIndex; SubobjectDesignator Designator; const APValue::LValueBase getLValueBase() const { return Base; } CharUnits &getLValueOffset() { return Offset; } const CharUnits &getLValueOffset() const { return Offset; } - CallStackFrame *getLValueFrame() const { return Frame; } + unsigned getLValueCallIndex() const { return CallIndex; } SubobjectDesignator &getLValueDesignator() { return Designator; } const SubobjectDesignator &getLValueDesignator() const { return Designator;} void moveInto(CCValue &V) const { - V = CCValue(Base, Offset, Frame, Designator); + V = CCValue(Base, Offset, CallIndex, Designator); } void setFrom(const CCValue &V) { assert(V.isLValue()); Base = V.getLValueBase(); Offset = V.getLValueOffset(); - Frame = V.getLValueFrame(); + CallIndex = V.getLValueCallIndex(); Designator = V.getLValueDesignator(); } - void set(APValue::LValueBase B, CallStackFrame *F = 0) { + void set(APValue::LValueBase B, unsigned I = 0) { Base = B; Offset = CharUnits::Zero(); - Frame = F; + CallIndex = I; Designator = SubobjectDesignator(getType(B)); } @@ -852,11 +887,10 @@ namespace { } static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E); -static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info, - const LValue &This, const Expr *E, - CheckConstantExpressionKind CCEK - = CCEK_Constant, - bool AllowNonLiteralTypes = false); +static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, + const LValue &This, const Expr *E, + CheckConstantExpressionKind CCEK = CCEK_Constant, + bool AllowNonLiteralTypes = false); static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info); static bool EvaluatePointer(const Expr *E, LValue &Result, EvalInfo &Info); static bool EvaluateMemberPointer(const Expr *E, MemberPtr &Result, @@ -928,47 +962,43 @@ static bool IsGlobalLValue(APValue::LValueBase B) { } } +static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) { + assert(Base && "no location for a null lvalue"); + const ValueDecl *VD = Base.dyn_cast(); + if (VD) + Info.Note(VD->getLocation(), diag::note_declared_at); + else + Info.Note(Base.dyn_cast()->getExprLoc(), + diag::note_constexpr_temporary_here); +} + /// Check that this reference or pointer core constant expression is a valid /// value for an address or reference constant expression. Type T should be /// either LValue or CCValue. Return true if we can fold this expression, /// whether or not it's a constant expression. -template -static bool CheckLValueConstantExpression(EvalInfo &Info, const Expr *E, - const T &LVal, APValue &Value, - CheckConstantExpressionKind CCEK) { +static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, + QualType Type, const LValue &LVal) { + bool IsReferenceType = Type->isReferenceType(); + APValue::LValueBase Base = LVal.getLValueBase(); const SubobjectDesignator &Designator = LVal.getLValueDesignator(); if (!IsGlobalLValue(Base)) { if (Info.getLangOpts().CPlusPlus0x) { const ValueDecl *VD = Base.dyn_cast(); - Info.Diag(E->getExprLoc(), diag::note_constexpr_non_global, 1) - << E->isGLValue() << !Designator.Entries.empty() - << !!VD << CCEK << VD; - if (VD) - Info.Note(VD->getLocation(), diag::note_declared_at); - else - Info.Note(Base.dyn_cast()->getExprLoc(), - diag::note_constexpr_temporary_here); + Info.Diag(Loc, diag::note_constexpr_non_global, 1) + << IsReferenceType << !Designator.Entries.empty() + << !!VD << VD; + NoteLValueLocation(Info, Base); } else { - Info.Diag(E->getExprLoc()); + Info.Diag(Loc); } // Don't allow references to temporaries to escape. return false; } - - bool IsReferenceType = E->isGLValue(); - - if (Designator.Invalid) { - // This is not a core constant expression. An appropriate diagnostic will - // have already been produced. - Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(), - APValue::NoLValuePath()); - return true; - } - - Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(), - Designator.Entries, Designator.IsOnePastTheEnd); + assert((Info.CheckingPotentialConstantExpression || + LVal.getLValueCallIndex() == 0) && + "have call index for global lvalue"); // Allow address constant expressions to be past-the-end pointers. This is // an extension: the standard requires them to point to an object. @@ -978,20 +1008,16 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, const Expr *E, // A reference constant expression must refer to an object. if (!Base) { // FIXME: diagnostic - Info.CCEDiag(E->getExprLoc()); + Info.CCEDiag(Loc); return true; } // Does this refer one past the end of some object? if (Designator.isOnePastTheEnd()) { const ValueDecl *VD = Base.dyn_cast(); - Info.Diag(E->getExprLoc(), diag::note_constexpr_past_end, 1) + Info.Diag(Loc, diag::note_constexpr_past_end, 1) << !Designator.Entries.empty() << !!VD << VD; - if (VD) - Info.Note(VD->getLocation(), diag::note_declared_at); - else - Info.Note(Base.dyn_cast()->getExprLoc(), - diag::note_constexpr_temporary_here); + NoteLValueLocation(Info, Base); } return true; @@ -1013,18 +1039,58 @@ static bool CheckLiteralType(EvalInfo &Info, const Expr *E) { } /// Check that this core constant expression value is a valid value for a -/// constant expression, and if it is, produce the corresponding constant value. -/// If not, report an appropriate diagnostic. Does not check that the expression -/// is of literal type. -static bool CheckConstantExpression(EvalInfo &Info, const Expr *E, - const CCValue &CCValue, APValue &Value, - CheckConstantExpressionKind CCEK - = CCEK_Constant) { - if (!CCValue.isLValue()) { - Value = CCValue; - return true; +/// constant expression. If not, report an appropriate diagnostic. Does not +/// check that the expression is of literal type. +static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, + QualType Type, const APValue &Value) { + // Core issue 1454: For a literal constant expression of array or class type, + // each subobject of its value shall have been initialized by a constant + // expression. + if (Value.isArray()) { + QualType EltTy = Type->castAsArrayTypeUnsafe()->getElementType(); + for (unsigned I = 0, N = Value.getArrayInitializedElts(); I != N; ++I) { + if (!CheckConstantExpression(Info, DiagLoc, EltTy, + Value.getArrayInitializedElt(I))) + return false; + } + if (!Value.hasArrayFiller()) + return true; + return CheckConstantExpression(Info, DiagLoc, EltTy, + Value.getArrayFiller()); + } + if (Value.isUnion() && Value.getUnionField()) { + return CheckConstantExpression(Info, DiagLoc, + Value.getUnionField()->getType(), + Value.getUnionValue()); + } + if (Value.isStruct()) { + RecordDecl *RD = Type->castAs()->getDecl(); + if (const CXXRecordDecl *CD = dyn_cast(RD)) { + unsigned BaseIndex = 0; + for (CXXRecordDecl::base_class_const_iterator I = CD->bases_begin(), + End = CD->bases_end(); I != End; ++I, ++BaseIndex) { + if (!CheckConstantExpression(Info, DiagLoc, I->getType(), + Value.getStructBase(BaseIndex))) + return false; + } + } + for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); + I != E; ++I) { + if (!CheckConstantExpression(Info, DiagLoc, (*I)->getType(), + Value.getStructField((*I)->getFieldIndex()))) + return false; + } + } + + if (Value.isLValue()) { + CCValue Val(Info.Ctx, Value, CCValue::GlobalValue()); + LValue LVal; + LVal.setFrom(Val); + return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal); } - return CheckLValueConstantExpression(Info, E, CCValue, Value, CCEK); + + // Everything else is fine. + return true; } const ValueDecl *GetLValueBaseDecl(const LValue &LVal) { @@ -1032,7 +1098,7 @@ const ValueDecl *GetLValueBaseDecl(const LValue &LVal) { } static bool IsLiteralLValue(const LValue &Value) { - return Value.Base.dyn_cast() && !Value.Frame; + return Value.Base.dyn_cast() && !Value.CallIndex; } static bool IsWeakLValue(const LValue &Value) { @@ -1583,7 +1649,6 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, return false; const Expr *Base = LVal.Base.dyn_cast(); - CallStackFrame *Frame = LVal.Frame; SourceLocation Loc = Conv->getExprLoc(); if (!LVal.Base) { @@ -1592,6 +1657,16 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, return false; } + CallStackFrame *Frame = 0; + if (LVal.CallIndex) { + Frame = Info.getCallFrame(LVal.CallIndex); + if (!Frame) { + Info.Diag(Loc, diag::note_constexpr_lifetime_ended, 1) << !Base; + NoteLValueLocation(Info, LVal.Base); + return false; + } + } + // C++11 DR1311: An lvalue-to-rvalue conversion on a volatile-qualified type // is not a constant expression (even if the object is non-volatile). We also // apply this rule to C++98, in order to conform to the expected 'volatile' @@ -1680,7 +1755,17 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, assert(RVal.getLValueOffset().isZero() && "offset for lvalue init of non-reference"); Base = RVal.getLValueBase().get(); - Frame = RVal.getLValueFrame(); + + if (unsigned CallIndex = RVal.getLValueCallIndex()) { + Frame = Info.getCallFrame(CallIndex); + if (!Frame) { + Info.Diag(Loc, diag::note_constexpr_lifetime_ended, 1) << !Base; + NoteLValueLocation(Info, RVal.getLValueBase()); + return false; + } + } else { + Frame = 0; + } } // Volatile temporary objects cannot be read in constant expressions. @@ -1896,7 +1981,7 @@ enum EvalStmtResult { } // Evaluate a statement. -static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, +static EvalStmtResult EvaluateStmt(CCValue &Result, EvalInfo &Info, const Stmt *S) { switch (S->getStmtClass()) { default: @@ -1907,11 +1992,8 @@ static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, return ESR_Succeeded; case Stmt::ReturnStmtClass: { - CCValue CCResult; const Expr *RetExpr = cast(S)->getRetValue(); - if (!Evaluate(CCResult, Info, RetExpr) || - !CheckConstantExpression(Info, RetExpr, CCResult, Result, - CCEK_ReturnValue)) + if (!Evaluate(Result, Info, RetExpr)) return ESR_Failed; return ESR_Returned; } @@ -2010,7 +2092,7 @@ static bool EvaluateArgs(ArrayRef Args, ArgVector &ArgValues, static bool HandleFunctionCall(SourceLocation CallLoc, const FunctionDecl *Callee, const LValue *This, ArrayRef Args, const Stmt *Body, - EvalInfo &Info, APValue &Result) { + EvalInfo &Info, CCValue &Result) { ArgVector ArgValues(Args.size()); if (!EvaluateArgs(Args, ArgValues, Info)) return false; @@ -2045,7 +2127,7 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, // If it's a delegating constructor, just delegate. if (Definition->isDelegatingConstructor()) { CXXConstructorDecl::init_const_iterator I = Definition->init_begin(); - return EvaluateConstantExpression(Result, Info, This, (*I)->getInit()); + return EvaluateInPlace(Result, Info, This, (*I)->getInit()); } // For a trivial copy or move constructor, perform an APValue copy. This is @@ -2137,8 +2219,8 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, llvm_unreachable("unknown base initializer kind"); } - if (!EvaluateConstantExpression(*Value, Info, Subobject, (*I)->getInit(), - (*I)->isBaseInitializer() + if (!EvaluateInPlace(*Value, Info, Subobject, (*I)->getInit(), + (*I)->isBaseInitializer() ? CCEK_Constant : CCEK_MemberInit)) { // If we're checking for a potential constant expression, evaluate all // initializers even if some of them fail. @@ -2488,14 +2570,14 @@ public: const FunctionDecl *Definition = 0; Stmt *Body = FD->getBody(Definition); - APValue Result; + CCValue Result; if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition) || !HandleFunctionCall(E->getExprLoc(), Definition, This, Args, Body, Info, Result)) return false; - return DerivedSuccess(CCValue(Info.Ctx, Result, CCValue::GlobalValue()), E); + return DerivedSuccess(Result, E); } RetTy VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) { @@ -2706,8 +2788,8 @@ public: // * BlockExpr // * CallExpr for a MakeStringConstant builtin // - Locals and temporaries -// * Any Expr, with a Frame indicating the function in which the temporary was -// evaluated. +// * Any Expr, with a CallIndex indicating the function in which the temporary +// was evaluated. // plus an offset in bytes. //===----------------------------------------------------------------------===// namespace { @@ -2777,7 +2859,7 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { if (!VD->getType()->isReferenceType()) { if (isa(VD)) { - Result.set(VD, Info.CurrentCall); + Result.set(VD, Info.CurrentCall->Index); return true; } return Success(VD); @@ -2795,9 +2877,9 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr( if (E->getType()->isRecordType()) return EvaluateTemporary(E->GetTemporaryExpr(), Result, Info); - Result.set(E, Info.CurrentCall); - return EvaluateConstantExpression(Info.CurrentCall->Temporaries[E], Info, - Result, E->GetTemporaryExpr()); + Result.set(E, Info.CurrentCall->Index); + return EvaluateInPlace(Info.CurrentCall->Temporaries[E], Info, + Result, E->GetTemporaryExpr()); } // Materialization of an lvalue temporary occurs when we need to force a copy @@ -2808,7 +2890,7 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr( if (!HandleLValueToRValueConversion(Info, E, E->getType(), Result, Info.CurrentCall->Temporaries[E])) return false; - Result.set(E, Info.CurrentCall); + Result.set(E, Info.CurrentCall->Index); return true; } @@ -3032,7 +3114,7 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) { uint64_t N = Value.getInt().extOrTrunc(Size).getZExtValue(); Result.Base = (Expr*)0; Result.Offset = CharUnits::fromQuantity(N); - Result.Frame = 0; + Result.CallIndex = 0; Result.Designator.setInvalid(); return true; } else { @@ -3046,9 +3128,9 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) { if (!EvaluateLValue(SubExpr, Result, Info)) return false; } else { - Result.set(SubExpr, Info.CurrentCall); - if (!EvaluateConstantExpression(Info.CurrentCall->Temporaries[SubExpr], - Info, Result, SubExpr)) + Result.set(SubExpr, Info.CurrentCall->Index); + if (!EvaluateInPlace(Info.CurrentCall->Temporaries[SubExpr], + Info, Result, SubExpr)) return false; } // The result is a pointer to the first element of the array. @@ -3175,7 +3257,8 @@ namespace { : ExprEvaluatorBaseTy(info), This(This), Result(Result) {} bool Success(const CCValue &V, const Expr *E) { - return CheckConstantExpression(Info, E, V, Result); + Result = V; + return true; } bool ZeroInitialization(const Expr *E); @@ -3225,7 +3308,7 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E, HandleLValueMember(Info, E, Subobject, *I, &Layout); ImplicitValueInitExpr VIE((*I)->getType()); - if (!EvaluateConstantExpression( + if (!EvaluateInPlace( Result.getStructField((*I)->getFieldIndex()), Info, Subobject, &VIE)) return false; } @@ -3248,8 +3331,7 @@ bool RecordExprEvaluator::ZeroInitialization(const Expr *E) { HandleLValueMember(Info, E, Subobject, *I); Result = APValue(*I); ImplicitValueInitExpr VIE((*I)->getType()); - return EvaluateConstantExpression(Result.getUnionValue(), Info, - Subobject, &VIE); + return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, &VIE); } return HandleClassZeroInitialization(Info, E, RD, This, Result); @@ -3304,8 +3386,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { LValue Subobject = This; HandleLValueMember(Info, InitExpr, Subobject, Field, &Layout); - return EvaluateConstantExpression(Result.getUnionValue(), Info, - Subobject, InitExpr); + return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, InitExpr); } assert((!isa(RD) || !cast(RD)->getNumBases()) && @@ -3334,7 +3415,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { // the initializer list. ImplicitValueInitExpr VIE(HaveInit ? Info.Ctx.IntTy : Field->getType()); - if (!EvaluateConstantExpression( + if (!EvaluateInPlace( Result.getStructField((*Field)->getFieldIndex()), Info, Subobject, HaveInit ? E->getInit(ElementNo++) : &VIE)) { if (!Info.keepEvaluatingAfterFailure()) @@ -3410,9 +3491,8 @@ public: /// Visit an expression which constructs the value of this temporary. bool VisitConstructExpr(const Expr *E) { - Result.set(E, Info.CurrentCall); - return EvaluateConstantExpression(Info.CurrentCall->Temporaries[E], Info, - Result, E); + Result.set(E, Info.CurrentCall->Index); + return EvaluateInPlace(Info.CurrentCall->Temporaries[E], Info, Result, E); } bool VisitCastExpr(const CastExpr *E) { @@ -3658,8 +3738,7 @@ namespace { LValue Subobject = This; Subobject.addArray(Info, E, CAT); ImplicitValueInitExpr VIE(CAT->getElementType()); - return EvaluateConstantExpression(Result.getArrayFiller(), Info, - Subobject, &VIE); + return EvaluateInPlace(Result.getArrayFiller(), Info, Subobject, &VIE); } bool VisitInitListExpr(const InitListExpr *E); @@ -3693,10 +3772,10 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { for (uint64_t I = 0; I < NumElements; ++I) { CCValue Char; if (!HandleLValueToRValueConversion(Info, E->getInit(0), - CAT->getElementType(), LV, Char) || - !CheckConstantExpression(Info, E->getInit(0), Char, - Result.getArrayInitializedElt(I)) || - !HandleLValueArrayAdjustment(Info, E->getInit(0), LV, + CAT->getElementType(), LV, Char)) + return false; + Result.getArrayInitializedElt(I) = Char.toAPValue(); + if (!HandleLValueArrayAdjustment(Info, E->getInit(0), LV, CAT->getElementType(), 1)) return false; } @@ -3712,8 +3791,8 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { unsigned Index = 0; for (InitListExpr::const_iterator I = E->begin(), End = E->end(); I != End; ++I, ++Index) { - if (!EvaluateConstantExpression(Result.getArrayInitializedElt(Index), - Info, Subobject, cast(*I)) || + if (!EvaluateInPlace(Result.getArrayInitializedElt(Index), + Info, Subobject, cast(*I)) || !HandleLValueArrayAdjustment(Info, cast(*I), Subobject, CAT->getElementType(), 1)) { if (!Info.keepEvaluatingAfterFailure()) @@ -3728,8 +3807,8 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { // but sometimes does: // struct S { constexpr S() : p(&p) {} void *p; }; // S s[10] = {}; - return EvaluateConstantExpression(Result.getArrayFiller(), Info, - Subobject, E->getArrayFiller()) && Success; + return EvaluateInPlace(Result.getArrayFiller(), Info, + Subobject, E->getArrayFiller()) && Success; } bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { @@ -3754,8 +3833,7 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { LValue Subobject = This; Subobject.addArray(Info, E, CAT); ImplicitValueInitExpr VIE(CAT->getElementType()); - return EvaluateConstantExpression(Result.getArrayFiller(), Info, - Subobject, &VIE); + return EvaluateInPlace(Result.getArrayFiller(), Info, Subobject, &VIE); } const CXXRecordDecl *RD = FD->getParent(); @@ -3783,8 +3861,7 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { if (ZeroInit && !HadZeroInit) { ImplicitValueInitExpr VIE(CAT->getElementType()); - if (!EvaluateConstantExpression(Result.getArrayFiller(), Info, Subobject, - &VIE)) + if (!EvaluateInPlace(Result.getArrayFiller(), Info, Subobject, &VIE)) return false; } @@ -4257,7 +4334,7 @@ static bool HasSameBase(const LValue &A, const LValue &B) { } return IsGlobalLValue(A.getLValueBase()) || - A.getLValueFrame() == B.getLValueFrame(); + A.getLValueCallIndex() == B.getLValueCallIndex(); } /// Perform the given integer operation, which is known to need at most BitWidth @@ -5807,13 +5884,13 @@ static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E) { return true; } else if (E->getType()->isArrayType()) { LValue LV; - LV.set(E, Info.CurrentCall); + LV.set(E, Info.CurrentCall->Index); if (!EvaluateArray(E, LV, Info.CurrentCall->Temporaries[E], Info)) return false; Result = Info.CurrentCall->Temporaries[E]; } else if (E->getType()->isRecordType()) { LValue LV; - LV.set(E, Info.CurrentCall); + LV.set(E, Info.CurrentCall->Index); if (!EvaluateRecord(E, LV, Info.CurrentCall->Temporaries[E], Info)) return false; Result = Info.CurrentCall->Temporaries[E]; @@ -5836,14 +5913,12 @@ static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E) { return true; } -/// EvaluateConstantExpression - Evaluate an expression as a constant expression -/// in-place in an APValue. In some cases, the in-place evaluation is essential, -/// since later initializers for an object can indirectly refer to subobjects -/// which were initialized earlier. -static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info, - const LValue &This, const Expr *E, - CheckConstantExpressionKind CCEK, - bool AllowNonLiteralTypes) { +/// EvaluateInPlace - Evaluate an expression in-place in an APValue. In some +/// cases, the in-place evaluation is essential, since later initializers for +/// an object can indirectly refer to subobjects which were initialized earlier. +static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, const LValue &This, + const Expr *E, CheckConstantExpressionKind CCEK, + bool AllowNonLiteralTypes) { if (!AllowNonLiteralTypes && !CheckLiteralType(Info, E)) return false; @@ -5858,8 +5933,10 @@ static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info, // For any other type, in-place evaluation is unimportant. CCValue CoreConstResult; - return Evaluate(CoreConstResult, Info, E) && - CheckConstantExpression(Info, E, CoreConstResult, Result, CCEK); + if (!Evaluate(CoreConstResult, Info, E)) + return false; + Result = CoreConstResult.toAPValue(); + return true; } /// EvaluateAsRValue - Try to evaluate this expression, performing an implicit @@ -5881,7 +5958,8 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { // Check this core constant expression is a constant expression, and if so, // convert it to one. - return CheckConstantExpression(Info, E, Value, Result); + Result = Value.toAPValue(); + return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result); } /// EvaluateAsRValue - Return true if this is a constant which we can fold using @@ -5935,9 +6013,15 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx) const { EvalInfo Info(Ctx, Result); LValue LV; - return EvaluateLValue(this, LV, Info) && !Result.HasSideEffects && - CheckLValueConstantExpression(Info, this, LV, Result.Val, - CCEK_Constant); + if (!EvaluateLValue(this, LV, Info) || Result.HasSideEffects || + !CheckLValueConstantExpression(Info, getExprLoc(), + Ctx.getLValueReferenceType(getType()), LV)) + return false; + + CCValue Tmp; + LV.moveInto(Tmp); + Result.Val = Tmp.toAPValue(); + return true; } bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, @@ -5965,14 +6049,18 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, if (Ctx.getLangOptions().CPlusPlus && !VD->hasLocalStorage() && !VD->getType()->isReferenceType()) { ImplicitValueInitExpr VIE(VD->getType()); - if (!EvaluateConstantExpression(Value, InitInfo, LVal, &VIE, CCEK_Constant, - /*AllowNonLiteralTypes=*/true)) + if (!EvaluateInPlace(Value, InitInfo, LVal, &VIE, CCEK_Constant, + /*AllowNonLiteralTypes=*/true)) return false; } - return EvaluateConstantExpression(Value, InitInfo, LVal, this, CCEK_Constant, - /*AllowNonLiteralTypes=*/true) && - !EStatus.HasSideEffects; + if (!EvaluateInPlace(Value, InitInfo, LVal, this, CCEK_Constant, + /*AllowNonLiteralTypes=*/true) || + EStatus.HasSideEffects) + return false; + + return CheckConstantExpression(InitInfo, VD->getLocation(), VD->getType(), + Value); } /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be @@ -6499,18 +6587,20 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, // is a temporary being used as the 'this' pointer. LValue This; ImplicitValueInitExpr VIE(RD ? Info.Ctx.getRecordType(RD) : Info.Ctx.IntTy); - This.set(&VIE, Info.CurrentCall); + This.set(&VIE, Info.CurrentCall->Index); - APValue Scratch; ArrayRef Args; SourceLocation Loc = FD->getLocation(); if (const CXXConstructorDecl *CD = dyn_cast(FD)) { + APValue Scratch; HandleConstructorCall(Loc, This, Args, CD, Info, Scratch); - } else + } else { + CCValue Scratch; HandleFunctionCall(Loc, FD, (MD && MD->isInstance()) ? &This : 0, Args, FD->getBody(), Info, Scratch); + } return Diags.empty(); } diff --git a/test/CXX/expr/expr.const/p2-0x.cpp b/test/CXX/expr/expr.const/p2-0x.cpp index a22d1e4e4e..8d3638ffc6 100644 --- a/test/CXX/expr/expr.const/p2-0x.cpp +++ b/test/CXX/expr/expr.const/p2-0x.cpp @@ -49,25 +49,23 @@ struct UndefinedConstexpr { }; // - an invocation of a constexpr function with arguments that, when substituted -// by function invocation substitution (7.1.5), do not produce a constant +// by function invocation substitution (7.1.5), do not produce a core constant // expression; namespace NonConstExprReturn { static constexpr const int &id_ref(const int &n) { - return n; // expected-note {{reference to temporary cannot be returned from a constexpr function}} + return n; } struct NonConstExprFunction { - int n : id_ref( // expected-error {{constant expression}} expected-note {{in call to 'id_ref(16)'}} - 16 // expected-note {{temporary created here}} - ); + int n : id_ref(16); // ok }; constexpr const int *address_of(const int &a) { - return &a; // expected-note {{pointer to 'n' cannot be returned from a constexpr function}} + return &a; } constexpr const int *return_param(int n) { // expected-note {{declared here}} - return address_of(n); // expected-note {{in call to 'address_of(n)'}} + return address_of(n); } struct S { - int n : *return_param(0); // expected-error {{constant expression}} expected-note {{in call to 'return_param(0)'}} + int n : *return_param(0); // expected-error {{constant expression}} expected-note {{read of variable whose lifetime has ended}} }; } @@ -78,16 +76,16 @@ namespace NonConstExprReturn { namespace NonConstExprCtor { struct T { constexpr T(const int &r) : - r(r) { // expected-note 2{{reference to temporary cannot be used to initialize a member in a constant expression}} + r(r) { } const int &r; }; constexpr int n = 0; constexpr T t1(n); // ok - constexpr T t2(0); // expected-error {{must be initialized by a constant expression}} expected-note {{temporary created here}} expected-note {{in call to 'T(0)'}} + constexpr T t2(0); // expected-error {{must be initialized by a constant expression}} expected-note {{temporary created here}} expected-note {{reference to temporary is not a constant expression}} struct S { - int n : T(4).r; // expected-error {{constant expression}} expected-note {{temporary created here}} expected-note {{in call to 'T(4)'}} + int n : T(4).r; // ok }; } @@ -176,12 +174,12 @@ namespace UndefinedBehavior { struct S { int m; }; - constexpr S s = { 5 }; // expected-note {{declared here}} + constexpr S s = { 5 }; constexpr const int *p = &s.m + 1; constexpr const int &f(const int *q) { - return q[0]; // expected-note {{dereferenced pointer past the end of subobject of 's' is not a constant expression}} + return q[0]; } - constexpr int n = (f(p), 0); // expected-error {{constant expression}} expected-note {{in call to 'f(&s.m + 1)'}} + constexpr int n = (f(p), 0); // ok struct T { int n : f(p); // expected-error {{not an integral constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}} }; diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index 58f36fdf9c..aa4031189d 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -190,26 +190,21 @@ namespace StaticMemberFunction { namespace ParameterScopes { const int k = 42; - constexpr const int &ObscureTheTruth(const int &a) { return a; } // expected-note 3{{reference to 'a' cannot be returned from a constexpr function}} + constexpr const int &ObscureTheTruth(const int &a) { return a; } constexpr const int &MaybeReturnJunk(bool b, const int a) { // expected-note 2{{declared here}} - return ObscureTheTruth(b ? a : k); // expected-note 2{{in call to 'ObscureTheTruth(a)'}} + return ObscureTheTruth(b ? a : k); } static_assert(MaybeReturnJunk(false, 0) == 42, ""); // ok - constexpr int a = MaybeReturnJunk(true, 0); // expected-error {{constant expression}} expected-note {{in call to 'MaybeReturnJunk(1, 0)'}} + constexpr int a = MaybeReturnJunk(true, 0); // expected-error {{constant expression}} expected-note {{read of variable whose lifetime has ended}} - constexpr const int MaybeReturnNonstaticRef(bool b, const int a) { // expected-note {{here}} - // If ObscureTheTruth returns a reference to 'a', the result is not a - // constant expression even though 'a' is still in scope. - return ObscureTheTruth(b ? a : k); // expected-note {{in call to 'ObscureTheTruth(a)'}} + constexpr const int MaybeReturnNonstaticRef(bool b, const int a) { + return ObscureTheTruth(b ? a : k); } static_assert(MaybeReturnNonstaticRef(false, 0) == 42, ""); // ok - constexpr int b = MaybeReturnNonstaticRef(true, 0); // expected-error {{constant expression}} expected-note {{in call to 'MaybeReturnNonstaticRef(1, 0)'}} + constexpr int b = MaybeReturnNonstaticRef(true, 0); // ok constexpr int InternalReturnJunk(int n) { - // TODO: We could reject this: it never produces a constant expression. - // However, we currently don't evaluate function calls while testing for - // potential constant expressions, for performance. - return MaybeReturnJunk(true, n); // expected-note {{in call to 'MaybeReturnJunk(1, 0)'}} + return MaybeReturnJunk(true, n); // expected-note {{read of variable whose lifetime has ended}} } constexpr int n3 = InternalReturnJunk(0); // expected-error {{must be initialized by a constant expression}} expected-note {{in call to 'InternalReturnJunk(0)'}} @@ -528,10 +523,10 @@ struct D { static_assert(D().c.n == 42, ""); struct E { - constexpr E() : p(&p) {} // expected-note {{pointer to subobject of temporary cannot be used to initialize a member in a constant expression}} + constexpr E() : p(&p) {} void *p; }; -constexpr const E &e1 = E(); // expected-error {{constant expression}} expected-note {{in call to 'E()'}} expected-note {{temporary created here}} +constexpr const E &e1 = E(); // expected-error {{constant expression}} expected-note {{reference to temporary is not a constant expression}} expected-note {{temporary created here}} // This is a constant expression if we elide the copy constructor call, and // is not a constant expression if we don't! But we do, so it is. constexpr E e2 = E(); @@ -1158,3 +1153,19 @@ namespace Fold { #undef fold } + +namespace DR1454 { + +constexpr const int &f(const int &n) { return n; } +constexpr int k1 = f(0); // ok + +struct Wrap { + const int &value; +}; +constexpr const Wrap &g(const Wrap &w) { return w; } +constexpr int k2 = g({0}).value; // ok + +constexpr const int &i = 0; // expected-error {{constant expression}} expected-note {{temporary}} expected-note 2{{here}} +constexpr const int j = i; // expected-error {{constant expression}} expected-note {{initializer of 'i' is not a constant expression}} + +} diff --git a/test/SemaCXX/constexpr-turing.cpp b/test/SemaCXX/constexpr-turing.cpp new file mode 100644 index 0000000000..c5153788ad --- /dev/null +++ b/test/SemaCXX/constexpr-turing.cpp @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -verify -std=c++11 %s + +// A direct proof that constexpr is Turing-complete, once DR1454 is implemented. + +const unsigned halt = (unsigned)-1; + +enum Dir { L, R }; +struct Action { + bool tape; + Dir dir; + unsigned next; +}; +using State = Action[2]; + +// An infinite tape! +struct Tape { + constexpr Tape() : l(0), val(false), r(0) {} + constexpr Tape(const Tape &old, bool write) : + l(old.l), val(write), r(old.r) {} + constexpr Tape(const Tape &old, Dir dir) : + l(dir == L ? old.l ? old.l->l : 0 : &old), + val(dir == L ? old.l ? old.l->val : false + : old.r ? old.r->val : false), + r(dir == R ? old.r ? old.r->r : 0 : &old) {} + const Tape *l; + bool val; + const Tape *r; +}; +constexpr Tape update(const Tape &old, bool write) { return Tape(old, write); } +constexpr Tape move(const Tape &old, Dir dir) { return Tape(old, dir); } + +// Run turing machine 'tm' on tape 'tape' from state 'state'. Return number of +// steps taken until halt. +constexpr unsigned run(const State *tm, const Tape &tape, unsigned state) { + return state == halt ? 1 : + run(tm, move(update(tape, tm[state][tape.val].tape), + tm[state][tape.val].dir), + tm[state][tape.val].next) + 1; +} + +// 3-state busy beaver. 14 steps. +constexpr State bb3[] = { + { { true, R, 1 }, { true, L, 2 } }, + { { true, L, 0 }, { true, R, 1 } }, + { { true, L, 1 }, { true, R, halt } } +}; +static_assert(run(bb3, Tape(), 0) == 14, ""); + +// 4-state busy beaver. 108 steps. +constexpr State bb4[] = { + { { true, R, 1 }, { true, L, 1 } }, + { { true, L, 0 }, { false, L, 2 } }, + { { true, R, halt }, { true, L, 3 } }, + { { true, R, 3 }, { false, R, 0 } } }; +static_assert(run(bb4, Tape(), 0) == 108, ""); -- 2.40.0