ayzhao updated this revision to Diff 473746.
ayzhao added a comment.

rebase + update commit message


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129531/new/

https://reviews.llvm.org/D129531

Files:
  clang/include/clang/AST/Decl.h
  clang/include/clang/AST/ExprCXX.h
  clang/include/clang/AST/RecursiveASTVisitor.h
  clang/include/clang/Basic/StmtNodes.td
  clang/include/clang/Sema/Initialization.h
  clang/lib/AST/Expr.cpp
  clang/lib/AST/ExprCXX.cpp
  clang/lib/AST/ExprClassification.cpp
  clang/lib/AST/ExprConstant.cpp
  clang/lib/AST/ItaniumMangle.cpp
  clang/lib/AST/JSONNodeDumper.cpp
  clang/lib/AST/StmtPrinter.cpp
  clang/lib/AST/StmtProfile.cpp
  clang/lib/AST/TextNodeDumper.cpp
  clang/lib/CodeGen/CGExprAgg.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaExceptionSpec.cpp
  clang/lib/Sema/SemaInit.cpp
  clang/lib/Sema/TreeTransform.h
  clang/lib/Serialization/ASTReaderStmt.cpp
  clang/lib/Serialization/ASTWriterStmt.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
  clang/test/CodeGen/P0960R3.cpp
  clang/test/SemaCXX/P0960R3.cpp

