mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 13:50:10 +01:00
ref parameters, arguments, returns and val returns (#5434)
- A parameter binding can be marked `ref` instead of `var` or the default. It will bind to reference argument expressions in the caller and produces a reference expression in the callee. - Unlike pointers, a `ref` binding can't be rebound to a different object. - This replaces `addr`, and is not restricted to the `self` parameter. - A `ref` binding, like a value binding, can't be used in fields of classes or structs. - When calling functions, arguments to non-`self` `ref` parameters are also marked with `ref`. - The return of a function can optionally be marked `ref`, `val`, or `var`. These control the category of the call expression invoking the function, and how the return expression is returned. - These may be mixed for functions returning tuple or struct forms. - The address of a `ref` binding is `nocapture` and `noalias`. - We mark parameters of a function that may be referenced by the return value with `bound`. --------- Co-authored-by: Josh L <josh11b@users.noreply.github.com> Co-authored-by: Geoff Romer <gromer@google.com> Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
This commit is contained in:
co-authored by
Josh L
Geoff Romer
Chandler Carruth
parent
e3a366f1c3
commit
ac98870e67
@@ -40,18 +40,8 @@ implement that interface, or to method calls on `IndexWith` otherwise.
|
||||
`IndirectIndexWith` provides a final blanket `impl` of `IndexWith`, so a type
|
||||
can implement at most one of those two interfaces.
|
||||
|
||||
The `Addr` methods of these interfaces, which are used to form durable reference
|
||||
expressions on indexing, must return a pointer and work similarly to the
|
||||
[pointer dereference customization interface](/docs/design/values.md#dereferencing-customization).
|
||||
The returned pointer is then dereferenced by the language to form the reference
|
||||
expression referring to the pointed-to object. These methods must return a raw
|
||||
pointer, and do not automatically chain with customized dereference interfaces.
|
||||
|
||||
**Open question:** It's not clear that the lack of chaining is necessary, and it
|
||||
might be more expressive for the pointer type returned by the `Addr` methods to
|
||||
be an associated facet with a default to allow types to produce custom
|
||||
pointer-like types on their indexing boundary and have them still be
|
||||
automatically dereferenced.
|
||||
The `Ref` methods of these interfaces, which are used to form durable reference
|
||||
expressions on indexing, must return by `ref`.
|
||||
|
||||
## Details
|
||||
|
||||
@@ -64,13 +54,13 @@ Its semantics are defined in terms of the following interfaces:
|
||||
```
|
||||
interface IndexWith(SubscriptType:! type) {
|
||||
let ElementType:! type;
|
||||
fn At[self: Self](subscript: SubscriptType) -> ElementType;
|
||||
fn Addr[addr self: Self*](subscript: SubscriptType) -> ElementType*;
|
||||
fn At[bound self: Self](subscript: SubscriptType) -> val ElementType;
|
||||
fn Ref[bound ref self: Self](subscript: SubscriptType) -> ref ElementType;
|
||||
}
|
||||
|
||||
interface IndirectIndexWith(SubscriptType:! type) {
|
||||
require Self impls IndexWith(SubscriptType);
|
||||
fn Addr[self: Self](subscript: SubscriptType) -> ElementType*;
|
||||
fn Ref[bound self: Self](subscript: SubscriptType) -> ref ElementType;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -79,11 +69,11 @@ rewritten based on the expression category of _lhs_ and whether `T` is known to
|
||||
implement `IndirectIndexWith(I)`:
|
||||
|
||||
- If `T` implements `IndirectIndexWith(I)`, the expression is rewritten to
|
||||
"`*((` _lhs_ `).(IndirectIndexWith(I).Addr)(` _index_ `))`".
|
||||
"`(` _lhs_ `).(IndirectIndexWith(I).Ref)(` _index_ `)`".
|
||||
- Otherwise, if _lhs_ is a
|
||||
[_durable reference expression_](/docs/design/values.md#durable-reference-expressions),
|
||||
the expression is rewritten to "`*((` _lhs_ `).(IndexWith(I).Addr)(` _index_
|
||||
`))`".
|
||||
the expression is rewritten to "`(` _lhs_ `).(IndexWith(I).Ref)(` _index_
|
||||
`)`".
|
||||
- Otherwise, the expression is rewritten to "`(` _lhs_ `).(IndexWith(I).At)(`
|
||||
_index_ `)`".
|
||||
|
||||
@@ -93,18 +83,18 @@ implement `IndirectIndexWith(I)`:
|
||||
final impl forall
|
||||
[SubscriptType:! type, T:! IndirectIndexWith(SubscriptType)]
|
||||
T as IndexWith(SubscriptType) {
|
||||
let ElementType:! type = T.(IndirectIndexWith(SubscriptType)).ElementType;
|
||||
fn At[self: Self](subscript: SubscriptType) -> ElementType {
|
||||
return *(self.(IndirectIndexWith(SubscriptType).Addr)(index));
|
||||
where ElementType = T.(IndirectIndexWith(SubscriptType).ElementType);
|
||||
fn At[bound self: Self](subscript: SubscriptType) -> val ElementType {
|
||||
return self.(IndirectIndexWith(SubscriptType).Ref)(index);
|
||||
}
|
||||
fn Addr[addr self: Self*](subscript: SubscriptType) -> ElementType* {
|
||||
return self->(IndirectIndexWith(SubscriptType).Addr)(index);
|
||||
fn Ref[bound ref self: Self](subscript: SubscriptType) -> ref ElementType {
|
||||
return self.(IndirectIndexWith(SubscriptType).Ref)(index);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Thus, a type that implements `IndirectIndexWith` need not, and cannot, provide
|
||||
its own definitions of `IndexWith.At` and `IndexWith.Addr`.
|
||||
its own definitions of `IndexWith.At` and `IndexWith.Ref`.
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -114,8 +104,8 @@ An array type could implement subscripting like so:
|
||||
class Array(template T:! type) {
|
||||
impl as IndexWith(like i64) {
|
||||
let ElementType:! type = T;
|
||||
fn At[self: Self](subscript: i64) -> T;
|
||||
fn Addr[addr self: Self*](subscript: i64) -> T*;
|
||||
fn At[bound self: Self](subscript: i64) -> val T;
|
||||
fn Ref[bound ref self: Self](subscript: i64) -> ref T;
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -126,7 +116,7 @@ And a type such as `std::span` could look like this:
|
||||
class Span(T:! type) {
|
||||
impl as IndirectIndexWith(like i64) {
|
||||
let ElementType:! type = T;
|
||||
fn Addr[self: Self](subscript: i64) -> T*;
|
||||
fn Ref[bound ref self: Self](subscript: i64) -> ref T;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
+17
-8
@@ -415,9 +415,9 @@ the available implementation strategies.
|
||||
> **Future work:** The interaction between a
|
||||
> [custom value representation](#value-representation-and-customization) and a
|
||||
> value expression used with a polymorphic type needs to be fully captured.
|
||||
> Either it needs to restrict to a `const Self*` style representation (to
|
||||
> prevent slicing) or it needs to have a model for the semantics when a
|
||||
> different value representation is used.
|
||||
> Either it needs to restrict to a `const ref` style representation (to prevent
|
||||
> slicing) or it needs to have a model for the semantics when a different value
|
||||
> representation is used.
|
||||
|
||||
### Interop with C++ `const &` and `const` methods
|
||||
|
||||
@@ -560,6 +560,12 @@ functions with a `()` return type for the purpose of expression categories.
|
||||
|
||||
#### Deferred initialization from values and references
|
||||
|
||||
TODO: This section needs to be updated to reflect the addition of `-> val`
|
||||
returns in [proposal #5434](/proposals/p5434.md). This section could be replaced
|
||||
by a statement that initializing returns may be replaced by value returns when
|
||||
that is safe and correct, moving much of this content into a description of how
|
||||
value returns works.
|
||||
|
||||
Carbon also makes the evaluation of function calls and return statements tightly
|
||||
linked in order to enable more efficiency improvements. It allows the actual
|
||||
initialization performed by the `return` statement with its expression to be
|
||||
@@ -645,6 +651,9 @@ specialized constructs given the specialized nature of these operations.
|
||||
|
||||
### Reference types
|
||||
|
||||
TODO: This section needs to be updated to reflect
|
||||
[proposal #5434](/proposals/p5434.md).
|
||||
|
||||
Unlike C++, Carbon does not currently have reference types. The only form of
|
||||
indirect access are pointers. There are a few aspects to this decision that need
|
||||
to be separated carefully from each other as the motivations and considerations
|
||||
@@ -888,12 +897,12 @@ keyword. It isn't final at all and likely will need to change to read well.
|
||||
The provided representation type must be one of the following:
|
||||
|
||||
- `const Self` -- this forces the use of a _copy_ of the object.
|
||||
- `const Self *` -- this forces the use of a [_pointer_](#pointers) to the
|
||||
original object.
|
||||
- `const ref` -- this forces the use of a [_pointer_](#pointers) to the
|
||||
original object, but with the `const` API subset.
|
||||
- A custom type that is not `Self`, `const Self`, or a pointer to either.
|
||||
|
||||
If the representation is `const Self` or `const Self *`, then the type fields
|
||||
will be accessible as [_value expressions_](#value-expressions) using the normal
|
||||
If the representation is `const Self` or `const ref`, then the type fields will
|
||||
be accessible as [_value expressions_](#value-expressions) using the normal
|
||||
member access syntax for value expressions of a type. These will be implemented
|
||||
by either accessing a copy of the object in the non-pointer case or a pointer to
|
||||
the original object in the pointer case. A representation of `const Self`
|
||||
@@ -904,7 +913,7 @@ used.
|
||||
If no customization is provided, the implementation will select one based on a
|
||||
set of heuristics. Some examples:
|
||||
|
||||
- Non-copyable types and polymorphic types would use a `const Self*`.
|
||||
- Non-copyable types and polymorphic types would use a `const ref`.
|
||||
- Small objects that are trivially copied in a machine register would use
|
||||
`const Self`.
|
||||
|
||||
|
||||
+1667
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user