diff --git a/regression-tests/pure2-alias-1-error.cpp2 b/regression-tests/pure2-alias-1-error.cpp2 new file mode 100644 index 000000000..22d033a6d --- /dev/null +++ b/regression-tests/pure2-alias-1-error.cpp2 @@ -0,0 +1 @@ +a1: namespace == { } diff --git a/regression-tests/pure2-alias-2-error.cpp2 b/regression-tests/pure2-alias-2-error.cpp2 new file mode 100644 index 000000000..66a495d5f --- /dev/null +++ b/regression-tests/pure2-alias-2-error.cpp2 @@ -0,0 +1 @@ +a2: namespace requires true == { } diff --git a/regression-tests/pure2-alias-3-error.cpp2 b/regression-tests/pure2-alias-3-error.cpp2 new file mode 100644 index 000000000..ca4363bdc --- /dev/null +++ b/regression-tests/pure2-alias-3-error.cpp2 @@ -0,0 +1 @@ +a3: type requires true = { } diff --git a/regression-tests/pure2-alias-4-error.cpp2 b/regression-tests/pure2-alias-4-error.cpp2 new file mode 100644 index 000000000..2630d7e24 --- /dev/null +++ b/regression-tests/pure2-alias-4-error.cpp2 @@ -0,0 +1 @@ +a4: specialize<> type == int; diff --git a/regression-tests/pure2-alias-5-error.cpp2 b/regression-tests/pure2-alias-5-error.cpp2 new file mode 100644 index 000000000..cdc8cf315 --- /dev/null +++ b/regression-tests/pure2-alias-5-error.cpp2 @@ -0,0 +1 @@ +a5: specialize<> namespace == std; diff --git a/regression-tests/pure2-namespace-1-error.cpp2 b/regression-tests/pure2-namespace-1-error.cpp2 new file mode 100644 index 000000000..3ac11a3f2 --- /dev/null +++ b/regression-tests/pure2-namespace-1-error.cpp2 @@ -0,0 +1 @@ +n1: namespace = { } diff --git a/regression-tests/pure2-namespace-2-error.cpp2 b/regression-tests/pure2-namespace-2-error.cpp2 new file mode 100644 index 000000000..41756e50c --- /dev/null +++ b/regression-tests/pure2-namespace-2-error.cpp2 @@ -0,0 +1 @@ +n2: namespace requires true = { } diff --git a/regression-tests/pure2-print.cpp2 b/regression-tests/pure2-print.cpp2 index 6b80d9cb7..3f5259991 100644 --- a/regression-tests/pure2-print.cpp2 +++ b/regression-tests/pure2-print.cpp2 @@ -104,6 +104,15 @@ outer: @print type = { } +std: namespace = { + common_type: @struct @print specialize type = { + type: type == outer; + } + numbers: namespace = { + pi_v: /*@print*/ specialize const double = pi_v; + } +} + main: () = { outer::test(); } diff --git a/regression-tests/pure2-template-specialization.cpp2 b/regression-tests/pure2-template-specialization.cpp2 new file mode 100644 index 000000000..279d53da4 --- /dev/null +++ b/regression-tests/pure2-template-specialization.cpp2 @@ -0,0 +1,47 @@ +t: @cpp1_rule_of_zero type = { + public a: i32 = 1; +} +t: @cpp1_rule_of_zero specialize type requires std::is_void_v = { + public b: i32 = 2; +} +t: @cpp1_rule_of_zero specialize type = { + public c: i32 = 3; +} +t: @cpp1_rule_of_zero specialize<* i8> type = { + public f: () = 17; + public v: int == 29; +} +t: @cpp1_rule_of_zero specialize<* T> type = { + public v: int == 17; + public f: () = 29; +} +u: @cpp1_rule_of_zero type = { + public a: i32 = 1; +} +u: @cpp1_rule_of_zero specialize<> type = { + public a: i32 = 2; +} +u: specialize type = { } +v: const i32 = 1; +v: <> specialize const i32 = 2; +v: specialize const i32 = 3; +v: specialize std::optional == 4; +v: <> specialize std::optional == 5; +v: specialize<* T> std::optional == 6; +main: () = { + assert(t().a == 1); + assert(t().b == 2); + assert(t().c == 3); + assert(t<* i8>::f() == 17); + assert(t<* i8>::v == 29); + assert(t<* i16>::v == 17); + assert(t<* i16>::f() == 29); + assert(u().a == 1); + assert(u<>().a == 1); + assert(v == 1); + assert(v == 2); + assert(v == 3); + static_assert(v == 4); + static_assert(v == 5); + static_assert(v<* int> == 6); +} diff --git a/regression-tests/test-results/pure2-alias-1-error.cpp2.output b/regression-tests/test-results/pure2-alias-1-error.cpp2.output new file mode 100644 index 000000000..777402256 --- /dev/null +++ b/regression-tests/test-results/pure2-alias-1-error.cpp2.output @@ -0,0 +1,3 @@ +pure2-alias-1-error.cpp2... +pure2-alias-1-error.cpp2(1,19): error: a namespace or namespace alias cannot have template parameters + diff --git a/regression-tests/test-results/pure2-alias-2-error.cpp2.output b/regression-tests/test-results/pure2-alias-2-error.cpp2.output new file mode 100644 index 000000000..49c356b42 --- /dev/null +++ b/regression-tests/test-results/pure2-alias-2-error.cpp2.output @@ -0,0 +1,3 @@ +pure2-alias-2-error.cpp2... +pure2-alias-2-error.cpp2(1,15): error: 'requires' is not allowed on a namespace alias (at 'requires') + diff --git a/regression-tests/test-results/pure2-alias-3-error.cpp2.output b/regression-tests/test-results/pure2-alias-3-error.cpp2.output new file mode 100644 index 000000000..3b6d9507e --- /dev/null +++ b/regression-tests/test-results/pure2-alias-3-error.cpp2.output @@ -0,0 +1,3 @@ +pure2-alias-3-error.cpp2... +pure2-alias-3-error.cpp2(1,10): error: 'requires' is not allowed on a type alias that does not have a template parameter list (at 'requires') + diff --git a/regression-tests/test-results/pure2-alias-4-error.cpp2.output b/regression-tests/test-results/pure2-alias-4-error.cpp2.output new file mode 100644 index 000000000..912cdd28e --- /dev/null +++ b/regression-tests/test-results/pure2-alias-4-error.cpp2.output @@ -0,0 +1,3 @@ +pure2-alias-4-error.cpp2... +pure2-alias-4-error.cpp2(1,18): error: a type alias cannot be specialized + diff --git a/regression-tests/test-results/pure2-alias-5-error.cpp2.output b/regression-tests/test-results/pure2-alias-5-error.cpp2.output new file mode 100644 index 000000000..b48a69944 --- /dev/null +++ b/regression-tests/test-results/pure2-alias-5-error.cpp2.output @@ -0,0 +1,3 @@ +pure2-alias-5-error.cpp2... +pure2-alias-5-error.cpp2(1,18): error: a namespace alias cannot be specialized + diff --git a/regression-tests/test-results/pure2-namespace-1-error.cpp2.output b/regression-tests/test-results/pure2-namespace-1-error.cpp2.output new file mode 100644 index 000000000..c1aed0138 --- /dev/null +++ b/regression-tests/test-results/pure2-namespace-1-error.cpp2.output @@ -0,0 +1,3 @@ +pure2-namespace-1-error.cpp2... +pure2-namespace-1-error.cpp2(1,19): error: a namespace or namespace alias cannot have template parameters + diff --git a/regression-tests/test-results/pure2-namespace-2-error.cpp2.output b/regression-tests/test-results/pure2-namespace-2-error.cpp2.output new file mode 100644 index 000000000..bb3424fed --- /dev/null +++ b/regression-tests/test-results/pure2-namespace-2-error.cpp2.output @@ -0,0 +1,3 @@ +pure2-namespace-2-error.cpp2... +pure2-namespace-2-error.cpp2(1,15): error: 'requires' is not allowed on a namespace alias (at 'requires') + diff --git a/regression-tests/test-results/pure2-print.cpp b/regression-tests/test-results/pure2-print.cpp index 47725b573..441a21f21 100644 --- a/regression-tests/test-results/pure2-print.cpp +++ b/regression-tests/test-results/pure2-print.cpp @@ -11,6 +11,15 @@ #line 6 "pure2-print.cpp2" class outer; +#line 107 "pure2-print.cpp2" +namespace std { + +#line 111 "pure2-print.cpp2" + namespace numbers { + + } +} + //=== Cpp2 type definitions and function declarations =========================== @@ -81,6 +90,15 @@ CPP2_REQUIRES_ (cpp2::impl::cmp_greater_eq(sizeof...(Args),0u)) ; #line 105 "pure2-print.cpp2" }; +namespace std { + template<> class common_type { + public: using type = outer; + }; + namespace numbers { + /*@print*/ + } +} + auto main() -> int; //=== Cpp2 function definitions ================================================= @@ -206,6 +224,15 @@ requires (cpp2::impl::cmp_greater_eq(sizeof...(Args),0u)) { auto outer::y([[maybe_unused]] cpp2::impl::in unnamed_param_1) -> void{} #line 107 "pure2-print.cpp2" +namespace std { + +#line 111 "pure2-print.cpp2" + namespace numbers { + template<> double const pi_v {pi_v}; + } +} + +#line 116 "pure2-print.cpp2" auto main() -> int{ outer::test(); } diff --git a/regression-tests/test-results/pure2-print.cpp2.output b/regression-tests/test-results/pure2-print.cpp2.output index 2d4920924..14ba3a7a7 100644 --- a/regression-tests/test-results/pure2-print.cpp2.output +++ b/regression-tests/test-results/pure2-print.cpp2.output @@ -151,5 +151,11 @@ outer:/* @print */ type = { } } + + +common_type:/* @struct @print */ specialize type = +{ + public type: type == outer; +} ok (all Cpp2, passes safety checks) diff --git a/regression-tests/test-results/pure2-template-specialization.cpp b/regression-tests/test-results/pure2-template-specialization.cpp new file mode 100644 index 000000000..99ef7c636 --- /dev/null +++ b/regression-tests/test-results/pure2-template-specialization.cpp @@ -0,0 +1,94 @@ + +#define CPP2_IMPORT_STD Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "pure2-template-specialization.cpp2" +template class t; +#line 2 "pure2-template-specialization.cpp2" + + +#line 18 "pure2-template-specialization.cpp2" +template class u; + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "pure2-template-specialization.cpp2" +template class t { +#line 2 "pure2-template-specialization.cpp2" + public: cpp2::i32 a {1}; +}; +template requires( std::is_void_v ) + class t {public: cpp2::i32 b {2}; +}; +template<> class t { + public: cpp2::i32 c {3}; +}; +template<> class t { + public: [[nodiscard]] static auto f() -> decltype(auto); + public: static const int v; +}; +template class t { + public: static const int v; + public: [[nodiscard]] static auto f() -> decltype(auto); +}; +template class u { + public: cpp2::i32 a {1}; +}; +template<> class u<> { + public: cpp2::i32 a {2}; +}; +template<> class u { + public: u() = default; + public: u(u const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(u const&) -> void = delete; +}; +#line 25 "pure2-template-specialization.cpp2" +template extern cpp2::i32 const v; + +#line 28 "pure2-template-specialization.cpp2" +template<> std::optional inline constexpr v{ 4 }; +template<> std::optional inline constexpr v{ 5 }; +template std::optional inline constexpr v{ 6 }; +auto main() -> int; + +//=== Cpp2 function definitions ================================================= + +#line 1 "pure2-template-specialization.cpp2" + +#line 11 "pure2-template-specialization.cpp2" + [[nodiscard]] auto t::f() -> decltype(auto) { return 17; } + inline CPP2_CONSTEXPR int t::v{ 29 }; + +#line 15 "pure2-template-specialization.cpp2" + template inline CPP2_CONSTEXPR int t::v{ 17 }; + template [[nodiscard]] auto t::f() -> decltype(auto) { return 29; } + +#line 25 "pure2-template-specialization.cpp2" +template cpp2::i32 const v {1}; +template<> cpp2::i32 const v {2}; +template<> cpp2::i32 const v {3}; + +#line 31 "pure2-template-specialization.cpp2" +auto main() -> int{ + if (cpp2::cpp2_default.is_active() && !(t().a == 1) ) { cpp2::cpp2_default.report_violation(""); } + if (cpp2::cpp2_default.is_active() && !(t().b == 2) ) { cpp2::cpp2_default.report_violation(""); } + if (cpp2::cpp2_default.is_active() && !(t().c == 3) ) { cpp2::cpp2_default.report_violation(""); } + if (cpp2::cpp2_default.is_active() && !(t::f() == 17) ) { cpp2::cpp2_default.report_violation(""); } + if (cpp2::cpp2_default.is_active() && !(t::v == 29) ) { cpp2::cpp2_default.report_violation(""); } + if (cpp2::cpp2_default.is_active() && !(t::v == 17) ) { cpp2::cpp2_default.report_violation(""); } + if (cpp2::cpp2_default.is_active() && !(t::f() == 29) ) { cpp2::cpp2_default.report_violation(""); } + if (cpp2::cpp2_default.is_active() && !(u().a == 1) ) { cpp2::cpp2_default.report_violation(""); } + if (cpp2::cpp2_default.is_active() && !(u<>().a == 1) ) { cpp2::cpp2_default.report_violation(""); } + if (cpp2::cpp2_default.is_active() && !(v == 1) ) { cpp2::cpp2_default.report_violation(""); } + if (cpp2::cpp2_default.is_active() && !(v == 2) ) { cpp2::cpp2_default.report_violation(""); } + if (cpp2::cpp2_default.is_active() && !(v == 3) ) { cpp2::cpp2_default.report_violation(""); } + static_assert(v == 4); + static_assert(v == 5); + static_assert(v == 6); +} + diff --git a/regression-tests/test-results/pure2-template-specialization.cpp2.output b/regression-tests/test-results/pure2-template-specialization.cpp2.output new file mode 100644 index 000000000..550585ef6 --- /dev/null +++ b/regression-tests/test-results/pure2-template-specialization.cpp2.output @@ -0,0 +1,2 @@ +pure2-template-specialization.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/source/parse.h b/source/parse.h index a8ad4bd9e..e0ee98cf8 100644 --- a/source/parse.h +++ b/source/parse.h @@ -217,7 +217,7 @@ struct literal_node { if ( !std::exchange(first, false) && p->as_string_view().starts_with("\"") - ) + ) { ret += " "; } @@ -2426,7 +2426,7 @@ struct parameter_declaration_list_node std::vector> parameters; - parameter_declaration_list_node(bool f = false, bool t = false, bool s = false) + parameter_declaration_list_node(bool f = false, bool t = false, bool s = false) : in_function_typeid{f} , in_template_param_list{t} , in_statement_param_list{s} @@ -2542,7 +2542,7 @@ struct function_type_node assert (parameters); auto ret = parameters->to_string(); - + if (throws) { ret += " throws"; } @@ -2999,6 +2999,7 @@ struct declaration_node std::vector> metafunctions; std::unique_ptr template_parameters; + std::unique_ptr specialization_template_arguments; source_position requires_pos = {}; std::unique_ptr requires_clause_expression; @@ -3347,6 +3348,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 @@ -5607,6 +5610,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) + ")"; @@ -5695,6 +5703,7 @@ auto pretty_print_visualize(declaration_node const& n, int indent, bool include_ assert(type_id); ret += metafunctions + template_params + + specialization_args + " " + pretty_print_visualize(*type_id, indent) + requires_clause + initializer; @@ -5704,6 +5713,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; } @@ -5722,7 +5732,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(a->initializer); ret += " type" @@ -5907,7 +5918,7 @@ class parser // // errors error list // - parser( + parser( std::vector& errors_, std::set& includes_ ) @@ -6370,9 +6381,9 @@ class parser // Next should be an expression-list followed by a ')' // If not, then this wasn't a call expression so backtrack to // the '(' which will be part of the next grammar production - is_inside_call_expr = true; + is_inside_call_expr = true; term.expr_list = expression_list(term.op, lexeme::RightParen); - is_inside_call_expr = false; + is_inside_call_expr = false; if ( term.expr_list @@ -6401,7 +6412,7 @@ class parser } } else if ( - ( + ( term.op->type() == lexeme::EllipsisLess || term.op->type() == lexeme::EllipsisEqual ) @@ -7160,6 +7171,7 @@ class parser //G //G template-arguments: //G template-arguments ',' template-argument + //G template-argument //G //G template-argument: //G # note: < > << >> are not allowed in expressions until new ( is opened @@ -7321,7 +7333,7 @@ class parser n->ids.push_back( std::move(term) ); - for ( + for ( auto first_time_through_loop = true; curr().type() == lexeme::Scope; first_time_through_loop = false @@ -7340,7 +7352,7 @@ class parser && first_uid_was_std && term.scope_op->type() == lexeme::Scope && *term.id->identifier == "forward" - ) + ) { error("std::forward is not needed in Cpp2 - use 'forward' parameters/arguments instead", false); return {}; @@ -7993,7 +8005,7 @@ class parser } if ( - peek(1) + peek(1) && *peek(1) == "namespace" ) { @@ -8826,9 +8838,9 @@ class parser //G unnamed-declaration: //G ':' meta-functions? template-parameters? function-type requires-clause? '=' statement //G ':' meta-functions? template-parameters? function-type statement - //G ':' meta-functions? template-parameters? type-id? requires-clause? '=' statement + //G ':' meta-functions? template-parameters? specialization-arguments? type-id? requires-clause? '=' statement //G ':' meta-functions? template-parameters? type-id - //G ':' meta-functions? template-parameters? 'final'? 'type' requires-clause? '=' statement + //G ':' meta-functions? template-parameters? specialization-arguments? 'final'? 'type' requires-clause? '=' statement //G ':' 'namespace' '=' statement //G //G meta-functions: @@ -8842,6 +8854,9 @@ class parser //G template-parameters: //G '<' parameter-declaration-seq '>' //G + //G specialization-arguments: + //G 'specialize' '<' template-arguments '>' + //G auto unnamed_declaration( source_position start, bool semicolon_required = true, @@ -8995,6 +9010,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); + } + // Next is an an optional type auto deduced_type = false; @@ -9408,7 +9438,7 @@ class parser //G alias: //G ':' template-parameters? 'type' requires-clause? '==' type-id ';' //G ':' 'namespace' '==' id-expression ';' - //G ':' template-parameters? type-id? requires-clause? '==' expression ';' + //G ':' template-parameters? specialization-arguments? type-id? requires-clause? '==' expression ';' //G //GT ':' function-type '==' expression ';' //GT # See commit 63efa6ed21c4d4f4f136a7a73e9f6b2c110c81d7 comment @@ -9437,6 +9467,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) { + pos = start_pos; // backtrack + return {}; + } + n->specialization_template_arguments = std::move(specialization_template_arguments); + } + auto a = std::make_unique( &curr() ); // Next must be 'type', 'namespace', a type-id, or we're at the 'requires' or '==' @@ -9468,7 +9513,7 @@ class parser if (curr() == "requires") { if ( - n->is_type_alias() + *a->type == "type" && !n->template_parameters ) { @@ -9476,7 +9521,7 @@ class parser return {}; } - if (n->is_namespace_alias()) + if (*a->type == "namespace") { error("'requires' is not allowed on a namespace alias"); return {}; @@ -9496,6 +9541,22 @@ class parser if (curr().type() == lexeme::EqualComparison) { next(); + if (n->is_specialization()) { + if (*a->type == "type") { + errors.emplace_back( + a->type->position(), + "a type alias cannot be specialized" + ); + return {}; + } + if (*a->type == "namespace") { + errors.emplace_back( + a->type->position(), + "a namespace alias cannot be specialized" + ); + return {}; + } + } } else { if (a->type->type() != lexeme::EqualComparison) { @@ -9593,6 +9654,11 @@ class parser n->type = std::move(a); + assert( + n->is_type_alias() + || n->is_object_alias() + || n->is_namespace_alias() + ); return n; } diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 4a891ad1d..9743be011 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -1255,7 +1255,7 @@ class cppfront // Now we'll open the Cpp1 file auto cpp1_filename = sourcefile.substr(0, std::ssize(sourcefile) - 1); - + // Use explicit filename override if present, // otherwise strip leading path if (!flag_cpp1_filename.empty()) { @@ -1721,7 +1721,8 @@ class cppfront int synthesized_multi_return_size = 0, bool is_local_name = true, bool is_qualified = false, - bool is_class_member_access = false + bool is_class_member_access = false, + bool emit_identifier = true ) -> void { STACKINSTR @@ -1813,7 +1814,9 @@ class cppfront } assert(n.identifier); - emit(*n.identifier, is_qualified); // inform the identifier if we know this is qualified + if (emit_identifier) { + emit(*n.identifier, is_qualified); // inform the identifier if we know this is qualified + } if (n.open_angle != source_position{}) { printer.print_cpp2("<", n.open_angle); @@ -3458,12 +3461,12 @@ class cppfront last_was_prefixed = true; } - // Handle the other Cpp2 postfix operators that stay postfix in Cpp1 + // Handle the other Cpp2 postfix operators that stay postfix in Cpp1 // (currently '...' for expansion, not when used as a range operator) else if ( is_postfix_operator(i->op->type()) && !i->last_expr // not being used as a range operator - ) + ) { flush_args(); suffix.emplace_back( i->op->to_string(), i->op->position()); @@ -3504,7 +3507,7 @@ class cppfront } auto print = print_to_string( - *i->id_expr, + *i->id_expr, false, // not a local name i->op->type() == lexeme::Dot || i->op->type() == lexeme::DotDot // member access ); @@ -4453,8 +4456,8 @@ class cppfront { assert(n.declaration); auto is_param_to_namespace_scope_type = - n.declaration->parent_is_type() - && n.declaration->parent_declaration->parent_is_namespace() + n.declaration->parent_is_type() + && n.declaration->parent_declaration->parent_is_namespace() ; auto emit_in_phase_0 = @@ -5091,7 +5094,7 @@ class cppfront || n.is_swap() || n.is_destructor() || ( - n.my_decl + n.my_decl && generating_move_from == n.my_decl ) ) @@ -5105,7 +5108,7 @@ class cppfront if ( n.is_assignment() || ( - n.my_decl + n.my_decl && generating_assignment_from == n.my_decl ) ) @@ -5274,7 +5277,10 @@ class cppfront ) { auto list = std::string{""}; - if (parent->template_parameters) { + if (parent->specialization_template_arguments) { + list = print_to_string(*parent->specialization_template_arguments, 0, false, false, false, false); + } + else if (parent->template_parameters) { auto separator = std::string{"<"}; for (auto& tparam : parent->template_parameters->parameters) { assert (tparam->has_name()); @@ -5754,6 +5760,18 @@ class cppfront return; } + // Do not forward declare specializations. + if ( + n.is_specialization() + && ( + (n.is_type() && printer.get_phase() == printer.phase0_type_decls) + || (n.is_object() && printer.get_phase() == printer.phase1_type_defs_func_decls) + ) + ) + { + return; + } + // If this is a generated declaration (negative source line number), // add a line break before if ( @@ -5831,6 +5849,8 @@ class cppfront printer.print_cpp2("template", n.position()); emit(*n.template_parameters, false, true); printer.print_cpp2(" ", n.position()); + } else if (n.is_specialization()) { + printer.print_cpp2("template<> ", n.position()); } // Emit requires clause if any @@ -5893,6 +5913,13 @@ class cppfront if (n.parent_is_type()) { assert (n.parent_declaration->name()); + if (n.specialization_template_arguments) { + errors.emplace_back( + n.position(), + "(temporary alpha limitation) an object alias in type scope cannot be specialized" + ); + return; + } if (printer.get_phase() == printer.phase1_type_defs_func_decls) { printer.print_cpp2( @@ -5936,10 +5963,16 @@ class cppfront intro = "inline constexpr"; } + auto specialization_template_arguments = std::string{}; + if (n.specialization_template_arguments) { + specialization_template_arguments = print_to_string(*n.specialization_template_arguments, 0, false, false, false, false); + } + printer.print_cpp2( type + " " + intro + " " + print_to_string(*n.identifier) + + specialization_template_arguments + print_initializer_to_string( *std::get(a->initializer) ) + ";\n", n.position() @@ -6114,7 +6147,10 @@ class cppfront // Now, emit our own template parameters if ( - n.template_parameters + ( + n.template_parameters + || n.is_specialization() + ) && ( printer.get_phase() < printer.phase2_func_defs || n.is_object() @@ -6132,7 +6168,12 @@ class cppfront ) { printer.print_cpp2("template", n.position()); - emit(*n.template_parameters, false, true); + if (n.template_parameters) { + emit(*n.template_parameters, false, true); + } else { + assert(n.is_specialization()); + printer.print_cpp2("<>", n.position()); + } printer.print_cpp2(" ", n.position()); } @@ -6155,6 +6196,9 @@ class cppfront printer.print_cpp2("class ", n.position()); emit(*n.identifier); + if (n.specialization_template_arguments) { + emit(*n.specialization_template_arguments, 0, false, false, false, false); + } // Type declaration if (printer.get_phase() == printer.phase0_type_decls) { @@ -6995,8 +7039,8 @@ class cppfront return; } } - printer.preempt_position_push(n.position()); - emit( *type, {}, print_to_string(*n.identifier) ); + printer.preempt_position_push(n.position()); + emit( *type, {}, print_to_string(*n.identifier) ); printer.preempt_position_pop(); if ( @@ -7045,6 +7089,9 @@ class cppfront } else if (!n.is_object_with_function_typeid()) { emit(*n.identifier); + if (n.specialization_template_arguments) { + emit(*n.specialization_template_arguments, 0, false, false, false, false); + } } if (