Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(to_cpp1): improve recognition of dependent types and deducible parameters #533

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions regression-tests/pure2-bugfix-for-deducible-parameters.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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> (in_ref x: std::integral_constant<i32, T::value>) = 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> (_: std::vector<T>) = {}(:std::vector<i32> = ());
:<T> (_: std::vector<std::vector<T>>) = {}(:std::vector<std::vector<i32>> = ());
// _ = :<T, U> (x: std::pair<T, typename U::value_type>, y: U) = {}(:std::pair = (0, 0), z); // Blocked on #727.
:<T, U> (_: std::array<T, U::value>, _: U) = {}(:std::array<i32, 0> = (), z);
init(out z, z);
JohelEGP marked this conversation as resolved.
Show resolved Hide resolved
id(z, z);

// Test that these are emitted unwrapped in case they are deducible.
(copy f := :<T> (_: std::vector<std::type_identity_t<T>>) = {})
static_assert(!std::is_invocable_v<decltype(f), std::vector<i32>>, "`T` is non-deducible.");
(copy f := :<T> (_: std::vector<std::vector<T>>) = {})
static_assert(std::is_invocable_v<decltype(f), std::vector<std::vector<i32>>>, "`T` is deducible.");
}

v: <T> type = {
operator=: (out this, _: T) = { }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
main: () = {
a: type == b;
b: type == a;
_ = a::t;
_ = b::t;
}
96 changes: 96 additions & 0 deletions regression-tests/pure2-bugfix-for-dependent-types.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
identity: <T> type == T;

f: <T> (inout _: T::value_type) = { }
f: <T> (move _: T::value_type) = { }
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; // Also test lookup through type aliases.
nptr: type == * i32;
_ = :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.
// _ = :identity<typename std::pointer_traits<ptr>::rebind<ptr>> = (); // Non type-only context. Blocked on #727.

// 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: @cpp1_rule_of_zero <T: type> type = {
u: type = {
x: T::value_type = ();
this: T::type = ();
// Test that there's no `typename` in the member initializer list.
operator=: (out this, that) = { }
}
x: T::value_type = 0;
}

