Skip to content

Commit 3aa3929

Browse files
committed
feat: allow template specialization
1 parent efd185f commit 3aa3929

14 files changed

+123
-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.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
sizeof(x) is 25
2+
(not a name)
3+
xyz

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 (
@@ -5283,7 +5298,7 @@ class cppfront
52835298

52845299
// Now, emit our own template parameters
52855300
if (
5286-
n.template_parameters
5301+
(n.template_parameters || n.is_specialization())
52875302
&& (
52885303
printer.get_phase() < printer.phase2_func_defs
52895304
|| n.is_object()
@@ -5301,7 +5316,12 @@ class cppfront
53015316
)
53025317
{
53035318
printer.print_cpp2("template", n.position());
5304-
emit(*n.template_parameters, false, true);
5319+
if (n.template_parameters) {
5320+
emit(*n.template_parameters, false, true);
5321+
} else {
5322+
assert(n.is_specialization());
5323+
printer.print_cpp2("<>", n.position());
5324+
}
53055325
printer.print_cpp2(" ", n.position());
53065326
}
53075327

@@ -5324,6 +5344,9 @@ class cppfront
53245344

53255345
printer.print_cpp2("class ", n.position());
53265346
emit(*n.identifier);
5347+
if (n.specialization_template_arguments) {
5348+
emit(*n.specialization_template_arguments, false, true, false, false);
5349+
}
53275350

53285351
// Type declaration
53295352
if (printer.get_phase() == printer.phase0_type_decls) {
@@ -6032,6 +6055,9 @@ class cppfront
60326055
}
60336056
else {
60346057
emit(*n.identifier);
6058+
if (n.specialization_template_arguments) {
6059+
emit(*n.specialization_template_arguments, false, true, false, false);
6060+
}
60356061
}
60366062

60376063
if (

source/parse.h

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

25012501
std::vector<std::unique_ptr<id_expression_node>> metafunctions;
25022502
std::unique_ptr<parameter_declaration_list_node> template_parameters;
2503+
std::unique_ptr<unqualified_id_node> specialization_template_arguments;
25032504
source_position requires_pos = {};
25042505
std::unique_ptr<logical_or_expression_node> requires_clause_expression;
25052506

@@ -2740,6 +2741,8 @@ struct declaration_node
27402741

27412742
auto is_function_expression () const -> bool
27422743
{ return is_function() && !identifier; }
2744+
auto is_specialization() const -> bool
2745+
{ return specialization_template_arguments != nullptr; }
27432746

27442747
auto is_polymorphic() const // has base types or virtual functions
27452748
-> bool
@@ -6016,6 +6019,7 @@ class parser
60166019
//G
60176020
//G template-argument-list:
60186021
//G template-argument-list ',' template-argument
6022+
//G template-argument
60196023
//G
60206024
//G template-argument:
60216025
//G # note: < > << >> are not allowed in expressions until new ( is opened
@@ -7480,9 +7484,9 @@ class parser
74807484

74817485
//G unnamed-declaration:
74827486
//G ':' meta-functions-list? template-parameter-declaration-list? function-type requires-clause? '=' statement
7483-
//G ':' meta-functions-list? template-parameter-declaration-list? type-id? requires-clause? '=' statement
7487+
//G ':' meta-functions-list? template-parameter-declaration-list? template-specialization-argument-list? type-id? requires-clause? '=' statement
74847488
//G ':' meta-functions-list? template-parameter-declaration-list? type-id
7485-
//G ':' meta-functions-list? template-parameter-declaration-list? 'final'? 'type' requires-clause? '=' statement
7489+
//G ':' meta-functions-list? template-parameter-declaration-list? template-specialization-argument-list? 'final'? 'type' requires-clause? '=' statement
74867490
//G ':' 'namespace' '=' statement
74877491
//G
74887492
//G meta-functions-list:
@@ -7494,7 +7498,10 @@ class parser
74947498
//G 'requires' logical-or-expression
74957499
//G
74967500
//G template-parameter-declaration-list
7497-
//G '<' parameter-declaration-seq '>'
7501+
//G '<' parameter-declaration-seq? '>'
7502+
//G
7503+
//G template-specialization-argument-list:
7504+
//G 'specialize' '<' template-argument-list '>'
74987505
//G
74997506
auto unnamed_declaration(
75007507
source_position start,
@@ -7637,6 +7644,21 @@ class parser
76377644
n->template_parameters = std::move(template_parameters);
76387645
}
76397646

7647+
// Next is an optional template specialization argument list
7648+
if (
7649+
curr() == "specialize"
7650+
&& peek(1)
7651+
&& peek(1)->type() == lexeme::Less
7652+
)
7653+
{
7654+
auto specialization_template_arguments = unqualified_id();
7655+
if (!specialization_template_arguments) {
7656+
error("invalid template specialization argument list");
7657+
return {};
7658+
}
7659+
n->specialization_template_arguments = std::move(specialization_template_arguments);
7660+
}
7661+
76407662
auto guard =
76417663
captures_allowed
76427664
? std::make_unique<capture_groups_stack_guard>(this, &n->captures)

0 commit comments

Comments
 (0)