Skip to content

Commit 698b44c

Browse files
committed
feat: recognize requires expressions
1 parent ccf7011 commit 698b44c

22 files changed

+870
-37
lines changed
Lines changed: 7 additions & 0 deletions
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>);
Lines changed: 8 additions & 0 deletions
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>);
Lines changed: 7 additions & 0 deletions
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>); }
Lines changed: 22 additions & 0 deletions
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+
}
Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
arithmetic: <T> concept = std::integral<T> || std::floating_point<T>;
2-
main: () = {
3-
assert<testing>( arithmetic<i32> );
4-
assert<testing>( arithmetic<float> );
2+
3+
number_difference_t: <T> type == std::type_identity_t<decltype(T() - T())>;
4+
number: <T> concept = std::regular<T> && requires(c: T)
5+
{
6+
!requires std::iter_reference_t<T>; // Negative requirement.
7+
{c + c} is std::common_with<T>; // Compound requirement.
8+
number_difference_t<T>; // Type requirement.
9+
_ = c - c; // Expression requirement.
10+
requires std::common_with<number_difference_t<T>, T>; // Nested requirement.
11+
};
12+
13+
test_nonthrowing_requirements: <T> concept = requires
14+
{ // clang-format off
15+
{ T() } !throws;
16+
{ -T() } !throws, is std::same_as<T>;
17+
}; // clang-format on
18+
19+
main: () = {
20+
static_assert(arithmetic<i32>);
21+
static_assert(arithmetic<float>);
22+
static_assert(number<i32>);
23+
static_assert(number<float>);
24+
static_assert(number<std::chrono::seconds>);
25+
static_assert(!number<* i32>);
26+
static_assert(!number<std::reverse_iterator<* i32>>);
27+
static_assert(test_nonthrowing_requirements<i32>);
28+
static_assert(!test_nonthrowing_requirements<std::chrono::seconds>);
529
}

regression-tests/pure2-print.cpp2

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,19 @@ outer: @print type = {
8787
is 43 = "forty-and-three";
8888
is _ = "default case";
8989
} << "\n";
90+
91+
_ =
92+
requires { std::vector<int>; }
93+
+ requires { _ = 0; }
94+
+ requires {
95+
requires true;
96+
!requires std::vector<int>;
97+
!requires _ = 0;
98+
{ 0 };
99+
{ 0 } !throws;
100+
{ 0 } is std::regular;
101+
{ 0 } !throws, is std::regular;
102+
};
90103
}
91104

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

regression-tests/pure2-requires-clauses.cpp2

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ f: (x) -> int requires true == x;
1919

2020
v: <T> const T requires std::same_as<T, i32> = 0;
2121

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

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,40 @@
1313

1414
#line 1 "pure2-concept-definition.cpp2"
1515
template<typename T> concept arithmetic = std::integral<T> || std::floating_point<T>;
16-
#line 2 "pure2-concept-definition.cpp2"
16+
17+
#line 3 "pure2-concept-definition.cpp2"
18+
template<typename T> using number_difference_t = std::type_identity_t<decltype(T() - T())>;
19+
template<typename T> concept number = std::regular<T> && requires(T const& c) {
20+
21+
requires !requires { typename std::iter_reference_t<T>; };// Negative requirement.
22+
{ c + c } -> std::common_with<T>; // Compound requirement.
23+
typename number_difference_t<T>; // Type requirement.
24+
c - c; // Expression requirement.
25+
requires std::common_with<number_difference_t<T>,T>;
26+
}; // Nested requirement.
27+
28+
template<typename T> concept test_nonthrowing_requirements = requires {
29+
// clang-format off
30+
{ T() } noexcept;
31+
{ -T() } noexcept -> std::same_as<T>;
32+
}; // clang-format on
33+
1734
auto main() -> int;
1835

1936
//=== Cpp2 function definitions =================================================
2037

2138
#line 1 "pure2-concept-definition.cpp2"
2239

23-
#line 2 "pure2-concept-definition.cpp2"
24-
auto main() -> int {
25-
if (cpp2::testing.is_active() && !(arithmetic<cpp2::i32>) ) { cpp2::testing.report_violation(""); }
26-
if (cpp2::testing.is_active() && !(arithmetic<float>) ) { cpp2::testing.report_violation(""); }
40+
#line 19 "pure2-concept-definition.cpp2"
41+
auto main() -> int{
42+
static_assert(arithmetic<cpp2::i32>);
43+
static_assert(arithmetic<float>);
44+
static_assert(number<cpp2::i32>);
45+
static_assert(number<float>);
46+
static_assert(number<std::chrono::seconds>);
47+
static_assert(!(number<cpp2::i32*>));
48+
static_assert(!(number<std::reverse_iterator<cpp2::i32*>>));
49+
static_assert(test_nonthrowing_requirements<cpp2::i32>);
50+
static_assert(!(test_nonthrowing_requirements<std::chrono::seconds>));
2751
}
2852

0 commit comments

Comments
 (0)