Skip to content

Commit d7cc82a

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 05ce45a commit d7cc82a

10 files changed

+266
-11
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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
t: @struct specialize<* i8> type = {
11+
f: () 17;
12+
v: int == 29;
13+
}
14+
t: @struct <T> specialize<* T> type = {
15+
v: int == 17;
16+
f: () 29;
17+
}
18+
v: <T> const i32 = 1;
19+
v: <> specialize<void> const i32 = 2;
20+
v: specialize<i64> const i32 = 3;
21+
v: specialize<i16> std::optional<i32> == 4;
22+
v: <> specialize<i8> std::optional<i8> == 5;
23+
v: <T> specialize<* T> std::optional<int> == 6;
24+
main: () = {
25+
assert(t<i32>().a == 1);
26+
assert(t<void>().b == 2);
27+
assert(t<i64>().c == 3);
28+
assert(t<* i8>::f() == 17);
29+
assert(t<* i8>::v == 29);
30+
assert(t<* i16>::v == 17);
31+
assert(t<* i16>::f() == 29);
32+
assert(v<i32> == 1);
33+
assert(v<void> == 2);
34+
assert(v<i64> == 3);
35+
static_assert(v<i16> == 4);
36+
static_assert(v<i8> == 5);
37+
static_assert(v<* int> == 6);
38+
}

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
@@ -11,6 +11,15 @@
1111
#line 4 "pure2-print.cpp2"
1212
class outer;
1313

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

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

@@ -76,6 +85,15 @@ CPP2_REQUIRES_ (cpp2::cmp_greater_eq(sizeof(Args)...,0)) ;
7685
#line 100 "pure2-print.cpp2"
7786
};
7887

88+
namespace std {
89+
template<> class common_type<outer,outer> {
90+
public: using type = outer;
91+
};
92+
namespace numbers {
93+
/*@print*/
94+
}
95+
}
96+
7997
auto main() -> int;
8098

8199
//=== Cpp2 function definitions =================================================
@@ -186,6 +204,14 @@ requires (cpp2::cmp_greater_eq(sizeof(Args)...,0)) {
186204
return (... && args); }
187205

188206
#line 102 "pure2-print.cpp2"
207+
namespace std {
208+
209+
#line 106 "pure2-print.cpp2"
210+
namespace numbers {
211+
template<> double const pi_v<outer> {pi_v<double>};
212+
}
213+
}
214+
189215
auto main() -> int{
190216
outer::test();
191217
}

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: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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<> class t<cpp2::i8*> {
24+
public: [[nodiscard]] static auto f() -> auto;
25+
public: static const int v;
26+
};
27+
template<typename T> class t<T*> {
28+
public: static const int v;
29+
public: [[nodiscard]] static auto f() -> auto;
30+
};
31+
template<typename T> extern cpp2::i32 const v;
32+
33+
#line 21 "pure2-template-specialization.cpp2"
34+
template<> std::optional<cpp2::i32> inline constexpr v<cpp2::i16> = 4;
35+
template<> std::optional<cpp2::i8> inline constexpr v<cpp2::i8> = 5;
36+
template<typename T> std::optional<int> inline constexpr v<T*> = 6;
37+
auto main() -> int;
38+
39+
40+
//=== Cpp2 function definitions =================================================
41+
42+
43+
#line 11 "pure2-template-specialization.cpp2"
44+
[[nodiscard]] auto t<cpp2::i8*>::f() -> auto { return 17; }
45+
inline CPP2_CONSTEXPR int t<cpp2::i8*>::v = 29;
46+
47+
#line 15 "pure2-template-specialization.cpp2"
48+
template <typename T> inline CPP2_CONSTEXPR int t<T*>::v = 17;
49+
template <typename T> [[nodiscard]] auto t<T*>::f() -> auto { return 29; }
50+
51+
template<typename T> cpp2::i32 const v {1};
52+
template<> cpp2::i32 const v<void> {2};
53+
template<> cpp2::i32 const v<cpp2::i64> {3};
54+
55+
#line 24 "pure2-template-specialization.cpp2"
56+
auto main() -> int{
57+
cpp2::Default.expects(t<cpp2::i32>().a == 1, "");
58+
cpp2::Default.expects(t<void>().b == 2, "");
59+
cpp2::Default.expects(t<cpp2::i64>().c == 3, "");
60+
cpp2::Default.expects(t<cpp2::i8*>::f() == 17, "");
61+
cpp2::Default.expects(t<cpp2::i8*>::v == 29, "");
62+
cpp2::Default.expects(t<cpp2::i16*>::v == 17, "");
63+
cpp2::Default.expects(t<cpp2::i16*>::f() == 29, "");
64+
cpp2::Default.expects(v<cpp2::i32> == 1, "");
65+
cpp2::Default.expects(v<void> == 2, "");
66+
cpp2::Default.expects(v<cpp2::i64> == 3, "");
67+
static_assert(v<cpp2::i16> == 4);
68+
static_assert(v<cpp2::i8> == 5);
69+
static_assert(v<int*> == 6);
70+
}
71+
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
@@ -2557,6 +2557,7 @@ struct declaration_node
25572557