Index: clang/test/SemaCXX/P0960R3.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/P0960R3.cpp
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -verify -std=c++20 %s -fsyntax-only
+
+struct A { // expected-note 3{{candidate constructor}}
+  char i;
+  double j;
+};
+
+struct B { // expected-note 3{{candidate constructor}}
+  A a;
+  int b[20];
+  int &&c; // expected-note {{reference member declared here}}
+};
+
+struct C { // expected-note 6{{candidate constructor}}
+  A a;
+  int b[20];
+};
+
+struct D : public C, public A { // expected-note 6{{candidate constructor}}
+  int a;
+};
+
+void foo() {
+  A a(1954, 9, 21);
+  // expected-error@-1 {{no matching constructor for initialization of 'A'}}
+  A b(2.1);
+  // expected-warning@-1 {{implicit conversion from 'double' to 'char'}}
+  A e(-1.2, 9.8);
+  // expected-warning@-1 {{implicit conversion from 'double' to 'char'}}
+  A s = static_cast<A>(1.1);
+  // expected-warning@-1 {{implicit conversion from 'double' to 'char'}}
+  A h = (A)3.1;
+  // expected-warning@-1 {{implicit conversion from 'double' to 'char'}}
+  A i = A(8.7);
+  // expected-warning@-1 {{implicit conversion from 'double' to 'char'}}
+
+  B n(2022, {7, 8});
+  // expected-error@-1 {{no matching constructor for initialization}}
+  B z(A(1), {}, 1);
+  // expected-error@-1 {{reference member 'c' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+
+  C o(A(1), 1, 2, 3, 4);
+  // expected-error@-1 {{no matching constructor for initialization of 'C'}}
+  D R(1);
+  // expected-error@-1 {{no matching constructor for initialization}}
+  D I(C(1));
+  // expected-error@-1 {{no matching conversion for functional-style cast from 'int' to 'C'}}
+  D P(C(A(1)), 1);
+  // expected-error@-1 {{no matching constructor for initialization}}
+
+  int arr1[](0, 1, 2, A(1));
+  // expected-error@-1 {{no viable conversion from 'A' to 'int'}}
+  int arr2[2](0, 1, 2);
+  // expected-error@-1 {{excess elements in array initializer}}
+}
Index: clang/test/CodeGen/P0960R3.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGen/P0960R3.cpp
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 -std=c++20 %s -emit-llvm -o - | FileCheck %s
+
+struct A {
+  char i;
+  double j;
+};
+
+struct B {
+  A a;
+  int b;
+};
+
+struct C : public B, public A {};
+
+// CHECK-LABEL:   entry:
+// CHECK-NEXT:    [[A1:%.*]] = alloca [[STRUCT_A:%.*]], align 8
+// CHECK-NEXT:    [[B1:%.*]] = alloca [[STRUCT_B:%.*]], align 8
+// CHECK-NEXT:    [[C1:%.*]] = alloca [[STRUCT_C:%.*]], align 8
+// CHECK-NEXT:    [[REF_TMP:%.*]] = alloca [[STRUCT_B]], align 8
+// CHECK-NEXT:    [[REF_TMP5:%.*]] = alloca [[STRUCT_A]], align 8
+// CHECK-NEXT:    [[I:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A1]], i32 0, i32 0
+// CHECK-NEXT:    store i8 3, ptr [[I]], align 8
+// CHECK-NEXT:    [[J:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A1]], i32 0, i32 1
+// CHECK-NEXT:    store double 2.000000e+00, ptr [[J]], align 8
+// CHECK-NEXT:    [[A:%.*]] = getelementptr inbounds [[STRUCT_B]], ptr [[B1]], i32 0, i32 0
+// CHECK-NEXT:    [[I1:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A]], i32 0, i32 0
+// CHECK-NEXT:    store i8 99, ptr [[I1]], align 8
+// CHECK-NEXT:    [[A2:%.*]] = getelementptr inbounds [[STRUCT_B]], ptr [[REF_TMP]], i32 0, i32 0
+// CHECK-NEXT:    [[I3:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A2]], i32 0, i32 0
+// CHECK-NEXT:    store i8 1, ptr [[I3]], align 8
+// CHECK-NEXT:    [[J4:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A2]], i32 0, i32 1
+// CHECK-NEXT:    store double 1.000000e+00, ptr [[J4]], align 8
+// CHECK-NEXT:    [[B:%.*]] = getelementptr inbounds [[STRUCT_B]], ptr [[REF_TMP]], i32 0, i32 1
+// CHECK-NEXT:    store i32 1, ptr [[B]], align 8
+// CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[C1]], ptr align 8 [[REF_TMP]], i64 24, i1 false)
+// CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[C1]], i64 24
+// CHECK-NEXT:    [[I6:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[REF_TMP5]], i32 0, i32 0
+// CHECK-NEXT:    store i8 97, ptr [[I6]], align 8
+// CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[REF_TMP5]], i64 16, i1 false)
+// CHECK-NEXT:    ret void
+//
+void foo1() {
+  A a1(3.1, 2.0);
+  B b1(A('c'));
+  C c1(B(A(1, 1), 1), A('a'));
+}
+
+
+// CHECK-LABEL:   entry:
+// CHECK-NEXT:    [[ARR1:%.*]] = alloca [4 x i32], align 16
+// CHECK-NEXT:    [[ARR2:%.*]] = alloca [5 x i32], align 16
+// CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds [4 x i32], ptr [[ARR1]], i64 0, i64 0
+// CHECK-NEXT:    store i32 1, ptr [[TMP0]], align 16
+// CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds [4 x i32], ptr [[ARR1]], i64 0, i64 1
+// CHECK-NEXT:    store i32 2, ptr [[TMP1]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds [4 x i32], ptr [[ARR1]], i64 0, i64 2
+// CHECK-NEXT:    store i32 3, ptr [[TMP2]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = getelementptr inbounds [5 x i32], ptr [[ARR2]], i64 0, i64 0
+// CHECK-NEXT:    store i32 0, ptr [[TMP3]], align 16
+// CHECK-NEXT:    [[TMP4:%.*]] = getelementptr inbounds [5 x i32], ptr [[ARR2]], i64 0, i64 1
+// CHECK-NEXT:    store i32 1, ptr [[TMP4]], align 4
+// CHECK-NEXT:    [[TMP5:%.*]] = getelementptr inbounds [5 x i32], ptr [[ARR2]], i64 0, i64 2
+// CHECK-NEXT:    store i32 2, ptr [[TMP5]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = getelementptr inbounds [5 x i32], ptr [[ARR2]], i64 0, i64 3
+// CHECK-NEXT:    store i32 3, ptr [[TMP6]], align 4
+// CHECK-NEXT:    [[TMP7:%.*]] = getelementptr inbounds [5 x i32], ptr [[ARR2]], i64 0, i64 4
+// CHECK-NEXT:    store i32 4, ptr [[TMP7]], align 16
+// CHECK-NEXT:    ret void
+//
+void foo2() {
+  int arr1[4](1, 2, 3);
+  int arr2[](0, 1, 2, 3, 4);
+}
Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1898,6 +1898,7 @@
     case Stmt::ConceptSpecializationExprClass:
     case Stmt::CXXRewrittenBinaryOperatorClass:
     case Stmt::RequiresExprClass:
+    case Expr::CXXParenListInitExprClass:
       // Fall through.
 
     // Cases we intentionally don't evaluate, since they don't need
Index: clang/lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterStmt.cpp
+++ clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2081,6 +2081,11 @@
   Code = serialization::EXPR_CXX_FOLD;
 }
 
