Skip to content

Commit

Permalink
feat: recognize requires expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
JohelEGP committed Oct 9, 2024
1 parent ccf7011 commit 698b44c
Show file tree
Hide file tree
Showing 22 changed files with 870 additions and 37 deletions.
7 changes: 7 additions & 0 deletions regression-tests/pure2-concept-definition-no-pitfall-1.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// https://youtu.be/CXn02MPkn8Y?t=2337
negatable: <T> concept = requires(t: T)
{
_ = -t is T; // Hopefully obviously wrong. Should be `{ -t } is T;`.
};

main: () = static_assert(negatable<char>);
8 changes: 8 additions & 0 deletions regression-tests/pure2-concept-definition-no-pitfall-2.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// https://youtu.be/CXn02MPkn8Y?t=2418
int_sized: <T> concept = requires(t: T)
{
_ = sizeof(T) == 4; // Hopefully obviously wrong. Should be `requires (sizeof(T) == 4);`.
};
// Could also be `int_sized: <T> concept = sizeof(T) == 4;`.

main: () = static_assert(int_sized<char>);
7 changes: 7 additions & 0 deletions regression-tests/pure2-concept-definition-no-pitfall-3.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// https://youtu.be/CXn02MPkn8Y?t=2455
nothrow_incrementable: <T> concept = requires(inout t: T)
{
_ = noexcept(t++); // Hopefully obviously wrong. Should be `requires noexcept(t++);` or `{ t++ } !throws;`.
};

main: () = { static_assert(nothrow_incrementable<char>); }
22 changes: 22 additions & 0 deletions regression-tests/pure2-concept-definition-pit-of-success-1.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// https://quuxplusone.github.io/blog/2021/06/09/another-concepts-chest-mimic/
has_a_but_not_b: <T> concept = requires(t: T)
{
_ = a(t);
!requires _ = b(t); // In Cpp2, this works and does the correct thing.
};

s1: @struct type = { }
s2: @struct type = { }
a: (_: s2) = { }
s3: @struct type = { }
b: (_: s3) = { }
s4: @struct type = { }
a: (_: s4) = { }
b: (_: s4) = { }

main: () = {
static_assert(!has_a_but_not_b<s1>); // as expected
static_assert(has_a_but_not_b<s2>); // as expected
static_assert(!has_a_but_not_b<s3>); // as expected
static_assert(!has_a_but_not_b<s4>); // pit of success!
}
30 changes: 27 additions & 3 deletions regression-tests/pure2-concept-definition.cpp2
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
arithmetic: <T> concept = std::integral<T> || std::floating_point<T>;
main: () = {
assert<testing>( arithmetic<i32> );
assert<testing>( arithmetic<float> );

number_difference_t: <T> type == std::type_identity_t<decltype(T() - T())>;
number: <T> concept = std::regular<T> && requires(c: T)
{
!requires std::iter_reference_t<T>; // Negative requirement.
{c + c} is std::common_with<T>; // Compound requirement.
number_difference_t<T>; // Type requirement.
_ = c - c; // Expression requirement.
requires std::common_with<number_difference_t<T>, T>; // Nested requirement.
};

test_nonthrowing_requirements: <T> concept = requires
{ // clang-format off
{ T() } !throws;
{ -T() } !throws, is std::same_as<T>;
}; // clang-format on

main: () = {
static_assert(arithmetic<i32>);
static_assert(arithmetic<float>);
static_assert(number<i32>);
static_assert(number<float>);
static_assert(number<std::chrono::seconds>);
static_assert(!number<* i32>);
static_assert(!number<std::reverse_iterator<* i32>>);
static_assert(test_nonthrowing_requirements<i32>);
static_assert(!test_nonthrowing_requirements<std::chrono::seconds>);
}
13 changes: 13 additions & 0 deletions regression-tests/pure2-print.cpp2
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ outer: @print type = {
is 43 = "forty-and-three";
is _ = "default case";
} << "\n";

_ =
requires { std::vector<int>; }
+ requires { _ = 0; }
+ requires {
requires true;
!requires std::vector<int>;
!requires _ = 0;
{ 0 };
{ 0 } !throws;
{ 0 } is std::regular;
{ 0 } !throws, is std::regular;
};
}

x: <Ts...: type> type = {
Expand Down
2 changes: 2 additions & 0 deletions regression-tests/pure2-requires-clauses.cpp2
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ f: (x) -> int requires true == x;

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

g: <T> () requires true = { }

main: () = {
_: X<int,int> = ();
std::cout << f<int,int>(2,5)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

#define CPP2_IMPORT_STD Yes

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


#include "cpp2util.h"

#line 1 "pure2-concept-definition-no-pitfall-1.cpp2"


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

#line 1 "pure2-concept-definition-no-pitfall-1.cpp2"
// https://youtu.be/CXn02MPkn8Y?t=2337
#line 2 "pure2-concept-definition-no-pitfall-1.cpp2"
template<typename T> concept negatable = requires(T const& t) {

cpp2::impl::is<T>(-t);
}; // Hopefully obviously wrong. Should be `{ -t } is T;`.

auto main() -> int;

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

#line 1 "pure2-concept-definition-no-pitfall-1.cpp2"

#line 7 "pure2-concept-definition-no-pitfall-1.cpp2"
auto main() -> int { static_assert(negatable<char>); }

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pure2-concept-definition-no-pitfall-1.cpp2... ok (all Cpp2, passes safety checks)

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

#define CPP2_IMPORT_STD Yes

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


#include "cpp2util.h"

#line 1 "pure2-concept-definition-no-pitfall-2.cpp2"


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

#line 1 "pure2-concept-definition-no-pitfall-2.cpp2"
// https://youtu.be/CXn02MPkn8Y?t=2418
#line 2 "pure2-concept-definition-no-pitfall-2.cpp2"
template<typename T> concept int_sized = requires(T const& t) {

sizeof(T) == 4;
}; // Hopefully obviously wrong. Should be `requires (sizeof(T) == 4);`.
// Could also be `int_sized: <T> concept = sizeof(T) == 4;`.

auto main() -> int;

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

#line 1 "pure2-concept-definition-no-pitfall-2.cpp2"

#line 8 "pure2-concept-definition-no-pitfall-2.cpp2"
auto main() -> int { static_assert(int_sized<char>); }

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pure2-concept-definition-no-pitfall-2.cpp2... ok (all Cpp2, passes safety checks)

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

#define CPP2_IMPORT_STD Yes

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


#include "cpp2util.h"

#line 1 "pure2-concept-definition-no-pitfall-3.cpp2"


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

#line 1 "pure2-concept-definition-no-pitfall-3.cpp2"
// https://youtu.be/CXn02MPkn8Y?t=2455
#line 2 "pure2-concept-definition-no-pitfall-3.cpp2"
template<typename T> concept nothrow_incrementable = requires(T& t) {

noexcept(++t);
}; // Hopefully obviously wrong. Should be `requires noexcept(t++);` or `{ t++ } !throws;`.

auto main() -> int;

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

#line 1 "pure2-concept-definition-no-pitfall-3.cpp2"

#line 7 "pure2-concept-definition-no-pitfall-3.cpp2"
auto main() -> int{static_assert(nothrow_incrementable<char>); }

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pure2-concept-definition-no-pitfall-3.cpp2... ok (all Cpp2, passes safety checks)

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@

#define CPP2_IMPORT_STD Yes

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


#include "cpp2util.h"

#line 1 "pure2-concept-definition-pit-of-success-1.cpp2"

#line 8 "pure2-concept-definition-pit-of-success-1.cpp2"
class s1;
class s2;

class s3;

class s4;


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

#line 1 "pure2-concept-definition-pit-of-success-1.cpp2"
// https://quuxplusone.github.io/blog/2021/06/09/another-concepts-chest-mimic/
#line 2 "pure2-concept-definition-pit-of-success-1.cpp2"
template<typename T> concept has_a_but_not_b = requires(T const& t) {

a(t);
requires !requires { b(t); };
}; // In Cpp2, this works and does the correct thing.

class s1 {};
class s2 {};
auto a([[maybe_unused]] cpp2::impl::in<s2> unnamed_param_1) -> void;
class s3 {};
auto b([[maybe_unused]] cpp2::impl::in<s3> unnamed_param_1) -> void;
class s4 {};
auto a([[maybe_unused]] cpp2::impl::in<s4> unnamed_param_1) -> void;
auto b([[maybe_unused]] cpp2::impl::in<s4> unnamed_param_1) -> void;

auto main() -> int;

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

#line 1 "pure2-concept-definition-pit-of-success-1.cpp2"

#line 10 "pure2-concept-definition-pit-of-success-1.cpp2"
auto a([[maybe_unused]] cpp2::impl::in<s2> unnamed_param_1) -> void{}

#line 12 "pure2-concept-definition-pit-of-success-1.cpp2"
auto b([[maybe_unused]] cpp2::impl::in<s3> unnamed_param_1) -> void{}

#line 14 "pure2-concept-definition-pit-of-success-1.cpp2"
auto a([[maybe_unused]] cpp2::impl::in<s4> unnamed_param_1) -> void{}
#line 15 "pure2-concept-definition-pit-of-success-1.cpp2"
auto b([[maybe_unused]] cpp2::impl::in<s4> unnamed_param_1) -> void{}

#line 17 "pure2-concept-definition-pit-of-success-1.cpp2"
auto main() -> int{
static_assert(!(has_a_but_not_b<s1>));// as expected
static_assert(has_a_but_not_b<s2>); // as expected
static_assert(!(has_a_but_not_b<s3>));// as expected
static_assert(!(has_a_but_not_b<s4>));// pit of success!
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pure2-concept-definition-pit-of-success-1.cpp2... ok (all Cpp2, passes safety checks)

34 changes: 29 additions & 5 deletions regression-tests/test-results/pure2-concept-definition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,40 @@

#line 1 "pure2-concept-definition.cpp2"
template<typename T> concept arithmetic = std::integral<T> || std::floating_point<T>;
#line 2 "pure2-concept-definition.cpp2"

#line 3 "pure2-concept-definition.cpp2"
template<typename T> using number_difference_t = std::type_identity_t<decltype(T() - T())>;
template<typename T> concept number = std::regular<T> && requires(T const& c) {

requires !requires { typename std::iter_reference_t<T>; };// Negative requirement.
{ c + c } -> std::common_with<T>; // Compound requirement.
typename number_difference_t<T>; // Type requirement.
c - c; // Expression requirement.
requires std::common_with<number_difference_t<T>,T>;
}; // Nested requirement.

template<typename T> concept test_nonthrowing_requirements = requires {
// clang-format off
{ T() } noexcept;
{ -T() } noexcept -> std::same_as<T>;
}; // clang-format on

auto main() -> int;

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

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

#line 2 "pure2-concept-definition.cpp2"
auto main() -> int {
if (cpp2::testing.is_active() && !(arithmetic<cpp2::i32>) ) { cpp2::testing.report_violation(""); }
if (cpp2::testing.is_active() && !(arithmetic<float>) ) { cpp2::testing.report_violation(""); }
#line 19 "pure2-concept-definition.cpp2"
auto main() -> int{
static_assert(arithmetic<cpp2::i32>);
static_assert(arithmetic<float>);
static_assert(number<cpp2::i32>);
static_assert(number<float>);
static_assert(number<std::chrono::seconds>);
static_assert(!(number<cpp2::i32*>));
static_assert(!(number<std::reverse_iterator<cpp2::i32*>>));
static_assert(test_nonthrowing_requirements<cpp2::i32>);
static_assert(!(test_nonthrowing_requirements<std::chrono::seconds>));
}

Loading

0 comments on commit 698b44c

Please sign in to comment.