25582558
std::vector<std::unique_ptr<id_expression_node>> metafunctions;
25592559
std::unique_ptr<parameter_declaration_list_node> template_parameters;
2560+
std::unique_ptr<unqualified_id_node> specialization_template_arguments;
25602561
source_position requires_pos = {};
25612562
std::unique_ptr<logical_or_expression_node> requires_clause_expression;
25622563

@@ -2841,6 +2842,8 @@ struct declaration_node
28412842

28422843
auto is_function_expression () const -> bool
28432844
{ return is_function() && !identifier; }
2845+
auto is_specialization() const -> bool
2846+
{ return specialization_template_arguments != nullptr; }
28442847

28452848
auto is_polymorphic() const // has base types or virtual functions
28462849
-> bool
@@ -4889,6 +4892,11 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
48894892
template_params += " " + pretty_print_visualize(*n.template_parameters, indent + 1, true);
48904893
}
48914894

4895+
auto specialization_args = std::string{};
4896+
if (n.specialization_template_arguments) {
4897+
specialization_args += " " + pretty_print_visualize(*n.specialization_template_arguments, indent + 1);
4898+
}
4899+
48924900
auto requires_clause = std::string{};
48934901
if (n.requires_clause_expression) {
48944902
requires_clause += " requires (" + pretty_print_visualize(*n.requires_clause_expression, indent) + ")";
@@ -4973,7 +4981,8 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
49734981
auto& type_id = std::get<declaration_node::an_object>(n.type);
49744982
assert(type_id);
49754983
ret += metafunctions
4976-
+ template_params;
4984+
+ template_params
4985+
+ specialization_args;
49774986
if (!n.has_wildcard_type()) {
49784987
ret += " " + pretty_print_visualize(*type_id, indent);
49794988
}
@@ -4985,6 +4994,7 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
49854994
assert(t);
49864995
ret += metafunctions
49874996
+ template_params
4997+
+ specialization_args
49884998
+ " " + pretty_print_visualize(*t)
49894999
+ initializer;
49905000
}
@@ -5003,7 +5013,8 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
50035013
object_type_id += " " + pretty_print_visualize(*a->type_id, indent);
50045014
}
50055015

