Skip to content

Commit b589f5d

Browse files
committedSep 22, 2023
Enable declaring constexpr funcs/vars with ==, improve enum/flag_enum
Simplifying `enum` and `flag_enum` needed: - evolving the `==` compile-time alias design to be the way we we express compile-time `constexpr` functions and objects - including to work around Cpp1's limitation on declaring `constexpr`/`inline`/`static` members of a type that have an incomplete type, including the same type being defined, by using the dance described in https://stackoverflow.com/questions/11928089/ - enabling emitting `operator<<` as a `friend` function
1 parent ba843af commit b589f5d

File tree

16 files changed

+581
-497
lines changed

16 files changed

+581
-497
lines changed
 

‎include/cpp2util.h

+9-48
Original file line numberDiff line numberDiff line change
@@ -1586,59 +1586,20 @@ constexpr auto unsafe_narrow( X&& x ) noexcept -> decltype(auto)
15861586

15871587
//-----------------------------------------------------------------------
15881588
//
1589-
// strict_value: a strong typedef-like helper for value types
1589+
// has_flags: query whether a flag_enum value has all flags in 'flags' set
15901590
//
1591-
// Intended for use as an underlying type for types/variables where you
1592-
// don't want implicit conversions or comparisons to happen between
1593-
// values even if they may share the same underlying type (e.g.,
1594-
// Color::Red and CardGame::Poker may both be represented as an `int`
1595-
// but shouldn't be interconvertible or intercomparable)
1596-
//
1597-
// Used by the `enum` and `flag_enum` metafunctions
1591+
// flags set of flags to check
15981592
//
1593+
// Returns a function object that takes a 'value' of the same type as
1594+
// 'flags', and evaluates to true if and only if 'value' has set all of
1595+
// the bits set in 'flags'
1596+
//
15991597
//-----------------------------------------------------------------------
16001598
//
1601-
template <typename T, typename Tag, bool BitwiseOps>
1602-
class strict_value {
1603-
T t = {};
1604-
public:
1605-
explicit constexpr strict_value() { }
1606-
1607-
template <typename U>
1608-
explicit constexpr strict_value(U const& u) : t{unsafe_narrow<T>(u)} { }
1609-
1610-
template <typename U> requires std::is_convertible_v<T,U>
1611-
explicit constexpr operator U() const { return t; }
1612-
1613-
template <typename U> requires std::is_convertible_v<T,U>
1614-
explicit constexpr operator U() { return t; }
1615-
1616-
constexpr auto operator<=>( strict_value const& ) const -> std::strong_ordering = default;
1617-
1618-
auto to_string() const -> std::string { return Tag::to_string(*this); }
1619-
1620-
friend auto operator<<(std::ostream& o, strict_value const& v) -> std::ostream& { return o << v.to_string(); }
1621-
1622-
// Bitwise operations
1623-
1624-
constexpr auto operator|=( strict_value const& that ) -> strict_value requires BitwiseOps { t |= that.t; return *this; }
1625-
constexpr auto operator&=( strict_value const& that ) -> strict_value requires BitwiseOps { t &= that.t; return *this; }
1626-
constexpr auto operator^=( strict_value const& that ) -> strict_value requires BitwiseOps { t ^= that.t; return *this; }
1627-
1628-
constexpr auto operator| ( strict_value const& that ) const -> strict_value requires BitwiseOps { return strict_value(t | that.t); }
1629-
constexpr auto operator& ( strict_value const& that ) const -> strict_value requires BitwiseOps { return strict_value(t & that.t); }
1630-
constexpr auto operator^ ( strict_value const& that ) const -> strict_value requires BitwiseOps { return strict_value(t ^ that.t); }
1631-
1632-
constexpr auto has ( strict_value const& that ) const -> bool requires BitwiseOps { return t & that.t; }
1633-
1634-
constexpr auto set ( strict_value const& that ) -> void requires BitwiseOps { t |= that.t; }
1635-
constexpr auto clear ( strict_value const& that ) -> void requires BitwiseOps { t &= ~that.t; }
1636-
};
1637-
1638-
template <typename T, typename Tag>
1639-
auto has_flags(strict_value<T, Tag, true> flags)
1599+
template <typename T>
1600+
auto has_flags(T flags)
16401601
{
1641-
return [=](strict_value<T, Tag, true> value) { return (value & flags) == flags; };
1602+
return [=](T value) { return (value & flags) == flags; };
16421603
}
16431604

16441605

‎regression-tests/pure2-enum.cpp2

