зеркало из https://github.com/microsoft/clang-1.git
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
This commit is contained in:
Родитель
afde200cda
Коммит
6391ea2897
|
@ -391,7 +391,7 @@ namespace {
|
||||||
|
|
||||||
/// EvaluatingDecl - This is the declaration whose initializer is being
|
/// EvaluatingDecl - This is the declaration whose initializer is being
|
||||||
/// evaluated, if any.
|
/// evaluated, if any.
|
||||||
const VarDecl *EvaluatingDecl;
|
APValue::LValueBase EvaluatingDecl;
|
||||||
|
|
||||||
/// EvaluatingDeclValue - This is the value being constructed for the
|
/// EvaluatingDeclValue - This is the value being constructed for the
|
||||||
/// declaration whose initializer is being evaluated, if any.
|
/// declaration whose initializer is being evaluated, if any.
|
||||||
|
@ -414,12 +414,12 @@ namespace {
|
||||||
CallStackDepth(0), NextCallIndex(1),
|
CallStackDepth(0), NextCallIndex(1),
|
||||||
StepsLeft(getLangOpts().ConstexprStepLimit),
|
StepsLeft(getLangOpts().ConstexprStepLimit),
|
||||||
BottomFrame(*this, SourceLocation(), 0, 0, 0),
|
BottomFrame(*this, SourceLocation(), 0, 0, 0),
|
||||||
EvaluatingDecl(0), EvaluatingDeclValue(0), HasActiveDiagnostic(false),
|
EvaluatingDecl((const ValueDecl*)0), EvaluatingDeclValue(0),
|
||||||
CheckingPotentialConstantExpression(false),
|
HasActiveDiagnostic(false), CheckingPotentialConstantExpression(false),
|
||||||
IntOverflowCheckMode(OverflowCheckMode) {}
|
IntOverflowCheckMode(OverflowCheckMode) {}
|
||||||
|
|
||||||
void setEvaluatingDecl(const VarDecl *VD, APValue &Value) {
|
void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
|
||||||
EvaluatingDecl = VD;
|
EvaluatingDecl = Base;
|
||||||
EvaluatingDeclValue = &Value;
|
EvaluatingDeclValue = &Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -899,19 +899,11 @@ namespace {
|
||||||
return false;
|
return false;
|
||||||
return LHS.Path == RHS.Path;
|
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 Evaluate(APValue &Result, EvalInfo &Info, const Expr *E);
|
||||||
static bool EvaluateInPlace(APValue &Result, EvalInfo &Info,
|
static bool EvaluateInPlace(APValue &Result, EvalInfo &Info,
|
||||||
const LValue &This, const Expr *E,
|
const LValue &This, const Expr *E,
|
||||||
CheckConstantExpressionKind CCEK = CCEK_Constant,
|
|
||||||
bool AllowNonLiteralTypes = false);
|
bool AllowNonLiteralTypes = false);
|
||||||
static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info);
|
static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info);
|
||||||
static bool EvaluatePointer(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,
|
/// Check that this core constant expression is of literal type, and if not,
|
||||||
/// produce an appropriate diagnostic.
|
/// 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))
|
if (!E->isRValue() || E->getType()->isLiteralType(Info.Ctx))
|
||||||
return true;
|
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.
|
// Prvalue constant expressions must be of literal types.
|
||||||
if (Info.getLangOpts().CPlusPlus11)
|
if (Info.getLangOpts().CPlusPlus11)
|
||||||
Info.Diag(E, diag::note_constexpr_nonliteral)
|
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
|
// If we're currently evaluating the initializer of this declaration, use that
|
||||||
// in-flight value.
|
// in-flight value.
|
||||||
if (Info.EvaluatingDecl == VD) {
|
if (Info.EvaluatingDecl.dyn_cast<const ValueDecl*>() == VD) {
|
||||||
Result = Info.EvaluatingDeclValue;
|
Result = Info.EvaluatingDeclValue;
|
||||||
return !Result->isUninit();
|
return !Result->isUninit();
|
||||||
}
|
}
|
||||||
|
@ -2134,9 +2135,6 @@ CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, AccessKinds AK,
|
||||||
NoteLValueLocation(Info, LVal.Base);
|
NoteLValueLocation(Info, LVal.Base);
|
||||||
return CompleteObject();
|
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
|
// 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,
|
// Unless we're looking at a local variable or argument in a constexpr call,
|
||||||
// the variable we're reading must be const.
|
// the variable we're reading must be const.
|
||||||
if (!Frame) {
|
if (!Frame) {
|
||||||
assert(AK == AK_Read && "can't modify non-local");
|
if (Info.getLangOpts().CPlusPlus1y &&
|
||||||
if (VD->isConstexpr()) {
|
VD == Info.EvaluatingDecl.dyn_cast<const ValueDecl *>()) {
|
||||||
|
// 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.
|
// OK, we can read this variable.
|
||||||
} else if (BaseType->isIntegralOrEnumerationType()) {
|
} else if (BaseType->isIntegralOrEnumerationType()) {
|
||||||
if (!BaseType.isConstQualified()) {
|
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
|
// In C++1y, we can't safely access any mutable state when checking a
|
||||||
// potential constant expression.
|
// potential constant expression.
|
||||||
if (Frame && Info.getLangOpts().CPlusPlus1y &&
|
if (Frame && Info.getLangOpts().CPlusPlus1y &&
|
||||||
|
@ -3210,9 +3225,7 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This,
|
||||||
llvm_unreachable("unknown base initializer kind");
|
llvm_unreachable("unknown base initializer kind");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EvaluateInPlace(*Value, Info, Subobject, (*I)->getInit(),
|
if (!EvaluateInPlace(*Value, Info, Subobject, (*I)->getInit())) {
|
||||||
(*I)->isBaseInitializer()
|
|
||||||
? CCEK_Constant : CCEK_MemberInit)) {
|
|
||||||
// If we're checking for a potential constant expression, evaluate all
|
// If we're checking for a potential constant expression, evaluate all
|
||||||
// initializers even if some of them fail.
|
// initializers even if some of them fail.
|
||||||
if (!Info.keepEvaluatingAfterFailure())
|
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
|
/// cases, the in-place evaluation is essential, since later initializers for
|
||||||
/// an object can indirectly refer to subobjects which were initialized earlier.
|
/// an object can indirectly refer to subobjects which were initialized earlier.
|
||||||
static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, const LValue &This,
|
static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, const LValue &This,
|
||||||
const Expr *E, CheckConstantExpressionKind CCEK,
|
const Expr *E, bool AllowNonLiteralTypes) {
|
||||||
bool AllowNonLiteralTypes) {
|
if (!AllowNonLiteralTypes && !CheckLiteralType(Info, E, &This))
|
||||||
if (!AllowNonLiteralTypes && !CheckLiteralType(Info, E))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (E->isRValue()) {
|
if (E->isRValue()) {
|
||||||
|
@ -7284,12 +7296,12 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
|
||||||
if (Ctx.getLangOpts().CPlusPlus && !VD->hasLocalStorage() &&
|
if (Ctx.getLangOpts().CPlusPlus && !VD->hasLocalStorage() &&
|
||||||
!VD->getType()->isReferenceType()) {
|
!VD->getType()->isReferenceType()) {
|
||||||
ImplicitValueInitExpr VIE(VD->getType());
|
ImplicitValueInitExpr VIE(VD->getType());
|
||||||
if (!EvaluateInPlace(Value, InitInfo, LVal, &VIE, CCEK_Constant,
|
if (!EvaluateInPlace(Value, InitInfo, LVal, &VIE,
|
||||||
/*AllowNonLiteralTypes=*/true))
|
/*AllowNonLiteralTypes=*/true))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EvaluateInPlace(Value, InitInfo, LVal, this, CCEK_Constant,
|
if (!EvaluateInPlace(Value, InitInfo, LVal, this,
|
||||||
/*AllowNonLiteralTypes=*/true) ||
|
/*AllowNonLiteralTypes=*/true) ||
|
||||||
EStatus.HasSideEffects)
|
EStatus.HasSideEffects)
|
||||||
return false;
|
return false;
|
||||||
|
@ -7834,7 +7846,7 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
|
||||||
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
|
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
|
||||||
const CXXRecordDecl *RD = MD ? MD->getParent()->getCanonicalDecl() : 0;
|
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.
|
// is a temporary being used as the 'this' pointer.
|
||||||
LValue This;
|
LValue This;
|
||||||
ImplicitValueInitExpr VIE(RD ? Info.Ctx.getRecordType(RD) : Info.Ctx.IntTy);
|
ImplicitValueInitExpr VIE(RD ? Info.Ctx.getRecordType(RD) : Info.Ctx.IntTy);
|
||||||
|
@ -7845,9 +7857,12 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
|
||||||
SourceLocation Loc = FD->getLocation();
|
SourceLocation Loc = FD->getLocation();
|
||||||
|
|
||||||
APValue Scratch;
|
APValue Scratch;
|
||||||
if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(FD))
|
if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(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);
|
HandleConstructorCall(Loc, This, Args, CD, Info, Scratch);
|
||||||
else
|
} else
|
||||||
HandleFunctionCall(Loc, FD, (MD && MD->isInstance()) ? &This : 0,
|
HandleFunctionCall(Loc, FD, (MD && MD->isInstance()) ? &This : 0,
|
||||||
Args, FD->getBody(), Info, Scratch);
|
Args, FD->getBody(), Info, Scratch);
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -25,7 +25,11 @@ A b { 4, "bazquux", .x = 42, .c = 9 };
|
||||||
A c { 1, 0, 'A', f(), { 3 } };
|
A c { 1, 0, 'A', f(), { 3 } };
|
||||||
|
|
||||||
// CHECK: @[[STR_A:.*]] = {{.*}} [7 x i8] c"foobar\00"
|
// 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: @[[STR_B:.*]] = {{.*}} [8 x i8] c"bazquux\00"
|
||||||
|
// CHECK: @b = global {{.*}} i32 4, {{.*}} @[[STR_B]], {{.*}} i8 117, i32 42, {{.*}} i8 9
|
||||||
|
|
||||||
B x;
|
B x;
|
||||||
B y {};
|
B y {};
|
||||||
|
@ -44,18 +48,9 @@ B z { 1 };
|
||||||
// CHECK: store i32 %{{.*}}, i32* getelementptr inbounds ({{.*}}* @a, i32 0, i32 3)
|
// CHECK: store i32 %{{.*}}, i32* getelementptr inbounds ({{.*}}* @a, i32 0, i32 3)
|
||||||
// CHECK: call void @{{.*}}C1Ev({{.*}} getelementptr inbounds (%struct.A* @a, i32 0, i32 4))
|
// 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-NOT: @b
|
||||||
// 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)
|
|
||||||
|
|
||||||
// Initialization of 'c':
|
// Initialization of 'c':
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче