ecatmur created this revision.
ecatmur added a reviewer: clang-language-wg.
Herald added subscribers: ChuanqiXu, JDevlieghere.
Herald added a project: All.
ecatmur requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

- p0960 Allow initializing aggregates from a parenthesized list of values
- p1975 Fixing the wording of parenthesized aggregate-initialization

Follows gcc for the most part, except for coroutines (cf. 
https://cpplang.slack.com/archives/CBTFTLR9R/p1657730176797929)

Fixes https://github.com/llvm/llvm-project/issues/53627

In competition with https://reviews.llvm.org/D129531, sorry - I got frustrated 
waiting.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D134231

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/Initialization.h
  clang/lib/Frontend/InitPreprocessor.cpp
  clang/lib/Sema/SemaCast.cpp
  clang/lib/Sema/SemaCoroutine.cpp
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaInit.cpp
  clang/test/CXX/class/class.compare/class.spaceship/p1.cpp
  clang/test/CXX/over/over.match/over.match.viable/p3.cpp
  clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp
  clang/test/Lexer/cxx-features.cpp
  clang/test/SemaCXX/cxx20-p0960-aggregate-paren-init.cpp
  clang/test/SemaCXX/cxx2a-explicit-bool.cpp

Index: clang/test/SemaCXX/cxx2a-explicit-bool.cpp
===================================================================
--- clang/test/SemaCXX/cxx2a-explicit-bool.cpp
+++ clang/test/SemaCXX/cxx2a-explicit-bool.cpp
@@ -30,6 +30,9 @@
 A<-1> a(0);
 // expected-error@-1 {{no matching constructor}}
 // expected-note@-2 {{in instantiation of template class}}
+#if __cpp_aggregate_paren_init >= 201902
+// expected-note@-4{{candidate aggregate parenthesis initializer not viable}}
+#endif
 
 template<int a>
 struct B {
Index: clang/test/SemaCXX/cxx20-p0960-aggregate-paren-init.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/cxx20-p0960-aggregate-paren-init.cpp
@@ -0,0 +1,239 @@
+// RUN: %clang_cc1 -std=c++20 -verify %s
+// RUN: %clang_cc1 -std=c++17 -verify %s
+
+// p0960 Allow initializing aggregates from a parenthesized list of values
+// p1975 Fixing the wording of parenthesized aggregate-initialization
+// class.temporary/6
+// dcl.init/17.5, 17.6
+
+#if __cplusplus >= 202002 && __cpp_aggregate_paren_init < 201902
+#   error "should set __cpp_aggregate_paren_init in C++20 mode"
+#endif
+
+// definitions for std::move
+namespace std { template <class T> T &&move(T &); }
+// definitions for std::is_constructible
+namespace std {
+template<class T, class... A>
+inline constexpr bool is_constructible_v = __is_constructible(T, A...);
+}
+
+int i1[](1, 2, 3);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{array initializer must be an initializer list}}
+#else
+static_assert(sizeof i1 == sizeof(int[3]));
+#endif
+
+int i2[2](1, 2, 3);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{array initializer must be an initializer list}}
+#else
+// expected-error@-4{{excess elements in array initializer}}
+#endif
+
+int i3[4](1, 2, 3);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{array initializer must be an initializer list}}
+#endif
+
+int i4[](1);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{array initializer must be an initializer list}}
+#else
+static_assert(sizeof i4 == sizeof(int[1]));
+#endif
+
+int *p1 = new int[](1, 2, 3);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{array 'new' cannot have initialization arguments}}
+#endif
+
+int *p2[2] = new int[2](1, 2, 3);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{array 'new' cannot have initialization arguments}}
+#else
+// expected-error@-4{{excess elements in array initializer}}
+#endif
+
+int *p3 = new int[4](1, 2, 3);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{array 'new' cannot have initialization arguments}}
+#endif
+
+struct E {
+#if __cpp_aggregate_paren_init >= 201902
+// expected-note@-2 +{{candidate constructor (the implicit copy constructor) not viable}}
+// expected-note@-3 +{{candidate constructor (the implicit move constructor) not viable}}
+#endif
+  E(int);
+#if __cpp_aggregate_paren_init >= 201902
+// expected-note@-2 +{{candidate constructor not viable: requires 1 argument, but 0 were provided}}
+#endif
+};
+
+E e1[3](1, 2, 3);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{array initializer must be an initializer list}}
+#endif
+
+E e2[4](1, 2, 3);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{array initializer must be an initializer list}}
+#else
+// expected-error@-4{{no matching constructor for initialization of 'E'}}
+// expected-note@-5{{in implicit initialization of array element 3 with omitted initializer}}
+#endif
+
+struct A {
+#if __cpp_aggregate_paren_init < 201902
+// expected-note@-2 +{{candidate constructor (the implicit copy constructor) not viable}}
+// expected-note@-3 +{{candidate constructor (the implicit move constructor) not viable}}
+// expected-note@-4 +{{candidate constructor (the implicit default constructor) not viable}}
+#endif
+  int a;
+  int&& r;
+};
+
+int f();
+int n = 10;
+
+A a1{1, f()};               // OK, lifetime is extended
+A a2(1, f());               // well-formed, but dangling reference
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{no matching constructor for initialization of 'A'}}
+#endif
+A a3{1.0, 1};               // error: narrowing conversion
+// expected-error@-1{{type 'double' cannot be narrowed to 'int' in initializer list}}
+// expected-note@-2{{insert an explicit cast to silence this issue}}
+A a4(1.0, 1);               // well-formed, but dangling reference
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{no matching constructor for initialization of 'A'}}
+#endif
+A a5(1.0, std::move(n));    // OK
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{no matching constructor for initialization of 'A'}}
+#endif
+
+struct B {
+// expected-note@-1 +{{candidate constructor (the implicit copy constructor) not viable}}
+// expected-note@-2 +{{candidate constructor (the implicit move constructor) not viable}}
+// expected-note@-3 +{{candidate constructor (the implicit default constructor) not viable}}
+  int a;
+  int b;
+};
+
+B b1(n);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{no matching constructor for initialization of 'B'}}
+#endif
+B b2(1.0);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{no matching constructor for initialization of 'B'}}
+#endif
+B b3(1, 2, 3); // expected-error{{no matching constructor for initialization of 'B'}}
+#if __cpp_aggregate_paren_init >= 201902
+// expected-note@-2{{candidate aggregate parenthesis initializer not viable}}
+#endif
+
+struct C {
+// expected-note@-1 +{{candidate constructor (the implicit copy constructor) not viable}}
+// expected-note@-2 +{{candidate constructor (the implicit move constructor) not viable}}
+// expected-note@-3 +{{candidate constructor (the implicit default constructor) not viable}}
+  int i;
+  E e;
+#if __cpp_aggregate_paren_init >= 201902
+// expected-note@-2{{in implicit initialization of field 'e' with omitted initializer}}
+#endif
+  E f = 10;
+};
+C c1(1); // expected-error{{no matching constructor for initialization of 'C'}}
+#if __cpp_aggregate_paren_init >= 201902
+// expected-note@-2{{candidate aggregate parenthesis initializer not viable}}
+#endif
+C c2(1, 2);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{no matching constructor for initialization of 'C'}}
+#endif
+
+struct D : B, E {
+// expected-note@-1 +{{candidate constructor (the implicit copy constructor) not viable}}
+// expected-note@-2 +{{candidate constructor (the implicit move constructor) not viable}}
+// expected-note@-3 +{{candidate constructor (the implicit default constructor) not viable}}
+#if __cpp_aggregate_paren_init >= 201902
+// expected-note@-5{{base class 'E' specified here}}
+#endif
+  int i;
+};
+D d1(B(1)); // expected-error{{no matching constructor for initialization of 'D'}}
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{no matching conversion for functional-style cast from 'int' to 'B'}}
+#else
+// expected-note@-4{{candidate aggregate parenthesis initializer not viable}}
+#endif
+D d2(B(), 10, 1);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2{{no matching constructor for initialization of 'D'}}
+#endif
+
+template<class T>
+struct F {
+// expected-note@-1 +{{candidate constructor (the implicit copy constructor) not viable}}
+// expected-note@-2 +{{candidate constructor (the implicit move constructor) not viable}}
+// expected-note@-3 +{{candidate constructor (the implicit default constructor) not viable}}
+// expected-note@-4 {{in instantiation of default member initializer 'F<int>::b' requested here}}
+  T a;
+  T b = "str";
+// expected-error@-1 {{cannot initialize a member subobject of type 'int' with an lvalue of type 'const char[4]'}}
+#if __cpp_aggregate_paren_init >= 201902
+// expected-note@-3 {{initializing field b with default member initializer}}
+#endif
+};
+F<char const*> f1;
+F<char const*> f2("ok");
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2 {{no matching constructor for initialization of 'F<const char *>'}}
+#endif
+F<char const*> f3("ok", "ok");
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2 {{no matching constructor for initialization of 'F<const char *>'}}
+#endif
+F<int> f4; // expected-note {{in evaluation of exception specification for 'F<int>::F' needed here}}
+F<int> f5(10); // expected-error {{no matching constructor for initialization of 'F<int>'}}
+#if __cpp_aggregate_paren_init >= 201902
+// expected-note@-2{{candidate aggregate parenthesis initializer not viable}}
+#endif
+F<int> f6(10, 10);
+#if __cpp_aggregate_paren_init < 201902
+// expected-error@-2 {{no matching constructor for initialization of 'F<int>'}}
+#endif
+
+#ifndef __cpp_aggregate_paren_init
+#   define __cpp_aggregate_paren_init 0
+#endif
+
+static_assert(std::is_constructible_v<B>);
+static_assert(std::is_constructible_v<B, int> == (__cpp_aggregate_paren_init >= 201902));
+static_assert(std::is_constructible_v<B, long> == (__cpp_aggregate_paren_init >= 201902));
+static_assert(std::is_constructible_v<B, float> == (__cpp_aggregate_paren_init >= 201902));
+static_assert(std::is_constructible_v<B, int, int> == (__cpp_aggregate_paren_init >= 201902));
+static_assert(not std::is_constructible_v<B, int, int, int>);
+static_assert(not std::is_constructible_v<B, char*>);
+static_assert(not std::is_constructible_v<B, int, char*>);
+static_assert(not std::is_constructible_v<C, int>);
+static_assert(std::is_constructible_v<C, int, int> == (__cpp_aggregate_paren_init >= 201902));
+static_assert(std::is_constructible_v<C, int, int, int> == (__cpp_aggregate_paren_init >= 201902));
+static_assert(not std::is_constructible_v<D, B>);
+static_assert(std::is_constructible_v<D, B, int, int> == (__cpp_aggregate_paren_init >= 201902));
+static_assert(std::is_constructible_v<F<char const*>, char const*> == (__cpp_aggregate_paren_init >= 201902));
+static_assert(not std::is_constructible_v<F<int>, int>);
+static_assert(std::is_constructible_v<F<int>, int, int> == (__cpp_aggregate_paren_init >= 201902));
+
+static_assert(std::is_constructible_v<int[2]>);
+static_assert(std::is_constructible_v<int[2], int> == (__cpp_aggregate_paren_init >= 201902));
+static_assert(std::is_constructible_v<int[2], int, int> == (__cpp_aggregate_paren_init >= 201902));
+static_assert(not std::is_constructible_v<int[2], int, int, int>);
+static_assert(not std::is_constructible_v<int[]>);
+static_assert(std::is_constructible_v<int[], int, int> == (__cpp_aggregate_paren_init >= 201902));
+static_assert(not std::is_constructible_v<E[2], int>);
+static_assert(std::is_constructible_v<int[2], int, int> == (__cpp_aggregate_paren_init >= 201902));
Index: clang/test/Lexer/cxx-features.cpp
===================================================================
--- clang/test/Lexer/cxx-features.cpp
+++ clang/test/Lexer/cxx-features.cpp
@@ -41,8 +41,7 @@
 
 // --- C++20 features ---
 