+13-6
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,17 @@ main: () = {
6565

6666
x = skat_game::diamonds; // ok, can assign one skat_game from another
6767

68+
std::cout << "file_attributes::cached.get_raw_value() is (file_attributes::cached.get_raw_value())$\n";
69+
std::cout << "file_attributes::current.get_raw_value() is (file_attributes::current.get_raw_value())$\n";
70+
std::cout << "file_attributes::obsolete.get_raw_value() is (file_attributes::obsolete.get_raw_value())$\n";
71+
std::cout << "file_attributes::cached_and_current.get_raw_value() is (file_attributes::cached_and_current.get_raw_value())$\n";
72+
6873
f: file_attributes = file_attributes::cached_and_current;
69-
f &= file_attributes::cached | file_attributes::obsolete;
74+
f &= file_attributes::cached | file_attributes::obsolete;
75+
std::cout << "f. get_raw_value() is (f. get_raw_value())$\n";
7076

7177
f2 := file_attributes::cached;
78+
std::cout << "f2.get_raw_value() is (f2.get_raw_value())$\n";
7279

7380
std::cout << "f is " << f << "\n";
7481
std::cout << "f2 is " << f2 << "\n";
@@ -78,8 +85,8 @@ main: () = {
7885
f2.set(file_attributes::cached);
7986
std::cout << "f2 is " << f2 << "\n";
8087

81-
std::cout << "f as int is (f as int )$\n";
82-
std::cout << "f2 as int is (f2 as int )$\n";
88+
std::cout << "f. get_raw_value() is (f. get_raw_value())$\n";
89+
std::cout << "f2.get_raw_value() is (f2.get_raw_value())$\n";
8390

8491
std::cout << "f is (f2) is (f is (f2))$\n";
8592
std::cout << "f2 is (f ) is (f2 is (f ))$\n\n";
@@ -91,14 +98,14 @@ main: () = {
9198

9299
std::cout << "f is " << f << "\n";
93100
std::cout << "f2 is " << f2 << "\n";
94-
std::cout << "f as int is (f as int )$\n";
95-
std::cout << "f2 as int is (f2 as int )$\n";
101+
std::cout << "f. get_raw_value() is (f. get_raw_value())$\n";
102+
std::cout << "f2.get_raw_value() is (f2.get_raw_value())$\n";
96103
std::cout << "f == f2 is (f == f2 )$\n";
97104
std::cout << "f is (f2) is (f is (f2))$\n";
98105
std::cout << "f2 is (f ) is (f2 is (f ))$\n";
99106
std::cout << "(f & f2) == f2 is ((f & f2) == f2)$\n";
100107

101-
std::cout << "inspecting: " << inspect f -> std::string {
108+
std::cout << "inspecting f: " << inspect f -> std::string {
102109
is (file_attributes::current) = "exactly 'current'";
103110
is (cpp2::has_flags(f2)) = "includes all f2's flags ('cached' and 'current')";
104111
is _ = "something else";

‎regression-tests/test-results/clang-12/pure2-enum.cpp.execution

+11-5
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,27 @@ using << prints clubs
44
with if else: clubs
55
with inspect: clubs
66

7+
file_attributes::cached.get_raw_value() is 1
8+
file_attributes::current.get_raw_value() is 2
9+
file_attributes::obsolete.get_raw_value() is 4
10+
file_attributes::cached_and_current.get_raw_value() is 3
11+
f. get_raw_value() is 1
12+
f2.get_raw_value() is 1
713
f is (cached)
814
f2 is (cached)
915
f2 is (none)
1016
f2 is (cached)
11-
f as int is 1
12-
f2 as int is 1
17+
f. get_raw_value() is 1
18+
f2.get_raw_value() is 1
1319
f is (f2) is true
1420
f2 is (f ) is true
1521

1622
f is (cached, current, obsolete, cached_and_current)
1723
f2 is (cached, current, cached_and_current)
18-
f as int is 7
19-
f2 as int is 3
24+
f. get_raw_value() is 7
25+
f2.get_raw_value() is 3
2026
f == f2 is false
2127
f is (f2) is false
2228
f2 is (f ) is false
2329
(f & f2) == f2 is true
24-
inspecting: includes all f2's flags ('cached' and 'current')
30+
inspecting f: includes all f2's flags ('cached' and 'current')

‎regression-tests/test-results/gcc-10/pure2-enum.cpp.execution

+11-5
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,27 @@ using << prints clubs
44
with if else: clubs
55
with inspect: clubs
66

7+
file_attributes::cached.get_raw_value() is 1
8+
file_attributes::current.get_raw_value() is 2
9+
file_attributes::obsolete.get_raw_value() is 4
10+
file_attributes::cached_and_current.get_raw_value() is 3
11+
f. get_raw_value() is 1
12+
f2.get_raw_value() is 1
713
f is (cached)
814
f2 is (cached)
915
f2 is (none)
1016
f2 is (cached)
11-
f as int is 1
12-
f2 as int is 1
17+
f. get_raw_value() is 1
18+
f2.get_raw_value() is 1
1319
f is (f2) is true
1420
f2 is (f ) is true
1521

1622
f is (cached, current, obsolete, cached_and_current)
1723
f2 is (cached, current, cached_and_current)
18-
f as int is 7
19-
f2 as int is 3
24+
f. get_raw_value() is 7
25+
f2.get_raw_value() is 3
2026
f == f2 is false
2127
f is (f2) is false
2228
f2 is (f ) is false
2329
(f & f2) == f2 is true
24-
inspecting: includes all f2's flags ('cached' and 'current')
30+
inspecting f: includes all f2's flags ('cached' and 'current')

‎regression-tests/test-results/gcc-10/pure2-print.cpp.output

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ In file included from pure2-print.cpp:7:
33
pure2-print.cpp2:7:1: note: in expansion of macro ‘CPP2_REQUIRES_’
44
../../../include/cpp2util.h:10005:33: error: expected initializer before ‘static_assert’
55
pure2-print.cpp2:89:1: note: in expansion of macro ‘CPP2_REQUIRES_’
6+
pure2-print.cpp2:6:24: error: ‘constexpr const T outer::object_alias’ is not a static data member of ‘class outer’
7+
pure2-print.cpp2:6:31: error: template definition of non-template ‘constexpr const T outer::object_alias’
68
pure2-print.cpp2:88:37: error: no declaration matches ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::cmp_greater_eq(sizeof (Args)..., 0)’
79
pure2-print.cpp2:88:37: note: no functions named ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::cmp_greater_eq(sizeof (Args)..., 0)’
810
pure2-print.cpp2:4:7: note: ‘class outer’ defined here

‎regression-tests/test-results/gcc-13/pure2-enum.cpp.execution

+11-5
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,27 @@ using << prints clubs
44
with if else: clubs
55
with inspect: clubs
66

7+
file_attributes::cached.get_raw_value() is 1
8+
file_attributes::current.get_raw_value() is 2
9+
file_attributes::obsolete.get_raw_value() is 4
10+
file_attributes::cached_and_current.get_raw_value() is 3
11+
f. get_raw_value() is 1
12+
f2.get_raw_value() is 1
713
f is (cached)
814
f2 is (cached)
915
f2 is (none)
1016
f2 is (cached)
11-
f as int is 1
12-
f2 as int is 1
17+
f. get_raw_value() is 1
18+
f2.get_raw_value() is 1
1319
f is (f2) is true
1420
f2 is (f ) is true
1521

1622
f is (cached, current, obsolete, cached_and_current)
1723
f2 is (cached, current, cached_and_current)
18-
f as int is 7
19-
f2 as int is 3
24+
f. get_raw_value() is 7
25+
f2.get_raw_value() is 3
2026
f == f2 is false
2127
f is (f2) is false
2228
f2 is (f ) is false
2329
(f & f2) == f2 is true
24-
inspecting: includes all f2's flags ('cached' and 'current')
30+
inspecting f: includes all f2's flags ('cached' and 'current')

‎regression-tests/test-results/msvc-2022/pure2-enum.cpp.execution

+11-5
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,27 @@ using << prints clubs
44
with if else: clubs
55
with inspect: clubs
66

7+
file_attributes::cached.get_raw_value() is 1
8+
file_attributes::current.get_raw_value() is 2
9+
file_attributes::obsolete.get_raw_value() is 4
10+
file_attributes::cached_and_current.get_raw_value() is 3
11+
f. get_raw_value() is 1
12+
f2.get_raw_value() is 1
713
f is (cached)
814
f2 is (cached)
915
f2 is (none)
1016
f2 is (cached)
11-
f as int is 1
12-
f2 as int is 1
17+
f. get_raw_value() is 1
18+
f2.get_raw_value() is 1
1319
f is (f2) is true
1420
f2 is (f ) is true
1521

1622
f is (cached, current, obsolete, cached_and_current)
1723
f2 is (cached, current, cached_and_current)
18-
f as int is 7
19-
f2 as int is 3
24+
f. get_raw_value() is 7
25+
f2.get_raw_value() is 3
2026
f == f2 is false
2127
f is (f2) is false
2228
f2 is (f ) is false
2329
(f & f2) == f2 is true
24-
inspecting: includes all f2's flags ('cached' and 'current')
30+
inspecting f: includes all f2's flags ('cached' and 'current')

‎regression-tests/test-results/pure2-bugfix-for-max-munch.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

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

13-
template<typename T> auto static constexpr v = 0;
13+
template<typename T> auto inline constexpr v = 0;
1414
auto main() -> int;
1515

1616

4 commit comments

Comments
 (4)

JohelEGP commented on Sep 27, 2023

@JohelEGP
Contributor

I wonder if we'll be using === to define the expert-level consteval functions.

JohelEGP commented on Sep 27, 2023

@JohelEGP
Contributor

Or for function-scope constexpr objects.

JohelEGP commented on Oct 3, 2023

@JohelEGP
Contributor

I'm thinking that, for consistency, a function scope object alias should also declare a constexpr object.
We can already use statement parameters for object aliases:

f: () = {
  (x := a,
   inout y: const _ = b) {
    …;
  }
}

Unrelated, and with regards to statement parameters.

x := a currently lowers to auto const& x = a.
But a could be a global, so optimizing like in parameters might make sense.
Although we have copy x := a for that case.
And unlike function parameters, statement parameters have more context to explicitly choose.
But I'm still unsure where the optimization for in parameters sits in the case of statement parameters.

JohelEGP commented on Oct 3, 2023

@JohelEGP
Contributor

The syntax for function scope constexpr objects
shouldn't close the possibility of also declaring them static.
It really makes a difference:

Please sign in to comment.