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