5006-
ret += template_params;
5016+
ret += template_params
5017+
+ specialization_args;
50075018
if (a->is_type_alias()) {
50085019
auto& t = std::get<alias_node::a_type>(a->initializer);
50095020
ret += " type"
@@ -6305,6 +6316,7 @@ class parser
63056316
//G
63066317
//G template-argument-list:
63076318
//G template-argument-list ',' template-argument
6319+
//G template-argument
63086320
//G
63096321
//G template-argument:
63106322
//G # note: < > << >> are not allowed in expressions until new ( is opened
@@ -7868,9 +7880,9 @@ class parser
78687880
//G unnamed-declaration:
78697881
//G ':' meta-functions-list? template-parameter-declaration-list? function-type requires-clause? '=' statement
78707882
//G ':' meta-functions-list? template-parameter-declaration-list? function-type statement
7871-
//G ':' meta-functions-list? template-parameter-declaration-list? type-id? requires-clause? '=' statement
7883+
//G ':' meta-functions-list? template-parameter-declaration-list? template-specialization-argument-list? type-id? requires-clause? '=' statement
78727884
//G ':' meta-functions-list? template-parameter-declaration-list? type-id
7873-
//G ':' meta-functions-list? template-parameter-declaration-list? 'final'? 'type' requires-clause? '=' statement
7885+
//G ':' meta-functions-list? template-parameter-declaration-list? template-specialization-argument-list? 'final'? 'type' requires-clause? '=' statement
78747886
//G ':' 'namespace' '=' statement
78757887
//G
78767888
//G meta-functions-list:
@@ -7882,7 +7894,10 @@ class parser
78827894
//G 'requires' logical-or-expression
78837895
//G
78847896
//G template-parameter-declaration-list
7885-
//G '<' parameter-declaration-seq '>'
7897+
//G '<' parameter-declaration-seq? '>'
7898+
//G
7899+
//G template-specialization-argument-list:
7900+
//G 'specialize' '<' template-argument-list '>'
78867901
//G
78877902
auto unnamed_declaration(
78887903
source_position start,
@@ -8027,6 +8042,21 @@ class parser
80278042
n->template_parameters = std::move(template_parameters);
80288043
}
80298044

8045+
// Next is an optional template specialization argument list
8046+
if (
8047+
curr() == "specialize"
8048+
&& peek(1)
8049+
&& peek(1)->type() == lexeme::Less
8050+
)
8051+
{
8052+
auto specialization_template_arguments = unqualified_id();
8053+
if (!specialization_template_arguments) {
8054+
error("invalid template specialization argument list");
8055+
return {};
8056+
}
8057+
n->specialization_template_arguments = std::move(specialization_template_arguments);
8058+
}
8059+
80308060
auto guard =
80318061
captures_allowed
80328062
? std::make_unique<capture_groups_stack_guard>(this, &n->captures)
@@ -8392,7 +8422,7 @@ class parser
83928422
//G alias:
83938423
//G ':' template-parameter-declaration-list? 'type' requires-clause? '==' type-id ';'
83948424
//G ':' 'namespace' '==' id-expression ';'
8395-
//G ':' template-parameter-declaration-list? type-id? requires-clause? '==' expression ';'
8425+
//G ':' template-parameter-declaration-list? template-specialization-argument-list? type-id? requires-clause? '==' expression ';'
83968426
//G
83978427
//GT ':' function-type '==' expression ';'
83988428
//GT # See commit 63efa6ed21c4d4f4f136a7a73e9f6b2c110c81d7 comment
@@ -8421,12 +8451,34 @@ class parser
84218451
n->template_parameters = std::move(template_parameters);
84228452
}
84238453

8454+
// Next is an optional template specialization argument list
8455+
if (
8456+
curr() == "specialize"
8457+
&& peek(1)
8458+
&& peek(1)->type() == lexeme::Less
8459+
)
8460+
{
8461+
auto specialization_template_arguments = unqualified_id();
8462+
if (!specialization_template_arguments) {
8463+
pos = start_pos; // backtrack
8464+
return {};
8465+
}
8466+
n->specialization_template_arguments = std::move(specialization_template_arguments);
8467+
}
8468+
84248469
auto a = std::make_unique<alias_node>( &curr() );
84258470

84268471
// Next must be 'type', 'namespace', a type-id, or we're at the 'requires' or '=='
84278472
if (curr() == "type")
84288473
{
84298474
next();
8475+
if (n->specialization_template_arguments) {
8476+
errors.emplace_back(
8477+
curr().position(),
8478+
"a type alias cannot be specialized"
8479+
);
8480+
return {};
8481+
}
84308482
}
84318483
else if (curr() == "namespace")
84328484
{
@@ -8438,6 +8490,13 @@ class parser
84388490
);
84398491
return {};
84408492
}
8493+
if (n->specialization_template_arguments) {
8494+
errors.emplace_back(
8495+
curr().position(),
8496+
"a namespace alias cannot be specialized"
8497+
);
8498+
return {};
8499+
}
84418500
}
84428501
else if (curr().type() != lexeme::EqualComparison && curr() != "requires")
84438502
{

0 commit comments

Comments
 (0)