From 780b99ae6b5a9ace9ff231a9d8d07410153d3f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Sat, 1 Jul 2023 10:07:41 -0400 Subject: [PATCH] fix(to_cpp1): improve recognition of dependent types and deducible parameters --- ...pure2-bugfix-for-deducible-parameters.cpp2 | 33 ++ ...-bugfix-for-dependent-types-recursion.cpp2 | 6 + .../pure2-bugfix-for-dependent-types.cpp2 | 91 ++++ ...fix-for-deducible-parameters.cpp.execution | 0 ...bugfix-for-deducible-parameters.cpp.output | 0 ...x-for-dependent-types-recursion.cpp.output | 13 + ...2-bugfix-for-dependent-types.cpp.execution | 0 ...ure2-bugfix-for-dependent-types.cpp.output | 0 .../pure2-bugfix-for-deducible-parameters.cpp | 87 ++++ ...ugfix-for-deducible-parameters.cpp2.output | 2 + ...2-bugfix-for-dependent-types-recursion.cpp | 24 + ...-for-dependent-types-recursion.cpp2.output | 2 + .../pure2-bugfix-for-dependent-types.cpp | 122 +++++ ...re2-bugfix-for-dependent-types.cpp2.output | 2 + source/parse.h | 7 +- source/to_cpp1.h | 435 +++++++++++++++--- 16 files changed, 763 insertions(+), 61 deletions(-) create mode 100644 regression-tests/pure2-bugfix-for-deducible-parameters.cpp2 create mode 100644 regression-tests/pure2-bugfix-for-dependent-types-recursion.cpp2 create mode 100644 regression-tests/pure2-bugfix-for-dependent-types.cpp2 create mode 100644 regression-tests/test-results/gcc-13/pure2-bugfix-for-deducible-parameters.cpp.execution create mode 100644 regression-tests/test-results/gcc-13/pure2-bugfix-for-deducible-parameters.cpp.output create mode 100644 regression-tests/test-results/gcc-13/pure2-bugfix-for-dependent-types-recursion.cpp.output create mode 100644 regression-tests/test-results/gcc-13/pure2-bugfix-for-dependent-types.cpp.execution create mode 100644 regression-tests/test-results/gcc-13/pure2-bugfix-for-dependent-types.cpp.output create mode 100644 regression-tests/test-results/pure2-bugfix-for-deducible-parameters.cpp create mode 100644 regression-tests/test-results/pure2-bugfix-for-deducible-parameters.cpp2.output create mode 100644 regression-tests/test-results/pure2-bugfix-for-dependent-types-recursion.cpp create mode 100644 regression-tests/test-results/pure2-bugfix-for-dependent-types-recursion.cpp2.output create mode 100644 regression-tests/test-results/pure2-bugfix-for-dependent-types.cpp create mode 100644 regression-tests/test-results/pure2-bugfix-for-dependent-types.cpp2.output diff --git a/regression-tests/pure2-bugfix-for-deducible-parameters.cpp2 b/regression-tests/pure2-bugfix-for-deducible-parameters.cpp2 new file mode 100644 index 0000000000..dbe01dc214 --- /dev/null +++ b/regression-tests/pure2-bugfix-for-deducible-parameters.cpp2 @@ -0,0 +1,33 @@ +// Dependent, non-deducible parameters +// are wrapped like non-dependent parameters. +init: (out x: std::integral_constant) = { x = (); } +init: (out x: std::integral_constant, _: T) = { x = (); } +id: (x: std::integral_constant) -> forward _ = x; +id: (x: std::integral_constant, y: T) = { assert(x& == y&); } + +main: () = { + zero: type == std::integral_constant; + + z: zero; + init(out z); + assert(id(z)& == z&); + + // Deducible parameters. + _ = :v = 0; + _ = : (x: std::vector) = {}(:std::vector = ()); + _ = : (x: std::vector>) = {}(:std::vector> = ()); + // _ = : (x: std::pair, y: U) = {}(:std::pair = (0, 0), z); // Blocked on #727. + _ = : (x: std::array, y: U) = {}(:std::array = (), z); + init(out z, z); + id(z, z); + + // Test that these are emitted unwrapped in case they are deducible. + (copy f := : (x: std::vector>) = {}) + static_assert(!std::is_invocable_v>, "`T` is non-deducible."); + (copy f := : (x: std::vector>) = {}) + static_assert(std::is_invocable_v>>, "`T` is deducible."); +} + +v: type = { + operator=: (out this, x: T) = { } +} diff --git a/regression-tests/pure2-bugfix-for-dependent-types-recursion.cpp2 b/regression-tests/pure2-bugfix-for-dependent-types-recursion.cpp2 new file mode 100644 index 0000000000..d449b8d551 --- /dev/null +++ b/regression-tests/pure2-bugfix-for-dependent-types-recursion.cpp2 @@ -0,0 +1,6 @@ +main: () = { + a: type == b; + b: type == a; + _ = a::t; + _ = b::t; +} diff --git a/regression-tests/pure2-bugfix-for-dependent-types.cpp2 b/regression-tests/pure2-bugfix-for-dependent-types.cpp2 new file mode 100644 index 0000000000..e4347f5a24 --- /dev/null +++ b/regression-tests/pure2-bugfix-for-dependent-types.cpp2 @@ -0,0 +1,91 @@ +identity: type == T; + +f: (x: T::value_type) -> T::value_type = { + assert(x is T::value_type); + y: T::value_type; + y = x; + z: type == T::value_type; + return (:T::value_type = x); + + // Dependent *template-id*s. + _ = :identity::value_type = (); // First identifier. + _ = :std::optional::value_type = (); // Non-first identifier. + _ = :std::array::value_type = (); + _ = :std::array::value_type = (); + + // Emitted `template`. + ptr: type == * T; // Also test lookup through type aliases. + nptr: type == * i32; + _ = :std::pointer_traits::rebind = (); // Type-only context. + _ = :std::pointer_traits::rebind = (); // Non-dependent. + _ = :std::pointer_traits::rebind = (); // Dependent on the nested template. + _ = :std::pointer_traits::rebind = (); // Dependent on the outer template. + // _ = :identity::rebind> = (); // Non type-only context. Blocked on #727. + + // Aliases. + w: type == T; + _ = :w::value_type = x; + v: type == w; + _ = :v::value_type = x; + a: type == T::type; + _ = :a::value_type = x; + + { + // Test that there's no prefixed `typename` to.... + _ = std::integral_constant(); // `T::value`. + _ = :std::type_identity_t = (); // `std::type_identity_t`. + + // Test that non-dependent names aren't emitted with `typename`. + a: type == std::integral_constant; + b: type == a; + c: type == b; + _ = :b::value_type = x; + _ = :c::value_type = x; + } +} + +t: @struct type = { + u: @struct type = { + x: T::value_type = (); + this: T::type = (); + } + x: T::value_type = 0; +} + +main: () = { + zero: type == std::integral_constant; + _ = f(0); + + // clang-format off + _ = : ::t = (); // clang-format on + + // Emitted `template` (noop, taken care of by the UFCS macro). + _ = :(f) = { _ = f.operator()(); }(: () = {}); + + // Nesting is not relevant to lookup. + _ = : () = { _ = :T::value_type = (); }; + _ = :() = { _ = : () = { _ = :T::value_type = (); }; }; + _ = :() = { _ = :() = { _ = : () = { _ = :T::value_type = (); }; }; }; + _ = :() = { _ = :() = { _ = :() = { _ = : () = { _ = :T::value_type = (); }; }; }; }; + _ = :() = { _ = :() = { _ = : () = { _ = :() = { _ = :T::value_type = (); }; }; }; }; + _ = :() = { _ = : () = { _ = :() = { _ = :() = { _ = :T::value_type = (); }; }; }; }; + _ = : () = { _ = :() = { _ = :() = { _ = :() = { _ = :T::value_type = (); }; }; }; }; + _ = : () = { _ = :() = { _ = :() = { _ = :(x: T::value_type) = {}; }; }; }; + _ = : () = { _ = :() = { _ = :(x: T::value_type) = { _ = :() = {}; }; }; }; + _ = : () = { _ = :(x: T::value_type) = { _ = :() = { _ = :() = {}; }; }; }; + _ = : (x: T::value_type) = { _ = :() = { _ = :() = { _ = :() = {}; }; }; }; + + // Lookup. + { + alias: type == std::integral_constant; + _ = :alias::value_type = 0; // Non-dependent. + } + _ = : (_: T) = { + alias: type == std::integral_constant; + _ = :alias::value_type = 0; // Dependent. + { + alias: type == std::integral_constant; + _ = :alias::value_type = 0; // Non-dependent. + } + }(0); +} diff --git a/regression-tests/test-results/gcc-13/pure2-bugfix-for-deducible-parameters.cpp.execution b/regression-tests/test-results/gcc-13/pure2-bugfix-for-deducible-parameters.cpp.execution new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/gcc-13/pure2-bugfix-for-deducible-parameters.cpp.output b/regression-tests/test-results/gcc-13/pure2-bugfix-for-deducible-parameters.cpp.output new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/gcc-13/pure2-bugfix-for-dependent-types-recursion.cpp.output b/regression-tests/test-results/gcc-13/pure2-bugfix-for-dependent-types-recursion.cpp.output new file mode 100644 index 0000000000..ef0687fdb7 --- /dev/null +++ b/regression-tests/test-results/gcc-13/pure2-bugfix-for-dependent-types-recursion.cpp.output @@ -0,0 +1,13 @@ +pure2-bugfix-for-dependent-types-recursion.cpp: In function ‘int main()’: +pure2-bugfix-for-dependent-types-recursion.cpp:19:13: error: ‘b’ does not name a type + 19 | using a = b; + | ^ +pure2-bugfix-for-dependent-types-recursion.cpp:20:13: error: ‘a’ does not name a type + 20 | using b = a; + | ^ +pure2-bugfix-for-dependent-types-recursion.cpp:21:21: error: ‘a’ has not been declared + 21 | static_cast(a::t); + | ^ +pure2-bugfix-for-dependent-types-recursion.cpp:22:21: error: ‘b’ has not been declared + 22 | static_cast(b::t); + | ^ diff --git a/regression-tests/test-results/gcc-13/pure2-bugfix-for-dependent-types.cpp.execution b/regression-tests/test-results/gcc-13/pure2-bugfix-for-dependent-types.cpp.execution new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/gcc-13/pure2-bugfix-for-dependent-types.cpp.output b/regression-tests/test-results/gcc-13/pure2-bugfix-for-dependent-types.cpp.output new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/pure2-bugfix-for-deducible-parameters.cpp b/regression-tests/test-results/pure2-bugfix-for-deducible-parameters.cpp new file mode 100644 index 0000000000..21018a8cf3 --- /dev/null +++ b/regression-tests/test-results/pure2-bugfix-for-deducible-parameters.cpp @@ -0,0 +1,87 @@ + +#define CPP2_IMPORT_STD Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + + +#line 31 "pure2-bugfix-for-deducible-parameters.cpp2" +template class v; + + +//=== Cpp2 type definitions and function declarations =========================== + +// Dependent, non-deducible parameters +// are wrapped like non-dependent parameters. +#line 3 "pure2-bugfix-for-deducible-parameters.cpp2" +template auto init(cpp2::out> x) -> void; +template auto init(cpp2::out> x, [[maybe_unused]] T const& param2) -> void; +template [[nodiscard]] auto id(cpp2::in> x) -> auto&&; +template auto id(cpp2::in> x, T const& y) -> void; + +auto main() -> int; + + +#line 31 "pure2-bugfix-for-deducible-parameters.cpp2" +template class v { + public: explicit v(T const& x); +#line 32 "pure2-bugfix-for-deducible-parameters.cpp2" + public: auto operator=(T const& x) -> v& ; + + public: v(v const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(v const&) -> void = delete; +#line 33 "pure2-bugfix-for-deducible-parameters.cpp2" +}; + + +//=== Cpp2 function definitions ================================================= + + +#line 3 "pure2-bugfix-for-deducible-parameters.cpp2" +template auto init(cpp2::out> x) -> void{x.construct(); } +template auto init(cpp2::out> x, [[maybe_unused]] T const& param2) -> void{x.construct(); } +template [[nodiscard]] auto id(cpp2::in> x) -> auto&& { return x; } +template auto id(cpp2::in> x, T const& y) -> void{cpp2::Default.expects(&x == &y, ""); } + +auto main() -> int{ + using zero = std::integral_constant; + + cpp2::deferred_init z; + init(cpp2::out(&z)); + cpp2::Default.expects(&id(z.value()) == &z.value(), ""); + + // Deducible parameters. + static_cast(v{0}); + static_cast([](std::vector const& x) -> void{}(std::vector{})); + static_cast([](std::vector> const& x) -> void{}(std::vector>{})); + // _ = : (x: std::pair, y: U) = {}(:std::pair = (0, 0), z); // Blocked on #727. + static_cast([](std::array const& x, U const& y) -> void{}(std::array{}, z.value())); + init(cpp2::out(&z.value()), z.value()); + id(z.value(), std::move(z.value())); +{ +auto f = [](std::vector> const& x) -> void{}; + + // Test that these are emitted unwrapped in case they are deducible. + +#line 26 "pure2-bugfix-for-deducible-parameters.cpp2" + static_assert(!(std::is_invocable_v>), "`T` is non-deducible."); +} +{ +auto f = [](std::vector> const& x) -> void{}; + +#line 28 "pure2-bugfix-for-deducible-parameters.cpp2" + static_assert(std::is_invocable_v>>, "`T` is deducible."); +} +#line 29 "pure2-bugfix-for-deducible-parameters.cpp2" +} + +#line 32 "pure2-bugfix-for-deducible-parameters.cpp2" + template v::v(T const& x){} +#line 32 "pure2-bugfix-for-deducible-parameters.cpp2" + template auto v::operator=(T const& x) -> v& { + return *this; +#line 32 "pure2-bugfix-for-deducible-parameters.cpp2" + } + diff --git a/regression-tests/test-results/pure2-bugfix-for-deducible-parameters.cpp2.output b/regression-tests/test-results/pure2-bugfix-for-deducible-parameters.cpp2.output new file mode 100644 index 0000000000..7e84d47be1 --- /dev/null +++ b/regression-tests/test-results/pure2-bugfix-for-deducible-parameters.cpp2.output @@ -0,0 +1,2 @@ +pure2-bugfix-for-deducible-parameters.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/regression-tests/test-results/pure2-bugfix-for-dependent-types-recursion.cpp b/regression-tests/test-results/pure2-bugfix-for-dependent-types-recursion.cpp new file mode 100644 index 0000000000..9ce611324d --- /dev/null +++ b/regression-tests/test-results/pure2-bugfix-for-dependent-types-recursion.cpp @@ -0,0 +1,24 @@ + +#define CPP2_IMPORT_STD Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + + + +//=== Cpp2 type definitions and function declarations =========================== + +auto main() -> int; + + +//=== Cpp2 function definitions ================================================= + +auto main() -> int{ + using a = b; + using b = a; + static_cast(a::t); + static_cast(b::t); +} + diff --git a/regression-tests/test-results/pure2-bugfix-for-dependent-types-recursion.cpp2.output b/regression-tests/test-results/pure2-bugfix-for-dependent-types-recursion.cpp2.output new file mode 100644 index 0000000000..759bf8d539 --- /dev/null +++ b/regression-tests/test-results/pure2-bugfix-for-dependent-types-recursion.cpp2.output @@ -0,0 +1,2 @@ +pure2-bugfix-for-dependent-types-recursion.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/regression-tests/test-results/pure2-bugfix-for-dependent-types.cpp b/regression-tests/test-results/pure2-bugfix-for-dependent-types.cpp new file mode 100644 index 0000000000..ce073cb116 --- /dev/null +++ b/regression-tests/test-results/pure2-bugfix-for-dependent-types.cpp @@ -0,0 +1,122 @@ + +#define CPP2_IMPORT_STD Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + + +#line 47 "pure2-bugfix-for-dependent-types.cpp2" +template class t; + + +//=== Cpp2 type definitions and function declarations =========================== + +template using identity = T; + +template [[nodiscard]] auto f(cpp2::in x) -> T::value_type; + + +#line 47 "pure2-bugfix-for-dependent-types.cpp2" +template class t { +struct u_x_as_base { T::value_type x; }; + +#line 48 "pure2-bugfix-for-dependent-types.cpp2" + public: class u: public u_x_as_base, public T::type { + +#line 51 "pure2-bugfix-for-dependent-types.cpp2" + }; + public: T::value_type x {0}; +}; + +auto main() -> int; + + +//=== Cpp2 function definitions ================================================= + + +#line 3 "pure2-bugfix-for-dependent-types.cpp2" +template [[nodiscard]] auto f(cpp2::in x) -> T::value_type{ + cpp2::Default.expects(cpp2::is(x), ""); + cpp2::deferred_init y; + y.construct(x); + using z = T::value_type; + return { typename T::value_type{x} }; + + // Dependent *template-id*s. + static_cast(typename identity::value_type{});// First identifier. + static_cast(typename std::optional::value_type{});// Non-first identifier. + static_cast(typename std::array::value_type{}); + static_cast(typename std::array::value_type{}); + + // Emitted `template`. + using ptr = T*; // Also test lookup through type aliases. + using nptr = cpp2::i32*; + static_cast(typename std::pointer_traits::template rebind{});// Type-only context. + static_cast(std::pointer_traits::rebind{});// Non-dependent. + static_cast(std::pointer_traits::rebind{});// Dependent on the nested template. + static_cast(typename std::pointer_traits::template rebind{});// Dependent on the outer template. + // _ = :identity::rebind> = (); // Non type-only context. Blocked on #727. + + // Aliases. + using w = T; + static_cast(typename w::value_type{x}); + using v = w; + static_cast(typename v::value_type{x}); + using a = T::type; + static_cast(typename a::value_type{x}); + + { + // Test that there's no prefixed `typename` to.... + static_cast(std::integral_constant());// `T::value`. + static_cast(std::type_identity_t{});// `std::type_identity_t`. + + // Test that non-dependent names aren't emitted with `typename`. + using a = std::integral_constant; + using b = a; + using c = b; + static_cast(b::value_type{x}); + static_cast(c::value_type{x}); + } +} + +#line 55 "pure2-bugfix-for-dependent-types.cpp2" +auto main() -> int{ + using zero = std::integral_constant; + static_cast(f(0)); + + // clang-format off + static_cast(::t{});// clang-format on + + // Emitted `template` (noop, taken care of by the UFCS macro). + static_cast([](auto const& f) -> void{static_cast(CPP2_UFCS_TEMPLATE_0(operator(), (), f)); }([]() -> void{})); + + // Nesting is not relevant to lookup. + static_cast([]() -> void{static_cast(typename T::value_type{}); }); + static_cast([]() -> void{static_cast([]() -> void{static_cast(typename T::value_type{}); }); }); + static_cast([]() -> void{static_cast([]() -> void{static_cast([]() -> void{static_cast(typename T::value_type{}); }); }); }); + static_cast([]() -> void{static_cast([]() -> void{static_cast([]() -> void{static_cast([]() -> void{static_cast(typename T::value_type{}); }); }); }); }); + static_cast([]() -> void{static_cast([]() -> void{static_cast([]() -> void{static_cast([]() -> void{static_cast(typename T::value_type{}); }); }); }); }); + static_cast([]() -> void{static_cast([]() -> void{static_cast([]() -> void{static_cast([]() -> void{static_cast(typename T::value_type{}); }); }); }); }); + static_cast([]() -> void{static_cast([]() -> void{static_cast([]() -> void{static_cast([]() -> void{static_cast(typename T::value_type{}); }); }); }); }); + static_cast([]() -> void{static_cast([]() -> void{static_cast([]() -> void{static_cast([](cpp2::in x) -> void{}); }); }); }); + static_cast([]() -> void{static_cast([]() -> void{static_cast([](cpp2::in x) -> void{static_cast([]() -> void{}); }); }); }); + static_cast([]() -> void{static_cast([](cpp2::in x) -> void{static_cast([]() -> void{static_cast([]() -> void{}); }); }); }); + static_cast([](cpp2::in x) -> void{static_cast([]() -> void{static_cast([]() -> void{static_cast([]() -> void{}); }); }); }); + + // Lookup. + { + using alias = std::integral_constant; + static_cast(alias::value_type{0});// Non-dependent. + } + static_cast([]([[maybe_unused]] T const& param1) -> void{ + using alias = std::integral_constant; + static_cast(typename alias::value_type{0});// Dependent. + { + using alias = std::integral_constant; + static_cast(typename alias::value_type{0});// Non-dependent. + } + }(0)); +} + diff --git a/regression-tests/test-results/pure2-bugfix-for-dependent-types.cpp2.output b/regression-tests/test-results/pure2-bugfix-for-dependent-types.cpp2.output new file mode 100644 index 0000000000..6896db60e6 --- /dev/null +++ b/regression-tests/test-results/pure2-bugfix-for-dependent-types.cpp2.output @@ -0,0 +1,2 @@ +pure2-bugfix-for-dependent-types.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/source/parse.h b/source/parse.h index 1215f15593..335330c0c2 100644 --- a/source/parse.h +++ b/source/parse.h @@ -1194,6 +1194,8 @@ struct type_id_node token const* > id; + bool type_only_context = false; + auto is_wildcard() const -> bool { @@ -6011,10 +6013,11 @@ class parser //G 'const' //G '*' //G - auto type_id() + auto type_id(bool type_only_context = true) -> std::unique_ptr { auto n = std::make_unique(); + n->type_only_context = type_only_context; while ( (curr().type() == lexeme::Keyword && curr() == "const") @@ -6221,7 +6224,7 @@ class parser } // Else try parsing it as a type id - else if (auto i = type_id()) { + else if (auto i = type_id(false)) { term.arg = std::move(i); } diff --git a/source/to_cpp1.h b/source/to_cpp1.h index aa8cfcfc01..ad5c816a37 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -1651,7 +1651,8 @@ class cppfront unqualified_id_node const& n, bool in_synthesized_multi_return = false, bool is_local_name = true, - bool is_qualified = false + bool is_qualified = false, + bool is_dependent = false ) -> void { @@ -1700,6 +1701,10 @@ class cppfront printer.print_cpp2("CPP2_FORWARD(", {n.position().lineno, n.position().colno - 8}); } + if (is_dependent && n.open_angle != source_position{}) { + printer.print_cpp2("template ", n.position()); + } + assert(n.identifier); emit(*n.identifier, is_qualified); // inform the identifier if we know this is qualified @@ -1762,9 +1767,277 @@ class cppfront } + auto has_template_parameter_named(declaration_node const& decl, token const& id) + -> bool + { + if (auto& params = decl.template_parameters) { + return std::any_of(params->parameters.begin(), params->parameters.end(), [&](auto& param) { + assert(param->has_name()); + return *param->name() == id; + }); + } + return false; + } + + auto is_template_parameter(token const& lookup_id) + -> bool + { + // If any parent declaration + return std::any_of(current_declarations.begin() + 1, current_declarations.end(), [&](auto& decl) { + return has_template_parameter_named(*decl, lookup_id); + }); + } + + std::vector looking_up = {}; + + auto is_dependent_alias(token const& lookup_id) + -> bool + { + // Prevent recursion. + if (contains(looking_up, &lookup_id)) { + return false; + } + looking_up.push_back(&lookup_id); + auto guard = finally([&]{ looking_up.pop_back(); }); + + bool res = false; + // If the first parent declaration + (void)std::find_if( + current_declarations.rbegin(), + current_declarations.rend() - 1, + [&](declaration_node const* decl) + { + // that can have aliases + if ((decl->is_function() + || decl->is_type() + || decl->is_namespace()) + && decl->initializer + && decl->initializer->is_compound()) + { + auto& stmts = decl->initializer->get_if()->statements; + // among its statements + return std::any_of(stmts.rbegin(), stmts.rend(), [&](decltype(stmts.front())& stmt) { + if (auto decl = stmt->get_if(); + decl + && decl->is_alias()) { + auto& alias = get(decl->type); + // has a type alias declaration of equal name + if (alias->is_type_alias() + && decl->identifier + && *decl->identifier->identifier == lookup_id) { + auto& type_id = get(alias->initializer); + // and its value is a dependent _type-id_. + res = is_dependent(*type_id); + return true; + } + } + return false; + }); + } + return false; + } + ); + + return res; + } + + auto is_dependent(const type_id_node& n) + -> bool + { + if (auto qual = get_if(&n.id)) { + return is_dependent(**qual); + } + else if (auto unqual = get_if(&n.id)) { + return is_dependent(**unqual); + } + return false; + } + + auto is_dependent(const id_expression_node& n) + -> bool + { + if (auto qual = get_if(&n.id)) { + return is_dependent(**qual); + } + else if (auto unqual = get_if(&n.id)) { + return is_dependent(**unqual); + } + return false; + } + + struct is_dependent_expression_visitor { + cppfront* self; + + auto operator()(expression_node const& expr) const + -> bool + { + return (*this)(*expr.expr); + } + + template + auto operator()(binary_expression_node const& expr) const + -> bool + { + return (*this)(*expr.expr) || std::any_of(expr.terms.begin(), expr.terms.end(), *this); + } + + template + auto operator()(BinaryExpressionTerm const& term) const + -> bool + requires requires { term.op; term.expr; } + { + return (*this)(*term.expr); + } + + auto operator()(is_as_expression_node const& expr) const + -> bool + { + return (*this)(*expr.expr) || std::any_of(expr.ops.begin(), expr.ops.end(), *this); + } + + auto operator()(is_as_expression_node::term const& expr) const + -> bool + { + if (expr.expr) { + return (*this)(*expr.expr); + } + return self->is_dependent(*expr.type); + } + + auto operator()(prefix_expression_node const& expr) const + -> bool + { + return (*this)(*expr.expr); + } + + auto operator()(postfix_expression_node const& expr) const + -> bool + { + return (*this)(*expr.expr) || std::any_of(expr.ops.begin(), expr.ops.end(), *this); + } + + auto operator()(postfix_expression_node::term const& expr) const + -> bool + { + if (expr.id_expr) { + return (*this)(*expr.id_expr); + } + return (*this)(*expr.expr_list); + } + + auto operator()(primary_expression_node const& expr) const + -> bool + { + return std::visit([&](T const& expr) { + if constexpr (std::is_same_v + || std::is_same_v + || std::is_same_v>) { + return false; + } else { + return (*this)(*expr); + } + }, expr.expr); + } + + auto operator()(expression_list_node const& expr) const + -> bool + { + return std::any_of(expr.expressions.begin(), expr.expressions.end(), *this); + } + + auto operator()(expression_list_node::term const& term) const + -> bool + { + return (*this)(*term.expr); + } + + auto operator()(id_expression_node const& expr) const + -> bool + { + return std::visit([&](T const& expr) { + if constexpr (std::is_same_v) { + return false; + } else { + return self->is_dependent(*expr); + } + }, expr.id); + } + + auto operator()(declaration_node const& n) const + -> bool + { + auto type_id = std::get_if(&n.type); + if ( + !type_id + || (*type_id)->is_wildcard() + ) + { + return false; // (*this)(*term.initializer); + } + return self->is_dependent(**type_id); + } + + auto operator()(inspect_expression_node const& expr) const + -> bool + { + return self->is_dependent(*expr.result_type); + } + }; + + auto is_dependent( + const unqualified_id_node& n, + bool /*is_qualified*/ = false, + bool is_first = true) + -> bool + { + if (is_first && n.open_angle == source_position{}) { + assert(n.identifier); + return is_template_parameter(*n.identifier) + || is_dependent_alias(*n.identifier); + } + // If it's a _template-id_ + if (n.open_angle != source_position{}) { + assert(n.identifier); + // and any of its template arguments is either + return std::any_of(n.template_args.begin(), n.template_args.end(), [&](template_argument const& arg) { + if (auto expr = get_if(&arg.arg)) { + // a dependent _expression_ + return is_dependent_expression_visitor{this}(**expr); + } + else if (auto type_id = get_if(&arg.arg)) { + // or a dependent _type-id_. + return is_dependent(**type_id); + } + return false; + }); + } + return false; + } + + auto is_dependent(const qualified_id_node& n, bool exclude_last = false) + -> bool + { + if (is_dependent(*n.ids[0].id, true, true)) { + return true; + } + return std::any_of( + n.ids.begin() + 1, + n.ids.end() - int{exclude_last && n.ids.size() > 1}, + [&](auto& id) { + return is_dependent(*id.id, true, false); + } + ); + } + + auto should_be_prefixed_with_typename(const qualified_id_node& n) + -> bool + { + return is_dependent(n, true); + } + //----------------------------------------------------------------------- // - auto emit(qualified_id_node const& n) + auto emit(qualified_id_node const& n, bool requires_typename_keyword = false) -> void { if (!sema.check(n)) { @@ -1788,12 +2061,20 @@ class cppfront auto ident = std::string{}; printer.emit_to_string(&ident); + if (requires_typename_keyword && should_be_prefixed_with_typename(n)) { + printer.print_cpp2("typename ", n.position()); + } + + auto is_dependent = false; for (auto const& id : n.ids) { if (id.scope_op) { emit(*id.scope_op); } - emit(*id.id, false, true, true); // inform the unqualified-id that it's qualified + emit(*id.id, false, true, true, is_dependent); // inform the unqualified-id that it's qualified + if (!is_dependent && this->is_dependent(*id.id)) { + is_dependent = true; + } } printer.emit_to_string(); @@ -1805,6 +2086,7 @@ class cppfront // auto emit( type_id_node const& n, + bool is_type_only_context = false, source_position pos = {} ) -> void @@ -1818,7 +2100,7 @@ class cppfront } else { try_emit(n.id, false, false); - try_emit(n.id); + try_emit(n.id, !is_type_only_context && n.type_only_context); try_emit(n.id); } @@ -3887,6 +4169,84 @@ class cppfront } + auto is_deducible_template_parameter(token const& lookup_id) + -> bool + { + assert( current_declarations.back() ); + return has_template_parameter_named(*current_declarations.back(), lookup_id) + || ( + current_declarations.back()->parent_is_type() + && current_declarations.back()->has_name("operator=") + && has_template_parameter_named(*current_declarations.back()->get_parent(), lookup_id) + ); + } + + auto is_deducible(const type_id_node& n) + -> bool + { + if (auto qual = get_if(&n.id)) { + return is_deducible(**qual); + } + else if (auto unqual = get_if(&n.id)) { + return is_deducible(**unqual); + } + return false; + } + + auto is_deducible(const id_expression_node& n) + -> bool + { + if (auto qual = get_if(&n.id)) { + return is_deducible(**qual); + } + else if (auto unqual = get_if(&n.id)) { + return is_deducible(**unqual); + } + return false; + } + + auto is_deducible( + const unqualified_id_node& n, + bool is_first = true) + -> bool + { + // If it's not a _template-id_ + if (is_first && n.open_angle == source_position{}) { + assert(n.identifier); + // and it names a deducible template parameter. + return is_deducible_template_parameter(*n.identifier); + } + // If it's a _template-id_ + if (n.open_angle != source_position{}) { + assert(n.identifier); + // and any of its template arguments is either + return std::any_of(n.template_args.begin(), n.template_args.end(), [&](template_argument const& arg) { + if (auto expr = get_if(&arg.arg); + expr && (*expr)->is_id_expression()) { + // a deducible _id-expression_ + auto& pid = (*expr)->expr->expr->expr->expr->expr->expr->expr->expr->expr->expr->expr->expr->expr->expr->expr->expr->expr; + if (auto id = get_if(&pid)) { + return is_deducible(**id); + } + } + else if (auto type_id = get_if(&arg.arg)) { + // or a deducible _type-id_. + return is_deducible(**type_id); + } + return false; + }); + } + return false; + } + + auto is_deducible(const qualified_id_node& n) + -> bool + { + assert(n.ids.size() > 0); + // Its non-terminal components are non-dependent and + // its terminal component is deducible. + return !is_dependent(n, true) && is_deducible(*n.ids.back().id); + } //----------------------------------------------------------------------- // auto emit( @@ -3994,7 +4354,7 @@ class cppfront auto const& type_id = *std::get(n.declaration->type); if (is_template_parameter) { - emit( type_id ); + emit( type_id, true ); printer.print_cpp2(" ", type_id.position()); assert (n.declaration->identifier); emit(*n.declaration->identifier); @@ -4006,50 +4366,7 @@ class cppfront auto param_type = print_to_string(type_id); - // If there are template parameters on this function or its enclosing - // type, see if this parameter's name is an unqualified-id with a - // template parameter name, or mentions a template parameter as a - // template argument - auto has_template_parameter_type_named = []( - declaration_node const& decl, - std::string_view name - ) - -> bool - { - if (decl.template_parameters) { - for (auto& tparam : decl.template_parameters->parameters) - { - assert( - tparam - && tparam->name() - ); - // For now just do a quick string match - auto tparam_name = tparam->name()->to_string(); - if ( - tparam->declaration->is_type() - && ( - name == tparam_name - || name.find("<"+tparam_name) != std::string_view::npos - || name.find(","+tparam_name) != std::string_view::npos - ) - ) - { - return true; - } - } - } - return false; - }; - - assert( current_declarations.back() ); - auto is_dependent_parameter_type = - has_template_parameter_type_named( *current_declarations.back(), param_type ) - || ( - current_declarations.back()->parent_is_type() - && current_declarations.back()->has_name("operator=") - && has_template_parameter_type_named( *current_declarations.back()->get_parent(), param_type) - ) - ; + auto is_deducible_parameter_type = is_deducible(type_id); assert( n.declaration->identifier ); auto identifier = print_to_string( *n.declaration->identifier ); @@ -4065,7 +4382,7 @@ class cppfront !is_returns && !n.declaration->is_variadic && !type_id.is_wildcard() - && !is_dependent_parameter_type + && !is_deducible_parameter_type && !type_id.is_pointer_qualified() ) { @@ -4087,12 +4404,12 @@ class cppfront } else if ( type_id.is_wildcard() - || is_dependent_parameter_type + || is_deducible_parameter_type || n.declaration->is_variadic ) { auto name = std::string{"auto"}; - if (is_dependent_parameter_type) { + if (is_deducible_parameter_type) { name = param_type; } else if ( @@ -4156,7 +4473,7 @@ class cppfront if ( !is_returns && !type_id.is_wildcard() - && !is_dependent_parameter_type + && !is_deducible_parameter_type && !type_id.is_pointer_qualified() && !n.declaration->is_variadic ) @@ -4425,7 +4742,7 @@ class cppfront auto& r = std::get(n.returns); assert(r.type); - auto return_type = print_to_string(*r.type); + auto return_type = print_to_string(*r.type, true); if (r.pass == passing_style::forward) { if (r.type->is_wildcard()) { @@ -5137,7 +5454,7 @@ class cppfront "using " + print_to_string(*n.identifier) + " = " - + print_to_string( *std::get(a->initializer) ) + + print_to_string( *std::get(a->initializer), true ) + ";\n", n.position() ); @@ -5323,7 +5640,7 @@ class cppfront + "_" + decl->name()->to_string() + "_as_base { " - + print_to_string( *decl->get_object_type() ) + + print_to_string( *decl->get_object_type(), true ) + " " + decl->name()->to_string() + "; };" @@ -5470,7 +5787,7 @@ class cppfront if (decl->has_name("this")) { if (printer.get_phase() == printer.phase1_type_defs_func_decls) { printer.print_cpp2( - separator + " public " + print_to_string(*decl->get_object_type()), + separator + " public " + print_to_string(*decl->get_object_type(), true), compound_stmt->position() ); separator = ","; @@ -6102,7 +6419,7 @@ class cppfront // Emit "auto" for deduced types (of course) if (type->is_wildcard()) { assert(n.initializer); - emit( *type, n.position() ); + emit( *type, false, n.position() ); } // Otherwise, emit the type else { @@ -6120,7 +6437,7 @@ class cppfront } } printer.preempt_position_push(n.position()); - emit( *type ); + emit( *type, n.parent_is_type() ); printer.preempt_position_pop(); // one pointer is enough for now, pointer-to-function fun can be later if (