Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f6d0ff5

Browse files
committedOct 30, 2023
feat: recognize requires expressions
1 parent 083c8a0 commit f6d0ff5

30 files changed

+840
-25
lines changed
 

‎include/cpp2util.h

+8-8
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@
116116
// in our -pure-cpp2 "import std;" simulation mode... if you need this,
117117
// use mixed mode (not -pure-cpp2) and #include all the headers you need
118118
// including this one
119-
//
119+
//
120120
// #include <execution>
121121
#ifdef __cpp_lib_expected
122122
#include <expected>
@@ -525,7 +525,7 @@ template<typename T>
525525
auto Typeid() -> decltype(auto) {
526526
#ifdef CPP2_NO_RTTI
527527
Type.expects(
528-
!"'any' dynamic casting is disabled with -fno-rtti", // more likely to appear on console
528+
!"'any' dynamic casting is disabled with -fno-rtti", // more likely to appear on console
529529
"'any' dynamic casting is disabled with -fno-rtti" // make message available to hooked handlers
530530
);
531531
#else
@@ -901,7 +901,7 @@ inline auto to_string(std::string const& s) -> std::string const&
901901

902902
template<typename T>
903903
inline auto to_string(T const& sv) -> std::string
904-
requires (std::is_convertible_v<T, std::string_view>
904+
requires (std::is_convertible_v<T, std::string_view>
905905
&& !std::is_convertible_v<T, const char*>)
906906
{
907907
return std::string{sv};
@@ -1041,17 +1041,17 @@ auto is( X const& ) -> bool {
10411041

10421042
template< typename C, typename X >
10431043
requires (
1044-
( std::is_base_of_v<X, C> ||
1045-
( std::is_polymorphic_v<C> && std::is_polymorphic_v<X>)
1044+
( std::is_base_of_v<X, C> ||
1045+
( std::is_polymorphic_v<C> && std::is_polymorphic_v<X>)
10461046
) && !std::is_same_v<C,X>)
10471047
auto is( X const& x ) -> bool {
10481048
return Dynamic_cast<C const*>(&x) != nullptr;
10491049
}
10501050

10511051
template< typename C, typename X >
10521052
requires (
1053-
( std::is_base_of_v<X, C> ||
1054-
( std::is_polymorphic_v<C> && std::is_polymorphic_v<X>)
1053+
( std::is_base_of_v<X, C> ||
1054+
( std::is_polymorphic_v<C> && std::is_polymorphic_v<X>)
10551055
) && !std::is_same_v<C,X>)
10561056
auto is( X const* x ) -> bool {
10571057
return Dynamic_cast<C const*>(x) != nullptr;
@@ -1682,7 +1682,7 @@ constexpr auto unsafe_narrow( X&& x ) noexcept -> decltype(auto)
16821682
// Returns a function object that takes a 'value' of the same type as
16831683
// 'flags', and evaluates to true if and only if 'value' has set all of
16841684
// the bits set in 'flags'
1685-
//
1685+
//
16861686
//-----------------------------------------------------------------------
16871687
//
16881688
template <typename T>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// https://youtu.be/CXn02MPkn8Y?t=2337
2+
negatable: <T> concept = requires(t: T)
3+
{
4+
_ = -t is T; // Hopefully obviously wrong. Should be `{ -t } is T;`.
5+
};
6+
7+
main: () = static_assert(negatable<char>);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// https://youtu.be/CXn02MPkn8Y?t=2418
2+
int_sized: <T> concept = requires(t: T)
3+
{
4+
_ = sizeof(T) == 4; // Hopefully obviously wrong. Should be `requires (sizeof(T) == 4);`.
5+
};
6+
// Could also be `int_sized: <T> concept = sizeof(T) == 4;`.
7+
8+
main: () = static_assert(int_sized<char>);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// https://youtu.be/CXn02MPkn8Y?t=2455
2+
nothrow_incrementable: <T> concept = requires(inout t: T)
3+
{
4+
_ = noexcept(t++); // Hopefully obviously wrong. Should be `requires noexcept(t++);` or `{ t++ } !throws;`.
5+
};
6+
7+
main: () = { static_assert(nothrow_incrementable<char>); }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// https://quuxplusone.github.io/blog/2021/06/09/another-concepts-chest-mimic/
2+
has_a_but_not_b: <T> concept = requires(t: T)
3+
{
4+
_ = a(t);
5+
!requires _ = b(t); // In Cpp2, this works and does the correct thing.
6+
};
7+
8+
s1: @struct type = { }
9+
s2: @struct type = { }
10+
a: (_: s2) = { }
11+
s3: @struct type = { }
12+
b: (_: s3) = { }
13+
s4: @struct type = { }
14+
a: (_: s4) = { }
15+
b: (_: s4) = { }
16+
17+
main: () = {
18+
static_assert(!has_a_but_not_b<s1>); // as expected
19+
static_assert(has_a_but_not_b<s2>); // as expected
20+
static_assert(!has_a_but_not_b<s3>); // as expected
21+
static_assert(!has_a_but_not_b<s4>); // pit of success!
22+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
arithmetic: <T> concept = std::integral<T> || std::floating_point<T>;
2-
main: () = {
3-
[[assert Testing: arithmetic<i32>]]
4-
[[assert Testing: arithmetic<float>]]
2+
number_difference_t: <T> type == std::type_identity_t<decltype(T() - T())>;
3+
number: <T> concept = std::regular<T> && requires(c: T)
4+
{
5+
!requires std::iter_reference_t<T>; // Negative requirement.
6+
{c + c} is std::common_with<T>; // Compound requirement.
7+
number_difference_t<T>; // Type requirement.
8+
_ = c - c; // Expression requirement.
9+
requires std::common_with<number_difference_t<T>, T>; // Nested requirement.
10+
};
11+
test_nonthrowing_requirements: <T> concept = requires
12+
{ // clang-format off
13+
{ T() } !throws;
14+
{ -T() } !throws, is std::same_as<T>;
15+
}; // clang-format on
16+
main: () = {
17+
static_assert(arithmetic<i32>);
18+
static_assert(arithmetic<float>);
19+
static_assert(number<i32>);
20+
static_assert(number<float>);
21+
static_assert(number<std::chrono::seconds>);
22+
static_assert(!number<* i32>);
23+
static_assert(!number<std::reverse_iterator<* i32>>);
24+
static_assert(test_nonthrowing_requirements<i32>);
25+
static_assert(!test_nonthrowing_requirements<std::chrono::seconds>);
526
}

‎regression-tests/pure2-print.cpp2

+13
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,19 @@ outer: @print type = {
8484
is 43 = "forty-and-three";
8585
is _ = "default case";
8686
} << "\n";
87+
88+
_ =
89+
requires { std::vector<int>; }
90+
+ requires { _ = 0; }
91+
+ requires {
92+
requires true;
93+
!requires std::vector<int>;
94+
!requires _ = 0;
95+
{ 0 };
96+
{ 0 } !throws;
97+
{ 0 } is std::regular;
98+
{ 0 } !throws, is std::regular;
99+
};
87100
}
88101

89102
x: <Ts...: type> type = {

‎regression-tests/pure2-requires-clauses.cpp2

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ f: <T: type, U: type>
1717

1818
v: <T> const T requires std::same_as<T, i32> = 0;
1919

20+
g: <T> () requires true = { }
21+
g: () (requires { _ = 0; });
22+
2023
main: () = {
2124
x: X<int,int> = ();
2225
std::cout << f<int,int>(2,5);

‎regression-tests/test-results/gcc-13/pure2-concept-definition-no-pitfall-1.cpp.execution

Whitespace-only changes.

‎regression-tests/test-results/gcc-13/pure2-concept-definition-no-pitfall-1.cpp.output

Whitespace-only changes.

‎regression-tests/test-results/gcc-13/pure2-concept-definition-no-pitfall-2.cpp.execution

Whitespace-only changes.

‎regression-tests/test-results/gcc-13/pure2-concept-definition-no-pitfall-2.cpp.output

Whitespace-only changes.

‎regression-tests/test-results/gcc-13/pure2-concept-definition-no-pitfall-3.cpp.execution

Whitespace-only changes.

‎regression-tests/test-results/gcc-13/pure2-concept-definition-no-pitfall-3.cpp.output

Whitespace-only changes.

‎regression-tests/test-results/gcc-13/pure2-concept-definition-pit-of-success-1.cpp.execution

Whitespace-only changes.

‎regression-tests/test-results/gcc-13/pure2-concept-definition-pit-of-success-1.cpp.output

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
#define CPP2_IMPORT_STD Yes
3+
4+
//=== Cpp2 type declarations ====================================================
5+
6+
7+
#include "cpp2util.h"
8+
9+
10+
11+
//=== Cpp2 type definitions and function declarations ===========================
12+
13+
// https://youtu.be/CXn02MPkn8Y?t=2337
14+
#line 2 "pure2-concept-definition-no-pitfall-1.cpp2"
15+
template<typename T> concept negatable = requires(T const& t) {
16+
17+
cpp2::is<T>(-t);
18+
}; // Hopefully obviously wrong. Should be `{ -t } is T;`.
19+
20+
auto main() -> int;
21+
22+
23+
//=== Cpp2 function definitions =================================================
24+
25+
26+
#line 7 "pure2-concept-definition-no-pitfall-1.cpp2"
27+
auto main() -> int { static_assert(negatable<char>); }
28+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-concept-definition-no-pitfall-1.cpp2... ok (all Cpp2, passes safety checks)
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
#define CPP2_IMPORT_STD Yes
3+
4+
//=== Cpp2 type declarations ====================================================
5+
6+
7+
#include "cpp2util.h"
8+
9+
10+
11+
//=== Cpp2 type definitions and function declarations ===========================
12+
13+
// https://youtu.be/CXn02MPkn8Y?t=2418
14+
#line 2 "pure2-concept-definition-no-pitfall-2.cpp2"
15+
template<typename T> concept int_sized = requires(T const& t) {
16+
17+
sizeof(T) == 4;
18+
}; // Hopefully obviously wrong. Should be `requires (sizeof(T) == 4);`.
19+
// Could also be `int_sized: <T> concept = sizeof(T) == 4;`.
20+
21+
auto main() -> int;
22+
23+
24+
//=== Cpp2 function definitions =================================================
25+
26+
27+
#line 8 "pure2-concept-definition-no-pitfall-2.cpp2"
28+
auto main() -> int { static_assert(int_sized<char>); }
29+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-concept-definition-no-pitfall-2.cpp2... ok (all Cpp2, passes safety checks)
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
#define CPP2_IMPORT_STD Yes
3+
4+
//=== Cpp2 type declarations ====================================================
5+
6+
7+
#include "cpp2util.h"
8+
9+
10+
11+
//=== Cpp2 type definitions and function declarations ===========================
12+
13+
// https://youtu.be/CXn02MPkn8Y?t=2455
14+
#line 2 "pure2-concept-definition-no-pitfall-3.cpp2"
15+
template<typename T> concept nothrow_incrementable = requires(T& t) {
16+
17+
noexcept(++t);
18+
}; // Hopefully obviously wrong. Should be `requires noexcept(t++);` or `{ t++ } !throws;`.
19+
20+
auto main() -> int;
21+
22+
23+
//=== Cpp2 function definitions =================================================
24+
25+
26+
#line 7 "pure2-concept-definition-no-pitfall-3.cpp2"
27+
auto main() -> int{static_assert(nothrow_incrementable<char>); }
28+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-concept-definition-no-pitfall-3.cpp2... ok (all Cpp2, passes safety checks)
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
2+
#define CPP2_IMPORT_STD Yes
3+
4+
//=== Cpp2 type declarations ====================================================
5+
6+
7+
#include "cpp2util.h"
8+
9+
10+
#line 8 "pure2-concept-definition-pit-of-success-1.cpp2"
11+
class s1;
12+
class s2;
13+
14+
class s3;
15+
16+
class s4;
17+
18+
19+
//=== Cpp2 type definitions and function declarations ===========================
20+
21+
// https://quuxplusone.github.io/blog/2021/06/09/another-concepts-chest-mimic/
22+
#line 2 "pure2-concept-definition-pit-of-success-1.cpp2"
23+
template<typename T> concept has_a_but_not_b = requires(T const& t) {
24+
25+
a(t);
26+
requires !requires { b(t); };
27+
}; // In Cpp2, this works and does the correct thing.
28+
29+
class s1 {};
30+
class s2 {};
31+
auto a([[maybe_unused]] cpp2::in<s2> param1) -> void;
32+
class s3 {};
33+
auto b([[maybe_unused]] cpp2::in<s3> param1) -> void;
34+
class s4 {};
35+
auto a([[maybe_unused]] cpp2::in<s4> param1) -> void;
36+
auto b([[maybe_unused]] cpp2::in<s4> param1) -> void;
37+
38+
auto main() -> int;
39+
40+
41+
//=== Cpp2 function definitions =================================================
42+
43+
44+
#line 10 "pure2-concept-definition-pit-of-success-1.cpp2"
45+
auto a([[maybe_unused]] cpp2::in<s2> param1) -> void{}
46+
47+
auto b([[maybe_unused]] cpp2::in<s3> param1) -> void{}
48+
49+
auto a([[maybe_unused]] cpp2::in<s4> param1) -> void{}
50+
auto b([[maybe_unused]] cpp2::in<s4> param1) -> void{}
51+
52+
auto main() -> int{
53+
static_assert(!(has_a_but_not_b<s1>));// as expected
54+
static_assert(has_a_but_not_b<s2>); // as expected
55+
static_assert(!(has_a_but_not_b<s3>));// as expected
56+
static_assert(!(has_a_but_not_b<s4>));// pit of success!
57+
}
58+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-concept-definition-pit-of-success-1.cpp2... ok (all Cpp2, passes safety checks)
2+

‎regression-tests/test-results/pure2-concept-definition.cpp

+25-4
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,36 @@
1111
//=== Cpp2 type definitions and function declarations ===========================
1212

1313
template<typename T> concept arithmetic = std::integral<T> || std::floating_point<T>;
14+
template<typename T> using number_difference_t = std::type_identity_t<decltype(T() - T())>;
15+
template<typename T> concept number = std::regular<T> && requires(T const& c) {
16+
17+
requires !requires { typename std::iter_reference_t<T>; };// Negative requirement.
18+
{ c + c } -> std::common_with<T>; // Compound requirement.
19+
typename number_difference_t<T>; // Type requirement.
20+
c - c; // Expression requirement.
21+
requires std::common_with<number_difference_t<T>,T>;
22+
}; // Nested requirement.
23+
template<typename T> concept test_nonthrowing_requirements = requires {
24+
// clang-format off
25+
{ T() } noexcept;
26+
{ -T() } noexcept -> std::same_as<T>;
27+
}; // clang-format on
1428
auto main() -> int;
1529

1630

1731
//=== Cpp2 function definitions =================================================
1832

1933

20-
#line 2 "pure2-concept-definition.cpp2"
21-
auto main() -> int {
22-
cpp2::Testing.expects(arithmetic<cpp2::i32>, "");
23-
cpp2::Testing.expects(arithmetic<float>, "");
34+
#line 16 "pure2-concept-definition.cpp2"
35+
auto main() -> int{
36+
static_assert(arithmetic<cpp2::i32>);
37+
static_assert(arithmetic<float>);
38+
static_assert(number<cpp2::i32>);
39+
static_assert(number<float>);
40+
static_assert(number<std::chrono::seconds>);
41+
static_assert(!(number<cpp2::i32*>));
42+
static_assert(!(number<std::reverse_iterator<cpp2::i32*>>));
43+
static_assert(test_nonthrowing_requirements<cpp2::i32>);
44+
static_assert(!(test_nonthrowing_requirements<std::chrono::seconds>));
2445
}
2546

0 commit comments

Comments
 (0)
Please sign in to comment.