Skip to content

Commit 955042b

Browse files
committed
Add ctad for template udt
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 ```
1 parent 6ea55d2 commit 955042b

File tree

1 file changed

+72
-0
lines changed

1 file changed

+72
-0
lines changed

source/cppfront.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5027,6 +5027,13 @@ class cppfront
50275027
{
50285028
printer.print_cpp2( " final", n.position() );
50295029
}
5030+
5031+
// CTAD
5032+
struct ctad_details {
5033+
std::string args;
5034+
std::string types;
5035+
};
5036+
auto ctads = std::vector<ctad_details>{};
50305037

50315038
// Type definition
50325039

@@ -5052,6 +5059,52 @@ class cppfront
50525059

50535060
if (decl->is_constructor()) {
50545061
found_constructor = true;
5062+
5063+
if (n.template_parameters) {
5064+
if (auto func = std::get_if<declaration_node::a_function>(&decl->type)) {
5065+
const auto& params = (*func)->parameters->parameters;
5066+
auto can_generate_ctad = std::equal(
5067+
std::cbegin(params)+1, std::cend(params),
5068+
std::cbegin(n.template_parameters->parameters), std::cend(n.template_parameters->parameters),
5069+
[](const auto& arg, const auto& type) -> bool {
5070+
if (const auto* arg_type = std::get_if<declaration_node::an_object>(&arg->declaration->type)) {
5071+
assert(type->name());
5072+
return (*arg_type)->get_token() && (*arg_type)->get_token()->as_string_view() == type->name()->as_string_view();
5073+
} else {
5074+
return false;
5075+
}
5076+
}
5077+
);
5078+
if (can_generate_ctad) {
5079+
ctads.emplace_back();
5080+
std::for_each(std::cbegin(params)+1, std::cend(params), [&](const auto& arg) {
5081+
if (const auto* type = std::get_if<declaration_node::an_object>(&arg->declaration->type)) {
5082+
auto& arg_t = *type;
5083+
assert(arg_t->get_token());
5084+
auto type_name = arg_t->get_token()->to_string(true);
5085+
auto arg_type = print_to_string(*arg_t);
5086+
switch (arg->pass) {
5087+
case passing_style::in : arg_type += " const &"; break;
5088+
case passing_style::out : arg_type = "cpp2::deferred_init<" + arg_type + ">*"; break;
5089+
case passing_style::inout : arg_type += "&"; break;
5090+
case passing_style::move :
5091+
case passing_style::forward: arg_type += "&&"; break;
5092+
case passing_style::copy : break;
5093+
default: assert(!"ICE: invalid argument passing style");
5094+
}
5095+
5096+
if (ctads.back().args.empty()) {
5097+
ctads.back().args = arg_type;
5098+
ctads.back().types = type_name;
5099+
} else {
5100+
ctads.back().args += ", " + arg_type;
5101+
ctads.back().types += ", " + type_name;
5102+
}
5103+
}
5104+
});
5105+
}
5106+
}
5107+
}
50555108
}
50565109
if (decl->is_constructor_with_that()) {
50575110
found_that_constructor = true;
@@ -5145,6 +5198,25 @@ class cppfront
51455198
}
51465199

51475200
printer.print_cpp2("};\n", compound_stmt->close_brace);
5201+
5202+
if (!ctads.empty()) {
5203+
printer.print_cpp2("\n", n.position());
5204+
}
5205+
5206+
for (const auto& ctad : ctads) {
5207+
printer.print_cpp2("template", n.position());
5208+
emit(*n.template_parameters, false, true);
5209+
printer.print_cpp2(" ", n.position());
5210+
5211+
printer.print_cpp2(n.name()->to_string(true), n.position());
5212+
printer.print_cpp2("(", n.position());
5213+
5214+
printer.print_cpp2(ctad.args, n.position());
5215+
printer.print_cpp2(") -> ", n.position());
5216+
printer.print_cpp2(n.name()->to_string(true) + "<", n.position());
5217+
printer.print_cpp2(ctad.types, n.position());
5218+
printer.print_cpp2(">;\n", n.position());
5219+
}
51485220
}
51495221
}
51505222

0 commit comments

Comments
 (0)