Skip to content

Commit

Permalink
fix(to_cpp1): improve recognition of dependent types and deducible pa…
Browse files Browse the repository at this point in the history
…rameters
  • Loading branch information
JohelEGP committed Oct 2, 2024
1 parent c5feb42 commit ace8cca
Show file tree
Hide file tree
Showing 11 changed files with 798 additions and 83 deletions.
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> (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> (_: 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);
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;
}
95 changes: 95 additions & 0 deletions regression-tests/pure2-bugfix-for-dependent-types.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
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; // 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);

// clang-format off
_ = : ::t<zero> = (); // clang-format on

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

// 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,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(cpp2::impl::in<std::integral_constant<cpp2::i32,T::value>> x) -> 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(cpp2::impl::in<std::integral_constant<cpp2::i32,T::value>> x) -> 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)

133 changes: 133 additions & 0 deletions regression-tests/test-results/pure2-bugfix-for-dependent-types.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@

#define CPP2_IMPORT_STD Yes

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


#include "cpp2util.h"

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

#line 47 "pure2-bugfix-for-dependent-types.cpp2"
template<typename T> class t;


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

#line 1 "pure2-bugfix-for-dependent-types.cpp2"
template<typename T> using identity = T;
#line 2 "pure2-bugfix-for-dependent-types.cpp2"

template<typename T, T::value_type V> [[nodiscard]] auto f(cpp2::impl::in<typename T::value_type> x) -> T::value_type;

#line 47 "pure2-bugfix-for-dependent-types.cpp2"
template<typename T> class t {
struct u_x_as_base { typename T::value_type x; };

#line 48 "pure2-bugfix-for-dependent-types.cpp2"
public: class u: public u_x_as_base, public T::type {

#line 51 "pure2-bugfix-for-dependent-types.cpp2"
// Test that there's no 'typename' in the member initializer list.
public: u(u const& that);
};
private: T::value_type x {0};
};

auto main() -> int;

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

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

#line 3 "pure2-bugfix-for-dependent-types.cpp2"
template<typename T, T::value_type V> [[nodiscard]] auto f(cpp2::impl::in<typename T::value_type> x) -> T::value_type{
if (cpp2::cpp2_default.is_active() && !(cpp2::impl::is<typename T::value_type>(x)) ) { cpp2::cpp2_default.report_violation(""); }
cpp2::impl::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*; // Also test lookup through type aliases.
using nptr = cpp2::i32*;
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.
// _ = :identity<typename std::pointer_traits<ptr>::rebind<ptr>> = (); // Non type-only context. Blocked on #727.

// 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 52 "pure2-bugfix-for-dependent-types.cpp2"
template <typename T> t<T>::u::u(u const& that)
: u_x_as_base{ that.x }
, T::type{ static_cast<typename T::type const&>(that) }{}

#line 57 "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).
// _ = :(move f) = f.operator()<i32>();(:<T> () = {}); // Blocked on #832.

// Nesting is not relevant to lookup.
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>([]([[maybe_unused]] cpp2::impl::in<typename T::value_type> unnamed_param_1) -> void{}); }); }); });
static_cast<void>([]<typename T>() -> void{static_cast<void>([]() -> void{static_cast<void>([]([[maybe_unused]] cpp2::impl::in<typename T::value_type> unnamed_param_1) -> void{static_cast<void>([]() -> void{}); }); }); });
static_cast<void>([]<typename T>() -> void{static_cast<void>([]([[maybe_unused]] cpp2::impl::in<typename T::value_type> unnamed_param_1) -> void{static_cast<void>([]() -> void{static_cast<void>([]() -> void{}); }); }); });
static_cast<void>([]<typename T>([[maybe_unused]] cpp2::impl::in<typename T::value_type> unnamed_param_1) -> 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& unnamed_param_1) -> 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>(alias::value_type{0});// Non-dependent.
}
}(0));

static_cast<void>([](auto const& r) -> std::type_identity_t<decltype(*cpp2::impl::assert_not_null(begin(r)))> { return CPP2_ASSERT_IN_BOUNDS_LITERAL(r, 0); }(std::vector<int>(1)));
}

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)

Loading

0 comments on commit ace8cca

Please sign in to comment.