Skip to content

Commit

Permalink
Add ctad for template udt
Browse files Browse the repository at this point in the history
Currently after compiling the following code:
```cpp
value: <T: type, Y: type, Z: type> type = {
    public data1: T;
    public data2: Y;
    public data3: Z;

    operator=: (out this, out t: T, inout y: Y, z: Z) = {
        t = 42;
        data1 = t;
        data2 = y;
        data3 = z;
    }
    operator=: (out this, t: * const T, y: *Y, move z: Z) = {
        data1 = t*;
        data2 = y*;
        data3 = z;
    }
}

main: () = {
    i : int;
    s := std::string("lvalue string");
    v: value = (out i, s, :std::string = "temporary string");
    std::cout << "(v.data1)$, (v.data2)$, (v.data3)$" << std::endl;

    s = "will be moved";
    w: value = (v.data1&, v.data2&, move s);
    std::cout << "(w.data1)$, (w.data2)$, (w.data3)$" << std::endl;
}
```
We will get the following error:
```
../tests/ctad.cpp2... ok (all Cpp2, passes safety checks)

../tests/ctad.cpp2:22:11: error: no viable constructor or deduction guide for deduction of template arguments of 'value'
    value v {&i, s, std::string{"temporary string"}};
          ^
../tests/ctad.cpp2:12:13: note: candidate template ignored: couldn't infer template argument 'T'
    public: value(cpp2::in<T const*> t, cpp2::in<Y*> y, Z&& z);
            ^
../tests/ctad.cpp2:6:13: note: candidate template ignored: could not match 'cpp2::out<T>' against 'cpp2::deferred_init<int> *'
    public: value(cpp2::out<T> t, Y& y, cpp2::in<Z> z);
            ^
../tests/ctad.cpp2:1:52: note: candidate function template not viable: requires 1 argument, but 3 were provided
template<typename T, typename Y, typename Z> class value {
                                                   ^
1 error generated.
```

After this change cppfront generates Class template argument deduction (CTAD)
for template class that has constructors with the same argument type list
as template arguments (order matters). cppfront will add the following
cpp1 code to `Cpp2 type definitions and function declarations` section:
```cpp
template<typename T, typename Y, typename Z> value(cpp2::deferred_init<T>*, Y&, Z const &) -> value<T, Y, Z>;
template<typename T, typename Y, typename Z> value(T const* const &, Y* const &, Z&&) -> value<T, Y, Z>;
```
And will make the code compile and run successfuly:
```
42, lvalue string, temporary string
42, lvalue string, will be moved
```
  • Loading branch information
filipsajdak committed Apr 22, 2023
1 parent 72004cd commit f186f11
Showing 1 changed file with 72 additions and 0 deletions.
72 changes: 72 additions & 0 deletions source/cppfront.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4989,6 +4989,13 @@ class cppfront
{
printer.print_cpp2( " final", n.position() );
}

// CTAD
struct ctad_details {
std::string args;
std::string types;
};
auto ctads = std::vector<ctad_details>{};

// Type definition

Expand All @@ -5014,6 +5021,52 @@ class cppfront

if (decl->is_constructor()) {
found_constructor = true;

if (n.template_parameters) {
if (auto func = std::get_if<declaration_node::a_function>(&decl->type)) {
const auto& params = (*func)->parameters->parameters;
auto can_generate_ctad = std::equal(
std::cbegin(params)+1, std::cend(params),
std::cbegin(n.template_parameters->parameters), std::cend(n.template_parameters->parameters),
[](const auto& arg, const auto& type) -> bool {
if (const auto* arg_type = std::get_if<declaration_node::an_object>(&arg->declaration->type)) {
assert(type->name());
return (*arg_type)->get_token() && (*arg_type)->get_token()->as_string_view() == type->name()->as_string_view();
} else {
return false;
}
}
);
if (can_generate_ctad) {
ctads.emplace_back();
std::for_each(std::cbegin(params)+1, std::cend(params), [&](const auto& arg) {
if (const auto* type = std::get_if<declaration_node::an_object>(&arg->declaration->type)) {
auto& arg_t = *type;
assert(arg_t->get_token());
auto type_name = arg_t->get_token()->to_string(true);
auto arg_type = print_to_string(*arg_t);
switch (arg->pass) {
case passing_style::in : arg_type += " const &"; break;
case passing_style::out : arg_type = "cpp2::deferred_init<" + arg_type + ">*"; break;
case passing_style::inout : arg_type += "&"; break;
case passing_style::move :
case passing_style::forward: arg_type += "&&"; break;
case passing_style::copy : break;
default: assert(!"ICE: invalid argument passing style");
}

if (ctads.back().args.empty()) {
ctads.back().args = arg_type;
ctads.back().types = type_name;
} else {
ctads.back().args += ", " + arg_type;
ctads.back().types += ", " + type_name;
}
}
});
}
}
}
}
if (decl->is_constructor_with_that()) {
found_that_constructor = true;
Expand Down Expand Up @@ -5107,6 +5160,25 @@ class cppfront
}

printer.print_cpp2("};\n", compound_stmt->close_brace);

if (!ctads.empty()) {
printer.print_cpp2("\n", n.position());
}

for (const auto& ctad : ctads) {
printer.print_cpp2("template", n.position());
emit(*n.template_parameters, false, true);
printer.print_cpp2(" ", n.position());

printer.print_cpp2(n.name()->to_string(true), n.position());
printer.print_cpp2("(", n.position());

printer.print_cpp2(ctad.args, n.position());
printer.print_cpp2(") -> ", n.position());
printer.print_cpp2(n.name()->to_string(true) + "<", n.position());
printer.print_cpp2(ctad.types, n.position());
printer.print_cpp2(">;\n", n.position());
}
}
}

Expand Down

0 comments on commit f186f11

Please sign in to comment.