Skip to content

Commit 955a8ec

Browse files
committed
feat: allow template specialization
The syntax is `specialize<specialization_arguments>` after the optional template parameter list. Some examples from the tests: ```Cpp2 std: namespace = { common_type: @struct @print specialize<outer, outer> type = { type: type == outer; } } v: <T> const i32 = 1; v: <> specialize<void> const i32 = 2; v: specialize<i64> const i32 = 3; v: <T> specialize<* T> std::optional<int> == 6; ```
1 parent 61550a5 commit 955a8ec

10 files changed

+231
-10
lines changed

regression-tests/pure2-print.cpp2

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@ outer: @print type = {
9999

100100
}
101101

102+
std: namespace = {
103+
common_type: @struct @print specialize<outer, outer> type = {
104+
type: type == outer;
105+
}
106+
numbers: namespace = {
107+
pi_v: /*@print*/ specialize<outer> const double = pi_v<double>;
108+
}
109+
}
110+
102111
main: () = {
103112
outer::test();
104113
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
t: @struct <T> type = {
2+
a: i32 = 1;
3+
}
4+
t: @struct <T> specialize<T> type requires std::is_void_v<T> = {
5+
b: i32 = 2;
6+
}
7+
t: @struct specialize<i64> type = {
8+
c: i32 = 3;
9+
}
10+
v: <T> const i32 = 1;
11+
v: <> specialize<void> const i32 = 2;
12+
v: specialize<i64> const i32 = 3;
13+
v: specialize<i16> std::optional<i32> == 4;
14+
v: <> specialize<i8> std::optional<i8> == 5;
15+
v: <T> specialize<* T> std::optional<int> == 6;
16+
main: () = {
17+
assert(t<i32>().a == 1);
18+
assert(t<void>().b == 2);
19+
assert(t<i64>().c == 3);
20+
assert(v<i32> == 1);
21+
assert(v<void> == 2);
22+
assert(v<i64> == 3);
23+
static_assert(v<i16> == 4);
24+
static_assert(v<i8> == 5);
25+
static_assert(v<* int> == 6);
26+
}

regression-tests/test-results/gcc-13/pure2-template-specialization.cpp.execution

Whitespace-only changes.

regression-tests/test-results/gcc-13/pure2-template-specialization.cpp.output

Whitespace-only changes.

regression-tests/test-results/pure2-print.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@
1010
#line 4 "pure2-print.cpp2"
1111
class outer;
1212

13+
#line 102 "pure2-print.cpp2"
14+
namespace std {
15+
16+
#line 106 "pure2-print.cpp2"
17+
namespace numbers {
18+
19+
}
20+
}
21+
1322

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

@@ -84,6 +93,15 @@ CPP2_REQUIRES_ (cpp2::cmp_greater_eq(sizeof(Args)...,0))
8493
#line 100 "pure2-print.cpp2"
8594
};
8695

96+
namespace std {
97+
template<> class common_type<outer,outer> {
98+
public: using type = outer;
99+
};
100+
namespace numbers {
101+
/*@print*/
102+
}
103+
}
104+
87105
auto main() -> int;
88106

89107

@@ -200,6 +218,14 @@ requires (cpp2::cmp_greater_eq(sizeof(Args)...,0))
200218
return (... && args); }
201219

202220
#line 102 "pure2-print.cpp2"
221+
namespace std {
222+
223+
#line 106 "pure2-print.cpp2"
224+
namespace numbers {
225+
template<> double const pi_v<outer> {pi_v<double>};
226+
}
227+
}
228+
203229
auto main() -> int{
204230
outer::test();
205231
}

regression-tests/test-results/pure2-print.cpp2.output

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,11 @@ outer: type =
146146

