[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