Skip to content

Commit

Permalink
feat: allow template specialization
Browse files Browse the repository at this point in the history
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;
```
  • Loading branch information
JohelEGP committed Dec 11, 2023
1 parent 05ce45a commit d7cc82a
Show file tree
Hide file tree
Showing 10 changed files with 266 additions and 11 deletions.
9 changes: 9 additions & 0 deletions regression-tests/pure2-print.cpp2
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,15 @@ outer: @print type = {

}

std: namespace = {
common_type: @struct @print specialize<outer, outer> type = {
type: type == outer;
}
numbers: namespace = {
pi_v: /*@print*/ specialize<outer> const double = pi_v<double>;
}
}

main: () = {
outer::test();
}
38 changes: 38 additions & 0 deletions regression-tests/pure2-template-specialization.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
t: @struct <T> type = {
a: i32 = 1;
}
t: @struct <T> specialize<T> type requires std::is_void_v<T> = {
b: i32 = 2;
}
t: @struct specialize<i64> type = {
c: i32 = 3;
}
t: @struct specialize<* i8> type = {
f: () 17;
v: int == 29;
}
t: @struct <T> specialize<* T> type = {
v: int == 17;
f: () 29;
}
v: <T> const i32 = 1;
v: <> specialize<void> const i32 = 2;
v: specialize<i64> const i32 = 3;
v: specialize<i16> std::optional<i32> == 4;
v: <> specialize<i8> std::optional<i8> == 5;
v: <T> specialize<* T> std::optional<int> == 6;
main: () = {
assert(t<i32>().a == 1);
assert(t<void>().b == 2);
assert(t<i64>().c == 3);
assert(t<* i8>::f() == 17);
assert(t<* i8>::v == 29);
assert(t<* i16>::v == 17);
assert(t<* i16>::f() == 29);
assert(v<i32> == 1);
assert(v<void> == 2);
assert(v<i64> == 3);
static_assert(v<i16> == 4);
static_assert(v<i8> == 5);
static_assert(v<* int> == 6);
}
Empty file.
Empty file.
26 changes: 26 additions & 0 deletions regression-tests/test-results/pure2-print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
#line 4 "pure2-print.cpp2"
class outer;

#line 102 "pure2-print.cpp2"
namespace std {

#line 106 "pure2-print.cpp2"
namespace numbers {

}
}


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

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

namespace std {
template<> class common_type<outer,outer> {
public: using type = outer;
};
namespace numbers {
/*@print*/
}
}

auto main() -> int;

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

#line 102 "pure2-print.cpp2"
namespace std {

#line 106 "pure2-print.cpp2"
namespace numbers {
template<> double const pi_v<outer> {pi_v<double>};
}
}

auto main() -> int{
outer::test();
}
Expand Down
6 changes: 6 additions & 0 deletions regression-tests/test-results/pure2-print.cpp2.output
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,11 @@ outer: type =

all: <Args...: type>(in args...: Args) -> move bool = (... && args);
}


common_type: specialize<outer, outer> type =
{
public type: type == outer;
}
ok (all Cpp2, passes safety checks)

71 changes: 71 additions & 0 deletions regression-tests/test-results/pure2-template-specialization.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@

#define CPP2_IMPORT_STD Yes

//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"

template<typename T> class t;


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

template<typename T> class t {
public: cpp2::i32 a {1};
};
template<typename T> requires( std::is_void_v<T> )
class t<T> {public: cpp2::i32 b {2};
};
template<> class t<cpp2::i64> {
public: cpp2::i32 c {3};
};
template<> class t<cpp2::i8*> {
public: [[nodiscard]] static auto f() -> auto;
public: static const int v;
};
template<typename T> class t<T*> {
public: static const int v;
public: [[nodiscard]] static auto f() -> auto;
};
template<typename T> extern cpp2::i32 const v;

#line 21 "pure2-template-specialization.cpp2"
template<> std::optional<cpp2::i32> inline constexpr v<cpp2::i16> = 4;
template<> std::optional<cpp2::i8> inline constexpr v<cpp2::i8> = 5;
template<typename T> std::optional<int> inline constexpr v<T*> = 6;
auto main() -> int;


//=== Cpp2 function definitions =================================================


#line 11 "pure2-template-specialization.cpp2"
[[nodiscard]] auto t<cpp2::i8*>::f() -> auto { return 17; }
inline CPP2_CONSTEXPR int t<cpp2::i8*>::v = 29;

#line 15 "pure2-template-specialization.cpp2"
template <typename T> inline CPP2_CONSTEXPR int t<T*>::v = 17;
template <typename T> [[nodiscard]] auto t<T*>::f() -> auto { return 29; }

template<typename T> cpp2::i32 const v {1};
template<> cpp2::i32 const v<void> {2};
template<> cpp2::i32 const v<cpp2::i64> {3};

#line 24 "pure2-template-specialization.cpp2"
auto main() -> int{
cpp2::Default.expects(t<cpp2::i32>().a == 1, "");
cpp2::Default.expects(t<void>().b == 2, "");
cpp2::Default.expects(t<cpp2::i64>().c == 3, "");
cpp2::Default.expects(t<cpp2::i8*>::f() == 17, "");
cpp2::Default.expects(t<cpp2::i8*>::v == 29, "");
cpp2::Default.expects(t<cpp2::i16*>::v == 17, "");
cpp2::Default.expects(t<cpp2::i16*>::f() == 29, "");
cpp2::Default.expects(v<cpp2::i32> == 1, "");
cpp2::Default.expects(v<void> == 2, "");
cpp2::Default.expects(v<cpp2::i64> == 3, "");
static_assert(v<cpp2::i16> == 4);
static_assert(v<cpp2::i8> == 5);
static_assert(v<int*> == 6);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pure2-template-specialization.cpp2... ok (all Cpp2, passes safety checks)

71 changes: 65 additions & 6 deletions source/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -2557,6 +2557,7 @@ struct declaration_node

std::vector<std::unique_ptr<id_expression_node>> metafunctions;
std::unique_ptr<parameter_declaration_list_node> template_parameters;
std::unique_ptr<unqualified_id_node> specialization_template_arguments;
source_position requires_pos = {};
std::unique_ptr<logical_or_expression_node> requires_clause_expression;

Expand Down Expand Up @@ -2841,6 +2842,8 @@ struct declaration_node

auto is_function_expression () const -> bool
{ return is_function() && !identifier; }
auto is_specialization() const -> bool
{ return specialization_template_arguments != nullptr; }

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

auto specialization_args = std::string{};
if (n.specialization_template_arguments) {
specialization_args += " " + pretty_print_visualize(*n.specialization_template_arguments, indent + 1);
}

auto requires_clause = std::string{};
if (n.requires_clause_expression) {
requires_clause += " requires (" + pretty_print_visualize(*n.requires_clause_expression, indent) + ")";
Expand Down Expand Up @@ -4973,7 +4981,8 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
auto& type_id = std::get<declaration_node::an_object>(n.type);
assert(type_id);
ret += metafunctions
+ template_params;
+ template_params
+ specialization_args;
if (!n.has_wildcard_type()) {
ret += " " + pretty_print_visualize(*type_id, indent);
}
Expand All @@ -4985,6 +4994,7 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
assert(t);
ret += metafunctions
+ template_params
+ specialization_args
+ " " + pretty_print_visualize(*t)
+ initializer;
}
Expand All @@ -5003,7 +5013,8 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_
object_type_id += " " + pretty_print_visualize(*a->type_id, indent);
}

ret += template_params;
ret += template_params
+ specialization_args;
if (a->is_type_alias()) {
auto& t = std::get<alias_node::a_type>(a->initializer);
ret += " type"
Expand Down Expand Up @@ -6305,6 +6316,7 @@ class parser
//G
//G template-argument-list:
//G template-argument-list ',' template-argument
//G template-argument
//G
//G template-argument:
//G # note: < > << >> are not allowed in expressions until new ( is opened
Expand Down Expand Up @@ -7868,9 +7880,9 @@ class parser
//G unnamed-declaration:
//G ':' meta-functions-list? template-parameter-declaration-list? function-type requires-clause? '=' statement
//G ':' meta-functions-list? template-parameter-declaration-list? function-type statement
//G ':' meta-functions-list? template-parameter-declaration-list? type-id? requires-clause? '=' statement
//G ':' meta-functions-list? template-parameter-declaration-list? template-specialization-argument-list? type-id? requires-clause? '=' statement
//G ':' meta-functions-list? template-parameter-declaration-list? type-id
//G ':' meta-functions-list? template-parameter-declaration-list? 'final'? 'type' requires-clause? '=' statement
//G ':' meta-functions-list? template-parameter-declaration-list? template-specialization-argument-list? 'final'? 'type' requires-clause? '=' statement
//G ':' 'namespace' '=' statement
//G
//G meta-functions-list:
Expand All @@ -7882,7 +7894,10 @@ class parser
//G 'requires' logical-or-expression
//G
//G template-parameter-declaration-list
//G '<' parameter-declaration-seq '>'
//G '<' parameter-declaration-seq? '>'
//G
//G template-specialization-argument-list:
//G 'specialize' '<' template-argument-list '>'
//G
auto unnamed_declaration(
source_position start,
Expand Down Expand Up @@ -8027,6 +8042,21 @@ class parser
n->template_parameters = std::move(template_parameters);
}

// Next is an optional template specialization argument list
if (
curr() == "specialize"
&& peek(1)
&& peek(1)->type() == lexeme::Less
)
{
auto specialization_template_arguments = unqualified_id();
if (!specialization_template_arguments) {
error("invalid template specialization argument list");
return {};
}
n->specialization_template_arguments = std::move(specialization_template_arguments);
}

auto guard =
captures_allowed
? std::make_unique<capture_groups_stack_guard>(this, &n->captures)
Expand Down Expand Up @@ -8392,7 +8422,7 @@ class parser
//G alias:
//G ':' template-parameter-declaration-list? 'type' requires-clause? '==' type-id ';'
//G ':' 'namespace' '==' id-expression ';'
//G ':' template-parameter-declaration-list? type-id? requires-clause? '==' expression ';'
//G ':' template-parameter-declaration-list? template-specialization-argument-list? type-id? requires-clause? '==' expression ';'
//G
//GT ':' function-type '==' expression ';'
//GT # See commit 63efa6ed21c4d4f4f136a7a73e9f6b2c110c81d7 comment
Expand Down Expand Up @@ -8421,12 +8451,34 @@ class parser
n->template_parameters = std::move(template_parameters);
}

// Next is an optional template specialization argument list
if (
curr() == "specialize"
&& peek(1)
&& peek(1)->type() == lexeme::Less
)
{
auto specialization_template_arguments = unqualified_id();
if (!specialization_template_arguments) {
pos = start_pos; // backtrack
return {};
}
n->specialization_template_arguments = std::move(specialization_template_arguments);
}

auto a = std::make_unique<alias_node>( &curr() );

// Next must be 'type', 'namespace', a type-id, or we're at the 'requires' or '=='
if (curr() == "type")
{
next();
if (n->specialization_template_arguments) {
errors.emplace_back(
curr().position(),
"a type alias cannot be specialized"
);
return {};
}
}
else if (curr() == "namespace")
{
Expand All @@ -8438,6 +8490,13 @@ class parser
);
return {};
}
if (n->specialization_template_arguments) {
errors.emplace_back(
curr().position(),
"a namespace alias cannot be specialized"
);
return {};
}
}
else if (curr().type() != lexeme::EqualComparison && curr() != "requires")
{
Expand Down
Loading

0 comments on commit d7cc82a

Please sign in to comment.