This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG444ec0957c58: [clang] p0388 array list initialization 
overloads (authored by urnathan).
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D103908

Files:
  clang/include/clang/Sema/Overload.h
  clang/lib/AST/ASTContext.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp
  clang/www/cxx_status.html

Index: clang/www/cxx_status.html
===================================================================
--- clang/www/cxx_status.html
+++ clang/www/cxx_status.html
@@ -1238,7 +1238,7 @@
     <tr>
       <td>Permit conversions to arrays of unknown bound</td>
       <td><a href="https://wg21.link/p0388r4";>P0388R4</a></td>
-      <td class="none" align="center">No</td>
+      <td class="unreleased" align="center">Clang 13</td>
     </tr>
     <tr>
       <td><tt>constinit</tt></td>
Index: clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp
===================================================================
--- clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp
+++ clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp
@@ -77,3 +77,97 @@
 }
 
 } // namespace Four
+
+namespace Five {
+// from the paper
+char (&b(int(&&)[]))[1];   // #1
+char (&b(long(&&)[]))[2];  // #2
+char (&b(int(&&)[1]))[3];  // #3
+char (&b(long(&&)[1]))[4]; // #4
+char (&b(int(&&)[2]))[5];  // #5
+#if __cplusplus < 202002
+    // expected-note@-6{{cannot convert initializer}}
+    // expected-note@-6{{cannot convert initializer}}
+    // expected-note@-6{{too many initializers}}
+    // expected-note@-6{{too many initializers}}
+    // expected-note@-6{{too many initializers}}
+#endif
+
+void f() {
+  static_assert(sizeof(b({1})) == 3);
+  static_assert(sizeof(b({1, 2})) == 5);
+  static_assert(sizeof(b({1, 2, 3})) == 1);
+#if __cplusplus < 202002
+  // expected-error@-2{{no matching function}}
+#endif
+}
+} // namespace Five
+
+#if __cplusplus >= 202002
+namespace Six {
+// from over.ics.rank 3.1
+char (&f(int(&&)[]))[1];    // #1
+char (&f(double(&&)[]))[2]; // #2
+char (&f(int(&&)[2]))[3];   // #3
+
+void toto() {
+  // Calls #1: Better than #2 due to conversion, better than #3 due to bounds
+  static_assert(sizeof(f({1})) == 1);
+
+  // Calls #2: Identity conversion is better than floating-integral conversion
+  static_assert(sizeof(f({1.0})) == 2);
+
+  // Calls #2: Identity conversion is better than floating-integral conversion
+  static_assert(sizeof(f({1.0, 2.0})) == 2);
+
+  // Calls #3: Converting to array of known bound is better than to unknown
+  //           bound, and an identity conversion is better than
+  //           floating-integral conversion
+  static_assert(sizeof(f({1, 2})) == 3);
+}
+
+} // namespace Six
+
+namespace Seven {
+
+char (&f(int(&&)[]))[1];     // #1
+char (&f(double(&&)[1]))[2]; // #2
+
+void quux() {
+  // Calls #2, float-integral conversion rather than create zero-sized array
+  static_assert(sizeof(f({})) == 2);
+}
+
+} // namespace Seven
+
+namespace Eight {
+
+// brace-elision is not a thing here:
+struct A {
+  int x, y;
+};
+
+char (&f1(int(&&)[]))[1]; // #1
+char (&f1(A(&&)[]))[2];   // #2
+
+void g1() {
+  // pick #1, even though that is more elements than #2
+  // 6 ints, as opposed to 3 As
+  static_assert(sizeof(f1({1, 2, 3, 4, 5, 6})) == 1);
+}
+
+void f2(A(&&)[]); // expected-note{{candidate function not viable}}
+void g2() {
+  f2({1, 2, 3, 4, 5, 6}); // expected-error{{no matching function}}
+}
+
+void f3(A(&&)[]);
+void g3() {
+  auto &f = f3;
+
+  f({1, 2, 3, 4, 5, 6}); // OK! We're coercing to an already-selected function
+}
+
+} // namespace Eight
+
+#endif
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -3820,8 +3820,8 @@
   //   if not that,
   // — L1 and L2 convert to arrays of the same element type, and either the
   //   number of elements n_1 initialized by L1 is less than the number of
