From: Richard Smith Date: Fri, 12 Aug 2016 00:39:32 +0000 (+0000) Subject: P0217R3: Constant expression evaluation for decomposition declarations. X-Git-Tag: llvmorg-4.0.0-rc1~12658 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=dca60b495822a869c08b9973e4c485a83e8b6c0e;p=platform%2Fupstream%2Fllvm.git P0217R3: Constant expression evaluation for decomposition declarations. llvm-svn: 278447 --- diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 48ada29..baeafe6 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -3385,20 +3385,28 @@ public: class BindingDecl : public ValueDecl { void anchor() override; + DecompositionDecl *Decomp; + /// The binding represented by this declaration. References to this /// declaration are effectively equivalent to this expression (except /// that it is only evaluated once at the point of declaration of the /// binding). Expr *Binding; - BindingDecl(DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id) - : ValueDecl(Decl::Binding, DC, IdLoc, Id, QualType()), Binding(nullptr) {} + BindingDecl(DeclContext *DC, DecompositionDecl *Decomp, SourceLocation IdLoc, + IdentifierInfo *Id) + : ValueDecl(Decl::Binding, DC, IdLoc, Id, QualType()), Decomp(Decomp), + Binding(nullptr) {} public: static BindingDecl *Create(ASTContext &C, DeclContext *DC, - SourceLocation IdLoc, IdentifierInfo *Id); + DecompositionDecl *Decomp, SourceLocation IdLoc, + IdentifierInfo *Id); static BindingDecl *CreateDeserialized(ASTContext &C, unsigned ID); + void setDecompositionDecl(DecompositionDecl *DD) { Decomp = DD; } + DecompositionDecl *getDecompositionDecl() const { return Decomp; } + /// Get the expression to which this declaration is bound. This may be null /// in two different cases: while parsing the initializer for the /// decomposition declaration, and when the initializer is type-dependent. diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 14df382..772ce10 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -2309,12 +2309,13 @@ StaticAssertDecl *StaticAssertDecl::CreateDeserialized(ASTContext &C, void BindingDecl::anchor() {} BindingDecl *BindingDecl::Create(ASTContext &C, DeclContext *DC, + DecompositionDecl *Decomp, SourceLocation IdLoc, IdentifierInfo *Id) { - return new (C, DC) BindingDecl(DC, IdLoc, Id); + return new (C, DC) BindingDecl(DC, Decomp, IdLoc, Id); } BindingDecl *BindingDecl::CreateDeserialized(ASTContext &C, unsigned ID) { - return new (C, ID) BindingDecl(nullptr, SourceLocation(), nullptr); + return new (C, ID) BindingDecl(nullptr, nullptr, SourceLocation(), nullptr); } void DecompositionDecl::anchor() {} diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 176f1ba..8fe27a5 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -3427,6 +3427,18 @@ static bool EvaluateDecl(EvalInfo &Info, const Decl *D) { Val = APValue(); return false; } + + // Evaluate initializers for any structured bindings. + if (auto *DD = dyn_cast(VD)) { + for (auto *BD : DD->bindings()) { + APValue &Val = Info.CurrentCall->createTemporary(BD, true); + + LValue Result; + if (!EvaluateLValue(BD->getBinding(), Result, Info)) + return false; + Result.moveInto(Val); + } + } } return true; @@ -4724,6 +4736,7 @@ public: LValueExprEvaluatorBaseTy(Info, Result) {} bool VisitVarDecl(const Expr *E, const VarDecl *VD); + bool VisitBindingDecl(const Expr *E, const BindingDecl *BD); bool VisitUnaryPreIncDec(const UnaryOperator *UO); bool VisitDeclRefExpr(const DeclRefExpr *E); @@ -4785,6 +4798,8 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { return Success(FD); if (const VarDecl *VD = dyn_cast(E->getDecl())) return VisitVarDecl(E, VD); + if (const BindingDecl *BD = dyn_cast(E->getDecl())) + return VisitBindingDecl(E, BD); return Error(E); } @@ -4812,6 +4827,53 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { return Success(*V, E); } +bool LValueExprEvaluator::VisitBindingDecl(const Expr *E, + const BindingDecl *BD) { + // If we've already evaluated the binding, just return the lvalue. + if (APValue *Value = Info.CurrentCall->getTemporary(BD)) { + if (Value->isUninit()) { + if (!Info.checkingPotentialConstantExpression()) + Info.FFDiag(E, diag::note_constexpr_use_uninit_reference); + return false; + } + return Success(*Value, E); + } + + // We've not evaluated the initializer of this binding. It's still OK if it + // is initialized by a constant expression. + // + // FIXME: We should check this at the point of declaration, since we're not + // supposed to be able to use it if it references something that was declared + // later. + auto *Binding = BD->getBinding(); + if (!Binding) { + Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); + return false; + } + + // Evaluate in an independent context to check whether the binding was a + // constant expression in an absolute sense, and without mutating any of + // our local state. + Expr::EvalStatus InitStatus; + SmallVector Diag; + InitStatus.Diag = &Diag; + EvalInfo InitInfo(Info.Ctx, InitStatus, EvalInfo::EM_ConstantExpression); + + if (!EvaluateLValue(Binding, Result, InitInfo) || InitStatus.HasSideEffects || + !CheckLValueConstantExpression( + InitInfo, Binding->getExprLoc(), + Info.Ctx.getLValueReferenceType(BD->getType()), Result) || + !Diag.empty()) { + // FIXME: Diagnose this better. Maybe produce the Diags to explain why + // the initializer was not constant. + if (!Info.checkingPotentialConstantExpression()) + Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); + return false; + } + + return true; +} + bool LValueExprEvaluator::VisitMaterializeTemporaryExpr( const MaterializeTemporaryExpr *E) { // Walk through the expression to find the materialized temporary itself. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 4a68d60..6445dac 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6107,9 +6107,12 @@ NamedDecl *Sema::ActOnVariableDeclarator( NewVD = cast(Res.get()); AddToScope = false; } else if (D.isDecompositionDeclarator()) { - NewVD = DecompositionDecl::Create(Context, DC, D.getLocStart(), - D.getIdentifierLoc(), R, TInfo, SC, - Bindings); + auto *NewDD = DecompositionDecl::Create(Context, DC, D.getLocStart(), + D.getIdentifierLoc(), R, TInfo, + SC, Bindings); + for (auto *B : Bindings) + B->setDecompositionDecl(NewDD); + NewVD = NewDD; } else NewVD = VarDecl::Create(Context, DC, D.getLocStart(), D.getIdentifierLoc(), II, R, TInfo, SC); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index fc593be..cecbee1 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -791,7 +791,7 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D, Diag(Old->getLocation(), diag::note_previous_definition); } - auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, B.Name); + auto *BD = BindingDecl::Create(Context, DC, nullptr, B.NameLoc, B.Name); PushOnScopeChains(BD, S, true); Bindings.push_back(BD); ParsingInitForAutoVars.insert(BD); @@ -1660,7 +1660,8 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, // C++11 and permitted in C++1y, so ignore them. continue; - case Decl::Var: { + case Decl::Var: + case Decl::Decomposition: { // C++1y [dcl.constexpr]p3 allows anything except: // a definition of a variable of non-literal type or of static or // thread storage duration or for which no initialization is performed. diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 208afa6..5c8aa72 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -599,7 +599,13 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { } Decl *TemplateDeclInstantiator::VisitBindingDecl(BindingDecl *D) { - return BindingDecl::Create(SemaRef.Context, Owner, D->getLocation(), + auto *NewDD = + dyn_cast_or_null(SemaRef.FindInstantiatedDecl( + D->getLocation(), D->getDecompositionDecl(), TemplateArgs)); + if (!NewDD) + return nullptr; + + return BindingDecl::Create(SemaRef.Context, Owner, NewDD, D->getLocation(), D->getIdentifier()); } diff --git a/clang/test/CXX/dcl.decl/dcl.decomp/p2.cpp b/clang/test/CXX/dcl.decl/dcl.decomp/p2.cpp index cd0518b..639aff6 100644 --- a/clang/test/CXX/dcl.decl/dcl.decomp/p2.cpp +++ b/clang/test/CXX/dcl.decl/dcl.decomp/p2.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -std=c++1z -verify %s int array() { - int arr[3] = {}; + static int arr[3] = {}; // FIXME: We are supposed to create an array object here and perform elementwise initialization. auto [a, b, c] = arr; // expected-error {{cannot decompose non-class, non-array}} @@ -11,8 +11,9 @@ int array() { auto &[r0, r1, r2] = arr; const auto &[cr0, cr1, cr2] = arr; - //static_assert(&arr[0] == &r0); - //static_assert(&arr[0] == &cr0); + static_assert(&arr[0] == &r0); + static_assert(&arr[0] == &cr0); + using T = int; using T = decltype(r0); using U = const int; diff --git a/clang/test/CXX/dcl.decl/dcl.decomp/p3.cpp b/clang/test/CXX/dcl.decl/dcl.decomp/p3.cpp index 1acf391..e4a7a6c 100644 --- a/clang/test/CXX/dcl.decl/dcl.decomp/p3.cpp +++ b/clang/test/CXX/dcl.decl/dcl.decomp/p3.cpp @@ -201,3 +201,32 @@ void test_value_category() { { auto [a] = wrap(); } // ok, const int &a can bind to float { auto [a] = wrap(); } // ok, int &&a can bind to float } + +namespace constant { + struct Q {}; + template constexpr int get(Q &&) { return N * N; } +} +template<> struct std::tuple_size { static const int value = 3; }; +template struct std::tuple_element { typedef int type; }; +namespace constant { + Q q; + // This creates and lifetime-extends a temporary to hold the result of each get() call. + auto [a, b, c] = q; // expected-note {{temporary}} + static_assert(a == 0); // expected-error {{constant expression}} expected-note {{temporary}} + + constexpr bool f() { + auto [a, b, c] = q; + return a == 0 && b == 1 && c == 4; + } + static_assert(f()); + + constexpr int g() { + int *p = nullptr; + { + auto [a, b, c] = q; + p = &c; + } + return *p; // expected-note {{read of object outside its lifetime}} + } + static_assert(g() == 4); // expected-error {{constant}} expected-note {{in call to 'g()'}} +} diff --git a/clang/test/CXX/dcl.decl/dcl.decomp/p4.cpp b/clang/test/CXX/dcl.decl/dcl.decomp/p4.cpp index 3a72ae0..ea225cb 100644 --- a/clang/test/CXX/dcl.decl/dcl.decomp/p4.cpp +++ b/clang/test/CXX/dcl.decl/dcl.decomp/p4.cpp @@ -163,6 +163,14 @@ namespace Bitfield { } } +namespace Constexpr { + struct Q { int a, b; constexpr Q() : a(1), b(2) {} }; + constexpr Q q; + auto &[qa, qb] = q; + static_assert(&qa == &q.a && &qb == &q.b); + static_assert(qa == 1 && qb == 2); +} + namespace std_example { struct S { int x1 : 2; volatile double y1; }; S f(); diff --git a/clang/test/SemaCXX/cxx1z-decomposition.cpp b/clang/test/SemaCXX/cxx1z-decomposition.cpp index 6541476..3b1e49c 100644 --- a/clang/test/SemaCXX/cxx1z-decomposition.cpp +++ b/clang/test/SemaCXX/cxx1z-decomposition.cpp @@ -6,9 +6,13 @@ void use_from_own_init() { // As a Clang extension, _Complex can be decomposed. float decompose_complex(_Complex float cf) { + static _Complex float scf; + auto &[sre, sim] = scf; + // ok, this is references initialized by constant expressions all the way down + static_assert(&sre == &__real scf); + static_assert(&sim == &__imag scf); + auto [re, im] = cf; - //static_assert(&re == &__real cf); - //static_assert(&im == &__imag cf); return re*re + im*im; } @@ -20,8 +24,20 @@ float decompose_vector(vf3 v) { return x + y + z; } +struct S { int a, b; }; +constexpr int f(S s) { + auto &[a, b] = s; + return a * 10 + b; +} +static_assert(f({1, 2}) == 12); + +constexpr bool g(S &&s) { + auto &[a, b] = s; + return &a == &s.a && &b == &s.b && &a != &b; +} +static_assert(g({1, 2})); + // FIXME: by-value array copies // FIXME: template instantiation // FIXME: ast file support // FIXME: code generation -// FIXME: constant expression evaluation