Skip to content

Commit 3e5a83a

Browse files
committed
Add basic _requires-clause_ support
Should merge user-written _requires-clause_ expressions with any generated from `forward` parameters
1 parent e606505 commit 3e5a83a

19 files changed

+143
-41
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
X: <T: type, U: type>
3+
type
4+
requires std::is_same_v<T, int>
5+
&& std::is_same_v<U, int>
6+
= {
7+
operator=: (out this) = { }
8+
}
9+
10+
f: <T: type, U: type>
11+
(forward a: int, forward b: int) -> int
12+
requires std::is_same_v<T, int>
13+
&& std::is_same_v<U, int>
14+
= {
15+
return a * b;
16+
}
17+
18+
main: () = {
19+
x: X<int,int> = ();
20+
std::cout << f<int,int>(2,5);
21+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
10

regression-tests/test-results/clang-12/pure2-requires-clauses.cpp.output

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
abc

regression-tests/test-results/gcc-10/pure2-function-multiple-forward-arguments.cpp.output

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
10

regression-tests/test-results/gcc-10/pure2-requires-clauses.cpp.output

Whitespace-only changes.

regression-tests/test-results/mixed-forwarding.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ auto use(auto const& x) -> void{}
3636

3737
// invoking each of these with an rvalue std::pair argument ...
3838
auto apply_implicit_forward(auto&& t) -> void
39-
requires std::is_same_v<CPP2_TYPEOF(t), std::pair<X,X>>
39+
requires (std::is_same_v<CPP2_TYPEOF(t), std::pair<X,X>>)
4040
#line 16 "mixed-forwarding.cpp2"
4141
{
4242
copy_from(t.first); // copies
4343
copy_from(CPP2_FORWARD(t).second);// moves
4444
}
4545
auto apply_explicit_forward(auto&& t) -> void
46-
requires std::is_same_v<CPP2_TYPEOF(t), std::pair<X,X>>
46+
requires (std::is_same_v<CPP2_TYPEOF(t), std::pair<X,X>>)
4747
#line 20 "mixed-forwarding.cpp2"
4848
{
4949
copy_from( CPP2_FORWARD(t).first);// moves

regression-tests/test-results/mixed-parameter-passing-with-forward.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ auto parameter_styles(
3434
std::string&& d,
3535
auto&& e
3636
) -> void
37-
requires std::is_same_v<CPP2_TYPEOF(e), std::string>
37+
requires (std::is_same_v<CPP2_TYPEOF(e), std::string>)
3838
#line 15 "mixed-parameter-passing-with-forward.cpp2"
3939
{
4040
int z {12};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
abc
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pure2-function-multiple-forward-arguments.cpp
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
10
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pure2-requires-clauses.cpp

regression-tests/test-results/pure2-function-multiple-forward-arguments.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33

44
#include "cpp2util.h"
55

6-
76
#line 1 "pure2-function-multiple-forward-arguments.cpp2"
87
auto fun(auto&& s1, auto&& s2, auto&& s3) -> void;
8+
99
#line 5 "pure2-function-multiple-forward-arguments.cpp2"
1010
auto main() -> int;
1111

@@ -14,8 +14,9 @@ auto main() -> int;
1414
#line 1 "pure2-function-multiple-forward-arguments.cpp2"
1515
auto fun(auto&& s1, auto&& s2, auto&& s3) -> void
1616
requires (std::is_same_v<CPP2_TYPEOF(s1), std::string> && std::is_same_v<CPP2_TYPEOF(s2), std::string> && std::is_same_v<CPP2_TYPEOF(s3), std::string>)
17-
#line 2 "pure2-function-multiple-forward-arguments.cpp2"
18-
{ std::cout << CPP2_FORWARD(s1) << CPP2_FORWARD(s2) << CPP2_FORWARD(s3) << std::endl;
17+
#line 1 "pure2-function-multiple-forward-arguments.cpp2"
18+
{
19+
std::cout << CPP2_FORWARD(s1) << CPP2_FORWARD(s2) << CPP2_FORWARD(s3) << std::endl;
1920
}
2021

2122
auto main() -> int{

regression-tests/test-results/pure2-function-multiple-forward-arguments.cpp2

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
2+
#define CPP2_USE_MODULES Yes
3+
4+
#include "cpp2util.h"
5+
6+
#line 2 "pure2-requires-clauses.cpp2"
7+
template<typename T, typename U>
8+
9+
requires( std::is_same_v<T,int>
10+
&& std::is_same_v<U,int>)
11+
class X;
12+
13+
#line 10 "pure2-requires-clauses.cpp2"
14+
template<typename T, typename U> [[nodiscard]] auto f
15+
(auto&& a, auto&& b) -> int;
16+
17+
#line 18 "pure2-requires-clauses.cpp2"
18+
auto main() -> int;
19+
20+
//=== Cpp2 definitions ==========================================================
21+
22+
23+
#line 2 "pure2-requires-clauses.cpp2"
24+
template<typename T, typename U>
25+
26+
requires( std::is_same_v<T,int>
27+
&& std::is_same_v<U,int>)
28+
class X {
29+
public: X (){}
30+
};
31+
32+
template<typename T, typename U> [[nodiscard]] auto f
33+
(auto&& a, auto&& b) -> int
34+
requires (std::is_same_v<T,int> && std::is_same_v<U,int> && std::is_same_v<CPP2_TYPEOF(a), int> && std::is_same_v<CPP2_TYPEOF(b), int>)
35+
#line 14 "pure2-requires-clauses.cpp2"
36+
{
37+
return CPP2_FORWARD(a) * CPP2_FORWARD(b);
38+
}
39+
40+
auto main() -> int{
41+
X<int,int> x {};
42+
std::cout << f<int,int>(2, 5);
43+
}
44+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-requires-clauses.cpp2... ok (all Cpp2, passes safety checks)
2+

source/cppfront.cpp

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4256,6 +4256,7 @@ class cppfront
42564256
}
42574257
}
42584258

4259+
42594260
//-----------------------------------------------------------------------
42604261
//
42614262
auto emit(
@@ -4337,6 +4338,12 @@ class cppfront
43374338
// User-defined type
43384339
if (n.is_type())
43394340
{
4341+
if (n.requires_clause_expression) {
4342+
printer.print_cpp2("requires( ", n.requires_pos);
4343+
emit(*n.requires_clause_expression);
4344+
printer.print_cpp2(")\n", n.requires_pos);
4345+
}
4346+
43404347
printer.print_cpp2("class ", n.position());
43414348
emit(*n.identifier);
43424349

@@ -4636,7 +4643,7 @@ class cppfront
46364643
}
46374644
}
46384645

4639-
// Function declaration
4646+
// End function declaration
46404647
if (printer.doing_declarations_only()) {
46414648
printer.print_cpp2( ";\n", n.position() );
46424649
return;
@@ -4724,28 +4731,32 @@ class cppfront
47244731

47254732
printer.preempt_position_push( n.equal_sign );
47264733

4727-
// TODO: something like this to get rid of extra blank lines
4728-
// inside the start of bodies of functions that have
4729-
// multiple contracts
4730-
//printer.skip_lines( std::ssize(current_functions.back().prolog.statements) );
4731-
4732-
// If processing the parameters generated any requires conditions,
4733-
// emit them here
4734-
if (!function_requires_conditions.empty()) {
4735-
printer.ignore_alignment( true, n.position().colno + 4 );
4734+
// Handle requires expression - an explicit one plus any generated
4735+
// from processing the parameters
4736+
if (
4737+
n.requires_clause_expression
4738+
|| !function_requires_conditions.empty()
4739+
)
4740+
{
47364741
printer.print_extra("\n");
4737-
auto is_multi_fwd = function_requires_conditions.size() > 1;
4738-
printer.print_extra("requires ");
4739-
if (is_multi_fwd) {
4740-
printer.print_extra("(");
4741-
}
4742-
printer.print_extra(function_requires_conditions.front());
4743-
for (auto it = std::cbegin(function_requires_conditions)+1; it != std::cend(function_requires_conditions); ++it) {
4744-
printer.print_extra(" && " + *it);
4742+
printer.ignore_alignment( true, n.position().colno + 4 );
4743+
printer.print_extra("requires (");
4744+
4745+
if (n.requires_clause_expression) {
4746+
emit(*n.requires_clause_expression);
4747+
if (!function_requires_conditions.empty()) {
4748+
printer.print_extra(" && ");
4749+
}
47454750
}
4746-
if (is_multi_fwd) {
4747-
printer.print_extra(")");
4751+
4752+
if (!function_requires_conditions.empty()) {
4753+
printer.print_extra(function_requires_conditions.front());
4754+
for (auto it = std::cbegin(function_requires_conditions)+1; it != std::cend(function_requires_conditions); ++it) {
4755+
printer.print_extra(" && " + *it);
4756+
}
47484757
}
4758+
4759+
printer.print_extra(")");
47494760
function_requires_conditions = {};
47504761
printer.ignore_alignment( false );
47514762
}

source/parse.h

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,6 +1509,8 @@ struct declaration_node
15091509
> type;
15101510

15111511
std::unique_ptr<parameter_declaration_list_node> template_parameters;
1512+
source_position requires_pos = {};
1513+
std::unique_ptr<expression_node> requires_clause_expression;
15121514

15131515
source_position equal_sign = {};
15141516
std::unique_ptr<statement_node> initializer;
@@ -2827,6 +2829,10 @@ class parser
28272829
{
28282830
typename Binary::term t{};
28292831

2832+
// Remember current position, because we may need to backtrack if this next
2833+
// t.op might be valid but isn't followed by a valid term and so isn't for us
2834+
auto term_pos = pos;
2835+
28302836
// Most of these predicates only look at the current token and return
28312837
// true/false == whether this is a valid operator for this production
28322838
if constexpr( requires{ bool{ validate_op(curr()) }; } ) {
@@ -2860,11 +2866,16 @@ class parser
28602866
assert (!"ICE: validate_op should take one token and return bool, or two tokens and return token const* ");
28612867
}
28622868

2863-
// At this point we have a valid t.op, so try to parse the next term
2869+
// At this point we may have a valid t.op, so try to parse the next term...
2870+
// If it's not a valid term, then this t.op wasn't for us, pop it and return
2871+
// what we found (e.g., with "requires expression = {...}" the = is a grammar
2872+
// element and not an operator, it isn't and can't be part of the expression)
28642873
if ( !(t.expr = term()) ) {
2865-
error("invalid expression after " + peek(-1)->to_string(true), true, {}, true);
2874+
pos = term_pos; // backtrack
28662875
return n;
28672876
}
2877+
2878+
// We got a term, so this op + term was for us
28682879
n->terms.push_back( std::move(t) );
28692880
}
28702881
return n;
@@ -4646,12 +4657,15 @@ class parser
46464657

46474658

46484659
//G unnamed-declaration:
4649-
//G ':' template-parameter-declaration-list? function-type '=' statement
4650-
//G ':' template-parameter-declaration-list? type-id? '=' statement
4660+
//G ':' template-parameter-declaration-list? function-type requires-clause? '=' statement
4661+
//G ':' template-parameter-declaration-list? type-id? requires-clause? '=' statement
46514662
//G ':' template-parameter-declaration-list? type-id
4652-
//G ':' template-parameter-declaration-list? 'type' meta-constraints? '=' statement
4663+
//G ':' template-parameter-declaration-list? 'type' meta-constraints? requires-clause? '=' statement
46534664
//G ':' 'namespace' '=' statement
46544665
//G
4666+
//G requires-clause:
4667+
//G 'requires' expression
4668+
//G
46554669
//G template-parameter-declaration-list
46564670
//G '<' parameter-declaration-seq '>'
46574671
//G
@@ -4825,6 +4839,18 @@ class parser
48254839
deduced_type = true;
48264840
}
48274841

4842+
// Next is optionally a requires clause
4843+
if (curr() == "requires") {
4844+
n->requires_pos = curr().position();
4845+
next();
4846+
auto e = expression();
4847+
if (!e) {
4848+
error("'requires' must be followed by an expression");
4849+
return {};
4850+
}
4851+
n->requires_clause_expression = std::move(e);
4852+
}
4853+
48284854
// Next is optionally = followed by an initializer
48294855

48304856
// If there is no =

0 commit comments

Comments
 (0)