-  //   elements n_2 initialized by L2, or (unimplemented:C++20) n_1 = n_2 and L2
-  //   converts to an array of unknown bound and L1 does not,
+  //   elements n_2 initialized by L2, or (C++20) n_1 = n_2 and L2 converts to
+  //   an array of unknown bound and L1 does not,
   // even if one of the other rules in this paragraph would otherwise apply.
   if (!ICS1.isBad()) {
     bool StdInit1 = false, StdInit2 = false;
@@ -3840,13 +3840,23 @@
       if (auto *CAT1 = S.Context.getAsConstantArrayType(
               ICS1.getInitializerListContainerType()))
         if (auto *CAT2 = S.Context.getAsConstantArrayType(
-                ICS2.getInitializerListContainerType()))
+                ICS2.getInitializerListContainerType())) {
           if (S.Context.hasSameUnqualifiedType(CAT1->getElementType(),
-                                               CAT2->getElementType()) &&
-              CAT1->getSize() != CAT2->getSize())
-            return CAT1->getSize().ult(CAT2->getSize())
-                       ? ImplicitConversionSequence::Better
-                       : ImplicitConversionSequence::Worse;
+                                               CAT2->getElementType())) {
+            // Both to arrays of the same element type
+            if (CAT1->getSize() != CAT2->getSize())
+              // Different sized, the smaller wins
+              return CAT1->getSize().ult(CAT2->getSize())
+                         ? ImplicitConversionSequence::Better
+                         : ImplicitConversionSequence::Worse;
+            if (ICS1.isInitializerListOfIncompleteArray() !=
+                ICS2.isInitializerListOfIncompleteArray())
+              // One is incomplete, it loses
+              return ICS2.isInitializerListOfIncompleteArray()
+                         ? ImplicitConversionSequence::Better
+                         : ImplicitConversionSequence::Worse;
+          }
+        }
   }
 
   if (ICS1.isStandard())
@@ -5004,9 +5014,15 @@
   ImplicitConversionSequence Result;
   Result.setBad(BadConversionSequence::no_conversion, From, ToType);
 
-  // We need a complete type for what follows. Incomplete types can never be
-  // initialized from init lists.
-  if (!S.isCompleteType(From->getBeginLoc(), ToType))
+  // We need a complete type for what follows.  With one C++20 exception,
+  // incomplete types can never be initialized from init lists.
+  QualType InitTy = ToType;
+  const ArrayType *AT = S.Context.getAsArrayType(ToType);
+  if (AT && S.getLangOpts().CPlusPlus20)
+    if (const auto *IAT = dyn_cast<IncompleteArrayType>(AT))
+      // C++20 allows list initialization of an incomplete array type.
+      InitTy = IAT->getElementType();
+  if (!S.isCompleteType(From->getBeginLoc(), InitTy))
     return Result;
 
   // Per DR1467:
@@ -5030,18 +5046,16 @@
                                      AllowObjCWritebackConversion);
     }
 
-    if (const auto *AT = S.Context.getAsArrayType(ToType)) {
-      if (S.IsStringInit(From->getInit(0), AT)) {
-        InitializedEntity Entity =
+    if (AT && S.IsStringInit(From->getInit(0), AT)) {
+      InitializedEntity Entity =
           InitializedEntity::InitializeParameter(S.Context, ToType,
                                                  /*Consumed=*/false);
-        if (S.CanPerformCopyInitialization(Entity, From)) {
-          Result.setStandard();
-          Result.Standard.setAsIdentityConversion();
-          Result.Standard.setFromType(ToType);
-          Result.Standard.setAllToTypes(ToType);
-          return Result;
-        }
+      if (S.CanPerformCopyInitialization(Entity, From)) {
+        Result.setStandard();
+        Result.Standard.setAsIdentityConversion();
+        Result.Standard.setFromType(ToType);
+        Result.Standard.setAllToTypes(ToType);
+        return Result;
       }
     }
   }
@@ -5059,22 +5073,21 @@
   //   default-constructible, and if all the elements of the initializer list
   //   can be implicitly converted to X, the implicit conversion sequence is
   //   the worst conversion necessary to convert an element of the list to X.
-  QualType InitTy = ToType;
-  ArrayType const *AT = S.Context.getAsArrayType(ToType);
   if (AT || S.isStdInitializerList(ToType, &InitTy)) {
     unsigned e = From->getNumInits();
     ImplicitConversionSequence DfltElt;
     DfltElt.setBad(BadConversionSequence::no_conversion, QualType(),
                    QualType());
+    QualType ContTy = ToType;
+    bool IsUnbounded = false;
     if (AT) {
-      // Result has been initialized above as a BadConversionSequence
       InitTy = AT->getElementType();
       if (ConstantArrayType const *CT = dyn_cast<ConstantArrayType>(AT)) {
         if (CT->getSize().ult(e)) {
           // Too many inits, fatally bad
           Result.setBad(BadConversionSequence::too_many_initializers, From,
                         ToType);
-          Result.setInitializerListContainerType(ToType);
+          Result.setInitializerListContainerType(ContTy, IsUnbounded);
           return Result;
         }
         if (CT->getSize().ugt(e)) {
@@ -5089,10 +5102,23 @@
             // No {} init, fatally bad
             Result.setBad(BadConversionSequence::too_few_initializers, From,
                           ToType);
-            Result.setInitializerListContainerType(ToType);
+            Result.setInitializerListContainerType(ContTy, IsUnbounded);
             return Result;
           }
         }
+      } else {
+        assert(isa<IncompleteArrayType>(AT) && "Expected incomplete array");
+        IsUnbounded = true;
+        if (!e) {
+          // Cannot convert to zero-sized.
+          Result.setBad(BadConversionSequence::too_few_initializers, From,
+                        ToType);
+          Result.setInitializerListContainerType(ContTy, IsUnbounded);
+          return Result;
+        }
+        llvm::APInt Size(S.Context.getTypeSize(S.Context.getSizeType()), e);
+        ContTy = S.Context.getConstantArrayType(InitTy, Size, nullptr,
+                                                ArrayType::Normal, 0);
       }
     }
 
@@ -5115,7 +5141,7 @@
         Result = ICS;
         // Bail as soon as we find something unconvertible.
         if (Result.isBad()) {
-          Result.setInitializerListContainerType(ToType);
+          Result.setInitializerListContainerType(ContTy, IsUnbounded);
           return Result;
         }
       }
