[clang] 444ec09 - [clang] p0388 array list initialization overloads

Nathan Sidwell via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 12 07:35:43 PDT 2021


Author: Nathan Sidwell
Date: 2021-10-12T07:35:21-07:00
New Revision: 444ec0957c58492ecce0569bb20b7d3ad59d37bb

URL: https://github.com/llvm/llvm-project/commit/444ec0957c58492ecce0569bb20b7d3ad59d37bb
DIFF: https://github.com/llvm/llvm-project/commit/444ec0957c58492ecce0569bb20b7d3ad59d37bb.diff

LOG: [clang] p0388 array list initialization overloads

This is the second part of p0388, dealing with overloads of list
initialization to incomplete array types.  It extends the handling
added in D103088 to permit incomplete arrays.  We have to record that
the conversion involved an incomplete array, and so (re-add) a bit flag
into the standard conversion sequence object.  Comparing such
conversion sequences requires knowing (a) the number of array elements
initialized and (b) whether the initialization is of an incomplete array.

This also updates the web page to indicate p0388 is implemented (there
is no feature macro).

Differential Revision: https://reviews.llvm.org/D103908

Added: 
    

Modified: 
    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

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index f16595c3319cd..7898a58c27960 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -535,7 +535,10 @@ class Sema;
     };
 
     /// 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 @@ class Sema;
     };
 
     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 @@ class Sema;
     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() &&

diff  --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index da49084f518a3..a6b11465d4bcd 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5865,14 +5865,14 @@ void ASTContext::UnwrapSimilarArrayTypes(QualType &T1, QualType &T2,
     // 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;

diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 5c513a4326933..044e3c4b254ad 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -3820,8 +3820,8 @@ CompareImplicitConversionSequences(Sema &S, SourceLocation Loc,
   //   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 @@ CompareImplicitConversionSequences(Sema &S, SourceLocation Loc,
       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 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
   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 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
                                      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 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
   //   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 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
             // 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 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
         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 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
                                 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;
   }
 

diff  --git a/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp b/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp
index 34f0869382b4d..f2d5cabad235d 100644
--- a/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp
+++ b/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp
@@ -77,3 +77,97 @@ auto *frob2(Mat *(*arp)[1]) {
 }
 
 } // 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 at -6{{cannot convert initializer}}
+    // expected-note at -6{{cannot convert initializer}}
+    // expected-note at -6{{too many initializers}}
+    // expected-note at -6{{too many initializers}}
+    // expected-note at -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 at -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

diff  --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 2be1fb4b68738..4b628e1f62fe2 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -1238,7 +1238,7 @@ <h2 id="cxx20">C++20 implementation status</h2>
     <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>


        


More information about the cfe-commits mailing list