+void ASTStmtWriter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
+  // FIXME: unimplemented
+  llvm_unreachable("unimplemented");
+}
+
 void ASTStmtWriter::VisitOpaqueValueExpr(OpaqueValueExpr *E) {
   VisitExpr(E);
   Record.AddStmt(E->getSourceExpr());
Index: clang/lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderStmt.cpp
+++ clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2167,6 +2167,11 @@
   E->Opcode = (BinaryOperatorKind)Record.readInt();
 }
 
+void ASTStmtReader::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
+  // FIXME: unimplemented
+  llvm_unreachable("unimplemented");
+}
+
 void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) {
   VisitExpr(E);
   E->SourceExpr = Record.readSubExpr();
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -13957,6 +13957,13 @@
   return Result;
 }
 
+template <typename Derived>
+ExprResult
+TreeTransform<Derived>::TransformCXXParenListInitExpr(CXXParenListInitExpr *E) {
+  // FIXME: unimplemented
+  llvm_unreachable("unimplemented");
+}
+
 template<typename Derived>
 ExprResult
 TreeTransform<Derived>::TransformCXXStdInitializerListExpr(
Index: clang/lib/Sema/SemaInit.cpp
===================================================================
--- clang/lib/Sema/SemaInit.cpp
+++ clang/lib/Sema/SemaInit.cpp
@@ -3529,11 +3529,13 @@
   case SK_StdInitializerListConstructorCall:
   case SK_OCLSamplerInit:
   case SK_OCLZeroOpaqueType:
+  case SK_ParenthesizedListInit:
     break;
 
   case SK_ConversionSequence:
   case SK_ConversionSequenceNoNarrowing:
     delete ICS;
+    break;
   }
 }
 
@@ -3588,6 +3590,7 @@
   case FK_PlaceholderType:
   case FK_ExplicitConstructor:
   case FK_AddressOfUnaddressableFunction:
+  case FK_ParenthesizedListInitFailed:
     return false;
 
   case FK_ReferenceInitOverloadFailed:
@@ -3823,6 +3826,13 @@
   Steps.push_back(S);
 }
 
