https://github.com/offsetof updated 
https://github.com/llvm/llvm-project/pull/131320

>From 6840e3be5a8019acdc115160fc748306e8ce384a Mon Sep 17 00:00:00 2001
From: offsetof <offse...@mailo.com>
Date: Sun, 16 Mar 2025 09:59:44 +0000
Subject: [PATCH 1/2] [clang] Refine handling of C++20 aggregate initialization

* Move parts of `InitializationSequence::InitializeFrom` corresponding
  to C++ [dcl.init.general] p16.6.1 and p16.6.2 into a separate
  function, `TryConstructorOrParenListInitialization`
* Use it in `TryListInitialization` to implement [dcl.init.list] p3.2
* Fix parenthesized aggregate initialization being attempted in
  copy-initialization contexts or when the constructor call is ambiguous
---
 clang/docs/ReleaseNotes.rst                   |   1 +
 clang/lib/Sema/SemaInit.cpp                   | 106 ++++++++------
 .../dcl.init/dcl.init.general/p16-cxx20.cpp   | 132 ++++++++++++++++--
 3 files changed, 183 insertions(+), 56 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2a1c5ee2d788e..22ebf86e05f7d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -281,6 +281,7 @@ Bug Fixes in This Version
 - Fixed a modules crash where exception specifications were not propagated 
