Skip to content

[BUG] @enum and @flag_enum are not models of std::regular #873

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

Closed
JohelEGP opened this issue Dec 5, 2023 · 2 comments
Closed

[BUG] @enum and @flag_enum are not models of std::regular #873

JohelEGP opened this issue Dec 5, 2023 · 2 comments
Labels
bug Something isn't working

Comments

@JohelEGP
Copy link
Contributor

JohelEGP commented Dec 5, 2023

Title: @enum and @flag_enum are not models of std::regular.

Description:

From #761 (comment):

In commit b589f5d, the enum metafunctions had to stop using basic_value to explicitly opt-into the == syntax for constexpr.

The change didn't add the default constructor.
I except @flag_enums to model std::regular.
Those already generate a none value initialized with 0 that is the perfect default value.

I was also expecting @enums to model std::regular.
But those don't have a none value,
and the first enumerator, which is initialized with 0, doesn't necessarily represent a default value.
But regularity just makes it easier to work with in some contexts.

Minimal reproducer (https://cpp2.godbolt.org/z/v3Ec5zM7r):

engine: @enum type = {
  off;
  on;
}
perms: @flag_enum type = {
  read;
  write;
}
main: () = {
  static_assert(std::default_initializable<engine>);
  static_assert(std::default_initializable<perms>);
}
Commands:
cppfront main.cpp2
clang++18 -std=c++23 -stdlib=libc++ -lc++abi -pedantic-errors -Wall -Wextra -Wconversion -Werror=unused-result -Werror=unused-value -Werror=unused-parameter -I . main.cpp

Expected result: Both types to model std::regular.

Actual result and error: Both static assertions failed.

Cpp2 lowered to Cpp1:
//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"

#line 1 "/app/example.cpp2"
class engine;
#line 2 "/app/example.cpp2"
  

#line 5 "/app/example.cpp2"
class perms;
  

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

#line 1 "/app/example.cpp2"
class engine {
private: cpp2::i8 _value; private: constexpr engine(cpp2::in<cpp2::i64> _val);

private: constexpr auto operator=(cpp2::in<cpp2::i64> _val) -> engine& ;
public: [[nodiscard]] constexpr auto get_raw_value() const& -> cpp2::i8;
public: constexpr engine(engine const& that);
public: constexpr auto operator=(engine const& that) -> engine& ;
public: constexpr engine(engine&& that) noexcept;
public: constexpr auto operator=(engine&& that) noexcept -> engine& ;
public: [[nodiscard]] auto operator<=>(engine const& that) const& -> std::strong_ordering = default;
public: static const engine off;
public: static const engine on;
public: [[nodiscard]] auto to_string() const& -> std::string;

#line 4 "/app/example.cpp2"
};
class perms {
private: cpp2::u8 _value; private: constexpr perms(cpp2::in<cpp2::i64> _val);

private: constexpr auto operator=(cpp2::in<cpp2::i64> _val) -> perms& ;
public: [[nodiscard]] constexpr auto get_raw_value() const& -> cpp2::u8;
public: constexpr perms(perms const& that);
public: constexpr auto operator=(perms const& that) -> perms& ;
public: constexpr perms(perms&& that) noexcept;
public: constexpr auto operator=(perms&& that) noexcept -> perms& ;
public: [[nodiscard]] auto operator<=>(perms const& that) const& -> std::strong_ordering = default;
public: constexpr auto operator|=(perms const& that) & -> void;
public: constexpr auto operator&=(perms const& that) & -> void;
public: constexpr auto operator^=(perms const& that) & -> void;
public: [[nodiscard]] constexpr auto operator|(perms const& that) const& -> perms;
public: [[nodiscard]] constexpr auto operator&(perms const& that) const& -> perms;
public: [[nodiscard]] constexpr auto operator^(perms const& that) const& -> perms;
public: [[nodiscard]] constexpr auto has(perms const& that) & -> bool;
public: constexpr auto set(perms const& that) & -> void;
public: constexpr auto clear(perms const& that) & -> void;
public: static const perms read;
public: static const perms write;
public: static const perms none;
public: [[nodiscard]] auto to_string() const& -> std::string;

#line 8 "/app/example.cpp2"
};
auto main() -> int;

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

#line 1 "/app/example.cpp2"


#line 1 "/app/example.cpp2"
constexpr engine::engine(cpp2::in<cpp2::i64> _val)
                                                          : _value{ cpp2::unsafe_narrow<cpp2::i8>(_val) } {  }
constexpr auto engine::operator=(cpp2::in<cpp2::i64> _val) -> engine&  { 
                                                          _value = cpp2::unsafe_narrow<cpp2::i8>(_val);
                                                          return *this; }
[[nodiscard]] constexpr auto engine::get_raw_value() const& -> cpp2::i8 { return _value; }
constexpr engine::engine(engine const& that)
                                              : _value{ that._value }{}
constexpr auto engine::operator=(engine const& that) -> engine& {
                                              _value = that._value;
                                              return *this;}
constexpr engine::engine(engine&& that) noexcept
                                              : _value{ std::move(that)._value }{}
constexpr auto engine::operator=(engine&& that) noexcept -> engine& {
                                              _value = std::move(that)._value;
                                              return *this;}
inline CPP2_CONSTEXPR engine engine::off = 0;

inline CPP2_CONSTEXPR engine engine::on = 1;

[[nodiscard]] auto engine::to_string() const& -> std::string{
if ((*this) == off) {return "off"; }
if ((*this) == on) {return "on"; }
return "invalid engine value"; 
}

constexpr perms::perms(cpp2::in<cpp2::i64> _val)
                                                          : _value{ cpp2::unsafe_narrow<cpp2::u8>(_val) } {  }
constexpr auto perms::operator=(cpp2::in<cpp2::i64> _val) -> perms&  { 
                                                          _value = cpp2::unsafe_narrow<cpp2::u8>(_val);
                                                          return *this; }
[[nodiscard]] constexpr auto perms::get_raw_value() const& -> cpp2::u8 { return _value; }
constexpr perms::perms(perms const& that)
                                              : _value{ that._value }{}
constexpr auto perms::operator=(perms const& that) -> perms& {
                                              _value = that._value;
                                              return *this;}
constexpr perms::perms(perms&& that) noexcept
                                              : _value{ std::move(that)._value }{}
constexpr auto perms::operator=(perms&& that) noexcept -> perms& {
                                              _value = std::move(that)._value;
                                              return *this;}
constexpr auto perms::operator|=(perms const& that) & -> void { _value |= that._value; }
constexpr auto perms::operator&=(perms const& that) & -> void { _value &= that._value; }
constexpr auto perms::operator^=(perms const& that) & -> void { _value ^= that._value; }
[[nodiscard]] constexpr auto perms::operator|(perms const& that) const& -> perms { return _value | that._value; }
[[nodiscard]] constexpr auto perms::operator&(perms const& that) const& -> perms { return _value & that._value; }
[[nodiscard]] constexpr auto perms::operator^(perms const& that) const& -> perms { return _value ^ that._value; }
[[nodiscard]] constexpr auto perms::has(perms const& that) & -> bool { return _value & that._value; }
constexpr auto perms::set(perms const& that) & -> void { _value |= that._value; }
constexpr auto perms::clear(perms const& that) & -> void { _value &= ~that._value; }
inline CPP2_CONSTEXPR perms perms::read = 1;

inline CPP2_CONSTEXPR perms perms::write = 2;

inline CPP2_CONSTEXPR perms perms::none = 0;

[[nodiscard]] auto perms::to_string() const& -> std::string{

std::string _ret {"("}; 

std::string _comma {}; 
if ((*this) == none) {return "(none)"; }
if (((*this) & read) == read) {_ret += _comma + "read";_comma = ", ";}
if (((*this) & write) == write) {_ret += _comma + "write";_comma = ", ";}
return _ret + ")"; 
}
#line 9 "/app/example.cpp2"
auto main() -> int{
  static_assert(std::default_initializable<engine>);
  static_assert(std::default_initializable<perms>);
}
Output:
Step cmake returned: 0
-- The CXX compiler identification is Clang 18.0.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /opt/compiler-explorer/clang-trunk/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.5s)
-- Generating done (0.0s)
-- Build files have been written to: /app/build
Step build returned: 1
[1/3] Generating main.cpp
main.cpp2... ok (all Cpp2, passes safety checks)