+void InitializationSequence::AddParenthesizedListInitStep(QualType T) {
+  Step S;
+  S.Kind = SK_ParenthesizedListInit;
+  S.Type = T;
+  Steps.push_back(S);
+}
+
 void InitializationSequence::RewrapReferenceInitList(QualType T,
                                                      InitListExpr *Syntactic) {
   assert(Syntactic->getNumInits() == 1 &&
@@ -4066,6 +4076,114 @@
   return CandidateSet.BestViableFunction(S, DeclLoc, Best);
 }
 
+static void VerifyOrPerformParenthesizedListInit(
+    Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind,
+    ArrayRef<Expr *> Args, InitializationSequence &Sequence, bool VerifyOnly,
+    ExprResult *Result = nullptr) {
+
+  unsigned Index = 0;
+  SmallVector<Expr *, 4> InitExprs;
+  QualType ResultType;
+
+  auto InitHelper = [&](auto Range) -> bool {
+    for (InitializedEntity SubEntity : Range) {
+      if (Index == Args.size())
+        // All the initializers are consumed, time to leave.
+        return true;
+
+      Expr *E = Args[Index++];
+      InitializationKind SubKind = InitializationKind::CreateForInit(
+          E->getExprLoc(), /*isDirectInit*/ false, E);
+      InitializationSequence Seq(S, SubEntity, SubKind, E);
+
+      if (Seq.Failed()) {
+        if (!VerifyOnly)
+          Seq.Diagnose(S, SubEntity, SubKind, E);
+        else if (!Sequence.Failed())
+          // Fall back to the "no matching constructor" path if the
+          // sequence has already failed.
+          Sequence.SetFailed(
+              InitializationSequence::FK_ParenthesizedListInitFailed);
+
+        return false;
+      }
+      if (!VerifyOnly) {
+        ExprResult ER = Seq.Perform(S, SubEntity, SubKind, E);
+        InitExprs.push_back(ER.get());
+      }
+    }
+    return true;
+  };
+
+  if (const ArrayType *AT =
+          S.getASTContext().getAsArrayType(Entity.getType())) {
+
+    SmallVector<InitializedEntity, 4> ElementEntities;
+    uint64_t ArrayLength;
+    if (const ConstantArrayType *CAT =
+            S.getASTContext().getAsConstantArrayType(Entity.getType()))
+      ArrayLength = CAT->getSize().getZExtValue();
+    else
+      ArrayLength = Args.size();
+
+    if (ArrayLength >= Args.size()) {
+      for (uint64_t I = 0; I < ArrayLength; ++I)
+        ElementEntities.push_back(
+            InitializedEntity::InitializeElement(S.getASTContext(), I, Entity));
+
+      if (!InitHelper(ElementEntities))
+        return;
+
+      ResultType = S.Context.getConstantArrayType(
+          AT->getElementType(), llvm::APInt(32, ArrayLength), nullptr,
+          ArrayType::Normal, 0);
+    }
+  } else if (isa<RecordType>(Entity.getType())) {
+    const CXXRecordDecl *RD =
+        cast<CXXRecordDecl>(Entity.getType()->getAs<RecordType>()->getDecl());
+
+    auto BaseRange = map_range(RD->bases(), [&S](auto &base) {
+      return InitializedEntity::InitializeBase(S.getASTContext(), &base, false);
+    });
+    auto FieldRange = map_range(RD->fields(), [](auto *field) {
+      return InitializedEntity::InitializeMember(field);
+    });
+
+    if (!InitHelper(BaseRange))
+      return;
+
+    if (!InitHelper(FieldRange))
+      return;
+
+    ResultType = Entity.getType();
+  }
+
+  if (Index != Args.size()) {
+    if (!VerifyOnly) {
+      QualType T = Entity.getType();
+      // FIXME: Union type unsupported.
+      int InitKind = T->isArrayType() ? 0 : 4;
+      S.Diag(Kind.getLocation(), diag::err_excess_initializers)
+          << InitKind << Args[Index]->getSourceRange();
+    } else if (!Sequence.Failed()) {
+      // Same as above, fall back to the "no matching constructor" path.
+      Sequence.SetFailed(
+          InitializationSequence::FK_ParenthesizedListInitFailed);
+    }
+    return;
+  }
+
+  if (VerifyOnly) {
+    Sequence.setSequenceKind(InitializationSequence::NormalSequence);
+    Sequence.AddParenthesizedListInitStep(Entity.getType());
+  } else if (Result) {
+    *Result = CXXParenListInitExpr::Create(S.getASTContext(), InitExprs,
+                                           ResultType, Kind.getLocation());
+  }
+
+  return;
+}
+
 /// Attempt initialization by constructor (C++ [dcl.init]), which
 /// enumerates the constructors of the initialized entity and performs overload
 /// resolution to select the best.
@@ -5915,7 +6033,10 @@
       TryListInitialization(S, Entity, Kind, cast<InitListExpr>(Initializer),
                             *this, TreatUnavailableAsInvalid);
       AddParenthesizedArrayInitStep(DestType);
-    } else if (DestAT->getElementType()->isCharType())
+    } else if (S.getLangOpts().CPlusPlus20 && !TopLevelOfInitList)
+      VerifyOrPerformParenthesizedListInit(S, Entity, Kind, Args, *this,
+                                           /*VerifyOnly=*/true);
+    else if (DestAT->getElementType()->isCharType())
       SetFailed(FK_ArrayNeedsInitListOrStringLiteral);
     else if (IsWideCharCompatible(DestAT->getElementType(), Context))
       SetFailed(FK_ArrayNeedsInitListOrWideStringLiteral);
@@ -5962,18 +6083,42 @@
     if (Kind.getKind() == InitializationKind::IK_Direct ||
         (Kind.getKind() == InitializationKind::IK_Copy &&
          (Context.hasSameUnqualifiedType(SourceType, DestType) ||
-          S.IsDerivedFrom(Initializer->getBeginLoc(), SourceType, DestType))))
-      TryConstructorInitialization(S, Entity, Kind, Args,
-                                   DestType, DestType, *this);
-    //     - Otherwise (i.e., for the remaining copy-initialization cases),
-    //       user-defined conversion sequences that can convert from the source
-    //       type to the destination type or (when a conversion function is
-    //       used) to a derived class thereof are enumerated as described in
-    //       13.3.1.4, and the best one is chosen through overload resolution
-    //       (13.3).
-    else
+          S.IsDerivedFrom(Initializer->getBeginLoc(), SourceType, DestType)))) {
+      TryConstructorInitialization(S, Entity, Kind, Args, DestType, DestType,
+                                   *this);
+      const CXXRecordDecl *RD =
+          dyn_cast<CXXRecordDecl>(DestType->getAs<RecordType>()->getDecl());
+      if (Failed() && S.getLangOpts().CPlusPlus20 && RD && RD->isAggregate())
+        // 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, the object is initialized as follows. Let e1,
+        //      ..., en be the elements of the aggregate . Let x1, ..., xk be
+        //      the elements of the expression-list. If k is greater than n, the
+        //      program is ill-formed. The element ei is copy-initialized with
+        //      xi for 1 <= i <= k. The remaining elements are initialized with
+        //      their default member initializers, if any, and otherwise are
+        //      value-initialized. For each 1 <= i < j <= n, every value
+        //      computation and side effect associated with the initialization
+        //      of ei is sequenced before those associated with the
+        //      initialization of ej. Note: By contrast with
+        //      direct-list-initialization, narrowing conversions (9.4.4) are
+        //      permitted, designators are not permitted, a temporary object
+        //      bound to a reference does not have its lifetime extended
+        //      (6.7.7), and there is no brace elision.
+        VerifyOrPerformParenthesizedListInit(S, Entity, Kind, Args, *this,
+                                             /*VerifyOnly=*/true);
+    } else {
+      //     - Otherwise (i.e., for the remaining copy-initialization cases),
+      //       user-defined conversion sequences that can convert from the
+      //       source type to the destination type or (when a conversion
+      //       function is used) to a derived class thereof are enumerated as
+      //       described in 13.3.1.4, and the best one is chosen through
+      //       overload resolution (13.3).
       TryUserDefinedConversion(S, DestType, Kind, Initializer, *this,
                                TopLevelOfInitList);
+    }
     return;
   }
 