-#if check(aggregate_paren_init, 0, 0, 0, 0, 0, 0)
-// FIXME: 201902 in C++20
+#if check(aggregate_paren_init, 0, 0, 0, 0, 201902, 201902)
 #error "wrong value for __cpp_aggregate_paren_init"
 #endif
 
Index: clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp
===================================================================
--- clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp
+++ clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp
@@ -128,6 +128,9 @@
 HasMixins<Mixins...>::HasMixins(int i): Mixins(i)... { }
 // expected-error@-1 {{no matching constructor for initialization of 'A'}}
 // expected-error@-2 {{no matching constructor for initialization of 'B'}}
+#if __cpp_aggregate_paren_init >= 201902
+// expected-note@-4 2{{candidate aggregate parenthesis initializer not viable}}
+#endif
 
 void test_has_mixins() {
   HasMixins<A, B> ab;
Index: clang/test/CXX/over/over.match/over.match.viable/p3.cpp
===================================================================
--- clang/test/CXX/over/over.match/over.match.viable/p3.cpp
+++ clang/test/CXX/over/over.match/over.match.viable/p3.cpp
@@ -25,6 +25,9 @@
   (void) static_cast<bool>(S1<int>());
   (void) static_cast<S2>(S1<int>());
   // expected-error@-1 {{no matching conversion for static_cast from 'S1<int>' to 'S2'}}
+#if __cpp_aggregate_paren_init >= 201902
+  // expected-note@-3 {{candidate aggregate parenthesis initializer not viable}}
+#endif
 }
 
 // Test that constraints are checked before implicit conversions are formed.