147147
all: <Args...: type>(in args...: Args) -> move bool = (... && args);
148148
}
149+
150+
151+
common_type: specialize<outer, outer> type =
152+
{
153+
public type: type == outer;
154+
}
149155
ok (all Cpp2, passes safety checks)
150156

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
#define CPP2_IMPORT_STD Yes
3+
4+
//=== Cpp2 type declarations ====================================================
5+
6+
7+
#include "cpp2util.h"
8+
9+
template<typename T> class t;
10+
11+
12+
//=== Cpp2 type definitions and function declarations ===========================
13+
14+
template<typename T> class t {
15+
public: cpp2::i32 a {1};
16+
};
17+
template<typename T> requires( std::is_void_v<T> )
18+
class t<T> {public: cpp2::i32 b {2};
19+
};
20+
template<> class t<cpp2::i64> {
21+
public: cpp2::i32 c {3};
22+
};
23+
template<typename T> extern cpp2::i32 const v;
24+
25+
#line 13 "pure2-template-specialization.cpp2"
26+
template<> std::optional<cpp2::i32> inline constexpr v<cpp2::i16> = 4;
27+
template<> std::optional<cpp2::i8> inline constexpr v<cpp2::i8> = 5;
28+
template<typename T> std::optional<int> inline constexpr v<T*> = 6;
29+
auto main() -> int;
30+
31+
32+
//=== Cpp2 function definitions =================================================
33+
34+
35+
#line 10 "pure2-template-specialization.cpp2"
36+
template<typename T> cpp2::i32 const v {1};
37+
template<> cpp2::i32 const v<void> {2};
38+
template<> cpp2::i32 const v<cpp2::i64> {3};
39+
40+
#line 16 "pure2-template-specialization.cpp2"
41+
auto main() -> int{
42+
cpp2::Default.expects(t<cpp2::i32>().a == 1, "");
43+
cpp2::Default.expects(t<void>().b == 2, "");
44+
cpp2::Default.expects(t<cpp2::i64>().c == 3, "");
45+
cpp2::Default.expects(v<cpp2::i32> == 1, "");
46+
cpp2::Default.expects(v<void> == 2, "");
47+
cpp2::Default.expects(v<cpp2::i64> == 3, "");
48+
static_assert(v<cpp2::i16> == 4);
49+
static_assert(v<cpp2::i8> == 5);
50+
static_assert(v<int*> == 6);
51+
}
52+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-template-specialization.cpp2... ok (all Cpp2, passes safety checks)
2+

source/parse.h

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2538,6 +2538,7 @@ struct declaration_node
25382538

25392539
std::vector<std::unique_ptr<id_expression_node>> metafunctions;
25402540
std::unique_ptr<parameter_declaration_list_node> template_parameters;
2541+
std::unique_ptr<unqualified_id_node> specialization_template_arguments;
25412542
source_position requires_pos = {};
25422543
std::unique_ptr<logical_or_expression_node> requires_clause_expression;
25432544

@@ -2822,6 +2823,8 @@ struct declaration_node
28222823

28232824
auto is_function_expression () const -> bool
28242825
{ return is_function() && !identifier; }
2826+
auto is_specialization() const -> bool
2827+
{ return specialization_template_arguments != nullptr; }
28252828

28262829
auto is_polymorphic() const // has base types or virtual functions
28272830
-> bool
@@ -4745,6 +4748,11 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
47454748
template_params += " " + pretty_print_visualize(*n.template_parameters, indent + 1, true);
47464749
}
47474750

4751+
auto specialization_args = std::string{};
4752+
if (n.specialization_template_arguments) {
4753+
specialization_args += " " + pretty_print_visualize(*n.specialization_template_arguments, indent + 1);
4754+
}
4755+
47484756
auto requires_clause = std::string{};
47494757
if (n.requires_clause_expression) {
47504758
requires_clause += " requires (" + pretty_print_visualize(*n.requires_clause_expression, indent) + ")";
@@ -4829,7 +4837,8 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
48294837
auto& type_id = std::get<declaration_node::an_object>(n.type);
48304838
assert(type_id);
48314839
ret += metafunctions
4832-
+ template_params;
4840+
+ template_params
4841+
+ specialization_args;
48334842
if (!n.has_wildcard_type()) {
48344843
ret += " " + pretty_print_visualize(*type_id, indent);
48354844
}
@@ -4841,6 +4850,7 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
48414850
assert(t);
48424851
ret += metafunctions
48434852
+ template_params
4853+
+ specialization_args
48444854
+ " " + pretty_print_visualize(*t)
48454855
+ initializer;
48464856
}
@@ -4859,7 +4869,8 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
48594869
object_type_id += " " + pretty_print_visualize(*a->type_id, indent);
48604870
}
48614871