@@ -8233,6 +8378,7 @@
   case SK_ConstructorInitializationFromList:
   case SK_StdInitializerListConstructorCall:
   case SK_ZeroInitialization:
+  case SK_ParenthesizedListInit:
     break;
   }
 
@@ -8922,6 +9068,14 @@
                                     CurInit.get()->getValueKind());
       break;
     }
+    case SK_ParenthesizedListInit: {
+      CurInit = false;
+      VerifyOrPerformParenthesizedListInit(S, Entity, Kind, Args, *this,
+                                           /*VerifyOnly=*/false, &CurInit);
+      if (CurInit.get() && ResultType)
+        *ResultType = CurInit.get()->getType();
+      break;
+    }
     }
   }
 
@@ -9523,6 +9677,10 @@
            diag::note_explicit_ctor_deduction_guide_here) << false;
     break;
   }
+
+  case FK_ParenthesizedListInitFailed:
+    VerifyOrPerformParenthesizedListInit(S, Entity, Kind, Args, *this,
+                                         /*VerifyOnly=*/false);
   }
 
   PrintInitLocationNote(S, Entity);
@@ -9689,6 +9847,10 @@
     case FK_ExplicitConstructor:
       OS << "list copy initialization chose explicit constructor";
       break;
+
+    case FK_ParenthesizedListInitFailed:
+      OS << "parenthesized list initialization failed";
+      break;
     }
     OS << '\n';
     return;
@@ -9860,6 +10022,9 @@
     case SK_OCLZeroOpaqueType:
       OS << "OpenCL opaque type from zero";
       break;
+    case SK_ParenthesizedListInit:
+      OS << "initialization from a parenthesized list of values";
+      break;
     }
 
     OS << " [" << S->Type << ']';
Index: clang/lib/Sema/SemaExceptionSpec.cpp
===================================================================
--- clang/lib/Sema/SemaExceptionSpec.cpp
+++ clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1411,6 +1411,10 @@
   case Expr::MSPropertySubscriptExprClass:
     llvm_unreachable("Invalid class for expression");
 
+  case Expr::CXXParenListInitExprClass:
+    // FIXME: unimplemented
+    llvm_unreachable("unimplemented");
+
     // Most statements can throw if any substatement can throw.
   case Stmt::AttributedStmtClass:
   case Stmt::BreakStmtClass:
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -12980,6 +12980,7 @@
 
   // Perform the initialization.
   ParenListExpr *CXXDirectInit = dyn_cast<ParenListExpr>(Init);
+  bool IsParenListInit = false;
   if (!VDecl->isInvalidDecl()) {
     InitializedEntity Entity = InitializedEntity::InitializeVariable(VDecl);
     InitializationKind Kind = InitializationKind::CreateForInit(
@@ -13022,6 +13023,8 @@
     }
 
     Init = Result.getAs<Expr>();
+    IsParenListInit = InitSeq.step_begin()->Kind ==
+                      InitializationSequence::SK_ParenthesizedListInit;
   }
 
   // Check for self-references within variable initializers.