Index: clang/test/CXX/class/class.compare/class.spaceship/p1.cpp
===================================================================
--- clang/test/CXX/class/class.compare/class.spaceship/p1.cpp
+++ clang/test/CXX/class/class.compare/class.spaceship/p1.cpp
@@ -3,6 +3,9 @@
 namespace std {
   struct strong_ordering { // expected-note 6{{candidate}}
     int n;
+#if __cpp_aggregate_paren_init >= 201902
+    // expected-note@-2 2{{member n declared here}}
+#endif
     constexpr operator int() const { return n; }
     static const strong_ordering less, equal, greater;
   };
@@ -104,6 +107,9 @@
       // expected-error@#cmp {{value of type 'void' is not contextually convertible to 'bool'}}
       Cmp<G2>() <=> Cmp<G2>(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}Cmp<{{.*}}G2>' first required here}}j
       // expected-error@#cmp {{no matching conversion for static_cast from 'void' to 'std::strong_ordering'}}
+#if __cpp_aggregate_paren_init >= 201902
+      // expected-note@#cmp {{candidate aggregate parenthesis initializer not viable}}
+#endif
       Cmp<H>() <=> Cmp<H>(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}Cmp<{{.*}}H>' first required here}}j
       0
     );
@@ -135,6 +141,9 @@
       // expected-error@#cmparray {{value of type 'void' is not contextually convertible to 'bool'}}
       CmpArray<G2>() <=> CmpArray<G2>(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}CmpArray<{{.*}}G2>' first required here}}j
       // expected-error@#cmparray {{no matching conversion for static_cast from 'void' to 'std::strong_ordering'}}
+#if __cpp_aggregate_paren_init >= 201902
+      // expected-note@#cmparray {{candidate aggregate parenthesis initializer not viable}}
+#endif
       CmpArray<H>() <=> CmpArray<H>(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}CmpArray<{{.*}}H>' first required here}}j
       0
     );
