Skip to content

feat: recognize requires expressions #596

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
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Expand Up @@ -6,12 +6,13 @@ In file included from pure2-print.cpp:7:
../../../include/cpp2util.h:10005:47: error: static assertion failed: GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.
pure2-print.cpp2:68:1: note: in expansion of macro ‘CPP2_REQUIRES_’
../../../include/cpp2util.h:10005:33: error: expected initializer before ‘static_assert’
pure2-print.cpp2:97:1: note: in expansion of macro ‘CPP2_REQUIRES_’
pure2-print.cpp2:110:1: note: in expansion of macro ‘CPP2_REQUIRES_’
pure2-print.cpp2:9:41: error: ‘constexpr const T outer::object_alias’ is not a static data member of ‘class outer’
pure2-print.cpp2:9:48: error: template definition of non-template ‘constexpr const T outer::object_alias’
pure2-print.cpp2:67:14: error: no declaration matches ‘void outer::mytype::variadic(const auto:97& ...) requires (is_convertible_v<typename std::remove_cv<typename std::remove_reference<decltype(outer::mytype::variadic::x)>::type>::type, int> && ...)’
pure2-print.cpp2:67:29: note: candidate is: ‘template<class ... auto:96> static void outer::mytype::variadic(const auto:96& ...)’
pure2-print.cpp2:10:19: note: ‘class outer::mytype’ defined here
pure2-print.cpp2:96:37: error: no declaration matches ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::impl::cmp_greater_eq(sizeof ... (Args ...), 0)’
pure2-print.cpp2:96:37: note: no functions named ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::impl::cmp_greater_eq(sizeof ... (Args ...), 0)’
pure2-print.cpp2:96:24: note: the expression ‘!requires{std::vector<int, std::allocator<int> >;}’ evaluated to ‘false’
pure2-print.cpp2:109:37: error: no declaration matches ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::impl::cmp_greater_eq(sizeof ... (Args ...), 0)’
pure2-print.cpp2:109:37: note: no functions named ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::impl::cmp_greater_eq(sizeof ... (Args ...), 0)’
pure2-print.cpp2:6:7: note: ‘class outer’ defined here
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pure2-concept-definition-no-pitfall-1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pure2-concept-definition-no-pitfall-2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pure2-concept-definition-no-pitfall-3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pure2-concept-definition-pit-of-success-1.cpp
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pure2-concept-definition.cpp
pure2-concept-definition.cpp2(28): error C2607: static assertion failed
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only regression test glitch I noticed ... ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never noticed, since I don't use MSVC.
Somehow this is failing: static_assert(!test_nonthrowing_requirements<std::chrono::seconds>);.

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