@@ -13270,7 +13273,8 @@
   // class type.
   if (CXXDirectInit) {
     assert(DirectInit && "Call-style initializer must be direct init.");
-    VDecl->setInitStyle(VarDecl::CallInit);
+    VDecl->setInitStyle(IsParenListInit ? VarDecl::ParenListInit
+                                        : VarDecl::CallInit);
   } else if (DirectInit) {
     // This must be list-initialization. No other way is direct-initialization.
     VDecl->setInitStyle(VarDecl::ListInit);
Index: clang/lib/CodeGen/CGExprAgg.cpp
===================================================================
--- clang/lib/CodeGen/CGExprAgg.cpp
+++ clang/lib/CodeGen/CGExprAgg.cpp
@@ -205,6 +205,7 @@
   }
 
   void VisitVAArgExpr(VAArgExpr *E);
+  void VisitCXXParenListInitExpr(CXXParenListInitExpr *E);
 
   void EmitInitializationToLValue(Expr *E, LValue Address);
   void EmitNullInitializationToLValue(LValue Address);
@@ -1591,6 +1592,49 @@
   }
 }
 
+void AggExprEmitter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
+
+  ArrayRef<Expr *> InitExprs = E->getInitExprs();
+  AggValueSlot Dest = EnsureSlot(E->getType());
+
+  if (const ArrayType *AT = dyn_cast<ArrayType>(E->getType())) {
+    for (auto Pair : llvm::enumerate(InitExprs)) {
+      // Initialization for every element of the array.
+      Address V = Builder.CreateConstArrayGEP(Dest.getAddress(), Pair.index());
+      LValue LV = CGF.MakeAddrLValue(V, AT->getElementType());
+      EmitInitializationToLValue(Pair.value(), LV);
+    }
+  } else if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(
+                 E->getType()->castAs<RecordType>()->getDecl())) {
+
+    unsigned Index = 0;
+    LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType());
+
+    // Initialize the base classes first.
+    for (auto &Base : RD->bases()) {
+      if (Index < InitExprs.size()) {
+        const CXXRecordDecl *BaseRD = Base.getType()->getAsCXXRecordDecl();
+        Address V = CGF.GetAddressOfDirectBaseInCompleteClass(
+            Dest.getAddress(), RD, BaseRD,
+            /*isBaseVirtual*/ false);
+        AggValueSlot AggSlot = AggValueSlot::forAddr(
+            V, Qualifiers(), AggValueSlot::IsDestructed,
+            AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsNotAliased,
+            CGF.getOverlapForBaseInit(RD, BaseRD, Base.isVirtual()));
+        CGF.EmitAggExpr(InitExprs[Index++], AggSlot);
+      }
+    }
+
+    // Then initialize the fields.
+    for (const FieldDecl *F : RD->fields()) {
+      if (Index < InitExprs.size()) {
+        LValue LV = CGF.EmitLValueForFieldInitialization(DestLV, F);
+        EmitInitializationToLValue(InitExprs[Index++], LV);
+      }
+    }
+  }
+}
+
 void AggExprEmitter::VisitInitListExpr(InitListExpr *E) {
 #if 0
   // FIXME: Assess perf here?  Figure out what cases are worth optimizing here
Index: clang/lib/AST/TextNodeDumper.cpp
===================================================================
--- clang/lib/AST/TextNodeDumper.cpp
+++ clang/lib/AST/TextNodeDumper.cpp
@@ -1806,6 +1806,8 @@
     case VarDecl::ListInit:
       OS << " listinit";
       break;
+    case VarDecl::ParenListInit:
+      OS << " parenlistinit";
     }
   }
   if (D->needsDestruction(D->getASTContext()))
Index: clang/lib/AST/StmtProfile.cpp
===================================================================
--- clang/lib/AST/StmtProfile.cpp
+++ clang/lib/AST/StmtProfile.cpp
@@ -2184,6 +2184,11 @@
   ID.AddInteger(S->getOperator());
 }
 
+void StmtProfiler::VisitCXXParenListInitExpr(const CXXParenListInitExpr *S) {
+  // FIXME: unimplemented
+  llvm_unreachable("unimplemented");
+}
+
 void StmtProfiler::VisitCoroutineBodyStmt(const CoroutineBodyStmt *S) {
   VisitStmt(S);
 }
Index: clang/lib/AST/StmtPrinter.cpp
===================================================================
--- clang/lib/AST/StmtPrinter.cpp
+++ clang/lib/AST/StmtPrinter.cpp
@@ -2465,6 +2465,11 @@
   OS << ")";
 }
 