Index: clang/lib/Sema/SemaInit.cpp
===================================================================
--- clang/lib/Sema/SemaInit.cpp
+++ clang/lib/Sema/SemaInit.cpp
@@ -3519,6 +3519,8 @@
   case SK_StdInitializerListConstructorCall:
   case SK_OCLSamplerInit:
   case SK_OCLZeroOpaqueType:
+  case SK_ArrayParenthesizedInit:
+  case SK_AggregateParenthesizedInit:
     break;
 
   case SK_ConversionSequence:
@@ -3578,6 +3580,7 @@
   case FK_PlaceholderType:
   case FK_ExplicitConstructor:
   case FK_AddressOfUnaddressableFunction:
+  case FK_ArrayParenthesizedInitFailed:
     return false;
 
   case FK_ReferenceInitOverloadFailed:
@@ -3813,6 +3816,20 @@
   Steps.push_back(S);
 }
 
+void InitializationSequence::AddArrayParenthesizedInitStep(QualType T) {
+  Step S;
+  S.Kind = SK_ArrayParenthesizedInit;
+  S.Type = T;
+  Steps.push_back(S);
+}
+
+void InitializationSequence::AddAggregateParenthesizedInitStep(QualType T) {
+  Step S;
+  S.Kind = SK_AggregateParenthesizedInit;
+  S.Type = T;
+  Steps.push_back(S);
+}
+
 void InitializationSequence::RewrapReferenceInitList(QualType T,
                                                      InitListExpr *Syntactic) {
   assert(Syntactic->getNumInits() == 1 &&
@@ -3932,6 +3949,255 @@
   return true;
 }
 
+static void TryValueInitialization(Sema &S, const InitializedEntity &Entity,
+                                   const InitializationKind &Kind,
+                                   InitializationSequence &Sequence,
+                                   InitListExpr *InitList = nullptr);
+
+enum AggregateParenInitKind {
+  APIK_Verify,
+  APIK_Diagnose,
+  APIK_Perform,
+};
+
+static ExprResult TryArrayParenthesizedInitialization(
+    Sema &S, const InitializedEntity &Entity, llvm::ArrayRef<Expr *> Args,
+    QualType DestType, QualType *ResultType, AggregateParenInitKind APIK) {
+  InitListExpr *Result = nullptr;
+  bool Invalid = false;
+  if (APIK == APIK_Perform)
+    Result = new (S.Context) InitListExpr(
+        S.Context, Args.front()->getBeginLoc(), None, Args.back()->getEndLoc());
+  InitializedEntity IE =
+      InitializedEntity::InitializeElement(S.Context, 0, Entity);
+
+  llvm::Optional<unsigned> NumElements;
+  if (DestType->isIncompleteArrayType()) {
+    NumElements = Args.size();
+    DestType = S.Context.getConstantArrayType(
+        S.Context.getAsArrayType(DestType)->getElementType(),
+        llvm::APInt(32, *NumElements), nullptr, ArrayType::Normal, 0);
+    if (ResultType) {
+      if ((*ResultType)->isRValueReferenceType())
+        *ResultType = S.Context.getRValueReferenceType(DestType);
+      else if ((*ResultType)->isLValueReferenceType())
+        *ResultType = S.Context.getLValueReferenceType(
+            DestType,
+            (*ResultType)->castAs<LValueReferenceType>()->isSpelledAsLValue());
+      else
+        *ResultType = DestType;
+    }
+  } else if (auto CAT = S.Context.getAsConstantArrayType(DestType)) {
+    NumElements = CAT->getSize().getZExtValue();
+  }
+
+  if (NumElements && Args.size() > *NumElements) {
+    // Diagnose excess initializers: int a[3](1, 2, 3, /*^*/ 4);
+    if (APIK == APIK_Verify)
+      return ExprError();
+    S.Diag(Args[*NumElements]->getBeginLoc(), diag::err_excess_initializers)
+        << /* initKind = */ 0 /* array */
+        << Args[*NumElements]->getSourceRange();
+    Invalid = true;
+  } else if (!NumElements || Args.size() < *NumElements) {
+    // Ensure that we can value-initialize trailing elements.
+    SourceLocation Loc = Args.back()->getEndLoc().getLocWithOffset(1);
+    auto Kind = InitializationKind::CreateValue(Loc, Loc, Loc, true);
+    MultiExprArg SubInit;
+    IE.setElementIndex(Args.size());
+    InitializationSequence ValueSequence(S, IE, Kind, SubInit);
+    TryValueInitialization(S, IE, Kind, ValueSequence);
+    if (ValueSequence.Failed()) {
+      if (APIK == APIK_Verify)
+        return ExprError();
+      ValueSequence.Diagnose(S, IE, Kind, SubInit);
+      S.Diag(Loc, diag::note_in_omitted_aggregate_initializer)
+          << 0 << Args.size();
+      Invalid = true;
+    } else if (Result) {
+      auto ER = ValueSequence.Perform(S, IE, Kind, SubInit);
+      Result->setArrayFiller(ER.get());
+    }
+  }
+
+  if (Result) {
+    Result->setType(DestType);
+    Result->resizeInits(S.Context, Args.size());
+  }
+  for (unsigned Index = 0; Index != Args.size(); ++Index) {
+    IE.setElementIndex(Index);
+    Expr *Init = Args[Index];
+    if (APIK == APIK_Verify) {
+      if (!S.CanPerformCopyInitialization(IE, Init))
+        return ExprError();
+    } else {
+      auto InitResult =
+          S.PerformCopyInitialization(IE, Init->getBeginLoc(), Init);
+      if (InitResult.isInvalid())
+        Invalid = true;
+      else if (Result)
+        Result->setInit(Index, InitResult.getAs<Expr>());
+    }
+  }
+
+  return Invalid ? ExprError() : Result;
+}
+
+static ExprResult TryAggregateParenthesizedInitialization(
+    Sema &S, const InitializedEntity &Entity, llvm::ArrayRef<Expr *> Args,
+    QualType DestType, AggregateParenInitKind APIK) {
+  InitListExpr *Result = nullptr;
+  bool Invalid = false;
+  if (APIK == APIK_Perform) {
+    Result = new (S.Context) InitListExpr(
+        S.Context, Args.front()->getBeginLoc(), None, Args.back()->getEndLoc());
+    Result->setType(DestType);
+  }
+
+  // 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.
+  RecordDecl *RD = DestType->castAs<RecordType>()->getDecl();
+  auto Bases =
+      CXXRecordDecl::base_class_range(CXXRecordDecl::base_class_iterator(),
+                                      CXXRecordDecl::base_class_iterator());
+  if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
+    Bases = CXXRD->bases();
+
+  unsigned Index = 0;
+  if (Result)
+    Result->resizeInits(S.Context, size(Bases));
+  for (auto &Base : Bases) {
+    Expr *Init = Index < Args.size() ? Args[Index] : nullptr;
+    InitializedEntity BaseEntity =
+        InitializedEntity::InitializeBase(S.Context, &Base, false, &Entity);
+    if (Init) {
+      // The element e_i is copy-initialized with x_i for 1 ≤ i ≤ k.
+      if (Result) {
+        auto InitResult =
+            S.PerformCopyInitialization(BaseEntity, Init->getBeginLoc(), Init);
+        if (InitResult.isInvalid())
+          Invalid = true;
+        else
+          Result->setInit(Index, InitResult.getAs<Expr>());
+      } else if (!S.CanPerformCopyInitialization(BaseEntity, Init)) {
+        if (APIK == APIK_Verify)
+          return ExprError();
+        S.Diag(Init->getBeginLoc(), diag::note_ovl_candidate_bad_conv)
+            << 11 << 0 << "" << Init->getType() << BaseEntity.getType() << 0
+            << Index + 1 << 0 << Init->getSourceRange();
+        S.Diag(Base.getBeginLoc(), diag::note_base_class_specified_here)
+            << BaseEntity.getType() << Base.getSourceRange();
+        Invalid = true;
+      }
+    } else {
+      // Bases don't have NSDMIs, so always value-init.
+      SourceLocation Loc = Args.back()->getEndLoc().getLocWithOffset(1);
+      auto Kind = InitializationKind::CreateValue(Loc, Loc, Loc, true);
+      MultiExprArg SubInit;
+      InitializationSequence ValueSequence(S, BaseEntity, Kind, SubInit);
+      TryValueInitialization(S, BaseEntity, Kind, ValueSequence);
+      if (ValueSequence.Failed()) {
+        if (APIK == APIK_Verify)
+          return ExprError();
+        S.Diag(Args.back()->getEndLoc(), diag::note_ovl_candidate_arity)
+            << 11 << 0 << "" << 0 << Index + 1 << Args.size();
+        S.Diag(Base.getBeginLoc(), diag::note_base_class_specified_here)
+            << BaseEntity.getType() << Base.getSourceRange();
+        Invalid = true;
+      } else if (Result) {
+        auto ER = ValueSequence.Perform(S, BaseEntity, Kind, SubInit);
+        Result->setInit(Index, ER.get());
+      }
+    }
+    ++Index;
+  }
+
+  for (auto *Field : RD->fields()) {
+    Expr *Init = Index < Args.size() ? Args[Index] : nullptr;
+    if (Result)
+      Result->resizeInits(S.Context, Index + 1);
+    InitializedEntity MemberEntity =
+        InitializedEntity::InitializeMember(Field, &Entity);
+    if (Init) {
+      // The element e_i is copy-initialized with x_i for 1 ≤ i ≤ k.
+      if (Result) {
+        auto InitResult = S.PerformCopyInitialization(
+            MemberEntity, Init->getBeginLoc(), Init);
+        if (InitResult.isInvalid())
+          Invalid = true;
+        else
+          Result->setInit(Index, InitResult.getAs<Expr>());
+      } else if (!S.CanPerformCopyInitialization(MemberEntity, Init)) {
+        if (APIK == APIK_Verify)
+          return ExprError();
+        S.Diag(Init->getBeginLoc(), diag::note_ovl_candidate_bad_conv)
+            << 11 << 0 << "" << Init->getType() << MemberEntity.getType() << 0
+            << Index + 1 << 0 << Init->getSourceRange();
+        S.Diag(Field->getBeginLoc(), diag::note_member_declared_here)
+            << Field->getName() << Field->getSourceRange();
+        Invalid = true;
+      }
+    } else if (Field->hasInClassInitializer()) {
+      // The remaining elements are initialized with their default member
+      // initializers, if any,
+      SourceLocation Loc = Args.back()->getEndLoc();
+      ExprResult DIE;
+      if (Result) {
+        DIE = S.BuildCXXDefaultInitExpr(Loc, Field);
+      } else {
+        bool WasInvalid = Field->isInvalidDecl();
+        Sema::TentativeAnalysisScope DisableDiag(S);
+        DIE = S.BuildCXXDefaultInitExpr(Loc, Field);
+        Field->setInvalidDecl(WasInvalid); // back out side effects
+      }
+      if (DIE.isInvalid()) {
+        if (APIK == APIK_Verify)
+          return ExprError();
+        S.Diag(Args.back()->getEndLoc(), diag::note_ovl_candidate_arity)
+            << 11 << 0 << "" << 0 << Index + 1 << Args.size();
+        S.Diag(Field->getBeginLoc(),
+               diag::note_init_with_default_member_initalizer)
+            << Field->getName() << Field->getSourceRange();
+        Invalid = true;
+      } else if (Result)
+        Result->setInit(Index, DIE.get());
+    } else {
+      // and otherwise are value-initialized.
+      SourceLocation Loc = Args.back()->getEndLoc().getLocWithOffset(1);
+      auto Kind = InitializationKind::CreateValue(Loc, Loc, Loc, true);
+      MultiExprArg SubInit;
+      InitializationSequence ValueSequence(S, MemberEntity, Kind, SubInit);
+      TryValueInitialization(S, MemberEntity, Kind, ValueSequence);
+      if (ValueSequence.Failed()) {
+        if (APIK == APIK_Verify)
+          return ExprError();
+        S.Diag(Args.back()->getEndLoc(), diag::note_ovl_candidate_arity)
+            << 11 << 0 << "" << 0 << Index + 1 << Args.size();
+        S.Diag(Field->getBeginLoc(),
+               diag::note_in_omitted_aggregate_initializer)
+            << 1 << Field << Field->getSourceRange();
+        Invalid = true;
+      } else if (Result) {
+        auto ER = ValueSequence.Perform(S, MemberEntity, Kind, SubInit);
+        Result->setInit(Index, ER.get());
+      }
+    }
+    ++Index;
+  }
+
+  if (Index < Args.size()) {
+    if (APIK == APIK_Verify)
+      return ExprError();
+    S.Diag(Args[Index]->getBeginLoc(), diag::note_ovl_candidate_arity)
+        << 11 << 0 << "" << 1 << Index << Args.size()
+        << Args[Index]->getSourceRange();
+    Invalid = true;
+  }
+
+  return Invalid ? ExprError() : Result;
+}
+
 /// Determine if the constructor has the signature of a copy or move
 /// constructor for the type T of the class in which it was found. That is,
 /// determine if its first parameter is of type T or reference to (possibly
@@ -4170,6 +4436,21 @@
                                         /*OnlyListConstructors=*/false,
                                         IsListInit);
   }