properly (#GH121245, relanded in #GH129982)
 - Fixed a problematic case with recursive deserialization within 
``FinishedDeserializing()`` where
   ``PassInterestingDeclsToConsumer()`` was called before the declarations were 
safe to be passed. (#GH129982)
+- Fixed C++20 aggregate initialization rules being incorrectly applied in 
certain contexts (#GH131320)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 56ec33fe37bf3..56b6cafe5c53d 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -4633,6 +4633,59 @@ static void TryConstructorInitialization(Sema &S,
       IsListInit | IsInitListCopy, AsInitializerList);
 }
 
+static void TryOrBuildParenListInitialization(
+    Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind,
+    ArrayRef<Expr *> Args, InitializationSequence &Sequence, bool VerifyOnly,
+    ExprResult *Result = nullptr);
+
+/// Attempt to initialize an object of a class type either by
+/// direct-initialization, or by copy-initialization from an
+/// expression of the same or derived class type. This corresponds
+/// to the first two sub-bullets of C++2c [dcl.init.general] p16.6.
+///
+/// \param IsAggrListInit Is this non-list-initialization being done as
+///                       part of a list-initialization of an aggregate
+///                       from a single expression of the same or
+///                       derived class type (C++2c [dcl.init.list] p3.2)?
+static void TryConstructorOrParenListInitialization(
+    Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind,
+    MultiExprArg Args, QualType DestType, InitializationSequence &Sequence,
+    bool IsAggrListInit) {
+  // C++2c [dcl.init.general] p16.6:
+  //   * Otherwise, if the destination type is a class type:
+  //     * If the initializer expression is a prvalue and
+  //       the cv-unqualified version of the source type is the same
+  //       as the destination type, the initializer expression is used
+  //       to initialize the destination object.
+  //     * Otherwise, if the initialization is direct-initialization,
+  //       or if it is copy-initialization where the cv-unqualified
+  //       version of the source type is the same as or is derived from
+  //       the class of the destination type, constructors are considered.
+  //       The applicable constructors are enumerated, and the best one
+  //       is chosen through overload resolution. Then:
+  //       * If overload resolution is successful, the selected
+  //         constructor is called to initialize the object, with
+  //         the initializer expression or expression-list as its
+  //         argument(s).
+  TryConstructorInitialization(S, Entity, Kind, Args, DestType, DestType,
+                               Sequence, /*IsListInit=*/false, IsAggrListInit);
+
+  //       * Otherwise, if no constructor is viable, the destination type
+  //         is an aggregate class, and the initializer is a parenthesized
+  //         expression-list, the object is initialized as follows. [...]
+  // Parenthesized initialization of aggregates is a C++20 feature.
+  if (S.getLangOpts().CPlusPlus20 &&
+      Kind.getKind() == InitializationKind::IK_Direct && Sequence.Failed() &&
+      Sequence.getFailureKind() ==
+          InitializationSequence::FK_ConstructorOverloadFailed &&
+      Sequence.getFailedOverloadResult() == OR_No_Viable_Function &&
+      (IsAggrListInit || DestType->isAggregateType()))
+    TryOrBuildParenListInitialization(S, Entity, Kind, Args, Sequence,
+                                      /*VerifyOnly=*/true);
+
+  //       * Otherwise, the initialization is ill-formed.
+}
+
 static bool
 ResolveOverloadedFunctionForReferenceBinding(Sema &S,
                                              Expr *Initializer,
@@ -4846,11 +4899,16 @@ static void TryListInitialization(Sema &S,
       QualType InitType = InitList->getInit(0)->getType();
       if (S.Context.hasSameUnqualifiedType(InitType, DestType) ||
           S.IsDerivedFrom(InitList->getBeginLoc(), InitType, DestType)) {
+        InitializationKind SubKind =
+            Kind.getKind() == InitializationKind::IK_DirectList
+                ? InitializationKind::CreateDirect(Kind.getLocation(),
+                                                   InitList->getLBraceLoc(),
+                                                   InitList->getRBraceLoc())
+                : Kind;
         Expr *InitListAsExpr = InitList;
-        TryConstructorInitialization(S, Entity, Kind, InitListAsExpr, DestType,
-                                     DestType, Sequence,
-                                     /*InitListSyntax*/false,
-                                     /*IsInitListCopy*/true);
+        TryConstructorOrParenListInitialization(
+            S, Entity, SubKind, InitListAsExpr, DestType, Sequence,
+            /*IsAggrListInit=*/true);
         return;
       }
     }
@@ -5709,7 +5767,7 @@ static void TryDefaultInitialization(Sema &S,
 static void TryOrBuildParenListInitialization(
     Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind,
     ArrayRef<Expr *> Args, InitializationSequence &Sequence, bool VerifyOnly,
-    ExprResult *Result = nullptr) {
+    ExprResult *Result) {
   unsigned EntityIndexToProcess = 0;
   SmallVector<Expr *, 4> InitExprs;
   QualType ResultType;
@@ -6688,42 +6746,8 @@ void InitializationSequence::InitializeFrom(Sema &S,
          (Context.hasSameUnqualifiedType(SourceType, DestType) ||
           (Initializer && S.IsDerivedFrom(Initializer->getBeginLoc(),
                                           SourceType, DestType))))) {
-      TryConstructorInitialization(S, Entity, Kind, Args, DestType, DestType,
-                                   *this);
-
-      // We fall back to the "no matching constructor" path if the
-      // failed candidate set has functions other than the three default
-      // constructors. For example, conversion function.
-      if (const auto *RD =
-              
dyn_cast<CXXRecordDecl>(DestType->getAs<RecordType>()->getDecl());
-          // In general, we should call isCompleteType for RD to check its
-          // completeness, we don't call it here as it was already called in 
the
-          // above TryConstructorInitialization.
-          S.getLangOpts().CPlusPlus20 && RD && RD->hasDefinition() &&
-          RD->isAggregate() && Failed() &&
-          getFailureKind() == FK_ConstructorOverloadFailed) {
-        // Do not attempt paren list initialization if overload resolution
-        // resolves to a deleted function .
-        //
-        // We may reach this condition if we have a union wrapping a class with
-        // a non-trivial copy or move constructor and we call one of those two
-        // constructors. The union is an aggregate, but the matched constructor
-        // is implicitly deleted, so we need to prevent aggregate 
initialization
-        // (otherwise, it'll attempt aggregate initialization by initializing
-        // the first element with a reference to the union).
-        OverloadCandidateSet::iterator Best;
-        OverloadingResult OR = getFailedCandidateSet().BestViableFunction(
-            S, Kind.getLocation(), Best);
-        if (OR != OverloadingResult::OR_Deleted) {
-          // C++20 [dcl.init] 17.6.2.2:
-          //   - Otherwise, if no constructor is viable, the destination type 
is
-          //   an
-          //      aggregate class, and the initializer is a parenthesized
-          //      expression-list.
-          TryOrBuildParenListInitialization(S, Entity, Kind, Args, *this,
-                                            /*VerifyOnly=*/true);
-        }
-      }
+      TryConstructorOrParenListInitialization(S, Entity, Kind, Args, DestType,
+                                              *this, /*IsAggrListInit=*/false);
     } else {
       //     - Otherwise (i.e., for the remaining copy-initialization cases),
       //       user-defined conversion sequences that can convert from the
diff --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp 
b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp
index 1ad205d769c38..fc6908fefac85 100644
--- a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp
@@ -1,18 +1,120 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
 
-// If the initializer is (), the object is value-initialized.
-
-// expected-no-diagnostics
 namespace GH69890 {
-struct A {
-    constexpr A() {}
-    int x;
-};
-
-struct B : A {
-    int y;
-};
-
-static_assert(B().x == 0);
-static_assert(B().y == 0);
-}
+    // If the initializer is (), the object is value-initialized.
+    struct A {
+        constexpr A() {}
+        int x;
+    };
+
+    struct B : A {
+        int y;
+    };
+
+    static_assert(B().x == 0);
+    static_assert(B().y == 0);
+} // namespace GH69890
+
+namespace P0960R3 {
+    struct A { // expected-note 22 {{candidate constructor}}
+        int i;
+        operator int() volatile;
+    };
+    volatile A va;
+
+    A a1(va);
+    A a2 = va; // expected-error {{no matching constructor for initialization 
of 'A'}}
+    A a3 {va};
+    A a4 = {va}; // expected-error {{no matching constructor for 
initialization of 'A'}}
+
+    A f() {
+        return va; // expected-error {{no matching constructor for 
initialization of 'A'}}
+        return {va}; // expected-error {{no matching constructor for 
initialization of 'A'}}
+    }
+
+    int g(A); // expected-note 2 {{passing argument to parameter here}}
+    int i = g(va); // expected-error {{no matching constructor for 
initialization of 'A'}}
+    int j = g({va}); // expected-error {{no matching constructor for 
initialization of 'A'}}
+
+    struct Ambig {
+        operator const A&(); // expected-note {{candidate function}}
+        operator A&&(); // expected-note {{candidate function}}
+        operator int();
+    };
+
+    A a5(Ambig {}); // expected-error {{call to constructor of 'A' is 
ambiguous}}
+    A a6 = Ambig {}; // expected-error {{conversion from 'Ambig' to 'A' is 
ambiguous}}
+    A a7 {Ambig {}};
+    A a8 = {Ambig {}};
+
+    A a9(1);
+    A a10 = 1; // expected-error {{no viable conversion from 'int' to 'A'}}
+    A a11 {1};
+    A a12 = {1};
+
+
+    struct B { // expected-note 12 {{candidate constructor}}
+        int i;
+        virtual operator int() volatile;
+    };
+    volatile B vb;
+
+    B b1(vb); // expected-error {{no matching constructor for initialization 
of 'B'}}
+    B b2 = vb; // expected-error {{no matching constructor for initialization 
of 'B'}}
+    B b3 {vb}; // expected-error {{no matching constructor for initialization 
of 'B'}}
+    B b4 = {vb}; // expected-error {{no matching constructor for 
initialization of 'B'}}
+
+
+    struct Immovable {
+        Immovable();
+        Immovable(const Immovable&) = delete; // #Imm_copy
+    };
+
+    struct C { // #C
+        int i;
+        Immovable j; // #C_j
+
+        operator int() volatile;
+    };
+    C c;
+    volatile C vc;
+
+    C c1(c); // expected-error {{call to implicitly-deleted copy constructor 
of 'C'}}
+    C c2 = c; // expected-error {{call to implicitly-deleted copy constructor 
of 'C'}}
+    C c3 {c}; // expected-error {{call to implicitly-deleted copy constructor 
of 'C'}}
+    C c4 = {c}; // expected-error {{call to implicitly-deleted copy 
constructor of 'C'}}
+    // expected-note@#C_j 4 {{copy constructor of 'C' is implicitly deleted}}
+    // expected-note@#Imm_copy 4 {{'Immovable' has been explicitly marked 
deleted here}}
+
+    C c5(vc);
+    C c6 = vc; // expected-error {{no matching constructor for initialization 
of 'C'}}
+    C c7 {vc};
+    C c8 = {vc}; // expected-error {{no matching constructor for 
initialization of 'C'}}
+    // expected-note@#C 4 {{candidate constructor}}
+
+    C c9(C {});
+    C c10 = C(123);
+    C c11 {C {0, Immovable()}};
+    C c12 = {C()};
+
+
+    struct D { // expected-note 6 {{candidate constructor}}
+        int i;
+    };
+
+    struct DD : private D { // expected-note 4 {{declared private here}}
+        virtual operator int() volatile;
+    };
+    DD dd;
+    volatile DD vdd;
+
+    D d1(dd); // expected-error {{cannot cast 'const DD' to its private base 
class 'const D'}}
+    D d2 = dd; // expected-error {{cannot cast 'const DD' to its private base 
class 'const D'}}
+    D d3 {dd}; // expected-error {{cannot cast 'const DD' to its private base 
class 'const D'}}
+    D d4 = {dd}; // expected-error {{cannot cast 'const DD' to its private 
base class 'const D'}}
+
+    D d5(vdd);
+    D d6 = vdd; // expected-error {{no matching constructor for initialization 
of 'D'}}
+    D d7 {vdd};
+    D d8 = {vdd}; // expected-error {{no matching constructor for 
initialization of 'D'}}
+} // namespace P0960R3

