Skip to content

Commit

Permalink
fix(cpp1): improve recognition of dependent types and deducible param…
Browse files Browse the repository at this point in the history
…eters
  • Loading branch information
JohelEGP committed Sep 22, 2023
1 parent efd185f commit 15b7551
Show file tree
Hide file tree
Showing 17 changed files with 722 additions and 64 deletions.
34 changes: 34 additions & 0 deletions regression-tests/pure2-bugfix-for-deducible-parameters.cpp2
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) = { }
}
92 changes: 92 additions & 0 deletions regression-tests/pure2-bugfix-for-dependent-types.cpp2
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);
}
4 changes: 4 additions & 0 deletions regression-tests/test-results/clang-18/clang-version.output
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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
sizeof(x) is 25
(not a name)
xyz
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"
}

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 regression-tests/test-results/pure2-bugfix-for-dependent-types.cpp
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));
}

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 15b7551

Please sign in to comment.