[clang] cb9b984 - PR47954 / DR2126: permit temporary objects that are lifetime-extended by

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 23 14:29:28 PDT 2020


Author: Richard Smith
Date: 2020-10-23T14:29:18-07:00
New Revision: cb9b9842d31d6082c6a2259a64eb5d47bd680d32

URL: https://github.com/llvm/llvm-project/commit/cb9b9842d31d6082c6a2259a64eb5d47bd680d32
DIFF: https://github.com/llvm/llvm-project/commit/cb9b9842d31d6082c6a2259a64eb5d47bd680d32.diff

LOG: PR47954 / DR2126: permit temporary objects that are lifetime-extended by
variables that are usable in constant expressions to themselves be
usable in constant expressions.

Added: 
    

Modified: 
    clang/include/clang/AST/ExprCXX.h
    clang/lib/AST/ExprCXX.cpp
    clang/lib/AST/ExprConstant.cpp
    clang/test/CXX/drs/dr21xx.cpp
    clang/test/CodeGenCXX/const-init-cxx11.cpp
    clang/test/SemaCXX/constant-expression-cxx11.cpp
    clang/www/cxx_dr_status.html

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 61a23ddaa368..2144279f132b 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -4519,6 +4519,10 @@ class MaterializeTemporaryExpr : public Expr {
     return getValueKind() == VK_LValue;
   }
 
+  /// Determine whether this temporary object is usable in constant
+  /// expressions, as specified in C++20 [expr.const]p4.
+  bool isUsableInConstantExpressions(const ASTContext &Context) const;
+
   SourceLocation getBeginLoc() const LLVM_READONLY {
     return getSubExpr()->getBeginLoc();
   }

diff  --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index c1ec86075772..b7f677051ea2 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1647,6 +1647,20 @@ void MaterializeTemporaryExpr::setExtendingDecl(ValueDecl *ExtendedBy,
   ES->ManglingNumber = ManglingNumber;
 }
 
+bool MaterializeTemporaryExpr::isUsableInConstantExpressions(
+    const ASTContext &Context) const {
+  // C++20 [expr.const]p4:
+  //   An object or reference is usable in constant expressions if it is [...]
+  //   a temporary object of non-volatile const-qualified literal type
+  //   whose lifetime is extended to that of a variable that is usable
+  //   in constant expressions
+  auto *VD = dyn_cast_or_null<VarDecl>(getExtendingDecl());
+  return VD && getType().isConstant(Context) &&
+         !getType().isVolatileQualified() &&
+         getType()->isLiteralType(Context) &&
+         VD->isUsableInConstantExpressions(Context);
+}
+
 TypeTraitExpr::TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind,
                              ArrayRef<TypeSourceInfo *> Args,
                              SourceLocation RParenLoc, bool Value)

diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index b33ce14b3658..fa666930ef1d 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -4093,27 +4093,32 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
         assert(MTE->getStorageDuration() == SD_Static &&
                "should have a frame for a non-global materialized temporary");
 
-        // Per C++1y [expr.const]p2:
+        // C++20 [expr.const]p4: [DR2126]
+        //   An object or reference is usable in constant expressions if it is
+        //   - a temporary object of non-volatile const-qualified literal type
+        //     whose lifetime is extended to that of a variable that is usable
+        //     in constant expressions
+        //
+        // C++20 [expr.const]p5:
         //  an lvalue-to-rvalue conversion [is not allowed unless it applies to]
-        //   - a [...] glvalue of integral or enumeration type that refers to
-        //     a non-volatile const object [...]
-        //   [...]
-        //   - a [...] glvalue of literal type that refers to a non-volatile
-        //     object whose lifetime began within the evaluation of e.
+        //   - a non-volatile glvalue that refers to an object that is usable
+        //     in constant expressions, or
+        //   - a non-volatile glvalue of literal type that refers to a
+        //     non-volatile object whose lifetime began within the evaluation
+        //     of E;
         //
         // C++11 misses the 'began within the evaluation of e' check and
         // instead allows all temporaries, including things like:
         //   int &&r = 1;
         //   int x = ++r;
         //   constexpr int k = r;
-        // Therefore we use the C++14 rules in C++11 too.
+        // Therefore we use the C++14-onwards rules in C++11 too.
         //
         // Note that temporaries whose lifetimes began while evaluating a
         // variable's constructor are not usable while evaluating the
         // corresponding destructor, not even if they're of const-qualified
         // types.