+void StmtPrinter::VisitCXXParenListInitExpr(CXXParenListInitExpr *Node) {
+  // FIXME: unimplemented
+  llvm_unreachable("unimplemented");
+}
+
 void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) {
   NestedNameSpecifierLoc NNS = E->getNestedNameSpecifierLoc();
   if (NNS)
Index: clang/lib/AST/JSONNodeDumper.cpp
===================================================================
--- clang/lib/AST/JSONNodeDumper.cpp
+++ clang/lib/AST/JSONNodeDumper.cpp
@@ -849,6 +849,9 @@
     case VarDecl::CInit: JOS.attribute("init", "c");  break;
     case VarDecl::CallInit: JOS.attribute("init", "call"); break;
     case VarDecl::ListInit: JOS.attribute("init", "list"); break;
+    case VarDecl::ParenListInit:
+      JOS.attribute("init", "paren-list");
+      break;
     }
   }
   attributeOnlyIfTrue("isParameterPack", VD->isParameterPack());
Index: clang/lib/AST/ItaniumMangle.cpp
===================================================================
--- clang/lib/AST/ItaniumMangle.cpp
+++ clang/lib/AST/ItaniumMangle.cpp
@@ -5126,6 +5126,9 @@
     Out << "E";
     break;
   }
+  case Expr::CXXParenListInitExprClass:
+    // FIXME: unimplemented
+    llvm_unreachable("unimplemented");
   }
 
   if (AsTemplateArg && !IsPrimaryExpr)
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -15589,6 +15589,7 @@
   case Expr::DependentCoawaitExprClass:
   case Expr::CoyieldExprClass:
   case Expr::SYCLUniqueStableNameExprClass:
+  case Expr::CXXParenListInitExprClass:
     return ICEDiag(IK_NotICE, E->getBeginLoc());
 
   case Expr::InitListExprClass: {
Index: clang/lib/AST/ExprClassification.cpp
===================================================================
--- clang/lib/AST/ExprClassification.cpp
+++ clang/lib/AST/ExprClassification.cpp
@@ -442,6 +442,10 @@
   case Expr::SYCLUniqueStableNameExprClass:
     return Cl::CL_PRValue;
     break;
+
+  case Expr::CXXParenListInitExprClass:
+    // FIXME: unimplemented
+    llvm_unreachable("unimplemented");
   }
 
   llvm_unreachable("unhandled expression kind in classification");
Index: clang/lib/AST/ExprCXX.cpp
===================================================================
--- clang/lib/AST/ExprCXX.cpp
+++ clang/lib/AST/ExprCXX.cpp
@@ -1758,3 +1758,11 @@
                            alignof(CUDAKernelCallExpr));
   return new (Mem) CUDAKernelCallExpr(NumArgs, HasFPFeatures, Empty);
 }
+
+CXXParenListInitExpr *CXXParenListInitExpr::Create(ASTContext &C,
+                                                   ArrayRef<Expr *> Args,
+                                                   QualType T,
+                                                   SourceLocation Loc) {
+  void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(Args.size()));
+  return new (Mem) CXXParenListInitExpr(Args, T, Loc);
+}
Index: clang/lib/AST/Expr.cpp
===================================================================
--- clang/lib/AST/Expr.cpp
+++ clang/lib/AST/Expr.cpp
@@ -3644,6 +3644,7 @@
   case ShuffleVectorExprClass:
   case ConvertVectorExprClass:
   case AsTypeExprClass:
+  case CXXParenListInitExprClass:
     // These have a side-effect if any subexpression does.
     break;
 
Index: clang/include/clang/Sema/Initialization.h
===================================================================
--- clang/include/clang/Sema/Initialization.h
+++ clang/include/clang/Sema/Initialization.h
@@ -923,7 +923,11 @@
     SK_OCLSamplerInit,
 
     /// Initialize an opaque OpenCL type (event_t, queue_t, etc.) with zero
-    SK_OCLZeroOpaqueType
+    SK_OCLZeroOpaqueType,
+
+    /// Initialize an aggreagate with parenthesized list of values.
+    /// This is a C++20 feature.
+    SK_ParenthesizedListInit
   };
 
   /// A single step in the initialization sequence.
@@ -1099,6 +1103,10 @@
 
     /// List-copy-initialization chose an explicit constructor.
     FK_ExplicitConstructor,
+
+    /// Parenthesized list initialization failed at some point.
+    /// This is a C++20 feature.
+    FK_ParenthesizedListInitFailed,
   };
 
 private:
@@ -1357,6 +1365,8 @@
   /// from a zero constant.
   void AddOCLZeroOpaqueTypeStep(QualType T);
 