+
+  // C++20 [dcl.init.general]p16.6.2.2:
+  // 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. [...]
+  if (S.getLangOpts().CPlusPlus20 && Result == OR_No_Viable_Function &&
+      DestType->isAggregateType() &&
+      Kind.getKind() == InitializationKind::IK_Direct &&
+      !TryAggregateParenthesizedInitialization(S, Entity, Args, DestType,
+                                               APIK_Verify)
+           .isInvalid()) {
+    Sequence.AddAggregateParenthesizedInitStep(DestType);
+    return;
+  }
+
   if (Result) {
     Sequence.SetOverloadFailure(
         IsListInit ? InitializationSequence::FK_ListConstructorOverloadFailed
@@ -4283,12 +4564,6 @@
                                            Qualifiers T2Quals,
                                            InitializationSequence &Sequence);
 
-static void TryValueInitialization(Sema &S,
-                                   const InitializedEntity &Entity,
-                                   const InitializationKind &Kind,
-                                   InitializationSequence &Sequence,
-                                   InitListExpr *InitList = nullptr);
-
 /// Attempt list initialization of a reference.
 static void TryReferenceListInitialization(Sema &S,
                                            const InitializedEntity &Entity,
@@ -5821,7 +6096,7 @@
   //       char16_t, an array of char32_t, or an array of wchar_t, and the
   //       initializer is a string literal, see 8.5.2.
   //     - Otherwise, if the destination type is an array, the program is
-  //       ill-formed.
+  //       ill-formed [until C++20].
   if (const ArrayType *DestAT = Context.getAsArrayType(DestType)) {
     if (Initializer && isa<VariableArrayType>(DestAT)) {
       SetFailed(FK_VariableLengthArrayHasInitializer);
@@ -5881,6 +6156,23 @@
       return;
     }
 
+    // C++20 permits array parenthesized initialization.
+    if (S.getLangOpts().CPlusPlus20 &&
+        Kind.getKind() == InitializationKind::IK_Direct) {
+      if (!TryArrayParenthesizedInitialization(S, Entity, Args, DestType,
+                                               nullptr, APIK_Verify)
+               .isInvalid()) {
+        AddArrayParenthesizedInitStep(DestType);
+        return;
+      } else if (Entity.getKind() == InitializedEntity::EK_Member &&
+                 Initializer && isa<InitListExpr>(Initializer)) {
+        ; // fall through to GNU C++ extension below
+      } else {
+        SetFailed(FK_ArrayParenthesizedInitFailed);
+        return;
+      }
+    }
+
     // Note: as an GNU C extension, we allow initialization of an
     // array from a compound literal that creates an array of the same
     // type, so long as the initializer has no side effects.
@@ -8223,6 +8515,8 @@
   case SK_ConstructorInitializationFromList:
   case SK_StdInitializerListConstructorCall:
   case SK_ZeroInitialization:
+  case SK_ArrayParenthesizedInit:
+  case SK_AggregateParenthesizedInit:
     break;
   }
 
@@ -8912,6 +9206,16 @@
                                     CurInit.get()->getValueKind());
       break;
     }
