r278447 - P0217R3: Constant expression evaluation for decomposition declarations.
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 11 17:39:32 PDT 2016
Author: rsmith
Date: Thu Aug 11 19:39:32 2016
New Revision: 278447
URL: http://llvm.org/viewvc/llvm-project?rev=278447&view=rev
Log:
P0217R3: Constant expression evaluation for decomposition declarations.
Modified:
cfe/trunk/include/clang/AST/DeclCXX.h
cfe/trunk/lib/AST/DeclCXX.cpp
cfe/trunk/lib/AST/ExprConstant.cpp
cfe/trunk/lib/Sema/SemaDecl.cpp
cfe/trunk/lib/Sema/SemaDeclCXX.cpp
cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p2.cpp
cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p3.cpp
cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p4.cpp
cfe/trunk/test/SemaCXX/cxx1z-decomposition.cpp
Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=278447&r1=278446&r2=278447&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Thu Aug 11 19:39:32 2016
@@ -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.
Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=278447&r1=278446&r2=278447&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Thu Aug 11 19:39:32 2016
@@ -2309,12 +2309,13 @@ StaticAssertDecl *StaticAssertDecl::Crea
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() {}
Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=278447&r1=278446&r2=278447&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Thu Aug 11 19:39:32 2016
@@ -3427,6 +3427,18 @@ static bool EvaluateDecl(EvalInfo &Info,
Val = APValue();
return false;
}
+
+ // Evaluate initializers for any structured bindings.
+ if (auto *DD = dyn_cast<DecompositionDecl>(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::VisitDeclRefEx
return Success(FD);
if (const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()))
return VisitVarDecl(E, VD);
+ if (const BindingDecl *BD = dyn_cast<BindingDecl>(E->getDecl()))
+ return VisitBindingDecl(E, BD);
return Error(E);
}
@@ -4812,6 +4827,53 @@ bool LValueExprEvaluator::VisitVarDecl(c
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<PartialDiagnosticAt, 8> 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.
Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=278447&r1=278446&r2=278447&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Thu Aug 11 19:39:32 2016
@@ -6107,9 +6107,12 @@ NamedDecl *Sema::ActOnVariableDeclarator
NewVD = cast<VarDecl>(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);
Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=278447&r1=278446&r2=278447&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Thu Aug 11 19:39:32 2016
@@ -791,7 +791,7 @@ Sema::ActOnDecompositionDeclarator(Scope
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
// 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.
Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp?rev=278447&r1=278446&r2=278447&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Thu Aug 11 19:39:32 2016
@@ -599,7 +599,13 @@ TemplateDeclInstantiator::VisitTypeAlias
}
Decl *TemplateDeclInstantiator::VisitBindingDecl(BindingDecl *D) {
- return BindingDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+ auto *NewDD =
+ dyn_cast_or_null<DecompositionDecl>(SemaRef.FindInstantiatedDecl(
+ D->getLocation(), D->getDecompositionDecl(), TemplateArgs));
+ if (!NewDD)
+ return nullptr;
+
+ return BindingDecl::Create(SemaRef.Context, Owner, NewDD, D->getLocation(),
D->getIdentifier());
}
Modified: cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p2.cpp?rev=278447&r1=278446&r2=278447&view=diff
==============================================================================
--- cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p2.cpp (original)
+++ cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p2.cpp Thu Aug 11 19:39:32 2016
@@ -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;
Modified: cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p3.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p3.cpp?rev=278447&r1=278446&r2=278447&view=diff
==============================================================================
--- cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p3.cpp (original)
+++ cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p3.cpp Thu Aug 11 19:39:32 2016
@@ -201,3 +201,32 @@ void test_value_category() {
{ auto [a] = wrap<const int, void, float&>(); } // ok, const int &a can bind to float
{ auto [a] = wrap<int, void, float>(); } // ok, int &&a can bind to float
}
+
+namespace constant {
+ struct Q {};
+ template<int N> constexpr int get(Q &&) { return N * N; }
+}
+template<> struct std::tuple_size<constant::Q> { static const int value = 3; };
+template<int N> struct std::tuple_element<N, constant::Q> { 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()'}}
+}
Modified: cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p4.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p4.cpp?rev=278447&r1=278446&r2=278447&view=diff
==============================================================================
--- cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p4.cpp (original)
+++ cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p4.cpp Thu Aug 11 19:39:32 2016
@@ -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();
Modified: cfe/trunk/test/SemaCXX/cxx1z-decomposition.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1z-decomposition.cpp?rev=278447&r1=278446&r2=278447&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1z-decomposition.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx1z-decomposition.cpp Thu Aug 11 19:39:32 2016
@@ -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
More information about the cfe-commits
mailing list