Skip to content

Commit b1c03f2

Browse files
committed
Allow requires clauses on aliases, closes #640
Note that `==` in the constraint expression must be parenthesized. So this works as expected: zero_v: <T> T requires something == zero<T>::value; and this requires the parentheses: zero_v: <T> T requires (something == something_else) == zero<T>::value; and means the second `==` is part of the initializer: zero_v: <T> T requires something == something_else == zero<T>::value; // same as: zero_v: <T> T requires something == (something_else == zero<T>::value);
1 parent 5feb122 commit b1c03f2

File tree

9 files changed

+120
-83
lines changed

9 files changed

+120
-83
lines changed

regression-tests/test-results/gcc-10/gcc-version.output

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
g++-10 (Ubuntu 10.3.0-1ubuntu1~20.04) 10.3.0
1+
g++-10 (Ubuntu 10.5.0-1ubuntu1~20.04) 10.5.0
22
Copyright (C) 2020 Free Software Foundation, Inc.
33
This is free software; see the source for copying conditions. There is NO
44
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
sizeof(x) is 33
2+
(not a name)
3+
xyzzy
Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +0,0 @@
1-
pure2-union.cpp2:6:42: error: expected ‘;’ at end of member declaration
2-
In file included from pure2-union.cpp:7:
3-
../../../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.
4-
pure2-union.cpp2:7:1: note: in expansion of macro ‘CPP2_REQUIRES_’
5-
pure2-union.cpp2:11:41: error: expected ‘;’ at end of member declaration
6-
In file included from pure2-union.cpp:7:
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.
8-
pure2-union.cpp2:12:1: note: in expansion of macro ‘CPP2_REQUIRES_’
9-
pure2-union.cpp2:28:6: error: no declaration matches ‘void name_or_number::set_name(auto:81&&) & requires is_same_v<typename std::remove_cv<typename std::remove_reference<decltype(name_or_number::set_name::value)>::type>::type, std::__cxx11::string>’
10-
pure2-union.cpp2:6:14: note: candidate is: ‘template<class auto:79> void name_or_number::set_name(auto:79&&) &’
11-
pure2-union.cpp2:2:7: note: ‘class name_or_number’ defined here
12-
pure2-union.cpp2:35:6: error: no declaration matches ‘void name_or_number::set_num(auto:82&&) & requires is_same_v<typename std::remove_cv<typename std::remove_reference<decltype(name_or_number::set_num::value)>::type>::type, cpp2::i32>’
13-
pure2-union.cpp2:11:14: note: candidate is: ‘template<class auto:80> void name_or_number::set_num(auto:80&&) &’
14-
pure2-union.cpp2:2:7: note: ‘class name_or_number’ defined here

regression-tests/test-results/gcc-13/gcc-version.output

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
gcc (GCC) 13.1.1 20230614 (Red Hat 13.1.1-4)
1+
gcc (GCC) 13.2.1 20230728 (Red Hat 13.2.1-1)
22
Copyright (C) 2023 Free Software Foundation, Inc.
33
This is free software; see the source for copying conditions. There is NO
44
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
pure2-statement-parse-error.cpp2...
2-
pure2-statement-parse-error.cpp2(3,9): error: Invalid statement encountered inside a compound-statement (at 'b')
2+
pure2-statement-parse-error.cpp2(3,9): error: invalid statement encountered inside a compound-statement (at 'b')
33

regression-tests/test-results/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
cppfront compiler v0.2.1 Build 8913:1554
2+
cppfront compiler v0.2.1 Build 8913:1723
33
Copyright(c) Herb Sutter All rights reserved
44

55
SPDX-License-Identifier: CC-BY-NC-ND-4.0

source/build.info

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"8913:1554"
1+
"8913:1723"