+    case SK_ArrayParenthesizedInit: {
+      CurInit = TryArrayParenthesizedInitialization(S, Entity, Args, Step->Type,
+                                                    ResultType, APIK_Perform);
+      break;
+    }
+    case SK_AggregateParenthesizedInit: {
+      CurInit = TryAggregateParenthesizedInitialization(
+          S, Entity, Args, Step->Type, APIK_Perform);
+      break;
+    }
     }
   }
 
@@ -9416,6 +9720,7 @@
                      diag::note_previous_decl)
                 << S.Context.getTagDeclType(Record->getDecl());
           }
+
           break;
         }
 
@@ -9425,6 +9730,10 @@
                 S.PDiag(diag::err_ovl_no_viable_function_in_init)
                     << DestType << ArgsRange),
             S, OCD_AllCandidates, Args);
+
+        if (S.getLangOpts().CPlusPlus20 && DestType->isAggregateType() &&
+            Kind.getKind() == InitializationKind::IK_Direct)
+          DiagnoseFailedAggregateParenthesizedInitialization(S, Entity, Args);
         break;
 
       case OR_Deleted: {
@@ -9514,12 +9823,24 @@
            diag::note_explicit_ctor_deduction_guide_here) << false;
     break;
   }
+
+  case FK_ArrayParenthesizedInitFailed: {
+    TryArrayParenthesizedInitialization(S, Entity, Args, DestType, nullptr,
+                                        APIK_Diagnose);
+    break;
+  }
   }
 
   PrintInitLocationNote(S, Entity);
   return true;
 }
 