4862-
ret += template_params;
4872+
ret += template_params
4873+
+ specialization_args;
48634874
if (a->is_type_alias()) {
48644875
auto& t = std::get<alias_node::a_type>(a->initializer);
48654876
ret += " type"
@@ -6160,6 +6171,7 @@ class parser
61606171
//G
61616172
//G template-argument-list:
61626173
//G template-argument-list ',' template-argument
6174+
//G template-argument
61636175
//G
61646176
//G template-argument:
61656177
//G # note: < > << >> are not allowed in expressions until new ( is opened
@@ -7718,9 +7730,9 @@ class parser
77187730
//G unnamed-declaration:
77197731
//G ':' meta-functions-list? template-parameter-declaration-list? function-type requires-clause? '=' statement
77207732
//G ':' meta-functions-list? template-parameter-declaration-list? function-type statement
7721-
//G ':' meta-functions-list? template-parameter-declaration-list? type-id? requires-clause? '=' statement
7733+
//G ':' meta-functions-list? template-parameter-declaration-list? template-specialization-argument-list? type-id? requires-clause? '=' statement
77227734
//G ':' meta-functions-list? template-parameter-declaration-list? type-id
7723-
//G ':' meta-functions-list? template-parameter-declaration-list? 'final'? 'type' requires-clause? '=' statement
7735+
//G ':' meta-functions-list? template-parameter-declaration-list? template-specialization-argument-list? 'final'? 'type' requires-clause? '=' statement
77247736
//G ':' 'namespace' '=' statement
77257737
//G
77267738
//G meta-functions-list:
@@ -7732,7 +7744,10 @@ class parser
77327744
//G 'requires' logical-or-expression
77337745
//G
77347746
//G template-parameter-declaration-list
7735-
//G '<' parameter-declaration-seq '>'
7747+
//G '<' parameter-declaration-seq? '>'
7748+
//G
7749+
//G template-specialization-argument-list:
7750+
//G 'specialize' '<' template-argument-list '>'
77367751
//G
77377752
auto unnamed_declaration(
77387753
source_position start,
@@ -7877,6 +7892,21 @@ class parser
78777892
n->template_parameters = std::move(template_parameters);
78787893
}
78797894

7895+
// Next is an optional template specialization argument list
7896+
if (
7897+
curr() == "specialize"
7898+
&& peek(1)
7899+
&& peek(1)->type() == lexeme::Less
7900+
)
7901+
{
7902+
auto specialization_template_arguments = unqualified_id();
7903+
if (!specialization_template_arguments) {
7904+
error("invalid template specialization argument list");
7905+
return {};
7906+
}
7907+
n->specialization_template_arguments = std::move(specialization_template_arguments);
7908+
}
7909+
78807910
auto guard =
78817911
captures_allowed
78827912
? std::make_unique<capture_groups_stack_guard>(this, &n->captures)
@@ -8242,7 +8272,7 @@ class parser
82428272
//G alias:
82438273
//G ':' template-parameter-declaration-list? 'type' requires-clause? '==' type-id ';'
82448274
//G ':' 'namespace' '==' id-expression ';'
8245-
//G ':' template-parameter-declaration-list? type-id? requires-clause? '==' expression ';'
8275+
//G ':' template-parameter-declaration-list? template-specialization-argument-list? type-id? requires-clause? '==' expression ';'
82468276
//G
82478277
//GT ':' function-type '==' expression ';'
82488278
//GT # See commit 63efa6ed21c4d4f4f136a7a73e9f6b2c110c81d7 comment
@@ -8271,12 +8301,34 @@ class parser
82718301
n->template_parameters = std::move(template_parameters);
82728302
}
82738303

8304+
// Next is an optional template specialization argument list
8305+
if (
8306+
curr() == "specialize"
8307+
&& peek(1)
8308+
&& peek(1)->type() == lexeme::Less
8309+
)
8310+
{
8311+
auto specialization_template_arguments = unqualified_id();
8312+
if (!specialization_template_arguments) {
8313+
pos = start_pos; // backtrack
8314+
return {};
8315+
}
8316+
n->specialization_template_arguments = std::move(specialization_template_arguments);
8317+
}
8318+
82748319
auto a = std::make_unique<alias_node>( &curr() );
82758320

82768321
// Next must be 'type', 'namespace', a type-id, or we're at the 'requires' or '=='
82778322
if (curr() == "type")
82788323
{
82798324
next();
8325+
if (n->specialization_template_arguments) {
8326+
errors.emplace_back(
8327+
curr().position(),
8328+
"a type alias cannot be specialized"
8329+
);
8330+
return {};
8331+
}
82808332
}
82818333
else if (curr() == "namespace")
82828334
{
@@ -8288,6 +8340,13 @@ class parser
82888340
);
82898341
return {};
82908342
}
8343+
if (n->specialization_template_arguments) {
8344+
errors.emplace_back(
8345+
curr().position(),
8346+
"a namespace alias cannot be specialized"
8347+
);
8348+
return {};
8349+
}
82918350
}
82928351
else if (curr().type() != lexeme::EqualComparison && curr() != "requires")
82938352
{

0 commit comments

Comments
 (0)