+  void AddParenthesizedListInitStep(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/StmtNodes.td
===================================================================
--- clang/include/clang/Basic/StmtNodes.td
+++ clang/include/clang/Basic/StmtNodes.td
@@ -160,6 +160,7 @@
 def MaterializeTemporaryExpr : StmtNode<Expr>;
 def LambdaExpr : StmtNode<Expr>;
 def CXXFoldExpr : StmtNode<Expr>;
+def CXXParenListInitExpr: StmtNode<Expr>;
 
 // C++ Coroutines TS expressions
 def CoroutineSuspendExpr : StmtNode<Expr, 1>;
Index: clang/include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- clang/include/clang/AST/RecursiveASTVisitor.h
+++ clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2850,6 +2850,7 @@
 DEF_TRAVERSE_STMT(FunctionParmPackExpr, {})
 DEF_TRAVERSE_STMT(CXXFoldExpr, {})
 DEF_TRAVERSE_STMT(AtomicExpr, {})
+DEF_TRAVERSE_STMT(CXXParenListInitExpr, {})
 
 DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {
   if (S->getLifetimeExtendedTemporaryDecl()) {
Index: clang/include/clang/AST/ExprCXX.h
===================================================================
--- clang/include/clang/AST/ExprCXX.h
+++ clang/include/clang/AST/ExprCXX.h
@@ -4714,6 +4714,86 @@
   }
 };
 
+/// Represents a list-initialization with parenthesis.
+///
+/// As per P0960R3, this is a C++20 feature that allows aggregate to
+/// be initialized with a parenthesized list of values:
+/// ```
+/// struct A {
+///   int a;
+///   double b;
+/// };
+///
+/// void foo() {
+///   A a1(0);        // legal in C++20
+///   A a2(1.5, 1.0); // legal in C++20
+/// }
+/// ```
+/// It has some sort of similiarity to braced
+/// list-initialization, with some differences such as
+/// it allows narrowing conversion whilst braced
+/// list-initialization doesn't.
+/// ```
+/// struct A {
+///   char a;
+/// };
+/// void foo() {
+///   A a(1.5); // legal in C++20
+///   A b{1.5}; // illegal !
+/// }
+/// ```
+class CXXParenListInitExpr final
+    : public Expr,
+      private llvm::TrailingObjects<CXXParenListInitExpr, Expr *> {
+  friend class TrailingObjects;
+
+  unsigned NumExprs;
+  SourceLocation Loc;
+
+  CXXParenListInitExpr(ArrayRef<Expr *> Args, QualType T, SourceLocation Loc)
+      : Expr(CXXParenListInitExprClass, T,
+             T->isLValueReferenceType()   ? VK_LValue
+             : T->isRValueReferenceType() ? VK_PRValue
+                                          : VK_XValue,
+             OK_Ordinary),
+        NumExprs(Args.size()), Loc(Loc) {
+    std::copy(Args.begin(), Args.end(), getTrailingObjects<Expr *>());
+  }
+
+  size_t numTrailingObjects(OverloadToken<Expr *>) const { return NumExprs; }
+
+public:
+  static CXXParenListInitExpr *Create(ASTContext &C, ArrayRef<Expr *> Args,
+                                      QualType T, SourceLocation Loc);
+
+  ArrayRef<Expr *> getInitExprs() {
+    return {getTrailingObjects<Expr *>(), NumExprs};
+  }
+
+  SourceLocation getBeginLoc() const LLVM_READONLY { return Loc; }
+
+  SourceLocation getEndLoc() const LLVM_READONLY { return Loc; }
+
+  SourceRange getSourceRange() const LLVM_READONLY {
+    return SourceRange(getBeginLoc(), getEndLoc());
+  }
+
+  child_range children() {
+    Stmt **Begin = reinterpret_cast<Stmt **>(getTrailingObjects<Expr *>());
+    return child_range(Begin, Begin + NumExprs);
+  }
+
+  const_child_range children() const {
+    Stmt *const *Begin =
+        reinterpret_cast<Stmt *const *>(getTrailingObjects<Expr *>());
+    return const_child_range(Begin, Begin + NumExprs);
+  }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == CXXParenListInitExprClass;
+  }
+};
+
 /// Represents an expression that might suspend coroutine execution;
 /// either a co_await or co_yield expression.
 ///
Index: clang/include/clang/AST/Decl.h
===================================================================
--- clang/include/clang/AST/Decl.h
+++ clang/include/clang/AST/Decl.h
@@ -889,7 +889,10 @@
     CallInit,
 
     /// Direct list-initialization (C++11)
-    ListInit
+    ListInit,
+
+    /// Parenthesized list-initialization (C++20)
+    ParenListInit
   };
 
   /// Kinds of thread-local storage.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to