From 8e1f3aa676b891c9566344ad2be046898df34a3a Mon Sep 17 00:00:00 2001 From: Gabor Spaits Date: Fri, 7 Jun 2024 14:38:08 +0200 Subject: [PATCH 1/5] [CTAD] Allow user defined conversion --- clang/include/clang/Sema/Initialization.h | 2 +- clang/lib/Sema/SemaInit.cpp | 6 +- .../SemaCXX/ctad-copy-init-list-narrowing.cpp | 90 +++++++++++++++++++ 3 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h index f443e327eaf329..4b876db436b48c 100644 --- a/clang/include/clang/Sema/Initialization.h +++ b/clang/include/clang/Sema/Initialization.h @@ -603,7 +603,7 @@ class InitializationKind { /// Normal context IC_Normal, - /// Normal context, but allows explicit conversion functionss + /// Normal context, but allows explicit conversion functions IC_ExplicitConvs, /// Implicit context (value initialization) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index ed8b226a6b39f5..211b6887befa32 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -10892,8 +10892,6 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // FIXME: The "second phase of [over.match.list] case can also // theoretically happen here, but it's not clear whether we can // ever have a parameter of the right type. - bool SuppressUserConversions = Kind.isCopyInit(); - if (TD) { SmallVector TmpInits; for (Expr *E : Inits) @@ -10903,12 +10901,12 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( TmpInits.push_back(E); AddTemplateOverloadCandidate( TD, FoundDecl, /*ExplicitArgs=*/nullptr, TmpInits, Candidates, - SuppressUserConversions, + /*SuppressUserConversions=*/false, /*PartialOverloading=*/false, AllowExplicit, ADLCallKind::NotADL, /*PO=*/{}, AllowAggregateDeductionCandidate); } else { AddOverloadCandidate(GD, FoundDecl, Inits, Candidates, - SuppressUserConversions, + /*SuppressUserConversions=*/false, /*PartialOverloading=*/false, AllowExplicit); } }; diff --git a/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp b/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp new file mode 100644 index 00000000000000..21b1137158d5a5 --- /dev/null +++ b/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused-value -std=c++20 %s +namespace std +{ + typedef long unsigned int size_t; +} + +namespace std +{ + template + class initializer_list + { + public: + typedef _E value_type; + typedef const _E& reference; + typedef const _E& const_reference; + typedef size_t size_type; + typedef const _E* iterator; + typedef const _E* const_iterator; + + private: + iterator _M_array; + size_type _M_len; + + + constexpr initializer_list(const_iterator __a, size_type __l) + : _M_array(__a), _M_len(__l) { } + + public: + constexpr initializer_list() noexcept + : _M_array(0), _M_len(0) { } + + + constexpr size_type + size() const noexcept { return _M_len; } + + + constexpr const_iterator + begin() const noexcept { return _M_array; } + + + constexpr const_iterator + end() const noexcept { return begin() + size(); } + }; + + template + constexpr const _Tp* + begin(initializer_list<_Tp> __ils) noexcept + { return __ils.begin(); } + + template + constexpr const _Tp* + end(initializer_list<_Tp> __ils) noexcept + { return __ils.end(); } +} + +template +class pair{ + private: + T fst; + Y snd; + public: + pair(T f, Y s) : fst(f), snd(s) {} +}; + +template +class map { + public: + map(std::initializer_list>, int a = 4, int b = 5) {} +}; + +template +class Contained { + public: + Contained(T, Y) {} +}; + +template +class A { + public: + A(std::initializer_list >, int) {} +}; + +int main() { + map mOk ={pair{5, 'a'}, {6, 'b'}, {7, 'c'}}; + map mNarrow ={pair{5, 'a'}, {6.0f, 'b'}, {7, 'c'}}; // expected-error {{type 'float' cannot be narrowed to 'int' in initializer list}} // expected-note {{insert an explicit cast to silence this issue}} + + A aOk = {{Contained{5, 'c'}, {5, 'c'}}, 5}; + A aNarrowNested = {{Contained{5, 'c'}, {5.0f, 'c'}}, 5}; // expected-error {{type 'float' cannot be narrowed to 'int' in initializer list}} // expected-note {{insert an explicit cast to silence this issue}} + A aNarrow = {{Contained{5, 'c'}, {5, 'c'}}, 5.0f}; // expected-error {{type 'float' cannot be narrowed to 'int' in initializer list}} // expected-note {{insert an explicit cast to silence this issue}} +} From 8e33ac051a04e5d5c6d73efdd4cf47868d5dc644 Mon Sep 17 00:00:00 2001 From: Gabor Spaits Date: Sun, 9 Jun 2024 14:22:42 +0200 Subject: [PATCH 2/5] Simplify boiler plate and re-organize tests --- .../SemaCXX/ctad-copy-init-list-narrowing.cpp | 95 +++++++------------ 1 file changed, 35 insertions(+), 60 deletions(-) diff --git a/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp b/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp index 21b1137158d5a5..a2de4cf8a2f900 100644 --- a/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp +++ b/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp @@ -1,65 +1,25 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused-value -std=c++20 %s -namespace std -{ - typedef long unsigned int size_t; -} - -namespace std -{ - template - class initializer_list - { - public: - typedef _E value_type; - typedef const _E& reference; - typedef const _E& const_reference; - typedef size_t size_type; - typedef const _E* iterator; - typedef const _E* const_iterator; - - private: - iterator _M_array; - size_type _M_len; - - - constexpr initializer_list(const_iterator __a, size_type __l) - : _M_array(__a), _M_len(__l) { } - - public: - constexpr initializer_list() noexcept - : _M_array(0), _M_len(0) { } - - - constexpr size_type - size() const noexcept { return _M_len; } - - constexpr const_iterator - begin() const noexcept { return _M_array; } +namespace std { + typedef decltype(sizeof(int)) size_t; + template + struct initializer_list + { + const E *p; + size_t n; + initializer_list(const E *p, size_t n) : p(p), n(n) {} + }; - constexpr const_iterator - end() const noexcept { return begin() + size(); } - }; - - template - constexpr const _Tp* - begin(initializer_list<_Tp> __ils) noexcept - { return __ils.begin(); } - - template - constexpr const _Tp* - end(initializer_list<_Tp> __ils) noexcept - { return __ils.end(); } -} + struct string { + string(const char *); + }; + // Classes to use to reproduce the exact scenario present in 62925. template class pair{ - private: - T fst; - Y snd; public: - pair(T f, Y s) : fst(f), snd(s) {} + pair(T f, Y s) {} }; template @@ -68,6 +28,10 @@ class map { map(std::initializer_list>, int a = 4, int b = 5) {} }; +} // namespace std + + +// Classes to test different levels of nestings and conversions. template class Contained { public: @@ -80,11 +44,22 @@ class A { A(std::initializer_list >, int) {} }; -int main() { - map mOk ={pair{5, 'a'}, {6, 'b'}, {7, 'c'}}; - map mNarrow ={pair{5, 'a'}, {6.0f, 'b'}, {7, 'c'}}; // expected-error {{type 'float' cannot be narrowed to 'int' in initializer list}} // expected-note {{insert an explicit cast to silence this issue}} - A aOk = {{Contained{5, 'c'}, {5, 'c'}}, 5}; - A aNarrowNested = {{Contained{5, 'c'}, {5.0f, 'c'}}, 5}; // expected-error {{type 'float' cannot be narrowed to 'int' in initializer list}} // expected-note {{insert an explicit cast to silence this issue}} - A aNarrow = {{Contained{5, 'c'}, {5, 'c'}}, 5.0f}; // expected-error {{type 'float' cannot be narrowed to 'int' in initializer list}} // expected-note {{insert an explicit cast to silence this issue}} +// This is the almost the exact code that was in issue #62925. +void testOneLevelNesting() { + std::map mOk = {std::pair{5, 'a'}, {6, 'b'}, {7, 'c'}}; + + // Verify that narrowing conversion is disabled in the first level of nesting. + std::map mNarrow = {std::pair{5, 'a'}, {6.0f, 'b'}, {7, 'c'}}; // expected-error {{type 'float' cannot be narrowed to 'int' in initializer list}} // expected-note {{insert an explicit cast to silence this issue}} +} + +void testMultipleLevelNesting() { + A aOk = {{Contained{5, 'c'}, {5, 'c'}}, 5}; + + // Verify that narrowing conversion is disabled when it is not in a nested + // in another std::initializer_list, but it happens in the most outer one. + A aNarrowNested = {{Contained{5, 'c'}, {5.0f, 'c'}}, 5}; // expected-error {{type 'float' cannot be narrowed to 'int' in initializer list}} // expected-note {{insert an explicit cast to silence this issue}} + + // Verify that narrowing conversion is disabled in the first level of nesting. + A aNarrow = {{Contained{5, 'c'}, {5, 'c'}}, 5.0f}; // expected-error {{type 'float' cannot be narrowed to 'int' in initializer list}} // expected-note {{insert an explicit cast to silence this issue}} } From 307615536e00d6fd9f679e8d3cfc9050fc51bf87 Mon Sep 17 00:00:00 2001 From: Gabor Spaits Date: Tue, 18 Jun 2024 12:43:40 +0200 Subject: [PATCH 3/5] Simplify test --- .../SemaCXX/ctad-copy-init-list-narrowing.cpp | 30 ++++--------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp b/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp index a2de4cf8a2f900..c58a204aee2545 100644 --- a/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp +++ b/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp @@ -11,11 +11,7 @@ namespace std { initializer_list(const E *p, size_t n) : p(p), n(n) {} }; - struct string { - string(const char *); - }; - - // Classes to use to reproduce the exact scenario present in 62925. +// Classes to use to reproduce the exact scenario present in #62925. template class pair{ public: @@ -25,26 +21,12 @@ class pair{ template class map { public: - map(std::initializer_list>, int a = 4, int b = 5) {} + map(std::initializer_list>) {} + map(std::initializer_list>, int a) {} }; } // namespace std - -// Classes to test different levels of nestings and conversions. -template -class Contained { - public: - Contained(T, Y) {} -}; - -template -class A { - public: - A(std::initializer_list >, int) {} -}; - - // This is the almost the exact code that was in issue #62925. void testOneLevelNesting() { std::map mOk = {std::pair{5, 'a'}, {6, 'b'}, {7, 'c'}}; @@ -54,12 +36,12 @@ void testOneLevelNesting() { } void testMultipleLevelNesting() { - A aOk = {{Contained{5, 'c'}, {5, 'c'}}, 5}; + std::map aOk = {{std::pair{5, 'c'}, {5, 'c'}}, 5}; // Verify that narrowing conversion is disabled when it is not in a nested // in another std::initializer_list, but it happens in the most outer one. - A aNarrowNested = {{Contained{5, 'c'}, {5.0f, 'c'}}, 5}; // expected-error {{type 'float' cannot be narrowed to 'int' in initializer list}} // expected-note {{insert an explicit cast to silence this issue}} + std::map aNarrowNested = {{std::pair{5, 'c'}, {5.0f, 'c'}}, 5}; // expected-error {{type 'float' cannot be narrowed to 'int' in initializer list}} // expected-note {{insert an explicit cast to silence this issue}} // Verify that narrowing conversion is disabled in the first level of nesting. - A aNarrow = {{Contained{5, 'c'}, {5, 'c'}}, 5.0f}; // expected-error {{type 'float' cannot be narrowed to 'int' in initializer list}} // expected-note {{insert an explicit cast to silence this issue}} + std::map aNarrow = {{std::pair{5, 'c'}, {5, 'c'}}, 5.0f}; // expected-error {{type 'float' cannot be narrowed to 'int' in initializer list}} // expected-note {{insert an explicit cast to silence this issue}} } From 44b6f2c840f91e39b1f59f62446b0f762f30bf2f Mon Sep 17 00:00:00 2001 From: Gabor Spaits Date: Tue, 18 Jun 2024 12:46:13 +0200 Subject: [PATCH 4/5] Format by hand --- .../SemaCXX/ctad-copy-init-list-narrowing.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp b/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp index c58a204aee2545..368857c83a88e7 100644 --- a/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp +++ b/clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp @@ -1,19 +1,18 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused-value -std=c++20 %s namespace std { - typedef decltype(sizeof(int)) size_t; +typedef decltype(sizeof(int)) size_t; - template - struct initializer_list - { - const E *p; - size_t n; - initializer_list(const E *p, size_t n) : p(p), n(n) {} - }; +template +struct initializer_list { + const E *p; + size_t n; + initializer_list(const E *p, size_t n) : p(p), n(n) {} +}; // Classes to use to reproduce the exact scenario present in #62925. template -class pair{ +class pair { public: pair(T f, Y s) {} }; From 026536acb91ed56f19202d0bb57c4b9be23c0380 Mon Sep 17 00:00:00 2001 From: Gabor Spaits Date: Tue, 18 Jun 2024 15:40:52 +0200 Subject: [PATCH 5/5] Add release notes --- clang/docs/ReleaseNotes.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index cf1ba02cbc4b2f..3d46f1ac3ff5f5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -199,6 +199,12 @@ C++20 Feature Support to update the ``__cpp_concepts`` macro to `202002L`. This enables ```` from libstdc++ to work correctly with Clang. +- User defined constructors are allowed for copy-list-initialization with CTAD. + The example code for deduction guides for std::map in + (`cppreference `_) + will now work. + (#GH62925). + C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^