From 384edff442d330d811982e5c9f3b7f5799b1e1d0 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Thu, 12 Mar 2026 02:23:10 +0900 Subject: [PATCH 01/17] Add `is_convertible_without_narrowing` --- include/iris/type_traits.hpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/iris/type_traits.hpp b/include/iris/type_traits.hpp index 7044a2c..7b6634d 100644 --- a/include/iris/type_traits.hpp +++ b/include/iris/type_traits.hpp @@ -283,6 +283,24 @@ struct aggregate_initialize_resolution< template struct aggregate_initialize_resolution : detail::aggregate_initialize_resolution {}; +// P0870R7: is_convertible_without_narrowing +// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p0870r7.html +template +struct is_convertible_without_narrowing + : std::false_type +{}; + +template + requires + std::is_convertible_v && + requires { std::type_identity_t{std::declval()}; } +struct is_convertible_without_narrowing + : std::true_type +{}; + +template +inline constexpr bool is_convertible_without_narrowing_v = is_convertible_without_narrowing::value; + } // iris #endif From 37bf9534bfb22625885170abc5c7b75393471376 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 00:41:21 +0900 Subject: [PATCH 02/17] Add test --- test/type_traits.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/test/type_traits.cpp b/test/type_traits.cpp index 59eae66..0847392 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -19,9 +19,75 @@ struct n_tuple; template struct n_list; +struct convertible_from_int { + convertible_from_int(int); +}; + +struct not_convertible_from_int {}; + +enum class scoped_enum {}; +enum unscoped_enum {}; + } // anonymous +TEST_CASE("is_convertible_without_narrowing") +{ + // Same type: always OK + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + + // Widening integer conversions: OK + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + + // Narrowing integer conversions: rejected + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Floating-point widening: OK + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + + // Floating-point narrowing: rejected + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Integer to floating-point: narrowing + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Floating-point to integer: narrowing + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Signed/unsigned mismatch + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Bool conversions + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // User-defined implicit conversion: OK (no narrowing concern) + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + + // Not convertible at all + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Enum conversions + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); +} + + TEST_CASE("specialization_of") { STATIC_CHECK(iris::is_ttp_specialization_of_v, tuple>); From 6443dfadd32d1dc275eef2543e48ba9939e79dcf Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 01:58:00 +0900 Subject: [PATCH 03/17] Styling fix --- test/type_traits.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/type_traits.cpp b/test/type_traits.cpp index 0847392..7ff2ccd 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -19,7 +19,8 @@ struct n_tuple; template struct n_list; -struct convertible_from_int { +struct convertible_from_int +{ convertible_from_int(int); }; From 65e4638af84cab7b92f774399be20229c379c5d4 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 02:19:56 +0900 Subject: [PATCH 04/17] Fix for MSVC --- test/type_traits.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/type_traits.cpp b/test/type_traits.cpp index 7ff2ccd..d1cbd24 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -42,13 +42,13 @@ TEST_CASE("is_convertible_without_narrowing") STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(iris::is_convertible_without_narrowing_v); - STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); // Narrowing integer conversions: rejected STATIC_CHECK(!iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); - STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); // Floating-point widening: OK STATIC_CHECK(iris::is_convertible_without_narrowing_v); From 9ba01d357dc6152332d38a69e544e87057b65f29 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 02:44:31 +0900 Subject: [PATCH 05/17] Refactor `aggregate_initialize_overload` using `is_convertible_without_narrowing_v` --- include/iris/type_traits.hpp | 46 ++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/include/iris/type_traits.hpp b/include/iris/type_traits.hpp index 7b6634d..d509395 100644 --- a/include/iris/type_traits.hpp +++ b/include/iris/type_traits.hpp @@ -222,6 +222,25 @@ template constexpr bool is_trivially_swappable_v = is_trivially_swappable::value; +// P0870R7: is_convertible_without_narrowing +// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p0870r7.html +template +struct is_convertible_without_narrowing + : std::false_type +{}; + +template + requires + std::is_convertible_v && + requires { std::type_identity_t{std::declval()}; } +struct is_convertible_without_narrowing + : std::true_type +{}; + +template +inline constexpr bool is_convertible_without_narrowing_v = is_convertible_without_narrowing::value; + + namespace detail { template @@ -231,19 +250,12 @@ struct aggregate_initialize_tag using type = Ti; }; -// This version works better than MSVC's, does not break IntelliSense or ReSharper template struct aggregate_initialize_overload { - using TiA = Ti[]; - - // https://eel.is/c++draft/dcl.init.general#14 - // https://eel.is/c++draft/dcl.init.list#3.4 - // https://eel.is/c++draft/dcl.init.aggr#3 - template auto operator()(Ti, T&&) -> aggregate_initialize_tag - requires requires(T&& t) { { TiA{std::forward(t)} }; } // emulate `Ti x[] = {std::forward(t)};` + requires is_convertible_without_narrowing_v { return {}; // silence MSVC warning } @@ -283,24 +295,6 @@ struct aggregate_initialize_resolution< template struct aggregate_initialize_resolution : detail::aggregate_initialize_resolution {}; -// P0870R7: is_convertible_without_narrowing -// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p0870r7.html -template -struct is_convertible_without_narrowing - : std::false_type -{}; - -template - requires - std::is_convertible_v && - requires { std::type_identity_t{std::declval()}; } -struct is_convertible_without_narrowing - : std::true_type -{}; - -template -inline constexpr bool is_convertible_without_narrowing_v = is_convertible_without_narrowing::value; - } // iris #endif From 0b16dda738c885cfcd24e02afb1cbcea4721ad4b Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 02:50:30 +0900 Subject: [PATCH 06/17] Add edge cases --- test/type_traits.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/type_traits.cpp b/test/type_traits.cpp index d1cbd24..e66d9e3 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -86,6 +86,37 @@ TEST_CASE("is_convertible_without_narrowing") STATIC_CHECK(!iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); STATIC_CHECK(iris::is_convertible_without_narrowing_v); + + // `To[]{from}` works, but not convertible + { + { + struct S { + union { + int x; + float y; + } u; + }; + [[maybe_unused]] S s{42}; + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + } + { + struct S { + int x[1]; + }; + [[maybe_unused]] S s{42}; + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + } + { + struct S { + struct { + int x; + } inner; + }; + [[maybe_unused]] S s{42}; + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + } + } + } From 1a0b140a878a5002f4a2520eb5834b97c11a4f3f Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 03:24:55 +0900 Subject: [PATCH 07/17] Add `is_assignable_without_narrowing` --- include/iris/type_traits.hpp | 18 ++++++++++++++++++ test/type_traits.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/include/iris/type_traits.hpp b/include/iris/type_traits.hpp index d509395..5b37eb3 100644 --- a/include/iris/type_traits.hpp +++ b/include/iris/type_traits.hpp @@ -241,6 +241,24 @@ template inline constexpr bool is_convertible_without_narrowing_v = is_convertible_without_narrowing::value; +// is_assignable_without_narrowing +// +// True when `Dest = Source` is valid AND does not involve a narrowing conversion. +// For non-arithmetic Dest types, narrowing is not checked — we trust that the +// user-defined conversion handles the assignment correctly. +template +struct is_assignable_without_narrowing + : std::bool_constant< + std::is_assignable_v && + (!std::is_arithmetic_v> || + is_convertible_without_narrowing_v, std::remove_reference_t>) + > +{}; + +template +inline constexpr bool is_assignable_without_narrowing_v = is_assignable_without_narrowing::value; + + namespace detail { template diff --git a/test/type_traits.cpp b/test/type_traits.cpp index e66d9e3..601c70f 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -120,6 +120,38 @@ TEST_CASE("is_convertible_without_narrowing") } +TEST_CASE("is_assignable_without_narrowing") +{ + // Non-narrowing: same type + STATIC_CHECK(iris::is_assignable_without_narrowing_v); + STATIC_CHECK(iris::is_assignable_without_narrowing_v); + + // Non-narrowing: widening + STATIC_CHECK(iris::is_assignable_without_narrowing_v); + STATIC_CHECK(iris::is_assignable_without_narrowing_v); + STATIC_CHECK(iris::is_assignable_without_narrowing_v); + + // Narrowing: lossy conversions + STATIC_CHECK(!iris::is_assignable_without_narrowing_v); + STATIC_CHECK(!iris::is_assignable_without_narrowing_v); + STATIC_CHECK(!iris::is_assignable_without_narrowing_v); + STATIC_CHECK(!iris::is_assignable_without_narrowing_v); + STATIC_CHECK(!iris::is_assignable_without_narrowing_v); + + // Signed/unsigned mismatch + STATIC_CHECK(!iris::is_assignable_without_narrowing_v); + STATIC_CHECK(!iris::is_assignable_without_narrowing_v); + + // Non-arithmetic dest: narrowing not checked (trusts user-defined conversion) + STATIC_CHECK(iris::is_assignable_without_narrowing_v); + STATIC_CHECK(iris::is_assignable_without_narrowing_v); + + // Not assignable at all + STATIC_CHECK(!iris::is_assignable_without_narrowing_v); + STATIC_CHECK(!iris::is_assignable_without_narrowing_v); +} + + TEST_CASE("specialization_of") { STATIC_CHECK(iris::is_ttp_specialization_of_v, tuple>); From 1a35a620ed75bfa590f3da5ccb6be3abd9ca063a Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 04:45:15 +0900 Subject: [PATCH 08/17] Add clarification --- include/iris/type_traits.hpp | 9 +++++++-- test/type_traits.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/include/iris/type_traits.hpp b/include/iris/type_traits.hpp index 5b37eb3..204defa 100644 --- a/include/iris/type_traits.hpp +++ b/include/iris/type_traits.hpp @@ -244,8 +244,13 @@ inline constexpr bool is_convertible_without_narrowing_v = is_convertible_withou // is_assignable_without_narrowing // // True when `Dest = Source` is valid AND does not involve a narrowing conversion. -// For non-arithmetic Dest types, narrowing is not checked — we trust that the -// user-defined conversion handles the assignment correctly. +// +// The arithmetic guard is intentional: narrowing conversions are only defined for +// arithmetic types ([dcl.init.list]), and the underlying `is_convertible_without_narrowing` +// uses brace-initialization (`To[]{from}`) which may select a different construction +// path than what `is_assignable` tests (e.g. initializer_list hijacking). Restricting +// the narrowing check to arithmetic Dest avoids false rejections for non-arithmetic +// types where the brace-init semantics diverge from assignment semantics. template struct is_assignable_without_narrowing : std::bool_constant< diff --git a/test/type_traits.cpp b/test/type_traits.cpp index 601c70f..d7b5ec6 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -26,6 +26,23 @@ struct convertible_from_int struct not_convertible_from_int {}; +struct explicit_from_int +{ + explicit explicit_from_int() = default; + explicit explicit_from_int(int); + explicit_from_int& operator=(int) { return *this; } +}; + +// "Broken" implementation: is_assignable_without_narrowing without the arithmetic guard. +// This demonstrates why the guard is needed. +template +struct broken_is_assignable_without_narrowing + : std::bool_constant< + std::is_assignable_v && + iris::is_convertible_without_narrowing_v, std::remove_reference_t> + > +{}; + enum class scoped_enum {}; enum unscoped_enum {}; @@ -149,6 +166,18 @@ TEST_CASE("is_assignable_without_narrowing") // Not assignable at all STATIC_CHECK(!iris::is_assignable_without_narrowing_v); STATIC_CHECK(!iris::is_assignable_without_narrowing_v); + + // Non-arithmetic dest with explicit ctor + assignment operator. + // Without the arithmetic guard, is_assignable_without_narrowing would + // incorrectly return false because is_convertible_without_narrowing + // rejects explicit conversions (brace-init uses copy-list-init). + STATIC_CHECK(std::is_assignable_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_assignable_without_narrowing_v); + + // Verify the "false" implementation (without arithmetic guard) breaks here: + // it incorrectly rejects this valid, non-narrowing assignment. + STATIC_CHECK(!broken_is_assignable_without_narrowing::value); } From 8a4f0528f463497fd7fb2a75f11e9ebe63ac3db2 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 05:12:30 +0900 Subject: [PATCH 09/17] Revert `is_assignable_without_narrowing` related change --- include/iris/type_traits.hpp | 23 ------------------- test/type_traits.cpp | 44 ------------------------------------ 2 files changed, 67 deletions(-) diff --git a/include/iris/type_traits.hpp b/include/iris/type_traits.hpp index 204defa..d509395 100644 --- a/include/iris/type_traits.hpp +++ b/include/iris/type_traits.hpp @@ -241,29 +241,6 @@ template inline constexpr bool is_convertible_without_narrowing_v = is_convertible_without_narrowing::value; -// is_assignable_without_narrowing -// -// True when `Dest = Source` is valid AND does not involve a narrowing conversion. -// -// The arithmetic guard is intentional: narrowing conversions are only defined for -// arithmetic types ([dcl.init.list]), and the underlying `is_convertible_without_narrowing` -// uses brace-initialization (`To[]{from}`) which may select a different construction -// path than what `is_assignable` tests (e.g. initializer_list hijacking). Restricting -// the narrowing check to arithmetic Dest avoids false rejections for non-arithmetic -// types where the brace-init semantics diverge from assignment semantics. -template -struct is_assignable_without_narrowing - : std::bool_constant< - std::is_assignable_v && - (!std::is_arithmetic_v> || - is_convertible_without_narrowing_v, std::remove_reference_t>) - > -{}; - -template -inline constexpr bool is_assignable_without_narrowing_v = is_assignable_without_narrowing::value; - - namespace detail { template diff --git a/test/type_traits.cpp b/test/type_traits.cpp index d7b5ec6..3c4ccb7 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -137,50 +137,6 @@ TEST_CASE("is_convertible_without_narrowing") } -TEST_CASE("is_assignable_without_narrowing") -{ - // Non-narrowing: same type - STATIC_CHECK(iris::is_assignable_without_narrowing_v); - STATIC_CHECK(iris::is_assignable_without_narrowing_v); - - // Non-narrowing: widening - STATIC_CHECK(iris::is_assignable_without_narrowing_v); - STATIC_CHECK(iris::is_assignable_without_narrowing_v); - STATIC_CHECK(iris::is_assignable_without_narrowing_v); - - // Narrowing: lossy conversions - STATIC_CHECK(!iris::is_assignable_without_narrowing_v); - STATIC_CHECK(!iris::is_assignable_without_narrowing_v); - STATIC_CHECK(!iris::is_assignable_without_narrowing_v); - STATIC_CHECK(!iris::is_assignable_without_narrowing_v); - STATIC_CHECK(!iris::is_assignable_without_narrowing_v); - - // Signed/unsigned mismatch - STATIC_CHECK(!iris::is_assignable_without_narrowing_v); - STATIC_CHECK(!iris::is_assignable_without_narrowing_v); - - // Non-arithmetic dest: narrowing not checked (trusts user-defined conversion) - STATIC_CHECK(iris::is_assignable_without_narrowing_v); - STATIC_CHECK(iris::is_assignable_without_narrowing_v); - - // Not assignable at all - STATIC_CHECK(!iris::is_assignable_without_narrowing_v); - STATIC_CHECK(!iris::is_assignable_without_narrowing_v); - - // Non-arithmetic dest with explicit ctor + assignment operator. - // Without the arithmetic guard, is_assignable_without_narrowing would - // incorrectly return false because is_convertible_without_narrowing - // rejects explicit conversions (brace-init uses copy-list-init). - STATIC_CHECK(std::is_assignable_v); - STATIC_CHECK(!iris::is_convertible_without_narrowing_v); - STATIC_CHECK(iris::is_assignable_without_narrowing_v); - - // Verify the "false" implementation (without arithmetic guard) breaks here: - // it incorrectly rejects this valid, non-narrowing assignment. - STATIC_CHECK(!broken_is_assignable_without_narrowing::value); -} - - TEST_CASE("specialization_of") { STATIC_CHECK(iris::is_ttp_specialization_of_v, tuple>); From b9c6216d8db43e3b8a2152534a934b6571ab617e Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 05:28:14 +0900 Subject: [PATCH 10/17] Cleanup --- include/iris/type_traits.hpp | 5 ++++- test/type_traits.cpp | 10 ---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/include/iris/type_traits.hpp b/include/iris/type_traits.hpp index d509395..f4d5640 100644 --- a/include/iris/type_traits.hpp +++ b/include/iris/type_traits.hpp @@ -10,6 +10,7 @@ #include +#include #include #include @@ -232,7 +233,9 @@ struct is_convertible_without_narrowing template requires std::is_convertible_v && - requires { std::type_identity_t{std::declval()}; } + requires (From&& x) { + { std::type_identity_t{std::forward(x)} } -> std::same_as; + } struct is_convertible_without_narrowing : std::true_type {}; diff --git a/test/type_traits.cpp b/test/type_traits.cpp index 3c4ccb7..149698e 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -33,16 +33,6 @@ struct explicit_from_int explicit_from_int& operator=(int) { return *this; } }; -// "Broken" implementation: is_assignable_without_narrowing without the arithmetic guard. -// This demonstrates why the guard is needed. -template -struct broken_is_assignable_without_narrowing - : std::bool_constant< - std::is_assignable_v && - iris::is_convertible_without_narrowing_v, std::remove_reference_t> - > -{}; - enum class scoped_enum {}; enum unscoped_enum {}; From cdec1b173a675a3659371d0b2dcd95494a4d9f18 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 05:53:29 +0900 Subject: [PATCH 11/17] Refine `is_convertible_without_narrowing` --- include/iris/type_traits.hpp | 24 ++- test/type_traits.cpp | 375 +++++++++++++++++++++++++++++++---- 2 files changed, 353 insertions(+), 46 deletions(-) diff --git a/include/iris/type_traits.hpp b/include/iris/type_traits.hpp index f4d5640..97a03c6 100644 --- a/include/iris/type_traits.hpp +++ b/include/iris/type_traits.hpp @@ -225,21 +225,39 @@ constexpr bool is_trivially_swappable_v = is_trivially_swappable::value; // P0870R7: is_convertible_without_narrowing // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p0870r7.html +namespace detail { + template -struct is_convertible_without_narrowing +struct is_convertible_without_narrowing_impl : std::false_type {}; +// void to void "conversion" is valid since `is_convertible` is defined +// as "returning `From` is valid for function whose return type is `To`?" +template<> +struct is_convertible_without_narrowing_impl + : std::true_type +{}; + template requires - std::is_convertible_v && requires (From&& x) { { std::type_identity_t{std::forward(x)} } -> std::same_as; } -struct is_convertible_without_narrowing +struct is_convertible_without_narrowing_impl : std::true_type {}; +} // namespace detail + +template +struct is_convertible_without_narrowing + : std::conjunction< + std::is_convertible, + detail::is_convertible_without_narrowing_impl + > +{}; + template inline constexpr bool is_convertible_without_narrowing_v = is_convertible_without_narrowing::value; diff --git a/test/type_traits.cpp b/test/type_traits.cpp index 149698e..d967f25 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -35,95 +35,384 @@ struct explicit_from_int enum class scoped_enum {}; enum unscoped_enum {}; +enum class scoped_enum_uint8 : unsigned char {}; +enum unscoped_enum_short : short {}; + +struct base {}; +struct derived : base {}; + +struct implicit_conversion_op +{ + operator int() const; +}; + +struct explicit_conversion_op +{ + explicit operator int() const; +}; + +struct abstract_class +{ + virtual void f() = 0; +}; + +struct multi_arg_implicit +{ + multi_arg_implicit(int, double); +}; + +struct has_initializer_list_ctor +{ + has_initializer_list_ctor(std::initializer_list); +}; + +struct member_ptr_test +{ + int member; + void func(); +}; } // anonymous -TEST_CASE("is_convertible_without_narrowing") +TEST_CASE("is_convertible_without_narrowing: same type identity") { - // Same type: always OK STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); +} - // Widening integer conversions: OK - STATIC_CHECK(iris::is_convertible_without_narrowing_v); +TEST_CASE("is_convertible_without_narrowing: integer widening") +{ STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); +} - // Narrowing integer conversions: rejected +TEST_CASE("is_convertible_without_narrowing: integer narrowing") +{ STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} - // Floating-point widening: OK +TEST_CASE("is_convertible_without_narrowing: signed/unsigned mismatch") +{ + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} + +TEST_CASE("is_convertible_without_narrowing: floating-point widening") +{ STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(iris::is_convertible_without_narrowing_v); +} - // Floating-point narrowing: rejected +TEST_CASE("is_convertible_without_narrowing: floating-point narrowing") +{ STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} +TEST_CASE("is_convertible_without_narrowing: integer/floating-point cross") +{ // Integer to floating-point: narrowing STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); // Floating-point to integer: narrowing STATIC_CHECK(!iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} - // Signed/unsigned mismatch - STATIC_CHECK(!iris::is_convertible_without_narrowing_v); - STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +TEST_CASE("is_convertible_without_narrowing: bool") +{ + STATIC_CHECK(iris::is_convertible_without_narrowing_v); - // Bool conversions + // bool to integer: widening (bool is an integer type with rank < int) STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + + // Integer/float to bool: narrowing STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} + +TEST_CASE("is_convertible_without_narrowing: char types") +{ + // char8_t, char16_t, char32_t are distinct types with specific widths + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // wchar_t cross: platform-dependent (wchar_t width varies) + // On platforms where sizeof(wchar_t) == sizeof(int), these may be non-narrowing. + + // char to int: widening + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + + // int to char: narrowing + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} - // User-defined implicit conversion: OK (no narrowing concern) +TEST_CASE("is_convertible_without_narrowing: enum types") +{ + // Scoped enums: not implicitly convertible to/from anything + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Unscoped enums: implicitly convertible to integer types + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Unscoped enum with fixed underlying type (short) + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} + +TEST_CASE("is_convertible_without_narrowing: pointer types") +{ + // Same pointer type + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + + // Derived-to-base pointer conversion + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // T* to void* + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Adding const via pointer + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // nullptr_t + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // pointer to bool: narrowing (all pointers/nullptr to bool is narrowing) + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Pointer to/from arithmetic: not convertible + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Pointer-to-member + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Member function pointer + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} + +TEST_CASE("is_convertible_without_narrowing: class types with implicit ctor") +{ + // Implicit converting constructor STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); // Not convertible at all STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} - // Enum conversions - STATIC_CHECK(!iris::is_convertible_without_narrowing_v); - STATIC_CHECK(!iris::is_convertible_without_narrowing_v); - STATIC_CHECK(iris::is_convertible_without_narrowing_v); +TEST_CASE("is_convertible_without_narrowing: class types with explicit ctor") +{ + // Explicit ctor: is_convertible is false, so trait is false + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} + +TEST_CASE("is_convertible_without_narrowing: class types with conversion operator") +{ + // Implicit conversion operator + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Explicit conversion operator: is_convertible is false + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} + +TEST_CASE("is_convertible_without_narrowing: class types inheritance") +{ + // Derived to base (by value): implicitly convertible + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + // Base to derived: not implicitly convertible + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Derived& to base: implicitly convertible + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); +} - // `To[]{from}` works, but not convertible +TEST_CASE("is_convertible_without_narrowing: aggregate types") +{ + // Aggregates with brace-init but no implicit conversion + { + struct S { + union { + int x; + float y; + } u; + }; + [[maybe_unused]] S s{42}; + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + } + { + struct S { + int x[1]; + }; + [[maybe_unused]] S s{42}; + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + } + { + struct S { + struct { + int x; + } inner; + }; + [[maybe_unused]] S s{42}; + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + } { - { - struct S { - union { - int x; - float y; - } u; - }; - [[maybe_unused]] S s{42}; - STATIC_CHECK(!iris::is_convertible_without_narrowing_v); - } - { - struct S { - int x[1]; - }; - [[maybe_unused]] S s{42}; - STATIC_CHECK(!iris::is_convertible_without_narrowing_v); - } - { - struct S { - struct { - int x; - } inner; - }; - [[maybe_unused]] S s{42}; - STATIC_CHECK(!iris::is_convertible_without_narrowing_v); - } + struct S { + int x; + double y; + }; + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); } +} + +TEST_CASE("is_convertible_without_narrowing: void") +{ + // void to void: is_convertible_v is true per the standard. + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + // void to/from anything else: not convertible + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} + +TEST_CASE("is_convertible_without_narrowing: cv-qualified types") +{ + // const arithmetic: same narrowing rules + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); +} + +TEST_CASE("is_convertible_without_narrowing: reference types as From") +{ + // Lvalue reference + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Rvalue reference + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} + +TEST_CASE("is_convertible_without_narrowing: function pointers") +{ + using fp = void(*)(); + using fp2 = int(*)(double); + + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Function pointer to bool: narrowing + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // Function pointer to void*: not convertible (standard C++) + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); +} + +TEST_CASE("is_convertible_without_narrowing: array and function types") +{ + // Array types as From decay to pointers + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + + // char[] to string + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + + // Function type decays to function pointer + using fn = void(); + using fnp = void(*)(); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); } From daab5d19ccf743e35093df3f1930f5d8ab937593 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 06:01:06 +0900 Subject: [PATCH 12/17] MSVC workaround --- test/type_traits.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/type_traits.cpp b/test/type_traits.cpp index d967f25..f7b41b2 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -395,8 +395,8 @@ TEST_CASE("is_convertible_without_narrowing: function pointers") // Function pointer to bool: narrowing STATIC_CHECK(!iris::is_convertible_without_narrowing_v); - // Function pointer to void*: not convertible (standard C++) - STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + // Function pointer to void*: not allowe per standard, but MSVC accepts this conversion + // STATIC_CHECK(!iris::is_convertible_without_narrowing_v); } TEST_CASE("is_convertible_without_narrowing: array and function types") From 1380b7e5def1782c96bba0af1b912180bee499b3 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 16:02:23 +0900 Subject: [PATCH 13/17] Rename "aggregate_initialize" to "no_narrowing" --- include/iris/rvariant/rvariant.hpp | 14 +++++++------- include/iris/type_traits.hpp | 23 ++++++++++++----------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/include/iris/rvariant/rvariant.hpp b/include/iris/rvariant/rvariant.hpp index b30d5ba..9d05b3b 100644 --- a/include/iris/rvariant/rvariant.hpp +++ b/include/iris/rvariant/rvariant.hpp @@ -620,10 +620,10 @@ class rvariant : private detail::rvariant_base_t (!std::is_same_v, rvariant>) && (!is_ttp_specialization_of_v, std::in_place_type_t>) && (!is_ctp_specialization_of_v, std::in_place_index_t>) && - std::is_constructible_v::type, T> + std::is_constructible_v::type, T> constexpr /* not explicit */ rvariant(T&& t) - noexcept(std::is_nothrow_constructible_v::type, T>) - : base_type(std::in_place_index::index>, std::forward(t)) + noexcept(std::is_nothrow_constructible_v::type, T>) + : base_type(std::in_place_index::index>, std::forward(t)) {} IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN @@ -632,12 +632,12 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN template requires (!std::is_same_v, rvariant>) && - detail::variant_assignable::type, T>::value + detail::variant_assignable::type, T>::value constexpr rvariant& operator=(T&& t) - noexcept(detail::variant_nothrow_assignable::type, T>::value) + noexcept(detail::variant_nothrow_assignable::type, T>::value) { - using Tj = aggregate_initialize_resolution::type; // either plain type or wrapped with recursive_wrapper - constexpr std::size_t j = aggregate_initialize_resolution::index; + using Tj = no_narrowing_resolution::type; // either plain type or wrapped with recursive_wrapper + constexpr std::size_t j = no_narrowing_resolution::index; static_assert(j != std::variant_npos); this->raw_visit([this, &t](std::in_place_index_t, [[maybe_unused]] Ti& ti) diff --git a/include/iris/type_traits.hpp b/include/iris/type_traits.hpp index 97a03c6..407affb 100644 --- a/include/iris/type_traits.hpp +++ b/include/iris/type_traits.hpp @@ -272,10 +272,10 @@ struct aggregate_initialize_tag }; template -struct aggregate_initialize_overload +struct no_narrowing_overload { template - auto operator()(Ti, T&&) -> aggregate_initialize_tag + auto operator()(T&&) -> aggregate_initialize_tag requires is_convertible_without_narrowing_v { return {}; // silence MSVC warning @@ -283,26 +283,27 @@ struct aggregate_initialize_overload }; template -struct aggregate_initialize_fun; +struct no_narrowing_fun; // Imaginary function FUN of https://eel.is/c++draft/variant#ctor-14 template -struct aggregate_initialize_fun, Ts...> - : aggregate_initialize_overload... +struct no_narrowing_fun, Ts...> + : no_narrowing_overload... { - using aggregate_initialize_overload::operator()...; + using no_narrowing_overload::operator()...; }; template -using aggregate_initialize_fun_for = aggregate_initialize_fun, Ts...>; +using aggregate_initialize_fun_for = no_narrowing_fun, Ts...>; template -struct aggregate_initialize_resolution {}; +struct no_narrowing_resolution {}; template -struct aggregate_initialize_resolution< +struct no_narrowing_resolution< std::void_t{}(std::declval(), std::declval()))>, T, Ts... -> { +> +{ using tag = decltype(aggregate_initialize_fun_for{}(std::declval(), std::declval())); using type = tag::type; static constexpr std::size_t index = tag::index; @@ -314,7 +315,7 @@ struct aggregate_initialize_resolution< // because they would lead to unnecessarily nested instantiation for // legitimate infinite recursion errors on recursive types. template -struct aggregate_initialize_resolution : detail::aggregate_initialize_resolution {}; +struct no_narrowing_resolution : detail::no_narrowing_resolution {}; } // iris From 66ca64bb98f31c8ce5c1b7fc1a5ee00b65adc5b9 Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 16:02:35 +0900 Subject: [PATCH 14/17] Rewrite const-west to east-const --- test/type_traits.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/type_traits.cpp b/test/type_traits.cpp index f7b41b2..67e5364 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -241,12 +241,12 @@ TEST_CASE("is_convertible_without_narrowing: pointer types") STATIC_CHECK(!iris::is_convertible_without_narrowing_v); // Adding const via pointer - STATIC_CHECK(iris::is_convertible_without_narrowing_v); - STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); // nullptr_t STATIC_CHECK(iris::is_convertible_without_narrowing_v); - STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); // pointer to bool: narrowing (all pointers/nullptr to bool is narrowing) @@ -372,7 +372,7 @@ TEST_CASE("is_convertible_without_narrowing: reference types as From") { // Lvalue reference STATIC_CHECK(iris::is_convertible_without_narrowing_v); - STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); @@ -403,7 +403,7 @@ TEST_CASE("is_convertible_without_narrowing: array and function types") { // Array types as From decay to pointers STATIC_CHECK(iris::is_convertible_without_narrowing_v); - STATIC_CHECK(iris::is_convertible_without_narrowing_v); + STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); // char[] to string From de994338b3e6d5d400a67497b3c3a7fa8091667d Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 16:05:58 +0900 Subject: [PATCH 15/17] Add element-to-array test for `is_convertible_without_narrowing` --- test/type_traits.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/type_traits.cpp b/test/type_traits.cpp index 67e5364..755e492 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -406,6 +406,9 @@ TEST_CASE("is_convertible_without_narrowing: array and function types") STATIC_CHECK(iris::is_convertible_without_narrowing_v); STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + // Element types to array types + STATIC_CHECK(!iris::is_convertible_without_narrowing_v); + // char[] to string STATIC_CHECK(iris::is_convertible_without_narrowing_v); From c378972919699dc3ea4ca3bcad2d954902ed2dda Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 16:26:23 +0900 Subject: [PATCH 16/17] Revert "Rename "aggregate_initialize" to "no_narrowing"" This reverts commit 1380b7e5def1782c96bba0af1b912180bee499b3. --- include/iris/rvariant/rvariant.hpp | 14 +++++++------- include/iris/type_traits.hpp | 23 +++++++++++------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/include/iris/rvariant/rvariant.hpp b/include/iris/rvariant/rvariant.hpp index 9d05b3b..b30d5ba 100644 --- a/include/iris/rvariant/rvariant.hpp +++ b/include/iris/rvariant/rvariant.hpp @@ -620,10 +620,10 @@ class rvariant : private detail::rvariant_base_t (!std::is_same_v, rvariant>) && (!is_ttp_specialization_of_v, std::in_place_type_t>) && (!is_ctp_specialization_of_v, std::in_place_index_t>) && - std::is_constructible_v::type, T> + std::is_constructible_v::type, T> constexpr /* not explicit */ rvariant(T&& t) - noexcept(std::is_nothrow_constructible_v::type, T>) - : base_type(std::in_place_index::index>, std::forward(t)) + noexcept(std::is_nothrow_constructible_v::type, T>) + : base_type(std::in_place_index::index>, std::forward(t)) {} IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN @@ -632,12 +632,12 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN template requires (!std::is_same_v, rvariant>) && - detail::variant_assignable::type, T>::value + detail::variant_assignable::type, T>::value constexpr rvariant& operator=(T&& t) - noexcept(detail::variant_nothrow_assignable::type, T>::value) + noexcept(detail::variant_nothrow_assignable::type, T>::value) { - using Tj = no_narrowing_resolution::type; // either plain type or wrapped with recursive_wrapper - constexpr std::size_t j = no_narrowing_resolution::index; + using Tj = aggregate_initialize_resolution::type; // either plain type or wrapped with recursive_wrapper + constexpr std::size_t j = aggregate_initialize_resolution::index; static_assert(j != std::variant_npos); this->raw_visit([this, &t](std::in_place_index_t, [[maybe_unused]] Ti& ti) diff --git a/include/iris/type_traits.hpp b/include/iris/type_traits.hpp index 407affb..97a03c6 100644 --- a/include/iris/type_traits.hpp +++ b/include/iris/type_traits.hpp @@ -272,10 +272,10 @@ struct aggregate_initialize_tag }; template -struct no_narrowing_overload +struct aggregate_initialize_overload { template - auto operator()(T&&) -> aggregate_initialize_tag + auto operator()(Ti, T&&) -> aggregate_initialize_tag requires is_convertible_without_narrowing_v { return {}; // silence MSVC warning @@ -283,27 +283,26 @@ struct no_narrowing_overload }; template -struct no_narrowing_fun; +struct aggregate_initialize_fun; // Imaginary function FUN of https://eel.is/c++draft/variant#ctor-14 template -struct no_narrowing_fun, Ts...> - : no_narrowing_overload... +struct aggregate_initialize_fun, Ts...> + : aggregate_initialize_overload... { - using no_narrowing_overload::operator()...; + using aggregate_initialize_overload::operator()...; }; template -using aggregate_initialize_fun_for = no_narrowing_fun, Ts...>; +using aggregate_initialize_fun_for = aggregate_initialize_fun, Ts...>; template -struct no_narrowing_resolution {}; +struct aggregate_initialize_resolution {}; template -struct no_narrowing_resolution< +struct aggregate_initialize_resolution< std::void_t{}(std::declval(), std::declval()))>, T, Ts... -> -{ +> { using tag = decltype(aggregate_initialize_fun_for{}(std::declval(), std::declval())); using type = tag::type; static constexpr std::size_t index = tag::index; @@ -315,7 +314,7 @@ struct no_narrowing_resolution< // because they would lead to unnecessarily nested instantiation for // legitimate infinite recursion errors on recursive types. template -struct no_narrowing_resolution : detail::no_narrowing_resolution {}; +struct aggregate_initialize_resolution : detail::aggregate_initialize_resolution {}; } // iris From 80ad201cbd5a77e8da84318e76ac8949f09c6e3b Mon Sep 17 00:00:00 2001 From: yaito3014 Date: Fri, 13 Mar 2026 16:27:15 +0900 Subject: [PATCH 17/17] Rename "aggregate_initialize" to "no_narrowing" v2 --- include/iris/rvariant/rvariant.hpp | 14 +++++++------- include/iris/type_traits.hpp | 26 +++++++++++++------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/include/iris/rvariant/rvariant.hpp b/include/iris/rvariant/rvariant.hpp index b30d5ba..9d05b3b 100644 --- a/include/iris/rvariant/rvariant.hpp +++ b/include/iris/rvariant/rvariant.hpp @@ -620,10 +620,10 @@ class rvariant : private detail::rvariant_base_t (!std::is_same_v, rvariant>) && (!is_ttp_specialization_of_v, std::in_place_type_t>) && (!is_ctp_specialization_of_v, std::in_place_index_t>) && - std::is_constructible_v::type, T> + std::is_constructible_v::type, T> constexpr /* not explicit */ rvariant(T&& t) - noexcept(std::is_nothrow_constructible_v::type, T>) - : base_type(std::in_place_index::index>, std::forward(t)) + noexcept(std::is_nothrow_constructible_v::type, T>) + : base_type(std::in_place_index::index>, std::forward(t)) {} IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN @@ -632,12 +632,12 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN template requires (!std::is_same_v, rvariant>) && - detail::variant_assignable::type, T>::value + detail::variant_assignable::type, T>::value constexpr rvariant& operator=(T&& t) - noexcept(detail::variant_nothrow_assignable::type, T>::value) + noexcept(detail::variant_nothrow_assignable::type, T>::value) { - using Tj = aggregate_initialize_resolution::type; // either plain type or wrapped with recursive_wrapper - constexpr std::size_t j = aggregate_initialize_resolution::index; + using Tj = no_narrowing_resolution::type; // either plain type or wrapped with recursive_wrapper + constexpr std::size_t j = no_narrowing_resolution::index; static_assert(j != std::variant_npos); this->raw_visit([this, &t](std::in_place_index_t, [[maybe_unused]] Ti& ti) diff --git a/include/iris/type_traits.hpp b/include/iris/type_traits.hpp index 97a03c6..c225856 100644 --- a/include/iris/type_traits.hpp +++ b/include/iris/type_traits.hpp @@ -265,17 +265,17 @@ inline constexpr bool is_convertible_without_narrowing_v = is_convertible_withou namespace detail { template -struct aggregate_initialize_tag +struct no_narrowing_tag { static constexpr std::size_t index = I; using type = Ti; }; template -struct aggregate_initialize_overload +struct no_narrowing_overload { template - auto operator()(Ti, T&&) -> aggregate_initialize_tag + auto operator()(Ti, T&&) -> no_narrowing_tag requires is_convertible_without_narrowing_v { return {}; // silence MSVC warning @@ -283,27 +283,27 @@ struct aggregate_initialize_overload }; template -struct aggregate_initialize_fun; +struct no_narrowing_fun; // Imaginary function FUN of https://eel.is/c++draft/variant#ctor-14 template -struct aggregate_initialize_fun, Ts...> - : aggregate_initialize_overload... +struct no_narrowing_fun, Ts...> + : no_narrowing_overload... { - using aggregate_initialize_overload::operator()...; + using no_narrowing_overload::operator()...; }; template -using aggregate_initialize_fun_for = aggregate_initialize_fun, Ts...>; +using no_narrowing_fun_for = no_narrowing_fun, Ts...>; template -struct aggregate_initialize_resolution {}; +struct no_narrowing_resolution {}; template -struct aggregate_initialize_resolution< - std::void_t{}(std::declval(), std::declval()))>, T, Ts... +struct no_narrowing_resolution< + std::void_t{}(std::declval(), std::declval()))>, T, Ts... > { - using tag = decltype(aggregate_initialize_fun_for{}(std::declval(), std::declval())); + using tag = decltype(no_narrowing_fun_for{}(std::declval(), std::declval())); using type = tag::type; static constexpr std::size_t index = tag::index; }; @@ -314,7 +314,7 @@ struct aggregate_initialize_resolution< // because they would lead to unnecessarily nested instantiation for // legitimate infinite recursion errors on recursive types. template -struct aggregate_initialize_resolution : detail::aggregate_initialize_resolution {}; +struct no_narrowing_resolution : detail::no_narrowing_resolution {}; } // iris