-
Notifications
You must be signed in to change notification settings - Fork 251
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(cpp1): improve recognition of dependent types and deducible param…
…eters
- Loading branch information
Showing
17 changed files
with
722 additions
and
64 deletions.
There are no files selected for viewing
34 changes: 34 additions & 0 deletions
34
regression-tests/pure2-bugfix-for-deducible-parameters.cpp2
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,34 @@ | ||
// Dependent, non-deducible parameters | ||
// are wrapped like non-dependent parameters. | ||
init: <T> (out x: std::integral_constant<i32, T::value>) = { x = (); } | ||
init: <T> (out x: std::integral_constant<i32, T::value>, _: T) = { x = (); } | ||
id: <T> (x: std::integral_constant<i32, T::value>) -> forward _ = x; | ||
id: <T> (x: std::integral_constant<i32, T::value>, y: T) = { [[assert: x& == y&]] } | ||
|
||
main: () = { | ||
zero: type == std::integral_constant<i32, 0>; | ||
|
||
z: zero; | ||
init<zero>(out z); | ||
[[assert: id<zero>(z)& == z&]] | ||
|
||
// Deducible parameters. | ||
_ = :v = 0; | ||
_ = :<T> (x: std::vector<T>) = {}(:std::vector<i32> = ()); | ||
_ = :<T> (x: std::vector<std::vector<T>>) = {}(:std::vector<std::vector<i32>> = ()); | ||
// Uncomment once `typename` is supported for template arguments. | ||
// _ = :<T, U> (x: std::pair<T, typename U::value_type>, y: U) = {}(:std::pair = (0, 0), z); | ||
_ = :<T, U> (x: std::array<T, U::value>, y: U) = {}(:std::array<i32, 0> = (), z); | ||
init(out z, z); | ||
id(z, z); | ||
|
||
// Test that these are emitted unwrapped in case they are deducible. | ||
(copy f := :<T> (x: std::vector<std::type_identity_t<T>>) = {}) | ||
static_assert(!std::is_invocable_v<decltype(f), std::vector<i32>>); | ||
(copy f := :<T> (x: std::vector<std::vector<T>>) = {}) | ||
static_assert(std::is_invocable_v<decltype(f), std::vector<std::vector<i32>>>); | ||
} | ||
|
||
v: <T> type = { | ||
operator=: (out this, x: T) = { } | ||
} |
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,92 @@ | ||
identity: <T> type == T; | ||
|
||
f: <T, V: T::value_type> (x: T::value_type) -> T::value_type = { | ||
[[assert: x is T::value_type]] | ||
y: T::value_type; | ||
y = x; | ||
z: type == T::value_type; | ||
return (:T::value_type = x); | ||
|
||
// Dependent *template-id*s. | ||
_ = :identity<T>::value_type = (); // First identifier. | ||
_ = :std::optional<T>::value_type = (); // Non-first identifier. | ||
_ = :std::array<i32, T::value>::value_type = (); | ||
_ = :std::array<i32, T::value + T::value>::value_type = (); | ||
|
||
// Emitted `template`. | ||
ptr: type == * T; // Needed, pending #502. | ||
nptr: type == * i32; // Needed, pending #502. | ||
_ = :std::pointer_traits<ptr>::rebind<ptr> = (); // Type-only context. | ||
_ = :std::pointer_traits<nptr>::rebind<nptr> = (); // Non-dependent. | ||
_ = :std::pointer_traits<nptr>::rebind<ptr> = (); // Dependent on the nested template. | ||
_ = :std::pointer_traits<ptr>::rebind<nptr> = (); // Dependent on the outer template. | ||
// Uncomment once `typename` is supported for template arguments. | ||
// _ = :identity<typename std::pointer_traits<ptr>::rebind<ptr>> = (); // Non type-only context. | ||
|
||
// Aliases. | ||
w: type == T; | ||
_ = :w::value_type = x; | ||
v: type == w; | ||
_ = :v::value_type = x; | ||
a: type == T::type; | ||
_ = :a::value_type = x; | ||
|
||
{ | ||
// Test that there's no prefixed `typename` to.... | ||
_ = std::integral_constant<i32, T::value>(); // `T::value`. | ||
_ = :std::type_identity_t<T> = (); // `std::type_identity_t<T>`. | ||
|
||
// Test that non-dependent names aren't emitted with `typename`. | ||
a: type == std::integral_constant<i32, 0>; | ||
b: type == a; | ||
c: type == b; | ||
_ = :b::value_type = x; | ||
_ = :c::value_type = x; | ||
} | ||
} | ||
|
||
t: @struct <T: type> type = { | ||
u: @struct type = { | ||
x: T::value_type = (); | ||
this: T::type = (); | ||
} | ||
x: T::value_type = 0; | ||
} | ||
|
||
main: () = { | ||
zero: type == std::integral_constant<i32, 0>; | ||
_ = f<zero, 0>(0); | ||
|
||
// clang-format off | ||
_ = : ::t<zero> = (); // clang-format on | ||
|
||
// Emitted `template` (noop, taken care of by the UFCS macro). | ||
_ = :(f) = { _ = f.operator() <i32>(); }(:<T> () = {}); | ||
|
||
// Nesting is irrelevant. | ||
_ = :<T> () = { _ = :T::value_type = (); }; | ||
_ = :() = { _ = :<T> () = { _ = :T::value_type = (); }; }; | ||
_ = :() = { _ = :() = { _ = :<T> () = { _ = :T::value_type = (); }; }; }; | ||
_ = :() = { _ = :() = { _ = :() = { _ = :<T> () = { _ = :T::value_type = (); }; }; }; }; | ||
_ = :() = { _ = :() = { _ = :<T> () = { _ = :() = { _ = :T::value_type = (); }; }; }; }; | ||
_ = :() = { _ = :<T> () = { _ = :() = { _ = :() = { _ = :T::value_type = (); }; }; }; }; | ||
_ = :<T> () = { _ = :() = { _ = :() = { _ = :() = { _ = :T::value_type = (); }; }; }; }; | ||
_ = :<T> () = { _ = :() = { _ = :() = { _ = :(x: T::value_type) = {}; }; }; }; | ||
_ = :<T> () = { _ = :() = { _ = :(x: T::value_type) = { _ = :() = {}; }; }; }; | ||
_ = :<T> () = { _ = :(x: T::value_type) = { _ = :() = { _ = :() = {}; }; }; }; | ||
_ = :<T> (x: T::value_type) = { _ = :() = { _ = :() = { _ = :() = {}; }; }; }; | ||
|
||
// Lookup. | ||
{ | ||
alias: type == std::integral_constant<i32, 0>; | ||
_ = :alias::value_type = 0; // Non-dependent. | ||
} | ||
_ = :<T> (_: T) = { | ||
alias: type == std::integral_constant<T, 0>; | ||
_ = :alias::value_type = 0; // Dependent. | ||
{ | ||
alias: type == std::integral_constant<i32, 0>; | ||
_ = :alias::value_type = 0; // Non-dependent. | ||
} | ||
}(0); | ||
} |
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,4 @@ | ||
clang version 18.0.0 (https://github.com/llvm/llvm-project.git 3723ede3cf5324827f8fbbe7f484c2ee4d7a7204) | ||
Target: x86_64-pc-linux-gnu | ||
Thread model: posix | ||
InstalledDir: /home/johel/root/clang-main/bin |
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
3 changes: 3 additions & 0 deletions
3
regression-tests/test-results/clang-18/pure2-union.cpp.execution
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,3 @@ | ||
sizeof(x) is 25 | ||
(not a name) | ||
xyz |
88 changes: 88 additions & 0 deletions
88
regression-tests/test-results/pure2-bugfix-for-deducible-parameters.cpp
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,88 @@ | ||
|
||
#define CPP2_USE_MODULES Yes | ||
|
||
//=== Cpp2 type declarations ==================================================== | ||
|
||
|
||
#include "cpp2util.h" | ||
|
||
|
||
#line 32 "pure2-bugfix-for-deducible-parameters.cpp2" | ||
template<typename T> class v; | ||
|
||
|
||
//=== Cpp2 type definitions and function declarations =========================== | ||
|
||
// Dependent, non-deducible parameters | ||
// are wrapped like non-dependent parameters. | ||
#line 3 "pure2-bugfix-for-deducible-parameters.cpp2" | ||
template<typename T> auto init(cpp2::out<std::integral_constant<cpp2::i32,T::value>> x) -> void; | ||
template<typename T> auto init(cpp2::out<std::integral_constant<cpp2::i32,T::value>> x, [[maybe_unused]] T const& param2) -> void; | ||
template<typename T> [[nodiscard]] auto id(cpp2::in<std::integral_constant<cpp2::i32,T::value>> x) -> auto&&; | ||
template<typename T> auto id(cpp2::in<std::integral_constant<cpp2::i32,T::value>> x, T const& y) -> void; | ||
|
||
auto main() -> int; | ||
|
||
|
||
#line 32 "pure2-bugfix-for-deducible-parameters.cpp2" | ||
template<typename T> class v { | ||
public: explicit v(T const& x); | ||
#line 33 "pure2-bugfix-for-deducible-parameters.cpp2" | ||
public: auto operator=(T const& x) -> v& ; | ||
|
||
public: v(v const&) = delete; /* No 'that' constructor, suppress copy */ | ||
public: auto operator=(v const&) -> void = delete; | ||
#line 34 "pure2-bugfix-for-deducible-parameters.cpp2" | ||
}; | ||
|
||
|
||
//=== Cpp2 function definitions ================================================= | ||
|
||
|
||
#line 3 "pure2-bugfix-for-deducible-parameters.cpp2" | ||
template<typename T> auto init(cpp2::out<std::integral_constant<cpp2::i32,T::value>> x) -> void{x.construct(); } | ||
template<typename T> auto init(cpp2::out<std::integral_constant<cpp2::i32,T::value>> x, [[maybe_unused]] T const& param2) -> void{x.construct(); } | ||
template<typename T> [[nodiscard]] auto id(cpp2::in<std::integral_constant<cpp2::i32,T::value>> x) -> auto&& { return x; } | ||
template<typename T> auto id(cpp2::in<std::integral_constant<cpp2::i32,T::value>> x, T const& y) -> void{cpp2::Default.expects(&x == &y, ""); } | ||
|
||
auto main() -> int{ | ||
using zero = std::integral_constant<cpp2::i32,0>; | ||
|
||
cpp2::deferred_init<zero> z; | ||
init<zero>(cpp2::out(&z)); | ||
cpp2::Default.expects(&id<zero>(z.value()) == &z.value(), ""); | ||
|
||
// Deducible parameters. | ||
static_cast<void>(v{0}); | ||
static_cast<void>([]<typename T>(std::vector<T> const& x) -> void{}(std::vector<cpp2::i32>{})); | ||
static_cast<void>([]<typename T>(std::vector<std::vector<T>> const& x) -> void{}(std::vector<std::vector<cpp2::i32>>{})); | ||
// Uncomment once `typename` is supported for template arguments. | ||
// _ = :<T, U> (x: std::pair<T, typename U::value_type>, y: U) = {}(:std::pair = (0, 0), z); | ||
static_cast<void>([]<typename T, typename U>(std::array<T,U::value> const& x, U const& y) -> void{}(std::array<cpp2::i32,0>{}, z.value())); | ||
init(cpp2::out(&z.value()), z.value()); | ||
id(z.value(), std::move(z.value())); | ||
{ | ||
auto f = []<typename T>(std::vector<std::type_identity_t<T>> const& x) -> void{}; | ||
|
||
// Test that these are emitted unwrapped in case they are deducible. | ||
|
||
#line 27 "pure2-bugfix-for-deducible-parameters.cpp2" | ||
static_assert(!(std::is_invocable_v<decltype(f),std::vector<cpp2::i32>>)); | ||
} | ||
{ | ||
auto f = []<typename T>(std::vector<std::vector<T>> const& x) -> void{}; | ||
|
||
#line 29 "pure2-bugfix-for-deducible-parameters.cpp2" | ||
static_assert(std::is_invocable_v<decltype(std::move(f)),std::vector<std::vector<cpp2::i32>>>); | ||
} | ||
#line 30 "pure2-bugfix-for-deducible-parameters.cpp2" | ||
} | ||
|
||
#line 33 "pure2-bugfix-for-deducible-parameters.cpp2" | ||
template <typename T> v<T>::v(T const& x){} | ||
#line 33 "pure2-bugfix-for-deducible-parameters.cpp2" | ||
template <typename T> auto v<T>::operator=(T const& x) -> v& { | ||
return *this; | ||
#line 33 "pure2-bugfix-for-deducible-parameters.cpp2" | ||
} | ||
|
2 changes: 2 additions & 0 deletions
2
regression-tests/test-results/pure2-bugfix-for-deducible-parameters.cpp2.output
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,2 @@ | ||
pure2-bugfix-for-deducible-parameters.cpp2... ok (all Cpp2, passes safety checks) | ||
|
123 changes: 123 additions & 0 deletions
123
regression-tests/test-results/pure2-bugfix-for-dependent-types.cpp
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,123 @@ | ||
|
||
#define CPP2_USE_MODULES Yes | ||
|
||
//=== Cpp2 type declarations ==================================================== | ||
|
||
|
||
#include "cpp2util.h" | ||
|
||
|
||
#line 48 "pure2-bugfix-for-dependent-types.cpp2" | ||
template<typename T> class t; | ||
|
||
|
||
//=== Cpp2 type definitions and function declarations =========================== | ||
|
||
template<typename T> using identity = T; | ||
|
||
template<typename T, T::value_type V> [[nodiscard]] auto f(cpp2::in<typename T::value_type> x) -> T::value_type; | ||
|
||
|
||
#line 48 "pure2-bugfix-for-dependent-types.cpp2" | ||
template<typename T> class t { | ||
struct u_x_as_base { T::value_type x; }; | ||
|
||
#line 49 "pure2-bugfix-for-dependent-types.cpp2" | ||
public: class u: public u_x_as_base, public T::type { | ||
|
||
#line 52 "pure2-bugfix-for-dependent-types.cpp2" | ||
}; | ||
public: T::value_type x {0}; | ||
}; | ||
|
||
auto main() -> int; | ||
|
||
|
||
//=== Cpp2 function definitions ================================================= | ||
|
||
|
||
#line 3 "pure2-bugfix-for-dependent-types.cpp2" | ||
template<typename T, T::value_type V> [[nodiscard]] auto f(cpp2::in<typename T::value_type> x) -> T::value_type{ | ||
cpp2::Default.expects(cpp2::is<typename T::value_type>(x), ""); | ||
cpp2::deferred_init<typename T::value_type> y; | ||
y.construct(x); | ||
using z = T::value_type; | ||
return { typename T::value_type{x} }; | ||
|
||
// Dependent *template-id*s. | ||
static_cast<void>(typename identity<T>::value_type{});// First identifier. | ||
static_cast<void>(typename std::optional<T>::value_type{});// Non-first identifier. | ||
static_cast<void>(typename std::array<cpp2::i32,T::value>::value_type{}); | ||
static_cast<void>(typename std::array<cpp2::i32,T::value + T::value>::value_type{}); | ||
|
||
// Emitted `template`. | ||
using ptr = T*; // Needed, pending #502. | ||
using nptr = cpp2::i32*; // Needed, pending #502. | ||
static_cast<void>(typename std::pointer_traits<ptr>::template rebind<ptr>{});// Type-only context. | ||
static_cast<void>(std::pointer_traits<nptr>::rebind<nptr>{});// Non-dependent. | ||
static_cast<void>(std::pointer_traits<nptr>::rebind<ptr>{});// Dependent on the nested template. | ||
static_cast<void>(typename std::pointer_traits<ptr>::template rebind<nptr>{});// Dependent on the outer template. | ||
// Uncomment once `typename` is supported for template arguments. | ||
// _ = :identity<typename std::pointer_traits<ptr>::rebind<ptr>> = (); // Non type-only context. | ||
|
||
// Aliases. | ||
using w = T; | ||
static_cast<void>(typename w::value_type{x}); | ||
using v = w; | ||
static_cast<void>(typename v::value_type{x}); | ||
using a = T::type; | ||
static_cast<void>(typename a::value_type{x}); | ||
|
||
{ | ||
// Test that there's no prefixed `typename` to.... | ||
static_cast<void>(std::integral_constant<cpp2::i32,T::value>());// `T::value`. | ||
static_cast<void>(std::type_identity_t<T>{});// `std::type_identity_t<T>`. | ||
|
||
// Test that non-dependent names aren't emitted with `typename`. | ||
using a = std::integral_constant<cpp2::i32,0>; | ||
using b = a; | ||
using c = b; | ||
static_cast<void>(b::value_type{x}); | ||
static_cast<void>(c::value_type{x}); | ||
} | ||
} | ||
|
||
#line 56 "pure2-bugfix-for-dependent-types.cpp2" | ||
auto main() -> int{ | ||
using zero = std::integral_constant<cpp2::i32,0>; | ||
static_cast<void>(f<zero,0>(0)); | ||
|
||
// clang-format off | ||
static_cast<void>(::t<zero>{});// clang-format on | ||
|
||
// Emitted `template` (noop, taken care of by the UFCS macro). | ||
static_cast<void>([](auto const& f) -> void{static_cast<void>(CPP2_UFCS_TEMPLATE_0(operator(), (<cpp2::i32>), f)); }([]<typename T>() -> void{})); | ||
|
||
// Nesting is irrelevant. | ||
static_cast<void>([]<typename T>() -> void{static_cast<void>(typename T::value_type{}); }); | ||
static_cast<void>([]() -> void{static_cast<void>([]<typename T>() -> void{static_cast<void>(typename T::value_type{}); }); }); | ||
static_cast<void>([]() -> void{static_cast<void>([]() -> void{static_cast<void>([]<typename T>() -> void{static_cast<void>(typename T::value_type{}); }); }); }); | ||
static_cast<void>([]() -> void{static_cast<void>([]() -> void{static_cast<void>([]() -> void{static_cast<void>([]<typename T>() -> void{static_cast<void>(typename T::value_type{}); }); }); }); }); | ||
static_cast<void>([]() -> void{static_cast<void>([]() -> void{static_cast<void>([]<typename T>() -> void{static_cast<void>([]() -> void{static_cast<void>(typename T::value_type{}); }); }); }); }); | ||
static_cast<void>([]() -> void{static_cast<void>([]<typename T>() -> void{static_cast<void>([]() -> void{static_cast<void>([]() -> void{static_cast<void>(typename T::value_type{}); }); }); }); }); | ||
static_cast<void>([]<typename T>() -> void{static_cast<void>([]() -> void{static_cast<void>([]() -> void{static_cast<void>([]() -> void{static_cast<void>(typename T::value_type{}); }); }); }); }); | ||
static_cast<void>([]<typename T>() -> void{static_cast<void>([]() -> void{static_cast<void>([]() -> void{static_cast<void>([](cpp2::in<typename T::value_type> x) -> void{}); }); }); }); | ||
static_cast<void>([]<typename T>() -> void{static_cast<void>([]() -> void{static_cast<void>([](cpp2::in<typename T::value_type> x) -> void{static_cast<void>([]() -> void{}); }); }); }); | ||
static_cast<void>([]<typename T>() -> void{static_cast<void>([](cpp2::in<typename T::value_type> x) -> void{static_cast<void>([]() -> void{static_cast<void>([]() -> void{}); }); }); }); | ||
static_cast<void>([]<typename T>(cpp2::in<typename T::value_type> x) -> void{static_cast<void>([]() -> void{static_cast<void>([]() -> void{static_cast<void>([]() -> void{}); }); }); }); | ||
|
||
// Lookup. | ||
{ | ||
using alias = std::integral_constant<cpp2::i32,0>; | ||
static_cast<void>(alias::value_type{0});// Non-dependent. | ||
} | ||
static_cast<void>([]<typename T>([[maybe_unused]] T const& param1) -> void{ | ||
using alias = std::integral_constant<T,0>; | ||
static_cast<void>(typename alias::value_type{0});// Dependent. | ||
{ | ||
using alias = std::integral_constant<cpp2::i32,0>; | ||
static_cast<void>(typename alias::value_type{0});// Non-dependent. | ||
} | ||
}(0)); | ||
} | ||
|
2 changes: 2 additions & 0 deletions
2
regression-tests/test-results/pure2-bugfix-for-dependent-types.cpp2.output
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,2 @@ | ||
pure2-bugfix-for-dependent-types.cpp2... ok (all Cpp2, passes safety checks) | ||
|
Oops, something went wrong.