[2/3] Building CXX object CMakeFiles/main.dir/main.cpp.o
FAILED: CMakeFiles/main.dir/main.cpp.o 
/opt/compiler-explorer/clang-trunk/bin/clang++ --gcc-toolchain=/opt/compiler-explorer/gcc-snapshot  -I/app -I/app -std=c++23 -pedantic-errors -Wall -Wextra -Wconversion -Werror=unused-result -Werror=unused-value -Werror=unused-parameter -stdlib=libc++ -Wno-read-modules-implicitly -MD -MT CMakeFiles/main.dir/main.cpp.o -MF CMakeFiles/main.dir/main.cpp.o.d -o CMakeFiles/main.dir/main.cpp.o -c /app/build/main.cpp
main.cpp2:10:17: error: static assertion failed
   10 |   static_assert(std::default_initializable<engine>);
      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp2:10:17: note: because 'engine' does not satisfy 'default_initializable'
/opt/compiler-explorer/clang-trunk-20231205/bin/../include/c++/v1/__concepts/constructible.h:35:33: note: because 'engine' does not satisfy 'constructible_from'
   35 | concept default_initializable = constructible_from<_Tp> && requires { _Tp{}; } && __default_initializable<_Tp>;
      |                                 ^
/opt/compiler-explorer/clang-trunk-20231205/bin/../include/c++/v1/__concepts/constructible.h:27:51: note: because 'is_constructible_v<engine>' evaluated to false
   27 | concept constructible_from = destructible<_Tp> && is_constructible_v<_Tp, _Args...>;
      |                                                   ^
main.cpp2:11:17: error: static assertion failed
   11 |   static_assert(std::default_initializable<perms>);
      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp2:11:17: note: because 'perms' does not satisfy 'default_initializable'
/opt/compiler-explorer/clang-trunk-20231205/bin/../include/c++/v1/__concepts/constructible.h:35:33: note: because 'perms' does not satisfy 'constructible_from'
   35 | concept default_initializable = constructible_from<_Tp> && requires { _Tp{}; } && __default_initializable<_Tp>;
      |                                 ^
/opt/compiler-explorer/clang-trunk-20231205/bin/../include/c++/v1/__concepts/constructible.h:27:51: note: because 'is_constructible_v<perms>' evaluated to false
   27 | concept constructible_from = destructible<_Tp> && is_constructible_v<_Tp, _Args...>;
      |                                                   ^
2 errors generated.
ninja: build stopped: subcommand failed.
@JohelEGP JohelEGP added the bug Something isn't working label Dec 5, 2023
@JohelEGP
Copy link
Contributor Author

JohelEGP commented Dec 5, 2023

But regularity just makes it easier to work with in some contexts.

The most relevant guidelines are

@hsutter
Copy link
Owner

hsutter commented Dec 14, 2023

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants