llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Gábor Spaits (spaits) <details> <summary>Changes</summary> This PR would solve issue #<!-- -->62925. The following code: ```cpp #include <map> int main() { std::map m1 = {std::pair{"foo", 2}, {"bar", 3}}; // guide #<!-- -->2 std::map m2(m1.begin(), m1.end()); // guide #<!-- -->1 } ``` Is rejected by clang, but accepted by both gcc and msvc: https://godbolt.org/z/6v4fvabb5 . So basically CTAD with copy-list-initialization is rejected. Note that this exact code is also used in a cppreference article: https://en.cppreference.com/w/cpp/container/map/deduction_guides I did some debugging and found out, that the reason why this does not work in clang is that, in the function `Sema::DeduceTemplateSpecializationFromInitializer` we explicitly suppress user conversion, if the initialization type is a copy initialization. I checked the C++11 and C++20 standard drafts to see whether suppressing user conversion is the correct thing to do for user conversions. Based on the standard I don't think that it is correct. ``` 13.3.1.4 Copy-initialization of class by user-defined conversion [over.match.copy] Under the conditions specified in 8.5, as part of a copy-initialization of an object of class type, a user-defined conversion can be invoked to convert an initializer expression to the type of the object being initialized. Overload resolution is used to select the user-defined conversion to be invoked ``` So we could use user defined conversions according to the standard. ``` If a narrowing conversion is required to initialize any of the elements, the program is ill-formed. ``` We should not do narrowing. ``` In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed. ``` We should not use explicit constructors. Also a note: In `InitializationKind::InitKind` there is no init kind called `IK_CopyList` but there is `IK_DirectList`, should we add `IK_CopyList`? Or it is unnecessary for now? --- Full diff: https://github.com/llvm/llvm-project/pull/94752.diff 3 Files Affected: - (modified) clang/include/clang/Sema/Initialization.h (+1-1) - (modified) clang/lib/Sema/SemaInit.cpp (+2-4) - (added) clang/test/SemaCXX/ctad-copy-init-list-narrowing.cpp (+90) ``````````diff diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h index f443e327eaf32..4b876db436b48 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 ed8b226a6b39f..211b6887befa3 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<Expr *, 8> 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 0000000000000..21b1137158d5a --- /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 _E> + 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<class _Tp> + constexpr const _Tp* + begin(initializer_list<_Tp> __ils) noexcept + { return __ils.begin(); } + + template<class _Tp> + constexpr const _Tp* + end(initializer_list<_Tp> __ils) noexcept + { return __ils.end(); } +} + +template<class T, class Y> +class pair{ + private: + T fst; + Y snd; + public: + pair(T f, Y s) : fst(f), snd(s) {} +}; + +template<class T, class Y> +class map { + public: + map(std::initializer_list<pair<T, Y>>, int a = 4, int b = 5) {} +}; + +template<class T, class Y> +class Contained { + public: + Contained(T, Y) {} +}; + +template<class T, class Y> +class A { + public: + A(std::initializer_list<Contained<T, Y> >, 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}} +} `````````` </details> https://github.com/llvm/llvm-project/pull/94752 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits