Skip to content

Commit 8dd89ec

Browse files
committed
Enable generic out parameters, removes temporary alpha limitation
Feature: Code like `f: (out x: _) = { x = 42; }` now works, and can work with any type that is constructible from the rhs (here `42`)... see the new regression test for an example Step toward self-hosting: Start including `cpp2util.h` in cppfront itself... and add (and use) unconditional `final_action`/`finally` Usability: nicer diagnostics for if/else that forgot the required `{` `}` around the body Also: closes #260
1 parent a200c17 commit 8dd89ec

16 files changed

+214
-74
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@
2222
*.vsidx
2323
*.lock
2424
.editorconfig
25+
*.xml
26+
*.sarif

include/cpp2util.h

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -546,9 +546,9 @@ class out {
546546
bool called_construct_ = false;
547547

548548
public:
549-
out(T* t) noexcept : t{ t}, has_t{true} { Default.expects( t); }
550-
out(deferred_init<T>* dt) noexcept : dt{dt}, has_t{false} { Default.expects(dt); }
551-
out(out<T>* ot) noexcept : ot{ot}, has_t{ot->has_t} { Default.expects(ot);
549+
out(T* t_) noexcept : t{ t_}, has_t{true} { Default.expects( t); }
550+
out(deferred_init<T>* dt_) noexcept : dt{dt_}, has_t{false} { Default.expects(dt); }
551+
out(out<T>* ot_) noexcept : ot{ot_}, has_t{ot_->has_t} { Default.expects(ot);
552552
if (has_t) { t = ot->t; }
553553
else { dt = ot->dt; }
554554
}
@@ -1280,6 +1280,39 @@ template <class F>
12801280
}
12811281

12821282

1283+
//
1284+
// Same, but clean up also on exceptional paths
1285+
//
1286+
1287+
template <class F>
1288+
class final_action
1289+
{
1290+
public:
1291+
explicit final_action(const F& ff) noexcept : f{ff} { }
1292+
explicit final_action(F&& ff) noexcept : f{std::move(ff)} { }
1293+
1294+
~final_action() noexcept { f(); }
1295+
1296+
final_action(final_action&& that) noexcept
1297+
: f(std::move(that.f)), invoke(std::exchange(that.invoke, false))
1298+
{ }
1299+
1300+
final_action (final_action const&) = delete;
1301+
void operator=(final_action const&) = delete;
1302+
void operator=(final_action&&) = delete;
1303+
1304+
private:
1305+
F f;
1306+
bool invoke = true;
1307+
};
1308+
1309+
template <class F>
1310+
[[nodiscard]] auto finally(F&& f) noexcept
1311+
{
1312+
return final_action<std::remove_cvref_t<F>>{std::forward<F>(f)};
1313+
}
1314+
1315+
12831316
//-----------------------------------------------------------------------
12841317
//
12851318
// to_string for string interpolation
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#include <iostream>
2+
3+
struct X {
4+
X(int) { }
5+
};
6+
auto operator<<(std::ostream& o, X const&) -> std::ostream& {
7+
o << "exxxx";
8+
return o;
9+
}
10+
11+
f: (out x: _) = {
12+
x = 42;
13+
}
14+
15+
main: ()->int = {
16+
a: int;
17+
f(out a);
18+
std::cout << a << "\n";
19+
20+
b: X;
21+
f(out b);
22+
std::cout << b << "\n";
23+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
42
2+
exxxx

regression-tests/test-results/clang-12/mixed-parameter-passing-generic-out.cpp.output

Whitespace-only changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
42
2+
exxxx

regression-tests/test-results/gcc-10/mixed-parameter-passing-generic-out.cpp.output

Whitespace-only changes.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
#line 1 "mixed-parameter-passing-generic-out.cpp2"
3+
#include <iostream>
4+
5+
struct X {
6+
X(int) { }
7+
};
8+
auto operator<<(std::ostream& o, X const&) -> std::ostream& {
9+
o << "exxxx";
10+
return o;
11+
}
12+
13+
#include "cpp2util.h"
14+
15+
16+
#line 11 "mixed-parameter-passing-generic-out.cpp2"
17+
auto f(auto* x_) -> void;
18+
#line 15 "mixed-parameter-passing-generic-out.cpp2"
19+
[[nodiscard]] auto main() -> int;
20+
21+
//=== Cpp2 definitions ==========================================================
22+
23+
#line 10 "mixed-parameter-passing-generic-out.cpp2"
24+
25+
auto f(auto* x_) -> void{
26+
auto x = cpp2::out(x_);
27+
#line 12 "mixed-parameter-passing-generic-out.cpp2"
28+
x.construct(42);
29+
}
30+
31+
[[nodiscard]] auto main() -> int{
32+
cpp2::deferred_init<int> a;
33+
f(& a);
34+
std::cout << std::move(a.value()) << "\n";
35+
36+
cpp2::deferred_init<X> b;
37+
f(& b);
38+
std::cout << std::move(b.value()) << "\n";
39+
}
40+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mixed-parameter-passing-generic-out.cpp2... ok (mixed Cpp1/Cpp2, Cpp2 code passes safety checks)
2+
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
Microsoft (R) C/C++ Optimizing Compiler Version 19.34.31937 for x86
1+
Microsoft (R) C/C++ Optimizing Compiler Version 19.34.31942 for x86
22
Copyright (C) Microsoft Corporation. All rights reserved.
33

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
42
2+
exxxx
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mixed-parameter-passing-generic-out.cpp

source/common.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
#ifdef _MSC_VER
1515
#pragma warning(disable: 4456)
1616
#endif
17-
//#include "../include/cpp2util.h"
17+
18+
#include "../include/cpp2util.h"
1819

1920

2021
//===========================================================================
@@ -226,15 +227,15 @@ struct String
226227
//
227228
template<typename T>
228229
requires std::is_same_v<T, std::string>
229-
auto as(bool b) -> T
230+
auto __as(bool b) -> T
230231
{
231232
return b ? "true" : "false";
232233
}
233234

234235

235236
// Explicit cast
236237
template<typename T>
237-
auto as(auto x) -> T {
238+
auto __as(auto x) -> T {
238239
return T(x);
239240
}
240241

@@ -247,7 +248,7 @@ auto strip_path(std::string const& file) -> std::string
247248
while (i >= 0 && file[i] != '\\' && file[i] != '/') {
248249
--i;
249250
}
250-
return {file, as<size_t>(i+1)};
251+
return {file, __as<size_t>(i+1)};
251252
}
252253

253254

@@ -379,9 +380,9 @@ class cmdline_processor
379380
}
380381

381382
for (auto& flag : flags) {
382-
auto length_to_match = std::max(flag.unique_prefix, as<int>(arg->text.length())-1);
383+
auto length_to_match = std::max(flag.unique_prefix, __as<int>(arg->text.length())-1);
383384
if (flag.opt_out && arg->text.ends_with("-")) {
384-
length_to_match = std::max(flag.unique_prefix, as<int>(arg->text.length())-2);
385+
length_to_match = std::max(flag.unique_prefix, __as<int>(arg->text.length())-2);
385386
}
386387

387388
// Allow a switch to start with either - or /

source/cppfront.cpp

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ auto pad(int padding) -> std::string_view
4949

5050
return {
5151
indent_str.c_str(),
52-
as<size_t>( std::min( padding, as<int>(std::ssize(indent_str))) )
52+
__as<size_t>( std::min( padding, __as<int>(std::ssize(indent_str))) )
5353
};
5454
}
5555

@@ -746,6 +746,12 @@ class cppfront
746746
};
747747
std::vector<arg_info> current_args = { {} };
748748

