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
