Skip to content

Commit 15b7551

Browse files
committed
fix(cpp1): improve recognition of dependent types and deducible parameters
1 parent efd185f commit 15b7551

17 files changed

+722
-64
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Dependent, non-deducible parameters
2+
// are wrapped like non-dependent parameters.
3+
init: <T> (out x: std::integral_constant<i32, T::value>) = { x = (); }
4+
init: <T> (out x: std::integral_constant<i32, T::value>, _: T) = { x = (); }
5+
id: <T> (x: std::integral_constant<i32, T::value>) -> forward _ = x;
6+
id: <T> (x: std::integral_constant<i32, T::value>, y: T) = { [[assert: x& == y&]] }
7+
8+
main: () = {
9+
zero: type == std::integral_constant<i32, 0>;
10+
11+
z: zero;
12+
init<zero>(out z);
13+
[[assert: id<zero>(z)& == z&]]
14+
15+
// Deducible parameters.
16+
_ = :v = 0;
17+
_ = :<T> (x: std::vector<T>) = {}(:std::vector<i32> = ());
18+
_ = :<T> (x: std::vector<std::vector<T>>) = {}(:std::vector<std::vector<i32>> = ());
19+
// Uncomment once `typename` is supported for template arguments.
20+
// _ = :<T, U> (x: std::pair<T, typename U::value_type>, y: U) = {}(:std::pair = (0, 0), z);
21+
_ = :<T, U> (x: std::array<T, U::value>, y: U) = {}(:std::array<i32, 0> = (), z);
22+
init(out z, z);
23+
id(z, z);
24+
25+
// Test that these are emitted unwrapped in case they are deducible.
26+
(copy f := :<T> (x: std::vector<std::type_identity_t<T>>) = {})
27+
static_assert(!std::is_invocable_v<decltype(f), std::vector<i32>>);
28+
(copy f := :<T> (x: std::vector<std::vector<T>>) = {})
29+
static_assert(std::is_invocable_v<decltype(f), std::vector<std::vector<i32>>>);
30+
}
31+
32+
v: <T> type = {
33+
operator=: (out this, x: T) = { }
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
identity: <T> type == T;
2+
3+
f: <T, V: T::value_type> (x: T::value_type) -> T::value_type = {
4+
[[assert: x is T::value_type]]
5+
y: T::value_type;
6+
y = x;
7+
z: type == T::value_type;
8+
return (:T::value_type = x);
9+
10+
// Dependent *template-id*s.
11+
_ = :identity<T>::value_type = (); // First identifier.
12+
_ = :std::optional<T>::value_type = (); // Non-first identifier.
13+
_ = :std::array<i32, T::value>::value_type = ();
14+
_ = :std::array<i32, T::value + T::value>::value_type = ();
15+
16+
// Emitted `template`.
17+
ptr: type == * T; // Needed, pending #502.
18+
nptr: type == * i32; // Needed, pending #502.
19+
_ = :std::pointer_traits<ptr>::rebind<ptr> = (); // Type-only context.
20+
_ = :std::pointer_traits<nptr>::rebind<nptr> = (); // Non-dependent.
21+
_ = :std::pointer_traits<nptr>::rebind<ptr> = (); // Dependent on the nested template.
22+
_ = :std::pointer_traits<ptr>::rebind<nptr> = (); // Dependent on the outer template.
23+
// Uncomment once `typename` is supported for template arguments.
24+
// _ = :identity<typename std::pointer_traits<ptr>::rebind<ptr>> = (); // Non type-only context.
25+
26+
// Aliases.
27+
w: type == T;
28+
_ = :w::value_type = x;
29+
v: type == w;
30+
_ = :v::value_type = x;
31+
a: type == T::type;
32+
_ = :a::value_type = x;
33+
34+
{
35+
// Test that there's no prefixed `typename` to....
36+
_ = std::integral_constant<i32, T::value>(); // `T::value`.
37+
_ = :std::type_identity_t<T> = (); // `std::type_identity_t<T>`.
38+
39+
// Test that non-dependent names aren't emitted with `typename`.
40+
a: type == std::integral_constant<i32, 0>;
41+
b: type == a;
42+
c: type == b;
43+
_ = :b::value_type = x;
44+
_ = :c::value_type = x;
45+
}
46+
}
47+
48+
t: @struct <T: type> type = {
49+
u: @struct type = {
50+
x: T::value_type = ();
51+
this: T::type = ();
52+
}
53+
x: T::value_type = 0;
54+
}
55+
56+
main: () = {
57+
zero: type == std::integral_constant<i32, 0>;
58+
_ = f<zero, 0>(0);
59+
60+
// clang-format off
61+
_ = : ::t<zero> = (); // clang-format on
62+
63+
// Emitted `template` (noop, taken care of by the UFCS macro).
64+
_ = :(f) = { _ = f.operator() <i32>(); }(:<T> () = {});
65+
66+
// Nesting is irrelevant.
67+
_ = :<T> () = { _ = :T::value_type = (); };
68+
_ = :() = { _ = :<T> () = { _ = :T::value_type = (); }; };
69+
_ = :() = { _ = :() = { _ = :<T> () = { _ = :T::value_type = (); }; }; };
70+
_ = :() = { _ = :() = { _ = :() = { _ = :<T> () = { _ = :T::value_type = (); }; }; }; };
71+
_ = :() = { _ = :() = { _ = :<T> () = { _ = :() = { _ = :T::value_type = (); }; }; }; };
72+
_ = :() = { _ = :<T> () = { _ = :() = { _ = :() = { _ = :T::value_type = (); }; }; }; };
73+
_ = :<T> () = { _ = :() = { _ = :() = { _ = :() = { _ = :T::value_type = (); }; }; }; };
74+
_ = :<T> () = { _ = :() = { _ = :() = { _ = :(x: T::value_type) = {}; }; }; };
75+
_ = :<T> () = { _ = :() = { _ = :(x: T::value_type) = { _ = :() = {}; }; }; };
76+
_ = :<T> () = { _ = :(x: T::value_type) = { _ = :() = { _ = :() = {}; }; }; };
77+
_ = :<T> (x: T::value_type) = { _ = :() = { _ = :() = { _ = :() = {}; }; }; };
78+
79+
// Lookup.
80+
{
81+
alias: type == std::integral_constant<i32, 0>;
82+
_ = :alias::value_type = 0; // Non-dependent.
83+
}
84+
_ = :<T> (_: T) = {
85+
alias: type == std::integral_constant<T, 0>;
86+
_ = :alias::value_type = 0; // Dependent.
87+
{
88+
alias: type == std::integral_constant<i32, 0>;
89+
_ = :alias::value_type = 0; // Non-dependent.
90+
}
91+
}(0);
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
clang version 18.0.0 (https://github.com/llvm/llvm-project.git 3723ede3cf5324827f8fbbe7f484c2ee4d7a7204)
2+
Target: x86_64-pc-linux-gnu
3+
Thread model: posix
4+
InstalledDir: /home/johel/root/clang-main/bin

regression-tests/test-results/clang-18/pure2-bugfix-for-deducible-parameters.cpp.execution

Whitespace-only changes.

regression-tests/test-results/clang-18/pure2-bugfix-for-deducible-parameters.cpp.output

Whitespace-only changes.

regression-tests/test-results/clang-18/pure2-bugfix-for-dependent-types.cpp.execution

Whitespace-only changes.

regression-tests/test-results/clang-18/pure2-bugfix-for-dependent-types.cpp.output

Whitespace-only changes.

regression-tests/test-results/clang-18/pure2-bugfix-for-non-local-function-expression.cpp.execution

Whitespace-only changes.

regression-tests/test-results/clang-18/pure2-bugfix-for-non-local-function-expression.cpp.output

Whitespace-only changes.

regression-tests/test-results/clang-18/pure2-print.cpp.output

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
sizeof(x) is 25
2+
(not a name)
3+
xyz
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
2+
#define CPP2_USE_MODULES Yes
3+
4+
//=== Cpp2 type declarations ====================================================
5+
6+
7+
#include "cpp2util.h"
8+
9+
10+
#line 32 "pure2-bugfix-for-deducible-parameters.cpp2"
11+
template<typename T> class v;
12+
13+
14+
//=== Cpp2 type definitions and function declarations ===========================
15+
16+
// Dependent, non-deducible parameters
17+
// are wrapped like non-dependent parameters.
18+
#line 3 "pure2-bugfix-for-deducible-parameters.cpp2"
19+
template<typename T> auto init(cpp2::out<std::integral_constant<cpp2::i32,T::value>> x) -> void;
20+
template<typename T> auto init(cpp2::out<std::integral_constant<cpp2::i32,T::value>> x, [[maybe_unused]] T const& param2) -> void;
21+
template<typename T> [[nodiscard]] auto id(cpp2::in<std::integral_constant<cpp2::i32,T::value>> x) -> auto&&;
22+
template<typename T> auto id(cpp2::in<std::integral_constant<cpp2::i32,T::value>> x, T const& y) -> void;
23+
24+
auto main() -> int;
25+
26+
27+
#line 32 "pure2-bugfix-for-deducible-parameters.cpp2"
28+
template<typename T> class v {
29+
public: explicit v(T const& x);
30+
#line 33 "pure2-bugfix-for-deducible-parameters.cpp2"
31+
public: auto operator=(T const& x) -> v& ;
32+
33+
public: v(v const&) = delete; /* No 'that' constructor, suppress copy */
34+
public: auto operator=(v const&) -> void = delete;
35+
#line 34 "pure2-bugfix-for-deducible-parameters.cpp2"
36+
};
37+
38+
39+
//=== Cpp2 function definitions =================================================
40+
41+
42+
#line 3 "pure2-bugfix-for-deducible-parameters.cpp2"
43+
template<typename T> auto init(cpp2::out<std::integral_constant<cpp2::i32,T::value>> x) -> void{x.construct(); }
44+
template<typename T> auto init(cpp2::out<std::integral_constant<cpp2::i32,T::value>> x, [[maybe_unused]] T const& param2) -> void{x.construct(); }
45+
template<typename T> [[nodiscard]] auto id(cpp2::in<std::integral_constant<cpp2::i32,T::value>> x) -> auto&& { return x; }
46+
template<typename T> auto id(cpp2::in<std::integral_constant<cpp2::i32,T::value>> x, T const& y) -> void{cpp2::Default.expects(&x == &y, ""); }
47+
48+
auto main() -> int{
49+
using zero = std::integral_constant<cpp2::i32,0>;
50+
51+
cpp2::deferred_init<zero> z;
52+
init<zero>(cpp2::out(&z));
53+
cpp2::Default.expects(&id<zero>(z.value()) == &z.value(), "");
54+
55+
// Deducible parameters.
56+
static_cast<void>(v{0});
57+
static_cast<void>([]<typename T>(std::vector<T> const& x) -> void{}(std::vector<cpp2::i32>{}));
58+
static_cast<void>([]<typename T>(std::vector<std::vector<T>> const& x) -> void{}(std::vector<std::vector<cpp2::i32>>{}));
59+
// Uncomment once `typename` is supported for template arguments.
60+
// _ = :<T, U> (x: std::pair<T, typename U::value_type>, y: U) = {}(:std::pair = (0, 0), z);
61+
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()));
62+
init(cpp2::out(&z.value()), z.value());
63+
id(z.value(), std::move(z.value()));
64+
{
65+
auto f = []<typename T>(std::vector<std::type_identity_t<T>> const& x) -> void{};
66+
67+
// Test that these are emitted unwrapped in case they are deducible.
68+
69+
#line 27 "pure2-bugfix-for-deducible-parameters.cpp2"
70+
static_assert(!(std::is_invocable_v<decltype(f),std::vector<cpp2::i32>>));
71+
}
72+
{
73+
auto f = []<typename T>(std::vector<std::vector<T>> const& x) -> void{};
74+
75+
#line 29 "pure2-bugfix-for-deducible-parameters.cpp2"
76+
static_assert(std::is_invocable_v<decltype(std::move(f)),std::vector<std::vector<cpp2::i32>>>);
77+
}
78+
#line 30 "pure2-bugfix-for-deducible-parameters.cpp2"
79+
}
80+
81+
#line 33 "pure2-bugfix-for-deducible-parameters.cpp2"
82+
template <typename T> v<T>::v(T const& x){}
83+
#line 33 "pure2-bugfix-for-deducible-parameters.cpp2"
84+
template <typename T> auto v<T>::operator=(T const& x) -> v& {
85+
return *this;
86+
#line 33 "pure2-bugfix-for-deducible-parameters.cpp2"
87+
}
88+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-bugfix-for-deducible-parameters.cpp2... ok (all Cpp2, passes safety checks)
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
2+
#define CPP2_USE_MODULES Yes
3+
4+
//=== Cpp2 type declarations ====================================================
5+
6+
7+
#include "cpp2util.h"
8+
9+
10+
#line 48 "pure2-bugfix-for-dependent-types.cpp2"
11+
template<typename T> class t;
12+
13+
14+
//=== Cpp2 type definitions and function declarations ===========================
15+
16+
template<typename T> using identity = T;
17+
18+
template<typename T, T::value_type V> [[nodiscard]] auto f(cpp2::in<typename T::value_type> x) -> T::value_type;
19+
20+
21+
#line 48 "pure2-bugfix-for-dependent-types.cpp2"
22+
template<typename T> class t {
23+
struct u_x_as_base { T::value_type x; };
24+
25+
#line 49 "pure2-bugfix-for-dependent-types.cpp2"
26+
public: class u: public u_x_as_base, public T::type {
27+
28+
#line 52 "pure2-bugfix-for-dependent-types.cpp2"
29+
};
30+
public: T::value_type x {0};
31+
};
32+
33+
auto main() -> int;
34+
35+
36+
//=== Cpp2 function definitions =================================================
37+
38+
39+
#line 3 "pure2-bugfix-for-dependent-types.cpp2"
40+
template<typename T, T::value_type V> [[nodiscard]] auto f(cpp2::in<typename T::value_type> x) -> T::value_type{
41+
cpp2::Default.expects(cpp2::is<typename T::value_type>(x), "");
42+
cpp2::deferred_init<typename T::value_type> y;
43+
y.construct(x);
44+
using z = T::value_type;
45+
return { typename T::value_type{x} };
46+
47+
// Dependent *template-id*s.
48+
static_cast<void>(typename identity<T>::value_type{});// First identifier.
49+
static_cast<void>(typename std::optional<T>::value_type{});// Non-first identifier.
50+
static_cast<void>(typename std::array<cpp2::i32,T::value>::value_type{});
51+
static_cast<void>(typename std::array<cpp2::i32,T::value + T::value>::value_type{});
52+
53+
// Emitted `template`.
54+
using ptr = T*; // Needed, pending #502.
55+
using nptr = cpp2::i32*; // Needed, pending #502.
56+
static_cast<void>(typename std::pointer_traits<ptr>::template rebind<ptr>{});// Type-only context.
57+
static_cast<void>(std::pointer_traits<nptr>::rebind<nptr>{});// Non-dependent.
58+
static_cast<void>(std::pointer_traits<nptr>::rebind<ptr>{});// Dependent on the nested template.
59+
static_cast<void>(typename std::pointer_traits<ptr>::template rebind<nptr>{});// Dependent on the outer template.
60+
// Uncomment once `typename` is supported for template arguments.
61+
// _ = :identity<typename std::pointer_traits<ptr>::rebind<ptr>> = (); // Non type-only context.
62+
63+
// Aliases.
64+
using w = T;
65+
static_cast<void>(typename w::value_type{x});
66+
using v = w;
67+
static_cast<void>(typename v::value_type{x});
68+
using a = T::type;
69+
static_cast<void>(typename a::value_type{x});
70+
71+
{
72+
// Test that there's no prefixed `typename` to....
73+
static_cast<void>(std::integral_constant<cpp2::i32,T::value>());// `T::value`.
74+
static_cast<void>(std::type_identity_t<T>{});// `std::type_identity_t<T>`.
75+
76+
// Test that non-dependent names aren't emitted with `typename`.
77+
using a = std::integral_constant<cpp2::i32,0>;
78+
using b = a;
79+
using c = b;
80+
static_cast<void>(b::value_type{x});
81+
static_cast<void>(c::value_type{x});
82+
}
83+
}
84+
85+
#line 56 "pure2-bugfix-for-dependent-types.cpp2"
86+
auto main() -> int{
87+
using zero = std::integral_constant<cpp2::i32,0>;
88+
static_cast<void>(f<zero,0>(0));
89+
90+
// clang-format off
91+
static_cast<void>(::t<zero>{});// clang-format on
92+
93+
// Emitted `template` (noop, taken care of by the UFCS macro).
94+
static_cast<void>([](auto const& f) -> void{static_cast<void>(CPP2_UFCS_TEMPLATE_0(operator(), (<cpp2::i32>), f)); }([]<typename T>() -> void{}));
95+
96+
// Nesting is irrelevant.
97+
static_cast<void>([]<typename T>() -> void{static_cast<void>(typename T::value_type{}); });
98+
static_cast<void>([]() -> void{static_cast<void>([]<typename T>() -> void{static_cast<void>(typename T::value_type{}); }); });
99+
static_cast<void>([]() -> void{static_cast<void>([]() -> void{static_cast<void>([]<typename T>() -> void{static_cast<void>(typename T::value_type{}); }); }); });
100+
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{}); }); }); }); });
101+
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{}); }); }); }); });
102+
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{}); }); }); }); });
103+
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{}); }); }); }); });
104+
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{}); }); }); });
105+
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{}); }); }); });
106+
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{}); }); }); });
107+
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{}); }); }); });
108+
109+
// Lookup.
110+
{
111+
using alias = std::integral_constant<cpp2::i32,0>;
112+
static_cast<void>(alias::value_type{0});// Non-dependent.
113+
}
114+
static_cast<void>([]<typename T>([[maybe_unused]] T const& param1) -> void{
115+
using alias = std::integral_constant<T,0>;
116+
static_cast<void>(typename alias::value_type{0});// Dependent.
117+
{
118+
using alias = std::integral_constant<cpp2::i32,0>;
119+
static_cast<void>(typename alias::value_type{0});// Non-dependent.
120+
}
121+
}(0));
122+
}
123+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-bugfix-for-dependent-types.cpp2... ok (all Cpp2, passes safety checks)
2+

0 commit comments

Comments
 (0)