forked from rust-lang/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Sema][CTAD] Allow user defined conversion for copy-list-initializati…
…on (llvm#94752) Fixes llvm#62925. The following code: ```cpp #include <map> int main() { std::map m1 = {std::pair{"foo", 2}, {"bar", 3}}; // guide rust-lang#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 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.
- Loading branch information
Showing
4 changed files
with
55 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused-value -std=c++20 %s | ||
|
||
namespace std { | ||
typedef decltype(sizeof(int)) size_t; | ||
|
||
template <typename E> | ||
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 T, class Y> | ||
class pair { | ||
public: | ||
pair(T f, Y s) {} | ||
}; | ||
|
||
template<class T, class Y> | ||
class map { | ||
public: | ||
map(std::initializer_list<pair<T, Y>>) {} | ||
map(std::initializer_list<pair<T, Y>>, int a) {} | ||
}; | ||
|
||
} // namespace std | ||
|
||
// 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() { | ||
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. | ||
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. | ||
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}} | ||
} |