>From 2f5bce697f1b5ba23403aa8b20f93e42a0b64244 Mon Sep 17 00:00:00 2001
From: offsetof <offse...@mailo.com>
Date: Tue, 18 Mar 2025 01:04:57 +0000
Subject: [PATCH 2/2] fixup! [clang] Refine handling of C++20 aggregate
 initialization

Move release note to a more appropriate location.
---
 clang/docs/ReleaseNotes.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 22ebf86e05f7d..9aba41c91c216 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -281,7 +281,6 @@ Bug Fixes in This Version
 - Fixed a modules crash where exception specifications were not propagated 
properly (#GH121245, relanded in #GH129982)
 - Fixed a problematic case with recursive deserialization within 
``FinishedDeserializing()`` where
   ``PassInterestingDeclsToConsumer()`` was called before the declarations were 
safe to be passed. (#GH129982)
-- Fixed C++20 aggregate initialization rules being incorrectly applied in 
certain contexts (#GH131320)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -319,6 +318,7 @@ Bug Fixes to C++ Support
 - Fixed an assertion failure affecting code that uses C++23 "deducing this". 
(#GH130272)
 - Clang now properly instantiates destructors for initialized members within 
non-delegating constructors. (#GH93251)
 - Correctly diagnoses if unresolved using declarations shadows template 
paramters (#GH129411)
+- Fixed C++20 aggregate initialization rules being incorrectly applied in 
certain contexts. (#GH131320)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to