[clang] 0ce6d6b - [Sema] List conversion validate character array.

Mark de Wever via cfe-commits cfe-commits at lists.llvm.org
Sat Oct 3 05:34:14 PDT 2020


Author: Mark de Wever
Date: 2020-10-03T14:33:28+02:00
New Revision: 0ce6d6b46eb7040283ad0800c5533672fbfb9bac

URL: https://github.com/llvm/llvm-project/commit/0ce6d6b46eb7040283ad0800c5533672fbfb9bac
DIFF: https://github.com/llvm/llvm-project/commit/0ce6d6b46eb7040283ad0800c5533672fbfb9bac.diff

LOG: [Sema] List conversion validate character array.

The function `TryListConversion` didn't properly validate the following
part of the standard:

    Otherwise, if the parameter type is a character array [... ]
    and the initializer list has a single element that is an
    appropriately-typed string literal (8.5.2 [dcl.init.string]), the
    implicit conversion sequence is the identity conversion.

This caused the following call to `f()` to be ambiguous.
    void f(int(&&)[1]);
    void f(unsigned(&&)[1]);

    void g(unsigned i) {
      f({i});
    }

This issue only occurs when the initializer list had one element.

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

Added: 
    

Modified: 
    clang/include/clang/Sema/Sema.h
    clang/lib/Sema/SemaInit.cpp
    clang/lib/Sema/SemaOverload.cpp
    clang/test/CXX/drs/dr14xx.cpp
    clang/test/SemaObjCXX/overload.mm

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 9559075935d8..3516d1c1b717 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3200,6 +3200,8 @@ class Sema final {
   bool CanPerformAggregateInitializationForOverloadResolution(
       const InitializedEntity &Entity, InitListExpr *From);
 
+  bool IsStringInit(Expr *Init, const ArrayType *AT);
+
   bool CanPerformCopyInitialization(const InitializedEntity &Entity,
                                     ExprResult Init);
   ExprResult PerformCopyInitialization(const InitializedEntity &Entity,

diff  --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index a9f707b8cf20..751b785ce531 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -141,6 +141,10 @@ static StringInitFailureKind IsStringInit(Expr *init, QualType declType,
   return IsStringInit(init, arrayType, Context);
 }
 
+bool Sema::IsStringInit(Expr *Init, const ArrayType *AT) {
+  return ::IsStringInit(Init, AT, Context) == SIF_None;
+}
+
 /// Update the type of a string literal, including any surrounding parentheses,
 /// to match the type of the object which it is initializing.
 static void updateStringLiteralType(Expr *E, QualType Ty) {

diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 95d110e754f4..0c252a488fea 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -4984,18 +4984,19 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
                                      InOverloadResolution,
                                      AllowObjCWritebackConversion);
     }
-    // FIXME: Check the other conditions here: array of character type,
-    // initializer is a string literal.
-    if (ToType->isArrayType()) {
-      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 (const auto *AT = S.Context.getAsArrayType(ToType)) {
+      if (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;
+        }
       }
     }
   }

