Skip to content

Commit eb04fb1

Browse files
committed
feat: allow template specialization
1 parent ecd3726 commit eb04fb1

10 files changed

+117
-8
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
clang version 18.0.0 (https://git.uplinklabs.net/mirrors/llvm-project.git c0abd3814564a568dfc607c216e6407eaa314f46)
1+
clang version 18.0.0 (https://github.com/llvm/llvm-project.git 3723ede3cf5324827f8fbbe7f484c2ee4d7a7204)
22
Target: x86_64-pc-linux-gnu
33
Thread model: posix
44
InstalledDir: /home/johel/root/clang-main/bin

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
@@ -1619,7 +1619,8 @@ class cppfront
16191619
unqualified_id_node const& n,
16201620
bool in_synthesized_multi_return = false,
16211621
bool is_local_name = true,
1622-
bool is_qualified = false
1622+
bool is_qualified = false,
1623+
bool emit_identifier = true
16231624
)
16241625
-> void
16251626
{
@@ -1669,7 +1670,9 @@ class cppfront
16691670
}
16701671

16711672
assert(n.identifier);
1672-
emit(*n.identifier, is_qualified); // inform the identifier if we know this is qualified
1673+
if (emit_identifier) {
1674+
emit(*n.identifier, is_qualified); // inform the identifier if we know this is qualified
1675+
}
16731676

16741677
if (n.open_angle != source_position{}) {
16751678
printer.print_cpp2("<", n.open_angle);
@@ -4893,6 +4896,18 @@ class cppfront
48934896
return;
48944897
}
48954898

4899+
// Do not forward declare specializations.
4900+
if (
4901+
n.is_specialization()
4902+
&& (
4903+
(n.is_type() && printer.get_phase() == printer.phase0_type_decls)
4904+
|| (n.is_object() && printer.get_phase() == printer.phase1_type_defs_func_decls)
4905+
)
4906+
)
4907+
{
4908+
return;
4909+
}
4910+
48964911
// If this is a generated declaration (negative source line number),
48974912
// add a line break before
48984913
if (
@@ -5160,7 +5175,7 @@ class cppfront
51605175

51615176
// Now, emit our own template parameters
51625177
if (
5163-
n.template_parameters
5178+
(n.template_parameters || n.is_specialization())
51645179
&& (
51655180
printer.get_phase() < printer.phase2_func_defs
51665181
|| n.is_object()
@@ -5178,7 +5193,12 @@ class cppfront
51785193
)
51795194
{
51805195
printer.print_cpp2("template", n.position());
5181-
emit(*n.template_parameters, false, true);
5196+
if (n.template_parameters) {
5197+
emit(*n.template_parameters, false, true);
5198+
} else {
5199+
assert(n.is_specialization());
5200+
printer.print_cpp2("<>", n.position());
5201+
}
51825202
printer.print_cpp2(" ", n.position());
51835203
}
51845204

@@ -5201,6 +5221,9 @@ class cppfront
52015221

52025222
printer.print_cpp2("class ", n.position());
52035223
emit(*n.identifier);
5224+
if (n.specialization_template_arguments) {
5225+
emit(*n.specialization_template_arguments, false, true, false, false);
5226+
}
52045227

52055228
// Type declaration
52065229
if (printer.get_phase() == printer.phase0_type_decls) {
@@ -5930,6 +5953,9 @@ class cppfront
59305953
}
59315954
else {
59325955
emit(*n.identifier);
5956+
if (n.specialization_template_arguments) {
5957+
emit(*n.specialization_template_arguments, false, true, false, false);
5958+
}
59335959
}
59345960

59355961
if (

source/parse.h

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

23642364
std::vector<std::unique_ptr<id_expression_node>> meta_functions;
23652365
std::unique_ptr<parameter_declaration_list_node> template_parameters;
2366+
std::unique_ptr<unqualified_id_node> specialization_template_arguments;
23662367
source_position requires_pos = {};
23672368
std::unique_ptr<logical_or_expression_node> requires_clause_expression;
23682369

@@ -2603,6 +2604,8 @@ struct declaration_node
26032604

26042605
auto is_function_expression () const -> bool
26052606
{ return is_function() && !identifier; }
2607+
auto is_specialization() const -> bool
2608+
{ return specialization_template_arguments != nullptr; }
26062609

26072610
auto is_polymorphic() const // has base types or virtual functions
26082611
-> bool
@@ -4991,6 +4994,7 @@ class parser
49914994
//G
49924995
//G template-argument-list:
49934996
//G template-argument-list ',' template-argument
4997+
//G template-argument
49944998
//G
49954999
//G template-argument:
49965000
//G # note: < > << >> are not allowed in expressions until new ( is opened
@@ -6470,9 +6474,9 @@ class parser
64706474

64716475
//G unnamed-declaration:
64726476
//G ':' meta-functions-list? template-parameter-declaration-list? function-type requires-clause? '=' statement
6473-
//G ':' meta-functions-list? template-parameter-declaration-list? type-id? requires-clause? '=' statement
6477+
//G ':' meta-functions-list? template-parameter-declaration-list? template-specialization-argument-list? type-id? requires-clause? '=' statement
64746478
//G ':' meta-functions-list? template-parameter-declaration-list? type-id
6475-
//G ':' meta-functions-list? template-parameter-declaration-list? 'final'? 'type' requires-clause? '=' statement
6479+
//G ':' meta-functions-list? template-parameter-declaration-list? template-specialization-argument-list? 'final'? 'type' requires-clause? '=' statement
64766480
//G ':' 'namespace' '=' statement
64776481
//G
64786482
//G meta-functions-list:
@@ -6483,7 +6487,10 @@ class parser
64836487
//G 'requires' logical-or-expression
64846488
//G
64856489
//G template-parameter-declaration-list
6486-
//G '<' parameter-declaration-seq '>'
6490+
//G '<' parameter-declaration-seq? '>'
6491+
//G
6492+
//G template-specialization-argument-list:
6493+
//G 'specialize' '<' template-argument-list '>'
64876494
//G
64886495
auto unnamed_declaration(
64896496
source_position start,
@@ -6624,6 +6631,21 @@ class parser
66246631
n->template_parameters = std::move(template_parameters);
66256632
}
66266633

6634+
// Next is an optional template specialization argument list
6635+
if (
6636+
curr() == "specialize"
6637+
&& peek(1)
6638+
&& peek(1)->type() == lexeme::Less
6639+
)
6640+
{
6641+
auto specialization_template_arguments = unqualified_id();
6642+
if (!specialization_template_arguments) {
6643+
error("invalid template specialization argument list");
6644+
return {};
6645+
}
6646+
n->specialization_template_arguments = std::move(specialization_template_arguments);
6647+
}
6648+
66276649
auto guard =
66286650
captures_allowed
66296651
? std::make_unique<capture_groups_stack_guard>(this, &n->captures)

0 commit comments

Comments
 (0)