source/cppfront.cpp

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3607,7 +3607,10 @@ class cppfront
36073607
&& !n.inside_initializer
36083608
&& parens_ok
36093609
;
3610-
add_parens |= n.is_fold_expression();
3610+
add_parens |=
3611+
n.is_fold_expression() &&
3612+
!(n.inside_initializer && current_declarations.back()->initializer->position() != n.open_paren->position())
3613+
;
36113614
if (add_parens) {
36123615
printer.print_cpp2( *n.open_paren, n.position());
36133616
}
@@ -4905,6 +4908,50 @@ class cppfront
49054908
)
49064909
-> void
49074910
{
4911+
// Helper for declarations that can have requires-clauses
4912+
auto const emit_requires_clause = [&]() {
4913+
if (
4914+
n.requires_clause_expression
4915+
|| !function_requires_conditions.empty()
4916+
)
4917+
{
4918+
printer.print_extra("\n");
4919+
printer.ignore_alignment( true, n.position().colno + 4 );
4920+
if (printer.get_phase() == printer.phase1_type_defs_func_decls) {
4921+
// Workaround GCC 10 not supporting requires in forward declarations in some cases.
4922+
// See commit 5a0d77f8e297902c0b9712c5aafb6208cfa4c139.
4923+
if (n.is_object() || n.parent_is_type()) {
4924+
printer.print_extra("CPP2_REQUIRES_ (");
4925+
}
4926+
else {
4927+
printer.print_extra("CPP2_REQUIRES (");
4928+
}
4929+
}
4930+
else {
4931+
printer.print_extra("requires (");
4932+
}
4933+
4934+
if (n.requires_clause_expression) {
4935+
emit(*n.requires_clause_expression);
4936+
if (!function_requires_conditions.empty()) {
4937+
printer.print_extra(" && ");
4938+
}
4939+
}
4940+
4941+
if (!function_requires_conditions.empty()) {
4942+
printer.print_extra(function_requires_conditions.front());
4943+
for (auto it = std::cbegin(function_requires_conditions)+1; it != std::cend(function_requires_conditions); ++it) {
4944+
printer.print_extra(" && " + *it);
4945+
}
4946+
}
4947+
4948+
printer.print_extra(")");
4949+
function_requires_conditions = {};
4950+
printer.ignore_alignment( false );
4951+
}
4952+
};
4953+
4954+
49084955
// Declarations are handled in multiple passes,
49094956
// but we only want to do the sema checks once
49104957
if (
@@ -4979,6 +5026,9 @@ class cppfront
49795026
printer.print_cpp2(" ", n.position());
49805027
}
49815028

5029+
// Emit requires clause if any
5030+
emit_requires_clause();
5031+
49825032
// Handle type aliases
49835033
if (a->is_type_alias()) {
49845034
printer.print_cpp2(
@@ -5371,50 +5421,6 @@ class cppfront
53715421
}
53725422

53735423

5374-
// Helper for declarations that can have requires-clauses
5375-
auto const emit_requires_clause = [&]() {
5376-
if (
5377-
n.requires_clause_expression
5378-
|| !function_requires_conditions.empty()
5379-
)
5380-
{
5381-
printer.print_extra("\n");
5382-
printer.ignore_alignment( true, n.position().colno + 4 );
5383-
if (printer.get_phase() == printer.phase1_type_defs_func_decls) {
5384-
// Workaround GCC 10 not supporting requires in forward declarations in some cases.
5385-
// See commit 5a0d77f8e297902c0b9712c5aafb6208cfa4c139.
5386-
if (n.is_object() || n.parent_is_type()) {
5387-
printer.print_extra("CPP2_REQUIRES_ (");
5388-
}
5389-
else {
5390-
printer.print_extra("CPP2_REQUIRES (");
5391-
}
5392-
}
5393-
else {
5394-
printer.print_extra("requires (");
5395-
}
5396-
5397-
if (n.requires_clause_expression) {
5398-
emit(*n.requires_clause_expression);
5399-
if (!function_requires_conditions.empty()) {
5400-
printer.print_extra(" && ");
5401-
}
5402-
}
5403-
5404-
if (!function_requires_conditions.empty()) {
5405-
printer.print_extra(function_requires_conditions.front());
5406-
for (auto it = std::cbegin(function_requires_conditions)+1; it != std::cend(function_requires_conditions); ++it) {
5407-
printer.print_extra(" && " + *it);
5408-
}
5409-
}
5410-
5411-
printer.print_extra(")");
5412-
function_requires_conditions = {};
5413-
printer.ignore_alignment( false );
5414-
}
5415-
};
5416-
5417-
54185424
// Namespace
54195425
if (n.is_namespace())
54205426
{

source/parse.h

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4632,64 +4632,72 @@ class parser
46324632
//G equality-expression '==' relational-expression
46334633
//G equality-expression '!=' relational-expression
46344634
//G
4635-
auto equality_expression(bool allow_angle_operators = true)
4635+
auto equality_expression(bool allow_angle_operators = true, bool allow_equality = true)
46364636
-> auto
46374637
{
4638-
return binary_expression<equality_expression_node> (
4639-
[](token const& t){ return t.type() == lexeme::EqualComparison || t.type() == lexeme::NotEqualComparison; },
4640-
[=,this]{ return relational_expression(allow_angle_operators); }
4641-
);
4638+
if (allow_equality) {
4639+
return binary_expression<equality_expression_node> (
4640+
[](token const& t){ return t.type() == lexeme::EqualComparison || t.type() == lexeme::NotEqualComparison; },
4641+
[=,this]{ return relational_expression(allow_angle_operators); }
4642+
);
4643+
}
4644+
else {
4645+
return binary_expression<equality_expression_node> (
4646+
[](token const& t){ return t.type() == lexeme::NotEqualComparison; },
4647+
[=,this]{ return relational_expression(allow_angle_operators); }
4648+
);
4649+
}
46424650
}
46434651

46444652
//G bit-and-expression:
46454653
//G equality-expression
46464654
//G bit-and-expression '&' equality-expression
46474655
//G
4648-
auto bit_and_expression(bool allow_angle_operators = true)
4656+
auto bit_and_expression(bool allow_angle_operators = true, bool allow_equality = true)
46494657
-> auto
46504658
{
46514659
return binary_expression<bit_and_expression_node> (
46524660
[](token const& t){ return t.type() == lexeme::Ampersand; },
4653-
[=,this]{ return equality_expression(allow_angle_operators); }
4661+
[=,this]{ return equality_expression(allow_angle_operators, allow_equality); }
46544662
);
46554663
}
46564664

46574665
//G bit-xor-expression:
46584666
//G bit-and-expression
46594667
//G bit-xor-expression '^' bit-and-expression
46604668
//G
4661-
auto bit_xor_expression(bool allow_angle_operators = true)
4669+
auto bit_xor_expression(bool allow_angle_operators = true, bool allow_equality = true)
46624670
-> auto
46634671
{
46644672
return binary_expression<bit_xor_expression_node> (
46654673
[](token const& t){ return t.type() == lexeme::Caret; },
4666-
[=,this]{ return bit_and_expression(allow_angle_operators); }
4674+
[=,this]{ return bit_and_expression(allow_angle_operators, allow_equality); }
46674675
);
46684676
}
46694677

46704678
//G bit-or-expression:
46714679
//G bit-xor-expression
46724680
//G bit-or-expression '|' bit-xor-expression
46734681
//G
4674-
auto bit_or_expression(bool allow_angle_operators = true)
4682+
auto bit_or_expression(bool allow_angle_operators = true, bool allow_equality = true)
46754683
-> auto
46764684
{
46774685
return binary_expression<bit_or_expression_node> (
46784686
[](token const& t){ return t.type() == lexeme::Pipe; },
4679-
[=,this]{ return bit_xor_expression(allow_angle_operators); }
4687+
[=,this]{ return bit_xor_expression(allow_angle_operators, allow_equality); }
46804688
);
46814689
}
46824690

46834691
//G logical-and-expression:
46844692
//G bit-or-expression
46854693
//G logical-and-expression '&&' bit-or-expression
46864694
//G
4687-
auto logical_and_expression(bool allow_angle_operators = true)
4695+
auto logical_and_expression(bool allow_angle_operators = true, bool allow_equality = true)
46884696
-> auto
46894697
{
46904698
return binary_expression<logical_and_expression_node> (
46914699
[](token const& t){ return t.type() == lexeme::LogicalAnd; },
4692-
[=,this]{ return bit_or_expression(allow_angle_operators); }
4700+
[=,this]{ return bit_or_expression(allow_angle_operators, allow_equality); }
46934701
);
46944702
}
46954703

@@ -4699,12 +4707,12 @@ class parser
46994707
//G logical-and-expression
47004708
//G logical-or-expression '||' logical-and-expression
47014709
//G
4702-
auto logical_or_expression(bool allow_angle_operators = true)
4710+
auto logical_or_expression(bool allow_angle_operators = true, bool allow_equality = true)
47034711
-> auto
47044712
{
47054713
return binary_expression<logical_or_expression_node> (
47064714
[](token const& t){ return t.type() == lexeme::LogicalOr; },
4707-
[=,this]{ return logical_and_expression(allow_angle_operators); }
4715+
[=,this]{ return logical_and_expression(allow_angle_operators, allow_equality); }
47084716
);
47094717
}
47104718

@@ -6764,6 +6772,12 @@ class parser
67646772
return {};
67656773
}
67666774

6775+
if (n->is_namespace())
6776+
{
6777+
error("'requires' is not allowed on a namespace");
6778+
return {};
6779+
}
6780+
67676781
n->requires_pos = curr().position();
67686782
next();
67696783
auto e = logical_or_expression();
@@ -6950,9 +6964,9 @@ class parser
69506964

69516965

69526966
//G alias
6953-
//G ':' template-parameter-declaration-list? 'type' '==' type-id ';'
6967+
//G ':' template-parameter-declaration-list? 'type' requires-clause? '==' type-id ';'
69546968
//G ':' 'namespace' '==' qualified-id ';'
6955-
//G ':' template-parameter-declaration-list? type-id? '==' expression ';'
6969+
//G ':' template-parameter-declaration-list? type-id? requires-clause? '==' expression ';'
69566970
//G
69576971
//GT ':' function-type '==' expression ';'
69586972
//GT # See commit 63efa6ed21c4d4f4f136a7a73e9f6b2c110c81d7 comment
@@ -6983,7 +6997,7 @@ class parser
69836997

69846998
auto a = std::make_unique<alias_node>( &curr() );
69856999

6986-
// Next must be 'type', 'namespace', a type-id, or we're at the '=='
7000+
// Next must be 'type', 'namespace', a type-id, or we're at the 'requires' or '=='
69877001
if (curr() == "type")
69887002
{
69897003
next();
@@ -6999,7 +7013,7 @@ class parser
69997013
return {};
70007014
}
70017015
}
7002-
else if (curr().type() != lexeme::EqualComparison)
7016+
else if (curr().type() != lexeme::EqualComparison && curr() != "requires")
70037017
{
70047018
a->type_id = type_id();
70057019
if (!a->type_id) {
@@ -7008,6 +7022,34 @@ class parser
70087022
}
70097023
}
70107024

7025+
// Next is optionally a requires clause
7026+
if (curr() == "requires")
7027+
{
7028+
if (
7029+
n->is_type_alias()
7030+
&& !n->template_parameters
7031+
)
7032+
{
7033+
error("'requires' is not allowed on a type alias that does not have a template parameter list");
7034+
return {};
7035+
}
7036+
7037+
if (n->is_namespace_alias())
7038+
{
7039+
error("'requires' is not allowed on a namespace alias");
7040+
return {};
7041+
}
7042+
7043+
n->requires_pos = curr().position();
7044+
next();
7045+
auto e = logical_or_expression(true, false);
7046+
if (!e) {
7047+
error("'requires' must be followed by an expression");
7048+
return {};
7049+
}
7050+
n->requires_clause_expression = std::move(e);
7051+
}
7052+
70117053
// Now we should be at the '==' if this is an alias
70127054

70137055
if (curr().type() == lexeme::EqualComparison) {

0 commit comments

Comments
 (0)