From: Richard Smith Date: Thu, 9 May 2013 07:14:00 +0000 (+0000) Subject: Implement C++1y constant initializer rules: in a constant initializer for an X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6391ea2897b595178a8a97e5080f59a6f55ce442;p=clang Implement C++1y constant initializer rules: in a constant initializer for an object x, x's subobjects can be constructed by constexpr constructor even if they are of non-literal type, and can be read and written even though they're not members of a constexpr object or temporary. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181506 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 08c26a3ce3..bf967961aa 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -391,7 +391,7 @@ namespace { /// EvaluatingDecl - This is the declaration whose initializer is being /// evaluated, if any. - const VarDecl *EvaluatingDecl; + APValue::LValueBase EvaluatingDecl; /// EvaluatingDeclValue - This is the value being constructed for the /// declaration whose initializer is being evaluated, if any. @@ -414,12 +414,12 @@ namespace { CallStackDepth(0), NextCallIndex(1), StepsLeft(getLangOpts().ConstexprStepLimit), BottomFrame(*this, SourceLocation(), 0, 0, 0), - EvaluatingDecl(0), EvaluatingDeclValue(0), HasActiveDiagnostic(false), - CheckingPotentialConstantExpression(false), + EvaluatingDecl((const ValueDecl*)0), EvaluatingDeclValue(0), + HasActiveDiagnostic(false), CheckingPotentialConstantExpression(false), IntOverflowCheckMode(OverflowCheckMode) {} - void setEvaluatingDecl(const VarDecl *VD, APValue &Value) { - EvaluatingDecl = VD; + void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) { + EvaluatingDecl = Base; EvaluatingDeclValue = &Value; } @@ -899,19 +899,11 @@ namespace { return false; return LHS.Path == RHS.Path; } - - /// Kinds of constant expression checking, for diagnostics. - enum CheckConstantExpressionKind { - CCEK_Constant, ///< A normal constant. - CCEK_ReturnValue, ///< A constexpr function return value. - CCEK_MemberInit ///< A constexpr constructor mem-initializer. - }; } static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E); 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); @@ -1079,10 +1071,19 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, /// Check that this core constant expression is of literal type, and if not, /// produce an appropriate diagnostic. -static bool CheckLiteralType(EvalInfo &Info, const Expr *E) { +static bool CheckLiteralType(EvalInfo &Info, const Expr *E, + const LValue *This = 0) { if (!E->isRValue() || E->getType()->isLiteralType(Info.Ctx)) return true; + // C++1y: A constant initializer for an object o [...] may also invoke + // constexpr constructors for o and its subobjects even if those objects + // are of non-literal class types. + if (Info.getLangOpts().CPlusPlus1y && This && + Info.EvaluatingDecl.getOpaqueValue() == + This->getLValueBase().getOpaqueValue()) + return true; + // Prvalue constant expressions must be of literal types. if (Info.getLangOpts().CPlusPlus11) Info.Diag(E, diag::note_constexpr_nonliteral) @@ -1672,7 +1673,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, // If we're currently evaluating the initializer of this declaration, use that // in-flight value. - if (Info.EvaluatingDecl == VD) { + if (Info.EvaluatingDecl.dyn_cast() == VD) { Result = Info.EvaluatingDeclValue; return !Result->isUninit(); } @@ -2134,9 +2135,6 @@ CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, AccessKinds AK, NoteLValueLocation(Info, LVal.Base); return CompleteObject(); } - } else if (AK != AK_Read) { - Info.Diag(E, diag::note_constexpr_modify_global); - return CompleteObject(); } // C++11 DR1311: An lvalue-to-rvalue conversion on a volatile-qualified type @@ -2190,8 +2188,16 @@ CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, AccessKinds AK, // Unless we're looking at a local variable or argument in a constexpr call, // the variable we're reading must be const. if (!Frame) { - assert(AK == AK_Read && "can't modify non-local"); - if (VD->isConstexpr()) { + if (Info.getLangOpts().CPlusPlus1y && + VD == Info.EvaluatingDecl.dyn_cast()) { + // OK, we can read and modify an object if we're in the process of + // evaluating its initializer, because its lifetime began in this + // evaluation. + } else if (AK != AK_Read) { + // All the remaining cases only permit reading. + Info.Diag(E, diag::note_constexpr_modify_global); + return CompleteObject(); + } else if (VD->isConstexpr()) { // OK, we can read this variable. } else if (BaseType->isIntegralOrEnumerationType()) { if (!BaseType.isConstQualified()) { @@ -2251,6 +2257,15 @@ CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, AccessKinds AK, } } + // During the construction of an object, it is not yet 'const'. + // FIXME: We don't set up EvaluatingDecl for local variables or temporaries, + // and this doesn't do quite the right thing for const subobjects of the + // object under construction. + if (LVal.getLValueBase() == Info.EvaluatingDecl) { + BaseType = Info.Ctx.getCanonicalType(BaseType); + BaseType.removeLocalConst(); + } + // In C++1y, we can't safely access any mutable state when checking a // potential constant expression. if (Frame && Info.getLangOpts().CPlusPlus1y && @@ -3210,9 +3225,7 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, llvm_unreachable("unknown base initializer kind"); } - if (!EvaluateInPlace(*Value, Info, Subobject, (*I)->getInit(), - (*I)->isBaseInitializer() - ? CCEK_Constant : CCEK_MemberInit)) { + if (!EvaluateInPlace(*Value, Info, Subobject, (*I)->getInit())) { // If we're checking for a potential constant expression, evaluate all // initializers even if some of them fail. if (!Info.keepEvaluatingAfterFailure()) @@ -7150,9 +7163,8 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) { /// 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)) + const Expr *E, bool AllowNonLiteralTypes) { + if (!AllowNonLiteralTypes && !CheckLiteralType(Info, E, &This)) return false; if (E->isRValue()) { @@ -7284,13 +7296,13 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, if (Ctx.getLangOpts().CPlusPlus && !VD->hasLocalStorage() && !VD->getType()->isReferenceType()) { ImplicitValueInitExpr VIE(VD->getType()); - if (!EvaluateInPlace(Value, InitInfo, LVal, &VIE, CCEK_Constant, + if (!EvaluateInPlace(Value, InitInfo, LVal, &VIE, /*AllowNonLiteralTypes=*/true)) return false; } - if (!EvaluateInPlace(Value, InitInfo, LVal, this, CCEK_Constant, - /*AllowNonLiteralTypes=*/true) || + if (!EvaluateInPlace(Value, InitInfo, LVal, this, + /*AllowNonLiteralTypes=*/true) || EStatus.HasSideEffects) return false; @@ -7834,7 +7846,7 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, const CXXMethodDecl *MD = dyn_cast(FD); const CXXRecordDecl *RD = MD ? MD->getParent()->getCanonicalDecl() : 0; - // FIXME: Fabricate an arbitrary expression on the stack and pretend that it + // Fabricate an arbitrary expression on the stack and pretend that it // is a temporary being used as the 'this' pointer. LValue This; ImplicitValueInitExpr VIE(RD ? Info.Ctx.getRecordType(RD) : Info.Ctx.IntTy); @@ -7845,9 +7857,12 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, SourceLocation Loc = FD->getLocation(); APValue Scratch; - if (const CXXConstructorDecl *CD = dyn_cast(FD)) + if (const CXXConstructorDecl *CD = dyn_cast(FD)) { + // Evaluate the call as a constant initializer, to allow the construction + // of objects of non-literal types. + Info.setEvaluatingDecl(This.getLValueBase(), Scratch); HandleConstructorCall(Loc, This, Args, CD, Info, Scratch); - else + } else HandleFunctionCall(Loc, FD, (MD && MD->isInstance()) ? &This : 0, Args, FD->getBody(), Info, Scratch); diff --git a/test/CodeGenCXX/const-init-cxx1y.cpp b/test/CodeGenCXX/const-init-cxx1y.cpp new file mode 100644 index 0000000000..02293c86dd --- /dev/null +++ b/test/CodeGenCXX/const-init-cxx1y.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin -emit-llvm -o - %s -std=c++1y | FileCheck %s + +struct A { + constexpr A() : n(1) {} + ~A(); + int n; +}; +struct B : A { + A a[3]; + constexpr B() { + ++a[0].n; + a[1].n += 2; + a[2].n = n + a[1].n; + } +}; +B b; + +// CHECK: @b = global {{.*}} i32 1, {{.*}} { i32 2 }, {{.*}} { i32 3 }, {{.*}} { i32 4 } +// CHECK-NOT: _ZN1BC +// CHECK: __cxa_atexit diff --git a/test/CodeGenCXX/cxx1y-initializer-aggregate.cpp b/test/CodeGenCXX/cxx1y-initializer-aggregate.cpp index ef78c434e3..ae49a047f6 100644 --- a/test/CodeGenCXX/cxx1y-initializer-aggregate.cpp +++ b/test/CodeGenCXX/cxx1y-initializer-aggregate.cpp @@ -25,7 +25,11 @@ A b { 4, "bazquux", .x = 42, .c = 9 }; A c { 1, 0, 'A', f(), { 3 } }; // CHECK: @[[STR_A:.*]] = {{.*}} [7 x i8] c"foobar\00" +// CHECK: @a = global {{.*}} zeroinitializer + +// @b has a constant initializer // CHECK: @[[STR_B:.*]] = {{.*}} [8 x i8] c"bazquux\00" +// CHECK: @b = global {{.*}} i32 4, {{.*}} @[[STR_B]], {{.*}} i8 117, i32 42, {{.*}} i8 9 B x; B y {}; @@ -44,18 +48,9 @@ B z { 1 }; // CHECK: store i32 %{{.*}}, i32* getelementptr inbounds ({{.*}}* @a, i32 0, i32 3) // CHECK: call void @{{.*}}C1Ev({{.*}} getelementptr inbounds (%struct.A* @a, i32 0, i32 4)) -// Initialization of 'b': +// No dynamic initialization of 'b': -// CHECK: store i32 4, i32* getelementptr inbounds ({{.*}} @b, i32 0, i32 0) -// CHECK: store i8* {{.*}} @[[STR_B]]{{.*}}, i8** getelementptr inbounds ({{.*}} @b, i32 0, i32 1) -// CHECK: load i32* getelementptr inbounds ({{.*}} @b, i32 0, i32 0) -// CHECK: load i8** getelementptr inbounds ({{.*}} @b, i32 0, i32 1) -// CHECK: getelementptr inbounds i8* %{{.*}}, {{.*}} %{{.*}} -// CHECK: store i8 %{{.*}}, i8* getelementptr inbounds ({{.*}} @b, i32 0, i32 2) -// CHECK-NOT: @_ZN1A1fEv -// CHECK: store i32 42, i32* getelementptr inbounds ({{.*}}* @b, i32 0, i32 3) -// CHECK-NOT: C1Ev -// CHECK: store i8 9, i8* {{.*}} @b, i32 0, i32 4) +// CHECK-NOT: @b // Initialization of 'c':