749+
struct function_info {
750+
std::vector<std::string> prolog = {};
751+
std::vector<std::string> epilog = {};
752+
};
753+
std::vector<function_info> current_function = { {} };
754+
749755
// For lowering
750756
//
751757
positional_printer printer;
@@ -1878,6 +1884,10 @@ class cppfront
18781884
return (pointer_declarators_cnt + addr_cnt - deref_cnt) > 0;
18791885
}
18801886

1887+
auto is_pointer_declaration(udt_type_node const*, int, int) -> bool {
1888+
return false;
1889+
}
1890+
18811891
auto is_pointer_declaration(declaration_sym const* decl, int deref_cnt, int addr_cnt) -> bool {
18821892
if (!decl) {
18831893
return false;
@@ -2741,6 +2751,9 @@ class cppfront
27412751
auto unqid = std::get_if<type_id_node::unqualified>(&type_id.id);
27422752
auto is_wildcard = unqid && *(*unqid)->identifier == "_";
27432753

2754+
assert( n.declaration->identifier );
2755+
auto identifier = print_to_string( *n.declaration->identifier );
2756+
27442757
// First any prefix
27452758
if (!returns && !is_wildcard)
27462759
{
@@ -2758,7 +2771,15 @@ class cppfront
27582771
break;case passing_style::in : printer.print_cpp2( "auto const&", n.position() );
27592772
break;case passing_style::copy : printer.print_cpp2( "auto", n.position() );
27602773
break;case passing_style::inout : printer.print_cpp2( "auto&", n.position() );
2761-
break;case passing_style::out : printer.print_cpp2( "auto&", n.position() ); // TODO: support out<auto> via rewrite to template param
2774+
2775+
// For generic out parameters, we take a pointer to anything with paramater named "identifier_"
2776+
// and then generate the out<> as a stack local with the expected name "identifier"
2777+
break;case passing_style::out : printer.print_cpp2( "auto*", n.position() );
2778+
current_function.back().prolog.push_back(
2779+
"auto " + identifier + " = cpp2::out(" + identifier + "_); "
2780+
);
2781+
identifier += "_";
2782+
27622783
break;case passing_style::move : printer.print_cpp2( "auto&&", n.position() );
27632784
break;case passing_style::forward: printer.print_cpp2( "auto&&", n.position() );
27642785
break;default: ;
@@ -2798,8 +2819,7 @@ class cppfront
27982819
}
27992820
}
28002821

2801-
printer.print_cpp2( " ", n.declaration->identifier->position() );
2802-
emit( *n.declaration->identifier );
2822+
printer.print_cpp2( " " + identifier, n.declaration->identifier->position());
28032823

28042824
if (!returns && n.declaration->initializer) {
28052825
printer.print_cpp2( " = ", n.declaration->initializer->position() );
@@ -3015,7 +3035,12 @@ class cppfront
30153035
}
30163036

30173037
// Function
3018-
if (n.is(declaration_node::function))
3038+
if (n.is(declaration_node::udt_type))
3039+
{
3040+
}
3041+
3042+
// Function
3043+
else if (n.is(declaration_node::function))
30193044
{
30203045
// Start fresh (there may be one spurious leftover
30213046
// requires-condition created during the declarations pass)
@@ -3024,8 +3049,11 @@ class cppfront
30243049
auto& func = std::get<declaration_node::function>(n.type);
30253050
assert(func);
30263051

3052+
current_function.push_back({});
3053+
auto guard = finally([&]{ current_function.pop_back(); });
3054+
30273055
// If this is at expression scope, we can't emit "[[nodiscard]] auto name"
3028-
// so print the provided intro instead, which will be a lambda-capture-list
3056+
// so print the provided intro instead, which will be a Cpp1 lambda-introducer
30293057
if (capture_intro != "") {
30303058
assert (!n.identifier);
30313059
printer.print_cpp2(capture_intro, n.position());
@@ -3061,15 +3089,12 @@ class cppfront
30613089
// Function body
30623090
assert( n.initializer );
30633091

3064-
auto function_return_locals = std::vector<std::string>{};
3065-
auto function_epilog = std::vector<std::string>{};
3066-
30673092
for (auto&& c : func->contracts) {
30683093
auto print = std::string();
30693094
printer.emit_to_string(&print);
30703095
emit(*c);
30713096
printer.emit_to_string();
3072-
function_return_locals.push_back(print);
3097+
current_function.back().prolog.push_back(print);
30733098
}
30743099

30753100
if (func->returns.index() == function_type_node::list)
@@ -3122,18 +3147,18 @@ class cppfront
31223147
loc += init;
31233148
}
31243149
loc += ";";
3125-
function_return_locals.push_back(loc);
3150+
current_function.back().prolog.push_back(loc);
31263151
}
31273152
}
31283153