diff  --git a/clang/test/CXX/drs/dr14xx.cpp b/clang/test/CXX/drs/dr14xx.cpp
index 50b0396a4b79..13bc0d148d77 100644
--- a/clang/test/CXX/drs/dr14xx.cpp
+++ b/clang/test/CXX/drs/dr14xx.cpp
@@ -334,6 +334,22 @@ namespace dr1467 {  // dr1467: 3.7 c++11
 
     X x;
     X x2{x};
+
+    void f1(int);                                  // expected-note {{candidate function}}
+    void f1(std::initializer_list<long>) = delete; // expected-note {{candidate function has been explicitly deleted}}
+    void g1() { f1({42}); }                        // expected-error {{call to deleted function 'f1'}}
+
+    template <class T, class U>
+    struct Pair {
+      Pair(T, U);
+    };
+    struct String {
+      String(const char *);
+    };
+
+    void f2(Pair<const char *, const char *>);       // expected-note {{candidate function}}
+    void f2(std::initializer_list<String>) = delete; // expected-note {{candidate function has been explicitly deleted}}
+    void g2() { f2({"foo", "bar"}); }                // expected-error {{call to deleted function 'f2'}}
   } // dr_example
 
   namespace nonaggregate {
@@ -379,6 +395,46 @@ namespace dr1467 {  // dr1467: 3.7 c++11
     struct Value { Value(Pair); Value(TwoPairs); };
     void f() { Value{{{1,2},{3,4}}}; }
   }
+  namespace NonAmbiguous {
+  // The original implementation made this case ambigious due to the special
+  // handling of one element initialization lists.
+  void f(int(&&)[1]);
+  void f(unsigned(&&)[1]);
+
+  void g(unsigned i) {
+    f({i});
+  }
+  } // namespace NonAmbiguous
+
+#if __cplusplus >= 201103L
+  namespace StringLiterals {
+  // When the array size is 4 the call will attempt to bind an lvalue to an
+  // rvalue and fail. Therefore #2 will be called. (rsmith will bring this
+  // issue to CWG)
+  void f(const char(&&)[4]);              // expected-note 5 {{no known conversion}}
+  void f(const char(&&)[5]) = delete;     // expected-note 2 {{candidate function has been explicitly deleted}} expected-note 3 {{no known conversion}}
+  void f(const wchar_t(&&)[4]);           // expected-note 5 {{no known conversion}}
+  void f(const wchar_t(&&)[5]) = delete;  // expected-note {{candidate function has been explicitly deleted}} expected-note 4 {{no known conversion}}
+#if __cplusplus >= 202002L
+  void f2(const char8_t(&&)[4]);          // expected-note {{no known conversion}}
+  void f2(const char8_t(&&)[5]) = delete; // expected-note {{candidate function has been explicitly deleted}}
+#endif
+  void f(const char16_t(&&)[4]);          // expected-note 5 {{no known conversion}}
+  void f(const char16_t(&&)[5]) = delete; // expected-note {{candidate function has been explicitly deleted}} expected-note 4 {{no known conversion}}
+  void f(const char32_t(&&)[4]);          // expected-note 5 {{no known conversion}}
+  void f(const char32_t(&&)[5]) = delete; // expected-note {{candidate function has been explicitly deleted}} expected-note 4 {{no known conversion}}
+  void g() {
+    f({"abc"});       // expected-error {{call to deleted function 'f'}}
+    f({((("abc")))}); // expected-error {{call to deleted function 'f'}}
+    f({L"abc"});      // expected-error {{call to deleted function 'f'}}
+#if __cplusplus >= 202002L
+    f2({u8"abc"});    // expected-error {{call to deleted function 'f2'}}
+#endif
+    f({uR"(abc)"});   // expected-error {{call to deleted function 'f'}}
+    f({(UR"(abc)")}); // expected-error {{call to deleted function 'f'}}
+  }
+  } // namespace StringLiterals
+#endif
 } // dr1467
 
 namespace dr1490 {  // dr1490: 3.7 c++11

diff  --git a/clang/test/SemaObjCXX/overload.mm b/clang/test/SemaObjCXX/overload.mm
index f3c06b4f2288..d6485b3ac28c 100644
--- a/clang/test/SemaObjCXX/overload.mm
+++ b/clang/test/SemaObjCXX/overload.mm
@@ -201,3 +201,17 @@ void test(NSDictionary *d1, NSDictionary<A *, A *> *d2, NSMutableDictionary<A *,
 }
 
 }
+
+namespace StringLiterals {
+void f(const char(&&)[5]);
+void f(const wchar_t(&&)[5]);
+void f(const char16_t(&&)[5]);
+void f(const char32_t(&&)[5]);
+void g() {
+  f({"abc"});
+  f({(((@encode(int))))});
+  f({L"abc"});
+  f({uR"(abc)"});
+  f({(UR"(abc)")});
+}
+} // namespace StringLiterals


        


More information about the cfe-commits mailing list