@@ -5128,8 +5154,8 @@
                                 S, From->getEndLoc(), DfltElt, Result) ==
                                 ImplicitConversionSequence::Worse)
       Result = DfltElt;
-
-    Result.setInitializerListContainerType(ToType);
+    // Record the type being initialized so that we may compare sequences
+    Result.setInitializerListContainerType(ContTy, IsUnbounded);
     return Result;
   }
 
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -5865,14 +5865,14 @@
     // FIXME: Consider also unwrapping array of unknown bound and VLA.
     if (auto *CAT1 = dyn_cast<ConstantArrayType>(AT1)) {
       auto *CAT2 = dyn_cast<ConstantArrayType>(AT2);
-      if (!(CAT2 && CAT1->getSize() == CAT2->getSize()) &&
-          !(getLangOpts().CPlusPlus20 && AllowPiMismatch &&
-            isa<IncompleteArrayType>(AT2)))
+      if (!((CAT2 && CAT1->getSize() == CAT2->getSize()) ||
+            (AllowPiMismatch && getLangOpts().CPlusPlus20 &&
+             isa<IncompleteArrayType>(AT2))))
         return;
     } else if (isa<IncompleteArrayType>(AT1)) {
-      if (!isa<IncompleteArrayType>(AT2) &&
-          !(getLangOpts().CPlusPlus20 && AllowPiMismatch &&
-            isa<ConstantArrayType>(AT2)))
+      if (!(isa<IncompleteArrayType>(AT2) ||
+            (AllowPiMismatch && getLangOpts().CPlusPlus20 &&
+             isa<ConstantArrayType>(AT2))))
         return;
     } else {
       return;
Index: clang/include/clang/Sema/Overload.h
===================================================================
--- clang/include/clang/Sema/Overload.h
+++ clang/include/clang/Sema/Overload.h
@@ -535,7 +535,10 @@
     };
 
     /// ConversionKind - The kind of implicit conversion sequence.
-    unsigned ConversionKind;
+    unsigned ConversionKind : 31;
+
+    // Whether the initializer list was of an incomplete array.
+    unsigned InitializerListOfIncompleteArray : 1;
 
     /// When initializing an array or std::initializer_list from an
     /// initializer-list, this is the array or std::initializer_list type being
@@ -573,12 +576,16 @@
     };
 
     ImplicitConversionSequence()
-        : ConversionKind(Uninitialized), InitializerListContainerType() {
+        : ConversionKind(Uninitialized),
+          InitializerListOfIncompleteArray(false),
+          InitializerListContainerType() {
       Standard.setAsIdentityConversion();
     }
 
     ImplicitConversionSequence(const ImplicitConversionSequence &Other)
         : ConversionKind(Other.ConversionKind),
+          InitializerListOfIncompleteArray(
+              Other.InitializerListOfIncompleteArray),
           InitializerListContainerType(Other.InitializerListContainerType) {
       switch (ConversionKind) {
       case Uninitialized: break;
@@ -680,8 +687,12 @@
     bool hasInitializerListContainerType() const {
       return !InitializerListContainerType.isNull();
     }
-    void setInitializerListContainerType(QualType T) {
+    void setInitializerListContainerType(QualType T, bool IA) {
       InitializerListContainerType = T;
+      InitializerListOfIncompleteArray = IA;
+    }
+    bool isInitializerListOfIncompleteArray() const {
+      return InitializerListOfIncompleteArray;
     }
     QualType getInitializerListContainerType() const {
       assert(hasInitializerListContainerType() &&
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to