Skip to content

Commit

Permalink
Added type and namespace alias declarations, closes #273
Browse files Browse the repository at this point in the history
An alias can be to a namespace, type, function, or object.

Aliases are always a non-mutable view of the original entity. This is natural for namespace and type and function aliases, since those kinds of entited can't be mutated, but it bears calling out for an object alias which behaves as `auto const&`.

The current syntax is like non-alias declarations, but with `==` to connote equality with an existing entity, instead of `=` which connotes setting the new entity's value. For example:

    lit: namespace == ::std::literals;

    pmr_vec: <T> type
        == std::vector<T, std::pmr::polymorphic_allocator<T>>;

    func :== some_original::inconvenient::function_name;

    vec :== my_vector;  // note: const&, aliases are never mutable

Note: Function aliases are subsumed under the object case, so you can't declare a function alias with an explicit signature. Rationale:
    - if there's no need for a (repetitive) explicit signature,
        the object case covers it
    - if the parameters/returns must be exact (no conversions),
        a pointer to function covers it
    - if parameter/return conversions are allowed,
        std::function covers it
But if we do learn about a reason to add support for function aliases having an explicit signature, and possibly with conversions to/from the parameter/return types, they'll be a natural fit here.
  • Loading branch information
hsutter committed Apr 2, 2023
1 parent feff640 commit 63efa6e
Show file tree
Hide file tree
Showing 12 changed files with 465 additions and 7 deletions.
32 changes: 32 additions & 0 deletions regression-tests/pure2-type-and-namespace-aliases.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

N: namespace = {
pmr_vec: <T> type == std::vector<T, std::pmr::polymorphic_allocator<T>>;
}

N1: namespace == N;

myclass: type = {
// Default to public
str: type == std::string;

private str2: type == std::string;
}

N3: namespace == ::std::literals;

myfunc: () = {
v: N1::pmr_vec<myclass::str> = ("xyzzy", "plugh");

v2 :== v;

for v2 do :(s) =
std::cout << "(s)$\n";
}

main: () = {
view: type == std::string_view;
N4: namespace == std::literals;

myfunc2 :== myfunc;
myfunc2();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
xyzzy
plugh
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
xyzzy
plugh
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
xyzzy
plugh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pure2-type-and-namespace-aliases.cpp
67 changes: 67 additions & 0 deletions regression-tests/test-results/pure2-type-and-namespace-aliases.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@

#define CPP2_USE_MODULES Yes

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


#include "cpp2util.h"


#line 2 "pure2-type-and-namespace-aliases.cpp2"
namespace N {

}

#line 8 "pure2-type-and-namespace-aliases.cpp2"
class myclass;

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


#line 2 "pure2-type-and-namespace-aliases.cpp2"
namespace N {
template<typename T> using pmr_vec = std::vector<T,std::pmr::polymorphic_allocator<T>>;
}

namespace N1 = N;

class myclass {
// Default to public
public: using str = std::string;

private: using str2 = std::string;
};

namespace N3 = ::std::literals;

auto myfunc() -> void;

#line 26 "pure2-type-and-namespace-aliases.cpp2"
auto main() -> int;

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


#line 2 "pure2-type-and-namespace-aliases.cpp2"
namespace N {

}

#line 17 "pure2-type-and-namespace-aliases.cpp2"
auto myfunc() -> void{
N1::pmr_vec<myclass::str> v {"xyzzy", "plugh"};

auto const& v2 = std::move(v);

for ( auto const& cpp2_range = v2; auto const& s : cpp2_range )
std::cout << cpp2::to_string(s) + "\n";
}

auto main() -> int{
using view = std::string_view;
namespace N4 = std::literals;

auto const& myfunc2 = myfunc;
myfunc2();
}

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

114 changes: 114 additions & 0 deletions source/cppfront.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2439,6 +2439,16 @@ class cppfront
return false;
}

auto is_pointer_declaration(
alias_node const*,
int,
int
)
-> bool
{
return false;
}

auto is_pointer_declaration(
declaration_sym const* decl,
int deref_cnt,
Expand Down Expand Up @@ -4423,6 +4433,7 @@ class cppfront
-> void
{
// In phase 0, only need to consider namespaces and types

if (
printer.get_phase() == printer.phase0_type_decls
&& !n.is_namespace()
Expand All @@ -4432,6 +4443,109 @@ class cppfront
return;
}


// Handle aliases

if (n.is_alias())
{
auto& a = std::get<declaration_node::an_alias>(n.type);
assert(a);

// Non-local aliases are emitted in phase 1, locals in phase 2
if (
(
!n.parent_is_function()
&& printer.get_phase() == printer.phase1_type_defs_func_decls
)
||
(
n.parent_is_function()
&& printer.get_phase() == printer.phase2_func_defs
)
)
{
assert(
a->is_type_alias()
|| a->is_namespace_alias()
|| a->is_object_alias()
);

// If we're in a type scope, handle the access specifier
if (n.parent_is_type()) {
if (n.access) {
printer.print_cpp2(n.access->to_string(true) + ": ", n.access->position());
}
else {
printer.print_cpp2("public: ", n.position());
}
}

// Emit template parameters if any
if (n.template_parameters) {
printer.print_cpp2("template", n.position());
emit(*n.template_parameters, false, true);
printer.print_cpp2(" ", n.position());
}

// Handle type aliases
if (a->is_type_alias()) {
printer.print_cpp2(
"using "
+ print_to_string(*n.identifier)
+ " = "
+ print_to_string( *std::get<alias_node::a_type>(a->initializer) )
+ ";\n",
n.position()
);
}

// Handle namespace aliases
else if (a->is_namespace_alias()) {
auto initializer = std::string{};
if (auto qid = std::get_if<alias_node::a_namespace_qualified>(&a->initializer)) {
assert(*qid);
initializer = print_to_string(**qid);
}
else if (auto uid = std::get_if<alias_node::a_namespace_unqualified>(&a->initializer)) {
assert(*uid);
initializer = print_to_string(**uid);
}
else {
assert(!"ICE: should be unreachable - invalid namespace alias initializer");
}
printer.print_cpp2(
"namespace "
+ print_to_string(*n.identifier)
+ " = "
+ initializer
+ ";\n",
n.position()
);
}

// Handle object aliases
else if (a->is_object_alias()) {
printer.print_cpp2(
"auto const& "
+ print_to_string(*n.identifier)
+ " = "
+ print_to_string( *std::get<alias_node::an_object>(a->initializer) )
+ ";\n",
n.position()
);
}

else {
assert(!"ICE: should be unreachable - invalid alias");
}

return;
}
}


// Handle other declarations

auto need_to_generate_assignment = false;
auto need_to_generate_move = false;

Expand Down
Loading

0 comments on commit 63efa6e

Please sign in to comment.