Skip to content

Commit 16174b3

Browse files
committed
feat: allow template specialization
1 parent 387d352 commit 16174b3

13 files changed

+120
-7
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
t: @struct <T: type> type = {
2+
a: i32 = 1;
3+
}
4+
t: @struct <T: type> 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+
main: () = {
13+
[[assert Testing: t<i32>().a == 1]]
14+
[[assert Testing: t<void>().b == 2]]
15+
[[assert Testing: t<i64>().c == 3]]
16+
[[assert Testing: (v<i32>) == 1]]
17+
[[assert Testing: (v<void>) == 2]]
18+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
clang version 18.0.0 (https://github.com/llvm/llvm-project.git 3723ede3cf5324827f8fbbe7f484c2ee4d7a7204)
2+
Target: x86_64-pc-linux-gnu
3+
Thread model: posix
4+
InstalledDir: /home/johel/root/clang-main/bin

regression-tests/test-results/clang-18/pure2-bugfix-for-non-local-function-expression.cpp.execution

Whitespace-only changes.

regression-tests/test-results/clang-18/pure2-bugfix-for-non-local-function-expression.cpp.output

Whitespace-only changes.

regression-tests/test-results/clang-18/pure2-print.cpp.output

Whitespace-only changes.

regression-tests/test-results/clang-18/pure2-template-specialization.cpp.execution

Whitespace-only changes.

regression-tests/test-results/clang-18/pure2-template-specialization.cpp.output

Whitespace-only changes.

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.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
#define CPP2_USE_MODULES 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+
auto main() -> int;
26+
27+
28+
//=== Cpp2 function definitions =================================================
29+
30+
31+
#line 10 "pure2-template-specialization.cpp2"
32+
template<typename T> cpp2::i32 const v {1};
33+
template<> cpp2::i32 const v<void> {2};
34+
auto main() -> int{
35+
cpp2::Testing.expects(t<cpp2::i32>().a == 1, "");
36+
cpp2::Testing.expects(t<void>().b == 2, "");
37+
cpp2::Testing.expects(t<cpp2::i64>().c == 3, "");
38+
cpp2::Testing.expects((v<cpp2::i32>) == 1, "");
39+
cpp2::Testing.expects((v<void>) == 2, "");
40+
}
41+
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/cppfront.cpp

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,7 +1617,8 @@ class cppfront
16171617
unqualified_id_node const& n,
16181618
bool in_synthesized_multi_return = false,
16191619
bool is_local_name = true,
1620-
bool is_qualified = false
1620+
bool is_qualified = false,
1621+
bool emit_identifier = true
16211622
)
16221623
-> void
16231624
{
@@ -1667,7 +1668,9 @@ class cppfront
16671668
}
16681669

16691670
assert(n.identifier);
1670-
emit(*n.identifier, is_qualified); // inform the identifier if we know this is qualified
1671+
if (emit_identifier) {
1672+
emit(*n.identifier, is_qualified); // inform the identifier if we know this is qualified
1673+
}
16711674

16721675
if (n.open_angle != source_position{}) {
16731676
printer.print_cpp2("<", n.open_angle);
@@ -4969,6 +4972,18 @@ class cppfront
49694972
return;
49704973
}
49714974

4975+
// Do not forward declare specializations.
4976+
if (
4977+
n.is_specialization()
4978+
&& (
4979+
(n.is_type() && printer.get_phase() == printer.phase0_type_decls)
4980+
|| (n.is_object() && printer.get_phase() == printer.phase1_type_defs_func_decls)
4981+
)
4982+
)
4983+
{
4984+
return;
4985+
}
4986+
49724987
// If this is a generated declaration (negative source line number),
49734988
// add a line break before
49744989
if (
@@ -5328,7 +5343,7 @@ class cppfront
53285343

53295344
// Now, emit our own template parameters
53305345
if (
5331-
n.template_parameters
5346+
(n.template_parameters || n.is_specialization())
53325347
&& (
53335348
printer.get_phase() < printer.phase2_func_defs
53345349
|| n.is_object()
@@ -5346,7 +5361,12 @@ class cppfront
53465361
)
53475362
{
53485363
printer.print_cpp2("template", n.position());
5349-
emit(*n.template_parameters, false, true);
5364+
if (n.template_parameters) {
5365+
emit(*n.template_parameters, false, true);
5366+
} else {
5367+
assert(n.is_specialization());
5368+
printer.print_cpp2("<>", n.position());
5369+
}
53505370
printer.print_cpp2(" ", n.position());
53515371
}
53525372

@@ -5369,6 +5389,9 @@ class cppfront
53695389

53705390
printer.print_cpp2("class ", n.position());
53715391
emit(*n.identifier);
5392+
if (n.specialization_template_arguments) {
5393+
emit(*n.specialization_template_arguments, false, true, false, false);
5394+
}
53725395

53735396
// Type declaration
53745397
if (printer.get_phase() == printer.phase0_type_decls) {
@@ -6111,6 +6134,9 @@ class cppfront
61116134
}
61126135
else {
61136136
emit(*n.identifier);
6137+
if (n.specialization_template_arguments) {
6138+
emit(*n.specialization_template_arguments, false, true, false, false);
6139+
}
61146140
}
61156141

61166142
if (

source/parse.h

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2512,6 +2512,7 @@ struct declaration_node
25122512

25132513
std::vector<std::unique_ptr<id_expression_node>> metafunctions;
25142514
std::unique_ptr<parameter_declaration_list_node> template_parameters;
2515+
std::unique_ptr<unqualified_id_node> specialization_template_arguments;
25152516
source_position requires_pos = {};
25162517
std::unique_ptr<logical_or_expression_node> requires_clause_expression;
25172518

@@ -2796,6 +2797,8 @@ struct declaration_node
27962797

27972798
auto is_function_expression () const -> bool
27982799
{ return is_function() && !identifier; }
2800+
auto is_specialization() const -> bool
2801+
{ return specialization_template_arguments != nullptr; }
27992802

28002803
auto is_polymorphic() const // has base types or virtual functions
28012804
-> bool
@@ -6109,6 +6112,7 @@ class parser
61096112
//G
61106113
//G template-argument-list:
61116114
//G template-argument-list ',' template-argument
6115+
//G template-argument
61126116
//G
61136117
//G template-argument:
61146118
//G # note: < > << >> are not allowed in expressions until new ( is opened
@@ -7606,9 +7610,9 @@ class parser
76067610
//G unnamed-declaration:
76077611
//G ':' meta-functions-list? template-parameter-declaration-list? function-type requires-clause? '=' statement
76087612
//G ':' meta-functions-list? template-parameter-declaration-list? function-type statement
7609-
//G ':' meta-functions-list? template-parameter-declaration-list? type-id? requires-clause? '=' statement
7613+
//G ':' meta-functions-list? template-parameter-declaration-list? template-specialization-argument-list? type-id? requires-clause? '=' statement
76107614
//G ':' meta-functions-list? template-parameter-declaration-list? type-id
7611-
//G ':' meta-functions-list? template-parameter-declaration-list? 'final'? 'type' requires-clause? '=' statement
7615+
//G ':' meta-functions-list? template-parameter-declaration-list? template-specialization-argument-list? 'final'? 'type' requires-clause? '=' statement
76127616
//G ':' 'namespace' '=' statement
76137617
//G
76147618
//G meta-functions-list:
@@ -7620,7 +7624,10 @@ class parser
76207624
//G 'requires' logical-or-expression
76217625
//G
76227626
//G template-parameter-declaration-list
7623-
//G '<' parameter-declaration-seq '>'
7627+
//G '<' parameter-declaration-seq? '>'
7628+
//G
7629+
//G template-specialization-argument-list:
7630+
//G 'specialize' '<' template-argument-list '>'
76247631
//G
76257632
auto unnamed_declaration(
76267633
source_position start,
@@ -7765,6 +7772,21 @@ class parser
77657772
n->template_parameters = std::move(template_parameters);
77667773
}
77677774

7775+
// Next is an optional template specialization argument list
7776+
if (
7777+
curr() == "specialize"
7778+
&& peek(1)
7779+
&& peek(1)->type() == lexeme::Less
7780+
)
7781+
{
7782+
auto specialization_template_arguments = unqualified_id();
7783+
if (!specialization_template_arguments) {
7784+
error("invalid template specialization argument list");
7785+
return {};
7786+
}
7787+
n->specialization_template_arguments = std::move(specialization_template_arguments);
7788+
}
7789+
77687790
auto guard =
77697791
captures_allowed
77707792
? std::make_unique<capture_groups_stack_guard>(this, &n->captures)

0 commit comments

Comments
 (0)