Skip to content

Commit 4c52d2d

Browse files
committed
Added support for that parameters
Must be `in` or `move`, and only allowed as the second parameter of `operator=` `that` has special powers, including that every use of a `move that` parameter is a move, including memberwise `that.member` uses... to make that safer, added checks for attempts to double move the same member (or the whole object and a member) And fixed some bugs, such as correcting the code gen when a constructor uses default initializers to initialize all members
1 parent a2c71a9 commit 4c52d2d

11 files changed

+396
-30
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
myclass : type = {
3+
4+
operator=: (out this) = { }
5+
6+
operator=: (out this, that) = {
7+
name = that.name;
8+
addr = that.addr;
9+
}
10+
11+
operator=: (out this, move that) = {
12+
name = that.name;
13+
addr = that.addr;
14+
}
15+
16+
print: (this) = {
17+
std::cout << "name '(name)$', addr '(addr)$'\n";
18+
}
19+
20+
name: std::string = "Henry";
21+
addr: std::string = "123 Ford Dr.";
22+
23+
}
24+
25+
main: () = {
26+
x: myclass = ();
27+
x.print();
28+
29+
std::cout << "-----\n";
30+
y := x;
31+
x.print();
32+
y.print();
33+
34+
std::cout << "-----\n";
35+
z := (move x);
36+
x.print();
37+
z.print();
38+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name 'Henry', addr '123 Ford Dr.'
2+
-----
3+
name 'Henry', addr '123 Ford Dr.'
4+
name 'Henry', addr '123 Ford Dr.'
5+
-----
6+
name '', addr ''
7+
name 'Henry', addr '123 Ford Dr.'

regression-tests/test-results/clang-12/pure2-types-that-parameters.cpp.output

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name 'Henry', addr '123 Ford Dr.'
2+
-----
3+
name 'Henry', addr '123 Ford Dr.'
4+
name 'Henry', addr '123 Ford Dr.'
5+
-----
6+
name '', addr ''
7+
name 'Henry', addr '123 Ford Dr.'

regression-tests/test-results/gcc-10/pure2-types-that-parameters.cpp.output

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name 'Henry', addr '123 Ford Dr.'
2+
-----
3+
name 'Henry', addr '123 Ford Dr.'
4+
name 'Henry', addr '123 Ford Dr.'
5+
-----
6+
name '', addr ''
7+
name 'Henry', addr '123 Ford Dr.'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pure2-types-that-parameters.cpp
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
2+
#define CPP2_USE_MODULES Yes
3+
4+
#include "cpp2util.h"
5+
6+
7+
#line 2 "pure2-types-that-parameters.cpp2"
8+
class myclass;
9+
#line 25 "pure2-types-that-parameters.cpp2"
10+
auto main() -> int;
11+
12+
//=== Cpp2 definitions ==========================================================
13+
14+
#line 1 "pure2-types-that-parameters.cpp2"
15+
16+
class myclass {
17+
18+
public: myclass()
19+
: name{ "Henry" }
20+
, addr{ "123 Ford Dr." }
21+
#line 5 "pure2-types-that-parameters.cpp2"
22+
{}
23+
public: explicit myclass(myclass const& that)
24+
: name{ that.name }
25+
, addr{ that.addr }
26+
#line 7 "pure2-types-that-parameters.cpp2"
27+
{
28+
29+
}public: auto operator=(myclass const& that) -> void{name = that.name;addr = that.addr;}
30+
31+
public: explicit myclass(myclass&& that)
32+
: name{ std::move(that).name }
33+
, addr{ std::move(that).addr }
34+
#line 12 "pure2-types-that-parameters.cpp2"
35+
{
36+
37+
}public: auto operator=(myclass&& that) -> void{name = std::move(that).name;addr = std::move(that).addr;}
38+
39+
public: auto print() const -> void{
40+
std::cout << "name '" + cpp2::to_string(name) + "', addr '" + cpp2::to_string(addr) + "'\n";
41+
}
42+
43+
private: std::string name {"Henry"};
44+
private: std::string addr {"123 Ford Dr."};
45+
46+
};
47+
48+
auto main() -> int{
49+
myclass x {};
50+
CPP2_UFCS_0(print, x);
51+
52+
std::cout << "-----\n";
53+
auto y {x};
54+
CPP2_UFCS_0(print, x);
55+
CPP2_UFCS_0(print, std::move(y));
56+
57+
std::cout << "-----\n";
58+
auto z {std::move(x)};
59+
CPP2_UFCS_0(print, std::move(x));
60+
CPP2_UFCS_0(print, std::move(z));
61+
}
62+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-types-that-parameters.cpp2... ok (all Cpp2, passes safety checks)
2+

source/cppfront.cpp

Lines changed: 176 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,9 @@ class cppfront
825825
bool violates_initialization_safety = false;
826826
bool suppress_move_from_last_use = false;
827827

828+
bool in_move_that_function = false;
829+
std::vector<token const*> already_moved_that_members = {};
830+
828831
struct arg_info {
829832
passing_style pass = passing_style::in;
830833
token const* ptoken = {};
@@ -1318,6 +1321,14 @@ class cppfront
13181321
)
13191322
&& !in_non_rvalue_context.back();
13201323

1324+
if (
1325+
in_move_that_function
1326+
&& *n.identifier == "that"
1327+
)
1328+
{
1329+
add_move = true;
1330+
}
1331+
13211332
// For an explicit 'forward' apply forwarding to correct identifier
13221333
assert (!current_args.empty());
13231334
if (current_args.back().pass == passing_style::forward) {
@@ -1357,10 +1368,11 @@ class cppfront
13571368
if (auto decl = sema.get_declaration_of(*n.identifier);
13581369
is_local_name
13591370
&& !(*n.identifier == "this")
1371+
&& !(*n.identifier == "that")
13601372
&& decl
1361-
// note pointer equality: if we're not in the actual declaration of n.identifier
13621373
&& (
13631374
in_synthesized_multi_return
1375+
// note pointer equality: if we're not in the actual declaration of n.identifier
13641376
|| decl->identifier != n.identifier
13651377
)
13661378
// and this variable was uninitialized
@@ -2252,6 +2264,51 @@ class cppfront
22522264
assert(n.expr);
22532265
last_postfix_expr_was_pointer = false;
22542266

2267+
// For a 'move that' parameter, track the members we already moved from
2268+
// so we can diagnose attempts to move from the same member twice
2269+
if (
2270+
in_move_that_function
2271+
&& *n.expr->get_token() == "that"
2272+
)
2273+
{
2274+
if (n.ops.empty()) {
2275+
if (!already_moved_that_members.empty()) {
2276+
errors.emplace_back(
2277+
n.position(),
2278+
"attempting to move from whole 'that' object after a 'that.member' was already moved from"
2279+
);
2280+
return;
2281+
}
2282+
// push a sentinel for "all members"
2283+
already_moved_that_members.push_back(nullptr);
2284+
}
2285+
else {
2286+
auto member = n.ops[0].id_expr->get_token();
2287+
assert(member);
2288+
2289+
for (
2290+
auto i = already_moved_that_members.begin();
2291+
i != already_moved_that_members.end();
2292+
++i
2293+
)
2294+
{
2295+
if (
2296+
!*i
2297+
|| **i == *member
2298+
)
2299+
{
2300+
errors.emplace_back(
2301+
n.position(),
2302+
"attempting to move twice from 'that." + member->to_string(true) + "'"
2303+
);
2304+
return;
2305+
}
2306+
}
2307+
2308+
already_moved_that_members.push_back(member);
2309+
}
2310+
}
2311+
22552312
// Ensure that forwarding postfix-expressions start with a forwarded parameter name
22562313
//
22572314
assert (!current_args.empty());
@@ -3235,6 +3292,43 @@ class cppfront
32353292
// Since we're skipping "out this," plus possibly "implicit " and
32363293
// whitespace, any following parameters on the same line can shift left
32373294
printer.add_pad_in_this_line(-18);
3295+
3296+
return;
3297+
}
3298+
3299+
//-----------------------------------------------------------------------
3300+
// Handle 'that' parameters
3301+
3302+
if (n.declaration->has_name("that"))
3303+
{
3304+
assert(
3305+
n.pass == passing_style::in
3306+
|| n.pass == passing_style::move
3307+
);
3308+
auto pass = std::string{" const&"};
3309+
if (n.pass == passing_style::move) {
3310+
pass = "&&";
3311+
in_move_that_function = true;
3312+
}
3313+
3314+
auto func_name = get_enclosing_function_name();
3315+
assert(func_name);
3316+
3317+
if (*func_name != "operator=") {
3318+
errors.emplace_back(
3319+
n.position(),
3320+
"only an operator= function may have a 'that' parameter"
3321+
);
3322+
return;
3323+
}
3324+
3325+
auto type_name = get_enclosing_type_name();
3326+
assert(type_name);
3327+
3328+
printer.print_cpp2(
3329+
type_name->to_string(true) + pass + " that",
3330+
n.position()
3331+
);
32383332
return;
32393333
}
32403334

@@ -3669,6 +3763,68 @@ class cppfront
36693763
}
36703764

36713765

3766+
//-----------------------------------------------------------------------
3767+
//
3768+
auto get_enclosing_type_name()
3769+
-> token const*
3770+
{
3771+
// Navigate to the enclosing type, if there is one...
3772+
for (auto parent = current_declarations.rbegin();
3773+
parent != current_declarations.rend();
3774+
++parent
3775+
)
3776+
{
3777+
if (
3778+
*parent
3779+
&& (*parent)->is_namespace()
3780+
)
3781+
{
3782+
break;
3783+
}
3784+
// ... and here it is, so...
3785+
if (
3786+
*parent
3787+
&& (*parent)->is_type()
3788+
)
3789+
{
3790+
return (*parent)->name();
3791+
}
3792+
}
3793+
return {};
3794+
}
3795+
3796+
3797+
//-----------------------------------------------------------------------
3798+
//
3799+
auto get_enclosing_function_name()
3800+
-> token const*
3801+
{
3802+
// Navigate to the enclosing function, if there is one...
3803+
for (auto parent = current_declarations.rbegin();
3804+
parent != current_declarations.rend();
3805+
++parent
3806+
)
3807+
{
3808+
if (
3809+
*parent
3810+
&& (*parent)->is_namespace()
3811+
)
3812+
{
3813+
break;
3814+
}
3815+
// ... and here it is, so...
3816+
if (
3817+
*parent
3818+
&& (*parent)->is_function()
3819+
)
3820+
{
3821+
return (*parent)->name();
3822+
}
3823+
}
3824+
return {};
3825+
}
3826+
3827+
36723828
//-----------------------------------------------------------------------
36733829
//
36743830
auto emit(
@@ -3680,6 +3836,15 @@ class cppfront
36803836
{
36813837
auto do_recursive_call_for_assignment = false;
36823838

3839+
if (
3840+
n.is_function()
3841+
&& n.has_name()
3842+
)
3843+
{ // reset the 'we have a move-that on this function' flag
3844+
in_move_that_function = false;
3845+
already_moved_that_members = {};
3846+
}
3847+
36833848
auto is_main =
36843849
!n.parent_declaration
36853850
&& n.has_name("main")
@@ -3913,6 +4078,15 @@ class cppfront
39134078
&& !emit_constructor_as_assignment
39144079
)
39154080
{
4081+
if (
4082+
func->parameters->ssize() > 1
4083+
&& (*func->parameters)[1]->has_name("that")
4084+
&& (*func->parameters)[1]->pass == passing_style::move
4085+
)
4086+
{
4087+
in_move_that_function = true;
4088+
}
4089+
39164090
assert(
39174091
!is_main
39184092
// prefix can be "explicit "
@@ -4035,6 +4209,7 @@ class cppfront
40354209
initializer +
40364210
" }"
40374211
);
4212+
separator = ", ";
40384213
}
40394214
else
40404215
{

0 commit comments

Comments
 (0)