-        if (!(BaseType.isConstQualified() &&
-              BaseType->isIntegralOrEnumerationType()) &&
+        if (!MTE->isUsableInConstantExpressions(Info.Ctx) &&
             !lifetimeStartedInEvaluation(Info, LVal.Base)) {
           if (!IsAccess)
             return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);

diff  --git a/clang/test/CXX/drs/dr21xx.cpp b/clang/test/CXX/drs/dr21xx.cpp
index 7e3ffa057ef7..6810944cde65 100644
--- a/clang/test/CXX/drs/dr21xx.cpp
+++ b/clang/test/CXX/drs/dr21xx.cpp
@@ -32,6 +32,40 @@ namespace dr2120 { // dr2120: 7
   static_assert(!__is_standard_layout(E), "");
 }
 
+namespace dr2126 { // dr2126: 12
+#if __cplusplus >= 201103L
+  struct A { int n; };
+
+  const A &a = {1};              // const temporary
+  A &b = (A &)(const A &)A{1};   // const temporary
+  A &&c = (A &&)(const A &)A{1}; // const temporary
+
+  A &&d = {1};                   // non-const temporary expected-note {{here}}
+  const A &e = (A &)(A &&) A{1}; // non-const temporary expected-note {{here}}
+  A &&f = (A &&)(A &&) A{1};     // non-const temporary expected-note {{here}}
+
+  constexpr const A &g = {1};    // const temporary
+  constexpr A &&h = {1};         // non-const temporary expected-note {{here}}
+
+  struct B { const A &a; };
+  B i = {{1}};           // extending decl not usable in constant expr expected-note {{here}}
+  const B j = {{1}};     // extending decl not usable in constant expr expected-note {{here}}
+  constexpr B k = {{1}}; // extending decl usable in constant expr
+
+  static_assert(a.n == 1, "");
+  static_assert(b.n == 1, "");
+  static_assert(c.n == 1, "");
+  static_assert(d.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
+  static_assert(e.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
+  static_assert(f.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
+  static_assert(g.n == 1, "");
+  static_assert(h.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
+  static_assert(i.a.n == 1, ""); // expected-error {{constant}} expected-note {{read of non-constexpr variable}}
+  static_assert(j.a.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
+  static_assert(k.a.n == 1, "");
+#endif
+}
+
 namespace dr2140 { // dr2140: 9
 #if __cplusplus >= 201103L
   union U { int a; decltype(nullptr) b; };

diff  --git a/clang/test/CodeGenCXX/const-init-cxx11.cpp b/clang/test/CodeGenCXX/const-init-cxx11.cpp
index 2673ac2baa10..3684ad1c5ff1 100644
--- a/clang/test/CodeGenCXX/const-init-cxx11.cpp
+++ b/clang/test/CodeGenCXX/const-init-cxx11.cpp
@@ -400,6 +400,22 @@ namespace UnemittedTemporaryDecl {
   // CHECK: @_ZN22UnemittedTemporaryDecl4ref2E = constant i32* @_ZGRN22UnemittedTemporaryDecl3refE_
 }
 
+namespace DR2126 {
+  struct A { int &&b; };
+  constexpr const A &a = {42};
+  // CHECK: @_ZGRN6DR21261aE0_ = internal global i32 42
+  // FIXME: This is unused and need not be emitted.
+  // CHECK: @_ZGRN6DR21261aE_ = internal constant {{.*}} { i32* @_ZGRN6DR21261aE0_ }
+  // CHECK: @_ZN6DR21261rE = constant i32* @_ZGRN6DR21261aE0_
+  int &r = a.b;
+
+  // Dynamically initialized: the temporary object bound to 'b' could be
+  // modified (eg, by placement 'new') before the initializer of 's' runs.
+  constexpr A &&b = {42};
+  // CHECK: @_ZN6DR21261sE = global i32* null
+  int &s = b.b;
+}
+
 // CHECK: @_ZZN12LocalVarInit3aggEvE1a = internal constant {{.*}} i32 101
 // CHECK: @_ZZN12LocalVarInit4ctorEvE1a = internal constant {{.*}} i32 102
 // CHECK: @__const._ZN12LocalVarInit8mutable_Ev.a = private unnamed_addr constant {{.*}} i32 103

diff  --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp
index 7d51a48eac42..0e97798cc197 100644
--- a/clang/test/SemaCXX/constant-expression-cxx11.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp
@@ -409,12 +409,23 @@ namespace ConstAddedByReference {
   const int &r = (0);
   constexpr int n = r;
 
+  int &&r2 = 0; // expected-note {{created here}}
+  constexpr int n2 = r2; // expected-error {{constant}} expected-note {{read of temporary}}
+
   struct A { constexpr operator int() const { return 0; }};
   struct B { constexpr operator const int() const { return 0; }};
   const int &ra = A();
   const int &rb = B();
   constexpr int na = ra;
   constexpr int nb = rb;
+
+  struct C { int &&r; };
+  constexpr C c1 = {1};
+  constexpr int &c1r = c1.r;
+  constexpr const C &c2 = {2};
+  constexpr int &c2r = c2.r;
+  constexpr C &&c3 = {3}; // expected-note {{created here}}
+  constexpr int &c3r = c3.r; // expected-error {{constant}} expected-note {{read of temporary}}
 }
 
 }
@@ -1843,6 +1854,11 @@ namespace InitializerList {
 
   static_assert(*std::initializer_list<int>{1, 2, 3}.begin() == 1, "");
   static_assert(std::initializer_list<int>{1, 2, 3}.begin()[2] == 3, "");
+
+  namespace DR2126 {
+    constexpr std::initializer_list<float> il = {1.0, 2.0, 3.0};
+    static_assert(il.begin()[1] == 2.0, "");
+  }
 }
 
 namespace StmtExpr {

diff  --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 74319b138943..89e4fe0f7dc9 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -12571,7 +12571,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://wg21.link/cwg2126">2126</a></td>
     <td>DRWP</td>
     <td>Lifetime-extended temporaries in constant expressions</td>
-    <td class="none" align="center">Unknown</td>
+    <td class="unreleased" align="center">Clang 12</td>
   </tr>
   <tr class="open" id="2127">
     <td><a href="https://wg21.link/cwg2127">2127</a></td>
@@ -13843,7 +13843,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://wg21.link/cwg2338">2338</a></td>
     <td>DRWP</td>
     <td>Undefined behavior converting to short enums with fixed underlying types</td>
-    <td class="none" align="center">Unknown</td>
+    <td class="unreleased" align="center">Clang 12</td>
   </tr>
   <tr id="2339">
     <td><a href="https://wg21.link/cwg2339">2339</a></td>


        


More information about the cfe-commits mailing list