Skip to content

Commit

Permalink
Add @union initial test case
Browse files Browse the repository at this point in the history
  • Loading branch information
hsutter committed Sep 12, 2023
1 parent 143b81f commit c78058a
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 0 deletions.
26 changes: 26 additions & 0 deletions regression-tests/pure2-union.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

name_or_number: @union type = {
name: std::string;
num : i32;
}

print_name: (non: name_or_number) = {
if non.is_name() {
std::cout << non.get_name() << "\n";
}
else {
std::cout << "(not a name)\n";
}
}

main: () = {
x: name_or_number = ();
std::cout << "sizeof(x) is (sizeof(x))$\n";

x.print_name();

s: std::string = "xyzzy";
x.set_name( s );

x.print_name();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
sizeof(x) is 33
(not a name)
xyzzy
Empty file.
14 changes: 14 additions & 0 deletions regression-tests/test-results/gcc-10/pure2-union.cpp.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pure2-union.cpp2:6:42: error: expected ‘;’ at end of member declaration
In file included from pure2-union.cpp:7:
../../../include/cpp2util.h:10005:47: error: static assertion failed: GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.
pure2-union.cpp2:7:1: note: in expansion of macro ‘CPP2_REQUIRES_’
pure2-union.cpp2:11:41: error: expected ‘;’ at end of member declaration
In file included from pure2-union.cpp:7:
../../../include/cpp2util.h:10005:47: error: static assertion failed: GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.
pure2-union.cpp2:12:1: note: in expansion of macro ‘CPP2_REQUIRES_’
pure2-union.cpp2:28:6: error: no declaration matches ‘void name_or_number::set_name(auto:82&&) & requires is_same_v<typename std::remove_cv<typename std::remove_reference<decltype(name_or_number::set_name::value)>::type>::type, std::__cxx11::string>’
pure2-union.cpp2:6:14: note: candidate is: ‘template<class auto:80> void name_or_number::set_name(auto:80&&) &’
pure2-union.cpp2:2:7: note: ‘class name_or_number’ defined here
pure2-union.cpp2:35:6: error: no declaration matches ‘void name_or_number::set_num(auto:83&&) & requires is_same_v<typename std::remove_cv<typename std::remove_reference<decltype(name_or_number::set_num::value)>::type>::type, cpp2::i32>’
pure2-union.cpp2:11:14: note: candidate is: ‘template<class auto:81> void name_or_number::set_num(auto:81&&) &’
pure2-union.cpp2:2:7: note: ‘class name_or_number’ defined here
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
sizeof(x) is 33
(not a name)
xyzzy
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
sizeof(x) is 25
(not a name)
xyzzy
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pure2-union.cpp
91 changes: 91 additions & 0 deletions regression-tests/test-results/pure2-union.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@

#define CPP2_USE_MODULES Yes

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


#include "cpp2util.h"


#line 2 "pure2-union.cpp2"
class name_or_number;


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


#line 2 "pure2-union.cpp2"
class name_or_number {
private: std::array<std::byte,cpp2::max(sizeof(std::string), sizeof(cpp2::i32))> storage__ {}; private: cpp2::i8 discriminator__ {-1}; public: [[nodiscard]] auto is_name() const& -> bool;

This comment has been minimized.

Copy link
@JohelEGP

JohelEGP Sep 19, 2023

Contributor

The resulting data structure doesn't have the alignment of the alternative with the strictest alignment (https://cpp2.godbolt.org/z/jExb3qfGb):

name_or_number: @union type = {
    name: std::string;
    num : i32;
}
main: () = {
  static_assert(alignof(name_or_number) < alignof(std::string));
}

This comment has been minimized.

Copy link
@JohelEGP

JohelEGP Sep 19, 2023

Contributor

This should be a more correct formulation.
Taking the generated code of the test,
and changing (https://cpp2.godbolt.org/z/vfvqndKdE):

-class name_or_number {
+class alignas(cpp2::max(alignof(std::string), alignof(cpp2::i32))) name_or_number {

With Libc++, it gives:

sizeof(x) is 32
(not a name)
xyzzy

This comment has been minimized.

Copy link
@JohelEGP

JohelEGP Sep 19, 2023

Contributor

The alignas specifier can also be put on the member variable: https://cpp2.godbolt.org/z/1EPsP111K.

This comment has been minimized.

Copy link
@hsutter

hsutter Sep 25, 2023

Author Owner

I know std::aligned_storage is looked down upon, but darnit, it's the best tool for the job I want, so I'll go reinstate it... 😁 Thanks for pointing out the alignment problem.

This comment has been minimized.

Copy link
@realgdman

realgdman Oct 24, 2023

@hsutter sorry for chiming in... Was googling what std::aligned_storage is and found out it's deprecated in C++23

This comment has been minimized.

Copy link
@gregmarr

gregmarr Oct 24, 2023

Contributor

Yes, that was discussed in another thread, but the replacement alignas() isn't implemented yet in cppfront.

public: [[nodiscard]] auto get_name() const& -> auto&&;
public: [[nodiscard]] auto get_name() & -> auto&&;
public: auto set_name(auto&& value) & -> void
CPP2_REQUIRES_ (std::is_same_v<CPP2_TYPEOF(value), std::string>);
public: [[nodiscard]] auto is_num() const& -> bool;
public: [[nodiscard]] auto get_num() const& -> auto&&;
public: [[nodiscard]] auto get_num() & -> auto&&;
public: auto set_num(auto&& value) & -> void
CPP2_REQUIRES_ (std::is_same_v<CPP2_TYPEOF(value), cpp2::i32>);
private: auto destroy() & -> void;
public: ~name_or_number() noexcept;

public: name_or_number() = default;
public: name_or_number(name_or_number const&) = delete; /* No 'that' constructor, suppress copy */
public: auto operator=(name_or_number const&) -> void = delete;

#line 5 "pure2-union.cpp2"
};

auto print_name(cpp2::in<name_or_number> non) -> void;


#line 16 "pure2-union.cpp2"
auto main() -> int;


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



[[nodiscard]] auto name_or_number::is_name() const& -> bool { return discriminator__ == 0; }
[[nodiscard]] auto name_or_number::get_name() const& -> auto&& {
cpp2::Default.expects(is_name(), "");return *cpp2::assert_not_null(reinterpret_cast<std::string const*>(&storage__)); }
[[nodiscard]] auto name_or_number::get_name() & -> auto&& {
cpp2::Default.expects(is_name(), "");return *cpp2::assert_not_null(reinterpret_cast<std::string*>(&storage__)); }
auto name_or_number::set_name(auto&& value) & -> void
requires (std::is_same_v<CPP2_TYPEOF(value), std::string>){if (!(is_name())) {destroy();std::construct_at(reinterpret_cast<std::string*>(&storage__), value);}else {*cpp2::assert_not_null(reinterpret_cast<std::string*>(&storage__)) = value;}discriminator__ = 0;}
[[nodiscard]] auto name_or_number::is_num() const& -> bool { return discriminator__ == 1; }
[[nodiscard]] auto name_or_number::get_num() const& -> auto&& {
cpp2::Default.expects(is_num(), "");return *cpp2::assert_not_null(reinterpret_cast<cpp2::i32 const*>(&storage__)); }
[[nodiscard]] auto name_or_number::get_num() & -> auto&& {
cpp2::Default.expects(is_num(), "");return *cpp2::assert_not_null(reinterpret_cast<cpp2::i32*>(&storage__)); }
auto name_or_number::set_num(auto&& value) & -> void
requires (std::is_same_v<CPP2_TYPEOF(value), cpp2::i32>){if (!(is_num())) {destroy();std::construct_at(reinterpret_cast<cpp2::i32*>(&storage__), value);}else {*cpp2::assert_not_null(reinterpret_cast<cpp2::i32*>(&storage__)) = value;}discriminator__ = 1;}
auto name_or_number::destroy() & -> void{
if (discriminator__ == 0) {std::destroy_at(reinterpret_cast<std::string*>(&storage__));}
if (discriminator__ == 1) {std::destroy_at(reinterpret_cast<cpp2::i32*>(&storage__));}
}

name_or_number::~name_or_number() noexcept{destroy();}
#line 7 "pure2-union.cpp2"
auto print_name(cpp2::in<name_or_number> non) -> void{
if (CPP2_UFCS_0(is_name, non)) {
std::cout << CPP2_UFCS_0(get_name, non) << "\n";
}
else {
std::cout << "(not a name)\n";
}
}

auto main() -> int{
name_or_number x {};
std::cout << "sizeof(x) is " + cpp2::to_string(sizeof(x)) + "\n";

CPP2_UFCS_0(print_name, x);

std::string s {"xyzzy"};
CPP2_UFCS(set_name, x, std::move(s));

CPP2_UFCS_0(print_name, std::move(x));
}

2 changes: 2 additions & 0 deletions regression-tests/test-results/pure2-union.cpp2.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pure2-union.cpp2... ok (all Cpp2, passes safety checks)

0 comments on commit c78058a

Please sign in to comment.