main: () = {
zero: type == std::integral_constant<i32, 0>;
_ = f<zero, 0>(0);

_ = : ::t<zero> = ();

// Emitted `template` (noop, taken care of by the UFCS macro).
_ = :(move f) = _ = f.operator()<i32>();(:<T> () = {});

// Nesting is not relevant to lookup.
_ = :<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> () = { _ = :() = { _ = :() = { _ = :(_: T::value_type) = {}; }; }; };
_ = :<T> () = { _ = :() = { _ = :(_: T::value_type) = { _ = :() = {}; }; }; };
_ = :<T> () = { _ = :(_: T::value_type) = { _ = :() = { _ = :() = {}; }; }; };
_ = :<T> (_: 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);

_ = :(r) -> std::type_identity_t<decltype(begin(r)*)> = r[0];(std::vector<int>(1));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pure2-bugfix-for-dependent-types-recursion.cpp2:2:13: error: unknown type name 'b'
using a = b;
^
pure2-bugfix-for-dependent-types-recursion.cpp2:3:13: error: unknown type name 'a'
using b = a;
^
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it intended that these test cases fail?

  • pure2-bugfix-for-dependent-types-recursion
  • pure2-bugfix-for-dependent-types

For some reason the latter works on GCC 14?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pure2-bugfix-for-dependent-types-recursion should fail.
It tests that the algorithm detects recursio,
and returns false (i.e., "not a dependent type"),
to let the Cpp1 compiler give its diagnostic.

pure2-bugfix-for-dependent-types shouldn't fail on a modern compiler.
I see some are failing because they don't yet support optional typename.

pure2-bugfix-for-dependent-types-recursion.cpp2:4:21: error: use of undeclared identifier 'a'
static_cast<void>(a::t);
^
pure2-bugfix-for-dependent-types-recursion.cpp2:5:21: error: use of undeclared identifier 'b'
static_cast<void>(b::t);
^
4 errors generated.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
pure2-bugfix-for-dependent-types.cpp2:5:22: error: missing 'typename' prior to dependent type name 'T::value_type'
template<typename T, T::value_type V> [[nodiscard]] auto f(cpp2::impl::in<typename T::value_type> x) -> T::value_type;
^~~~~~~~~~~~~
typename
pure2-bugfix-for-dependent-types.cpp2:5:105: error: missing 'typename' prior to dependent type name 'T::value_type'
template<typename T, T::value_type V> [[nodiscard]] auto f(cpp2::impl::in<typename T::value_type> x) -> T::value_type;
^~~~~~~~~~~~~
typename
pure2-bugfix-for-dependent-types.cpp2:56:12: error: missing 'typename' prior to dependent type name 'T::value_type'
private: T::value_type x {0};
^~~~~~~~~~~~~
typename
pure2-bugfix-for-dependent-types.cpp2:5:22: error: missing 'typename' prior to dependent type name 'T::value_type'
template<typename T, T::value_type V> [[nodiscard]] auto f(cpp2::impl::in<typename T::value_type> x) -> T::value_type{
^~~~~~~~~~~~~
typename
pure2-bugfix-for-dependent-types.cpp2:5:105: error: missing 'typename' prior to dependent type name 'T::value_type'
template<typename T, T::value_type V> [[nodiscard]] auto f(cpp2::impl::in<typename T::value_type> x) -> T::value_type{
^~~~~~~~~~~~~
typename
pure2-bugfix-for-dependent-types.cpp2:9:13: error: missing 'typename' prior to dependent type name 'T::value_type'
using z = T::value_type;
^~~~~~~~~~~~~
typename
pure2-bugfix-for-dependent-types.cpp2:32:13: error: missing 'typename' prior to dependent type name 'T::type'
using a = T::type;
^~~~~~~
typename
7 errors generated.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pure2-bugfix-for-dependent-types-recursion.cpp2: In function ‘int main()’:
pure2-bugfix-for-dependent-types-recursion.cpp2:2:13: error: ‘b’ does not name a type
pure2-bugfix-for-dependent-types-recursion.cpp2:3:13: error: ‘a’ does not name a type
pure2-bugfix-for-dependent-types-recursion.cpp2:4:21: error: ‘a’ has not been declared
pure2-bugfix-for-dependent-types-recursion.cpp2:5:21: error: ‘b’ has not been declared
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pure2-bugfix-for-dependent-types.cpp2: In function ‘typename T::value_type f(cpp2::impl::in<typename T::value_type>) [with T = std::integral_constant<int, 0>; typename T::value_type V = 0]’:
pure2-bugfix-for-dependent-types.cpp2:47:1: warning: control reaches end of non-void function [-Wreturn-type]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pure2-bugfix-for-dependent-types-recursion.cpp2: In function ‘int main()’:
pure2-bugfix-for-dependent-types-recursion.cpp2:2:13: error: ‘b’ does not name a type
pure2-bugfix-for-dependent-types-recursion.cpp2:3:13: error: ‘a’ does not name a type
pure2-bugfix-for-dependent-types-recursion.cpp2:4:21: error: ‘a’ has not been declared
pure2-bugfix-for-dependent-types-recursion.cpp2:5:21: error: ‘b’ has not been declared
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pure2-bugfix-for-deducible-parameters.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pure2-bugfix-for-dependent-types-recursion.cpp
pure2-bugfix-for-dependent-types-recursion.cpp2(2): error C2061: syntax error: identifier 'b'
pure2-bugfix-for-dependent-types-recursion.cpp2(3): error C2061: syntax error: identifier 'a'
pure2-bugfix-for-dependent-types-recursion.cpp2(4): error C2653: 'a': is not a class or namespace name
pure2-bugfix-for-dependent-types-recursion.cpp2(4): error C2065: 't': undeclared identifier
pure2-bugfix-for-dependent-types-recursion.cpp2(5): error C2653: 'b': is not a class or namespace name
pure2-bugfix-for-dependent-types-recursion.cpp2(5): error C2065: 't': undeclared identifier
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pure2-bugfix-for-dependent-types.cpp
pure2-bugfix-for-dependent-types.cpp2(66): fatal error C1202: recursive type or function dependency context too complex
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@

#define CPP2_IMPORT_STD Yes

//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"

#line 1 "pure2-bugfix-for-deducible-parameters.cpp2"

#line 30 "pure2-bugfix-for-deducible-parameters.cpp2"
template<typename T> class v;


//=== Cpp2 type definitions and function declarations ===========================

#line 1 "pure2-bugfix-for-deducible-parameters.cpp2"
// Dependent, non-deducible parameters are wrapped like non-dependent parameters.
#line 2 "pure2-bugfix-for-deducible-parameters.cpp2"
template<typename T> auto init(cpp2::impl::out<std::integral_constant<cpp2::i32,T::value>> x) -> void;
template<typename T> auto init(cpp2::impl::out<std::integral_constant<cpp2::i32,T::value>> x, [[maybe_unused]] T const& unnamed_param_2) -> void;
template<typename T> [[nodiscard]] auto id(std::integral_constant<cpp2::i32,T::value> const& x) -> decltype(auto);
template<typename T> auto id(cpp2::impl::in<std::integral_constant<cpp2::i32,T::value>> x, T const& y) -> void;

auto main() -> int;

#line 30 "pure2-bugfix-for-deducible-parameters.cpp2"
template<typename T> class v {
public: explicit v([[maybe_unused]] T const& unnamed_param_2);
#line 31 "pure2-bugfix-for-deducible-parameters.cpp2"
public: auto operator=([[maybe_unused]] T const& unnamed_param_2) -> v& ;
public: v(v const&) = delete; /* No 'that' constructor, suppress copy */
public: auto operator=(v const&) -> void = delete;

#line 32 "pure2-bugfix-for-deducible-parameters.cpp2"
};


//=== Cpp2 function definitions =================================================

#line 1 "pure2-bugfix-for-deducible-parameters.cpp2"

#line 2 "pure2-bugfix-for-deducible-parameters.cpp2"
template<typename T> auto init(cpp2::impl::out<std::integral_constant<cpp2::i32,T::value>> x) -> void{x.construct(); }
#line 3 "pure2-bugfix-for-deducible-parameters.cpp2"
template<typename T> auto init(cpp2::impl::out<std::integral_constant<cpp2::i32,T::value>> x, [[maybe_unused]] T const& unnamed_param_2) -> void{x.construct(); }
#line 4 "pure2-bugfix-for-deducible-parameters.cpp2"
template<typename T> [[nodiscard]] auto id(std::integral_constant<cpp2::i32,T::value> const& x) -> decltype(auto) { return x; }
#line 5 "pure2-bugfix-for-deducible-parameters.cpp2"
template<typename T> auto id(cpp2::impl::in<std::integral_constant<cpp2::i32,T::value>> x, T const& y) -> void{if (cpp2::cpp2_default.is_active() && !(&x == &y) ) { cpp2::cpp2_default.report_violation(""); }}

#line 7 "pure2-bugfix-for-deducible-parameters.cpp2"
auto main() -> int{
using zero = std::integral_constant<cpp2::i32,0>;

cpp2::impl::deferred_init<zero> z;
init<zero>(cpp2::impl::out(&z));
if (cpp2::cpp2_default.is_active() && !(&id<zero>(z.value()) == &z.value()) ) { cpp2::cpp2_default.report_violation(""); }

// Deducible parameters.
static_cast<void>(v{ 0});
[]<typename T>([[maybe_unused]] std::vector<T> const& unnamed_param_1) -> void{}(std::vector<cpp2::i32>{});
[]<typename T>([[maybe_unused]] std::vector<std::vector<T>> const& unnamed_param_1) -> void{}(std::vector<std::vector<cpp2::i32>>{});
// _ = :<T, U> (x: std::pair<T, typename U::value_type>, y: U) = {}(:std::pair = (0, 0), z); // Blocked on #727.
[]<typename T, typename U>([[maybe_unused]] std::array<T,U::value> const& unnamed_param_1, [[maybe_unused]] U const& unnamed_param_2) -> void{}(std::array<cpp2::i32,0>{}, z.value());
init(cpp2::impl::out(&z.value()), z.value());
id(z.value(), cpp2::move(z.value()));
{
auto f{[]<typename T>([[maybe_unused]] std::vector<std::type_identity_t<T>> const& unnamed_param_1) -> void{}};

// Test that these are emitted unwrapped in case they are deducible.

#line 25 "pure2-bugfix-for-deducible-parameters.cpp2"
static_assert(!(std::is_invocable_v<decltype(cpp2::move(f)),std::vector<cpp2::i32>>), "`T` is non-deducible.");
}
{
auto f{[]<typename T>([[maybe_unused]] std::vector<std::vector<T>> const& unnamed_param_1) -> void{}};

#line 27 "pure2-bugfix-for-deducible-parameters.cpp2"
static_assert(std::is_invocable_v<decltype(cpp2::move(f)),std::vector<std::vector<cpp2::i32>>>, "`T` is deducible.");
}
#line 28 "pure2-bugfix-for-deducible-parameters.cpp2"
}

#line 31 "pure2-bugfix-for-deducible-parameters.cpp2"
template <typename T> v<T>::v([[maybe_unused]] T const& unnamed_param_2){}
#line 31 "pure2-bugfix-for-deducible-parameters.cpp2"
template <typename T> auto v<T>::operator=([[maybe_unused]] T const& unnamed_param_2) -> v& {
return *this; }

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)

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

#define CPP2_IMPORT_STD Yes

//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"

#line 1 "pure2-bugfix-for-dependent-types-recursion.cpp2"


//=== Cpp2 type definitions and function declarations ===========================

#line 1 "pure2-bugfix-for-dependent-types-recursion.cpp2"
auto main() -> int;

//=== Cpp2 function definitions =================================================

#line 1 "pure2-bugfix-for-dependent-types-recursion.cpp2"
auto main() -> int{
#line 2 "pure2-bugfix-for-dependent-types-recursion.cpp2"
using a = b;
using b = a;
static_cast<void>(a::t);
static_cast<void>(b::t);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pure2-bugfix-for-dependent-types-recursion.cpp2... ok (all Cpp2, passes safety checks)

Loading
Loading