Skip to content

Commit

Permalink
types
Browse files Browse the repository at this point in the history
  • Loading branch information
gregmarr committed Feb 17, 2024
1 parent 4ee9a36 commit 8a14a56
Showing 1 changed file with 17 additions and 11 deletions.
28 changes: 17 additions & 11 deletions docs/cpp2/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ A `this` parameter of an `operator=` function can additionally be declared as:

## `operator=` — Construction, assignment, and destruction

All value operations are spelled `operator=`, including construction, assignment, and destruction. `operator=` sets the value of `this` object, so the `this` parameter can be pass as anything but `in` (which would imply `const`):
All value operations are spelled `operator=`, including construction, assignment, and destruction. `operator=` sets the value of `this` object, so the `this` parameter can be passed as anything but `in` (which would imply `const`):

- **`out this`:** Writing `operator=: (out this /*...*/ )` is naturally both a constructor and an assignment operator, because an `out` parameter can take an uninitialized or initialized argument. If you don't write a more-specialized `inout this` assignment operator, Cpp2 will use the `out this` function also for assignment.

Expand All @@ -120,11 +120,13 @@ All value operations are spelled `operator=`, including construction, assignment

Unifying `operator=` enables usable `out` parameters, which is essential for composable guaranteed initialization. We want the expression syntax `x = value` to be able to call a constructor or an assignment operator, so naming them both `operator=` is consistent.

TODO Return type of assignment operator?

> Note: Writing `=` always invokes an `operator=` (in fact for a Cpp2-authored type, and semantically for a Cpp1-authored type). This avoids the Cpp1 inconsistency that "writing `=` calls `operator=`, except when it doesn't" (such as in a Cpp1 variable initialization). Conversely, `operator=` is always invoked by `=` in Cpp2.
### `that` — A source parameter

All functions can have a **`that`** is a synonym for the object to be copied/moved from. Like `this`, at type scope it is never declared with an explicit `: its_type` because its type is always the current type.
All type-scope functions can have **`that`** as its second parameter, which is a synonym for the object to be copied/moved from. Like `this`, at type scope it is never declared with an explicit `: its_type` because its type is always the current type.

`that` can be an `in` (default) or `move` parameter. Which you choose naturally determines what kind of member function is being declared:

Expand Down Expand Up @@ -157,7 +159,7 @@ In Cpp1 terms, they can be described as follows:

- **M2 is preferred over A2.** Both M2 and A2 can generate a missing `(inout this, move that)` function. If both options are available, Cpp2 prefers to use M2 (generate move assignment from copy assignment, which could itself have been generated from copy construction) rather than A2 (generate move assignment from move construction). This is because M2 is a better fit: Move assignment is more like copy assignment than like move construction, because assignments are designed structurally to set the value of an existing `this` object.

The most general `operator=` with `that` is `(out this, that)`. In Cpp1 terms, it generates all four combinations of { copy, move } x { constructor, assignment }. This is often sufficient, so you can write all these value-setting just once. If you do want to write a more specific version that does something else, though, you can always write it too.
The most general `operator=` with `that` is `(out this, that)`. In Cpp1 terms, it generates all four combinations of { copy, move } x { constructor, assignment }. This is often sufficient, so you can write all these value-setting functions just once. If you do want to write a more specific version that does something else, though, you can always write it too.

> Note: Generating `inout this` (assignment) from `out this` also generates **converting assignment** from converting construction, which is a new thing. Today in Cpp1, if you write a converting constructor from another type `X`, you may or may not write the corresponding assignment from `X`; in Cpp2 you will get that by default, and it sets the object to the same state as the converting constructor from `X` does.
Expand All @@ -169,21 +171,23 @@ There are only two defaults the language will generate implicitly for a type:

- The only special function every type must have is the destructor. If you don't write it by hand, a public nonvirtual destructor is generated by default.

- If no `operator=` functions are written by hand, a public default constructor is generated by default.
- If no `operator=` functions other than the destructor are written by hand, a public default constructor is generated by default.

All other `operator=` functions are explicitly written, either by hand or by opting into applying a metafunction (see below).

> Note: Because generated functions are always opt-in, you can never get a generated function that's wrong for your type, and so Cpp2 doesn’t need to support "=delete" for the purpose of suppressing unwanted generated functions.
### Memberwise by default

All copy/move/comparison `operator=` functions are memberwise by default in Cpp2. That includes when you write memberwise construction and assignment yourself. In a hand-written `operator=`:
All copy/move/comparison `operator=` functions are memberwise by default in Cpp2. That includes when you write memberwise construction and assignment yourself.

In a hand-written `operator=`:

- The body must begin with a series of `member = value;` statements, one for each of the type's data members in order.
- The body must begin with a series of `member = value;` statements, one for each of the type's data members (including base classes) in declaration order.

- If the body does not mention a member, by default the member's default initializer is used.
- If the body does not mention a member in the appropriate place in the beginning section, by default the member's default initializer is used.

- In an assignment operator (`inout this`), you an explicitly skip setting a member by writing `member = _;` where it would normally be set, if you know you have a reason to set its value later instead.
- In an assignment operator (`inout this`), you can explicitly skip setting a member by writing `member = _;` where it would normally be set if you know you have a reason to set its value later instead or if the existing value needs to be preserved.

For example:

Expand Down Expand Up @@ -213,6 +217,8 @@ mytype: type
print();
}

// TODO skipping the default memberwise assignment for later setting

print: (this) = std::cout << "value is [(name)$] [(social_handle)$]\n";
}

Expand All @@ -224,12 +230,12 @@ main: () = {
y = x; // copy assign
z := (move x); // move construct
z = (move y); // move assign
x.print(); // [] [] - moved from
y.print(); // [] [] - moved from
x.print(); // "value is [] []" - moved from
y.print(); // "value is [] []" - moved from
}
```

> Note: This makes memberwise semantics symmetric for construction and assignment. In Cpp1, only non-copy/move constructors have a default, which is to initialize a member with its default initializer. In Cpp2, both constructors and assignment operators default to using the default initializer for if it's a conversion function (non-`that`, aka non-copy/move), and using memberwise `member = that.member;` for copy/move functions.
> Note: This makes memberwise semantics symmetric for construction and assignment. In Cpp1, only non-copy/move constructors have a default, which is to initialize a member with its default initializer. In Cpp2, both constructors and assignment operators default to using the default initializer if it's a conversion function (non-`that`, aka non-copy/move), and using memberwise `member = that.member;` for copy/move functions.


Expand Down

0 comments on commit 8a14a56

Please sign in to comment.