+void InitializationSequence::DiagnoseFailedAggregateParenthesizedInitialization(
+    Sema &S, const InitializedEntity &Entity, ArrayRef<Expr *> Args) const {
+  TryAggregateParenthesizedInitialization(S, Entity, Args, Entity.getType(),
+                                          APIK_Diagnose);
+}
+
 void InitializationSequence::dump(raw_ostream &OS) const {
   switch (SequenceKind) {
   case FailedSequence: {
@@ -9680,6 +10001,10 @@
     case FK_ExplicitConstructor:
       OS << "list copy initialization chose explicit constructor";
       break;
+
+    case FK_ArrayParenthesizedInitFailed:
+      OS << "array parenthesized initialization failed";
+      break;
     }
     OS << '\n';
     return;
@@ -9851,6 +10176,14 @@
     case SK_OCLZeroOpaqueType:
       OS << "OpenCL opaque type from zero";
       break;
+
+    case SK_ArrayParenthesizedInit:
+      OS << "array initialization from parenthesized list";
+      break;
+
+    case SK_AggregateParenthesizedInit:
+      OS << "aggregate initialization from parenthesized list";
+      break;
     }
 
     OS << " [" << S->Type << ']';
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -1904,17 +1904,23 @@
                      Initializer);
 }
 
-static bool isLegalArrayNewInitializer(CXXNewExpr::InitializationStyle Style,
+static bool isLegalArrayNewInitializer(Sema &S,
+                                       CXXNewExpr::InitializationStyle Style,
                                        Expr *Init) {
   if (!Init)
     return true;
   if (ParenListExpr *PLE = dyn_cast<ParenListExpr>(Init))
-    return PLE->getNumExprs() == 0;
+    return S.getLangOpts().CPlusPlus20 ||
+           llvm::all_of(PLE->exprs(),
+                        [](Expr *E) { return isa<PackExpansionExpr>(E); });
   if (isa<ImplicitValueInitExpr>(Init))
     return true;
   else if (CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(Init))
     return !CCE->isListInitialization() &&
-           CCE->getConstructor()->isDefaultConstructor();
+           (S.getLangOpts().CPlusPlus20 ||
+            CCE->getConstructor()->isDefaultConstructor() ||
+            llvm::all_of(CCE->arguments(),
+                         [](Expr *E) { return isa<PackExpansionExpr>(E); }));
   else if (Style == CXXNewExpr::ListInit) {
     assert(isa<InitListExpr>(Init) &&
            "Shouldn't create list CXXConstructExprs for arrays.");
@@ -2353,7 +2359,7 @@
   // Array 'new' can't have any initializers except empty parentheses.
   // Initializer lists are also allowed, in C++11. Rely on the parser for the
   // dialect distinction.
-  if (ArraySize && !isLegalArrayNewInitializer(initStyle, Initializer)) {
+  if (ArraySize && !isLegalArrayNewInitializer(*this, initStyle, Initializer)) {
     SourceRange InitRange(Exprs.front()->getBeginLoc(),
                           Exprs.back()->getEndLoc());
     Diag(StartLoc, diag::err_new_array_init_args) << InitRange;
@@ -5332,7 +5338,9 @@
 
     // Make sure the first argument is not incomplete nor a function type.
     QualType T = Args[0]->getType();
-    if (T->isIncompleteType() || T->isFunctionType())
+    if ((T->isIncompleteType() || T->isFunctionType()) &&
+        !(S.getLangOpts().CPlusPlus20 && T->isIncompleteArrayType() &&
+          Args.size() > 1))
       return false;
 
     // Make sure the first argument is not an abstract type.
Index: clang/lib/Sema/SemaCoroutine.cpp
===================================================================
--- clang/lib/Sema/SemaCoroutine.cpp
+++ clang/lib/Sema/SemaCoroutine.cpp
@@ -563,7 +563,7 @@
     // assembling an argument list  q_1 ... q_n . If a viable constructor is
     // found ([over.match.viable]), then promise-constructor-arguments is ( q_1
     // , ...,  q_n ), otherwise promise-constructor-arguments is empty.
-    if (InitSeq) {
+    if (InitSeq && InitSeq.isConstructorInitialization()) {
       ExprResult Result = InitSeq.Perform(*this, Entity, Kind, CtorArgExprs);
       if (Result.isInvalid()) {
         VD->setInvalidDecl();
Index: clang/lib/Sema/SemaCast.cpp
===================================================================
--- clang/lib/Sema/SemaCast.cpp
+++ clang/lib/Sema/SemaCast.cpp
@@ -486,6 +486,9 @@
                                        << src->getSourceRange()),
       S, howManyCandidates, src);
 
+  if (S.getLangOpts().CPlusPlus20 && destType->isAggregateType())
+    sequence.DiagnoseFailedAggregateParenthesizedInitialization(S, entity, src);
+
   return true;
 }
 
Index: clang/lib/Frontend/InitPreprocessor.cpp
===================================================================
--- clang/lib/Frontend/InitPreprocessor.cpp
+++ clang/lib/Frontend/InitPreprocessor.cpp
@@ -673,7 +673,7 @@
 
   // C++20 features.
   if (LangOpts.CPlusPlus20) {
-    // Builder.defineMacro("__cpp_aggregate_paren_init", "201902L");
+    Builder.defineMacro("__cpp_aggregate_paren_init", "201902L");
 
     // P0848 is implemented, but we're still waiting for other concepts
     // issues to be addressed before bumping __cpp_concepts up to 202002L.
Index: clang/include/clang/Sema/Initialization.h
===================================================================
--- clang/include/clang/Sema/Initialization.h
+++ clang/include/clang/Sema/Initialization.h
@@ -923,7 +923,13 @@
     SK_OCLSamplerInit,
 
     /// Initialize an opaque OpenCL type (event_t, queue_t, etc.) with zero
-    SK_OCLZeroOpaqueType
+    SK_OCLZeroOpaqueType,
+
+    /// Array initialization from a parenthesized list: int a[](1, 2, 3) (C++20)
+    SK_ArrayParenthesizedInit,
+
+    /// Initialize an aggregate from a parenthesized list (C++20)
+    SK_AggregateParenthesizedInit,
   };
 
   /// A single step in the initialization sequence.
@@ -1099,6 +1105,9 @@
 
     /// List-copy-initialization chose an explicit constructor.
     FK_ExplicitConstructor,
+
+    /// Array parenthesized initialization failed.
+    FK_ArrayParenthesizedInitFailed,
   };
 
 private:
@@ -1200,6 +1209,11 @@
                 const InitializationKind &Kind,
                 ArrayRef<Expr *> Args);
 
+  /// Add diagnostics for failure in aggregate parenthesis initialization,
+  /// shared between constructor overload and cast diagnostics.
+  void DiagnoseFailedAggregateParenthesizedInitialization(
+      Sema &S, const InitializedEntity &Entity, ArrayRef<Expr *> Args) const;
+
   /// Determine the kind of initialization sequence computed.
   enum SequenceKind getKind() const { return SequenceKind; }
 
@@ -1357,6 +1371,12 @@
   /// from a zero constant.
   void AddOCLZeroOpaqueTypeStep(QualType T);
 
+  /// Add an array parenthesized initialization step.
+  void AddArrayParenthesizedInitStep(QualType T);
+
+  /// Add an aggregate parenthesized initialization step.
+  void AddAggregateParenthesizedInitStep(QualType T);
+
   /// Add steps to unwrap a initializer list for a reference around a
   /// single element and rewrap it at the end.
   void RewrapReferenceInitList(QualType T, InitListExpr *Syntactic);
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4464,7 +4464,8 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "function (the implicit 'operator==' for this 'operator<=>)'|"
-    "inherited constructor}0%select{| template| %2}1">;
+    "inherited constructor|"
+    "aggregate parenthesis initializer}0%select{| template| %2}1">;
 
 def note_ovl_candidate : Note<
     "candidate %sub{select_ovl_candidate_kind}0,1,3"
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D134231: [clang][C... Ed Catmur via Phabricator via cfe-commits

Reply via email to