3129-
//function_epilog.push_back("/*EPILOG-TEST*/");
3154+
//current_function.back().epilog.push_back("/*EPILOG-TEST*/");
31303155

31313156
printer.preempt_position_push( n.equal_sign );
31323157

31333158
// TODO: something like this to get rid of extra blank lines
31343159
// inside the start of bodies of functions that have
31353160
// multiple contracts
3136-
//printer.skip_lines( std::ssize(function_return_locals) );
3161+
//printer.skip_lines( std::ssize(current_function.back().prolog) );
31373162

31383163
// If processing the parameters generated any requires conditions,
31393164
// emit them here
@@ -3150,7 +3175,7 @@ class cppfront
31503175
emit(
31513176
*n.initializer,
31523177
true, func->position(), n.identifier && func->returns.index() == function_type_node::empty,
3153-
function_return_locals, function_epilog, n.position().colno
3178+
current_function.back().prolog, current_function.back().epilog, n.position().colno
31543179
);
31553180

31563181
printer.preempt_position_pop();

source/lex.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ auto close_paren_type(lexeme l) -> lexeme {
125125

126126
template<typename T>
127127
requires std::is_same_v<T, std::string>
128-
auto as(lexeme l) -> std::string
128+
auto __as(lexeme l) -> std::string
129129
{
130130
switch (l) {
131131
break;case lexeme::SlashEq: return "SlashEq";
@@ -233,7 +233,7 @@ class token
233233
lexeme type
234234
)
235235
: start {sz}
236-
, count {as<int16_t>(std::strlen(sz))}
236+
, count {__as<int16_t>(std::strlen(sz))}
237237
, pos {pos }
238238
, lex_type{type }
239239
{
@@ -262,7 +262,7 @@ class token
262262
return text;
263263
}
264264
else {
265-
return as<std::string>(lex_type) + std::string(": ") + text;
265+
return __as<std::string>(lex_type) + std::string(": ") + text;
266266
}
267267
}
268268

@@ -883,7 +883,7 @@ auto lex_line(
883883
comments.push_back({
884884
comment::comment_kind::line_comment,
885885
{lineno, i},
886-
{lineno, as<colno_t>(std::ssize(line))},
886+
{lineno, __as<colno_t>(std::ssize(line))},
887887
std::string(&line[i], std::ssize(line) - i)
888888
});
889889
in_comment = false;
@@ -1434,7 +1434,7 @@ class tokens
14341434
for (auto const& token : entry) {
14351435
o << " " << token << " (" << token.position().lineno
14361436
<< "," << token.position().colno << ") "
1437-
<< as<std::string>(token.type()) << "\n";
1437+
<< __as<std::string>(token.type()) << "\n";
14381438
}
14391439

14401440
}
@@ -1452,7 +1452,7 @@ class tokens
14521452
for (auto const& token : generated_tokens) {
14531453
o << " " << token << " (" << token.position().lineno
14541454
<< "," << token.position().colno << ") "
1455-
<< as<std::string>(token.type()) << "\n";
1455+
<< __as<std::string>(token.type()) << "\n";
14561456
}
14571457

14581458
o << "--- Generated text\n";

0 commit comments

Comments
 (0)