llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Matthias Wippich (Tsche)

<details>
<summary>Changes</summary>

This patch implements 
[CWG3135](https://cplusplus.github.io/CWG/issues/3135.html). This has been 
accepted into C++26 at the Croydon meeting through [CWG Motion 
3](https://github.com/cplusplus/draft/issues/8824).

This change has not been designated a defect report. However, there is 
currently some discussion on the core reflector regarding this designation - 
GCC implements it as DR. Please advise whether we should also treat this as a 
DR (it would break a handful of tests).

---
Full diff: https://github.com/llvm/llvm-project/pull/191880.diff


5 Files Affected:

- (modified) clang/docs/ReleaseNotes.rst (+3) 
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+19-13) 
- (modified) 
clang/test/CodeGenCXX/bad-codegen-for-constexpr-structured-bindings.cpp (+1-1) 
- (added) clang/test/SemaCXX/cxx2c-decomposition-prvalues.cpp (+37) 
- (modified) clang/test/SemaCXX/cxx2c-decomposition.cpp (+2-2) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index fd58d7847717c..63c27d1fb7066 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -49,6 +49,9 @@ C++ Specific Potentially Breaking Changes
 - Clang now correctly rejects ``export`` declarations in module implementation
   partitions. (#GH107602)
 
+- Clang now uses non-reference types for structured bindings whose initializer
+  returns a prvalue.
+
 ABI Changes in This Version
 ---------------------------
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 6647e52535114..f07e4d66d61d8 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1361,25 +1361,28 @@ static bool checkTupleLikeDecomposition(Sema &S,
       return true;
     Expr *Init = E.get();
 
-    //   Given the type T designated by std::tuple_element<i - 1, E>::type,
+    //   Given the type T designated by std::tuple_element<i - 1, E>::type
     QualType T = getTupleLikeElementType(S, Loc, I, DecompType);
     if (T.isNull())
       return true;
 
-    //   each vi is a variable of type "reference to T" initialized with the
-    //   initializer, where the reference is an lvalue reference if the
-    //   initializer is an lvalue and an rvalue reference otherwise
-    QualType RefType =
-        S.BuildReferenceType(T, E.get()->isLValue(), Loc, B->getDeclName());
-    if (RefType.isNull())
+    // C++26 [dcl.struct.bind]p7:
+    //   and the type Ui, defined as Ti if the initializer is a prvalue,
+    //   as "lvalue reference to Ti" if the initializer is an lvalue,
+    //   or as "rvalue reference to Ti" otherwise
+    // "defined as Ti if the initializer is a prvalue" was introduced by 
CWG3135
+    QualType U = E.get()->isPRValue() && S.getLangOpts().CPlusPlus26
+                     ? T
+                     : S.BuildReferenceType(T, E.get()->isLValue(), Loc,
+                                            B->getDeclName());
+    if (U.isNull())
       return true;
 
     // Don't give this VarDecl a TypeSourceInfo, since this is a synthesized
     // entity and this type was never written in source code.
-    auto *RefVD =
-        VarDecl::Create(S.Context, Src->getDeclContext(), Loc, Loc,
-                        B->getDeclName().getAsIdentifierInfo(), RefType,
-                        /*TInfo=*/nullptr, Src->getStorageClass());
+    auto *RefVD = VarDecl::Create(S.Context, Src->getDeclContext(), Loc, Loc,
+                                  B->getDeclName().getAsIdentifierInfo(), U,
+                                  /*TInfo=*/nullptr, Src->getStorageClass());
     RefVD->setLexicalDeclContext(Src->getLexicalDeclContext());
     RefVD->setTSCSpec(Src->getTSCSpec());
     RefVD->setImplicit();
@@ -1388,7 +1391,10 @@ static bool checkTupleLikeDecomposition(Sema &S,
     RefVD->getLexicalDeclContext()->addHiddenDecl(RefVD);
 
     InitializedEntity Entity = InitializedEntity::InitializeBinding(RefVD);
-    InitializationKind Kind = InitializationKind::CreateCopy(Loc, Loc);
+    InitializationKind Kind =
+        E.get()->isPRValue() && S.getLangOpts().CPlusPlus26
+            ? InitializationKind::CreateDirect(Loc, Loc, Loc)
+            : InitializationKind::CreateCopy(Loc, Loc);
     InitializationSequence Seq(S, Entity, Kind, Init);
     E = Seq.Perform(S, Entity, Kind, Init);
     if (E.isInvalid())
@@ -1405,7 +1411,7 @@ static bool checkTupleLikeDecomposition(Sema &S,
     if (E.isInvalid())
       return true;
 
-    B->setBinding(T, E.get());
+    B->setBinding(U, E.get());
     I++;
   }
 
diff --git 
a/clang/test/CodeGenCXX/bad-codegen-for-constexpr-structured-bindings.cpp 
b/clang/test/CodeGenCXX/bad-codegen-for-constexpr-structured-bindings.cpp
index ce81f7d8d026e..87c2055627aa7 100644
--- a/clang/test/CodeGenCXX/bad-codegen-for-constexpr-structured-bindings.cpp
+++ b/clang/test/CodeGenCXX/bad-codegen-for-constexpr-structured-bindings.cpp
@@ -35,6 +35,6 @@ const u8 &f() {
   return I;
 }
 
-// CHECK: @[[TMP:_ZGR.*]] = internal constant i8 0, align 1
+// CHECK: @[[TMP:_ZZ1fvE1I]] = internal constant i8 0, align 1
 // CHECK-LABEL: define {{.*}} @_Z1fv(
 // CHECK: ret ptr @[[TMP]]
diff --git a/clang/test/SemaCXX/cxx2c-decomposition-prvalues.cpp 
b/clang/test/SemaCXX/cxx2c-decomposition-prvalues.cpp
new file mode 100644
index 0000000000000..85ed2c70c3528
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-decomposition-prvalues.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s
+// expected-no-diagnostics
+// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify=since-cxx26 %s
+
+
+namespace std {
+  using size_t = decltype(sizeof(0));
+  template<typename> struct tuple_size;
+  template<size_t, typename> struct tuple_element;
+}
+
+struct Pinned {
+  Pinned(const Pinned&) = delete; 
+  // since-cxx26-note@-1 {{'Pinned' has been explicitly marked deleted here}}
+  Pinned& operator=(const Pinned&) = delete;
+};
+
+struct Source {
+  operator Pinned&&() const;
+  
+  template<std::size_t>
+  Source get() noexcept;
+};
+
+template<>
+struct std::tuple_size<Source> {
+  static constexpr std::size_t value = 1;
+};
+
+template<>
+struct std::tuple_element<0, Source> { using type = Pinned; };
+
+// CWG3135: In C++26 mode `x` is of type Pinned rather than Pinned&&. 
+// This leads to the deleted copy ctor being called in C++26 mode.
+auto [x] = Source{};
+// since-cxx26-error@-1 {{call to deleted constructor of 
'std::tuple_element<0UL, Source>::type' (aka 'Pinned')}}
+// since-cxx26-note@-2 {{in implicit initialization of binding declaration 
'x'}}
\ No newline at end of file
diff --git a/clang/test/SemaCXX/cxx2c-decomposition.cpp 
b/clang/test/SemaCXX/cxx2c-decomposition.cpp
index 99278c6575ef1..df2e3fa90263a 100644
--- a/clang/test/SemaCXX/cxx2c-decomposition.cpp
+++ b/clang/test/SemaCXX/cxx2c-decomposition.cpp
@@ -66,8 +66,8 @@ void test() {
 constexpr auto [a, b] = B{};
 static_assert(a.n == 0);
 // expected-error@-1 {{static assertion expression is not an integral constant 
expression}} \
-// expected-note@-1 {{read of temporary is not allowed in a constant 
expression outside the expression that created the temporary}}\
-// expected-note@-2 {{temporary created here}}
+// expected-note@-1 {{read of non-constexpr variable 'a' is not allowed in a 
constant expression}} \
+// expected-note@-2 {{declared here}}
 
 constinit auto [init1] = Y {42};
 constinit auto [init2] = X {};  // expected-error {{variable does not have a 
constant initializer}} \

``````````

</details>


https://github.com/llvm/llvm-project/pull/191880
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to