Skip to content

Commit 8df9720

Browse files
committed
Delete content moved to carbon-language#3802
1 parent 9a922ad commit 8df9720

File tree

1 file changed

+6
-315
lines changed

1 file changed

+6
-315
lines changed

proposals/p3720.md

Lines changed: 6 additions & 315 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2525
- [C++ pointer to member](#c-pointer-to-member)
2626
- [Instance interface members](#instance-interface-members)
2727
- [Non-instance interface members](#non-instance-interface-members)
28-
- [Member forwarding](#member-forwarding)
29-
- [`extend api`](#extend-api)
30-
- [Binding to the members of another type](#binding-to-the-members-of-another-type)
31-
- [Other uses of `extend api`](#other-uses-of-extend-api)
3228
- [C++ operator overloading](#c-operator-overloading)
3329
- [Future work](#future-work)
3430
- [Future: tuple indexing](#future-tuple-indexing)
@@ -39,17 +35,14 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3935
- [Swap the bind interface parameters](#swap-the-bind-interface-parameters)
4036
- [Reference binding produces a value that wraps a pointer](#reference-binding-produces-a-value-that-wraps-a-pointer)
4137
- [Separate interface for compile-time binding instead of type binding](#separate-interface-for-compile-time-binding-instead-of-type-binding)
42-
- [Allow `extend api` of an incomplete type](#allow-extend-api-of-an-incomplete-type)
43-
- [Make some name resolutions cases unambiguous with `extend api`](#make-some-name-resolutions-cases-unambiguous-with-extend-api)
4438

4539
<!-- tocstop -->
4640

4741
## Abstract
4842

4943
Define the binding operation used to compute the result of `x.y`, `p->y`,
5044
`x.(C.y)`, and `p->(C.y)` as calling a method from user-implementable
51-
interfaces. Also allow classes to `extend api` other classes, for forwarding use
52-
cases.
45+
interfaces.
5346

5447
## Problem
5548

@@ -157,43 +150,6 @@ by how they rewrite into the `x.(y)` form using these two rules:
157150
the `T.(`\_\_\_`)` form.
158151
- `x->y` and `x->(y)` are interpreted as `(*x).y` and `(*x).(y)` respectively.
159152

160-
In addition, we propose adding a declaration in a type definition to find names
161-
defined in another type, [`extend api T`](#extend-api):
162-
163-
```carbon
164-
class Extended {
165-
fn F[self: Self]();
166-
fn G[self: Self]();
167-
}
168-
169-
class Extending {
170-
fn G[self: Self]();
171-
extend api Extended;
172-
impl as ImplicitAs(Extended);
173-
}
174-
175-
var e: Extending;
176-
```
177-
178-
This means that lookup into `Extending` also looks in the type `Extended` for
179-
members. In this way, `e.F` will find `e.(Extended.F)`. Note that does not make
180-
`e.F()` actually work unless `Extended.F` was somehow legal to call on an value
181-
of type `Extending`. In the example, this is accomplished by defining an
182-
implicit conversion from `Extending` to `Extended`.
183-
184-
`extend api T` is equivalent to defining an `alias` for every member of `T` that
185-
doesn't have a conflicting name. This means `Extending` is equivalent to:
186-
187-
```carbon
188-
class Extending {
189-
fn G[self: Self]();
190-
alias F = Extended.F;
191-
impl as ImplicitAs(Extended);
192-
}
193-
```
194-
195-
This is used for [member forwarding use cases](#member-forwarding).
196-
197153
## Details
198154

199155
To use instance members of a class, we need to go through the additional step of
@@ -798,253 +754,13 @@ Which calls the above implementation that calls `Fireworks()`.
798754
> result type. Furthermore, we want `TypeBind` implementation no matter which
799755
> facet of the type is used in the code.
800756
801-
### Member forwarding
802-
803-
Consider a class that we want to act like it has the members of another type.
804-
For example, a type `Box(T)` that has a pointer to a `T` object allocated on the
805-
heap:
806-
807-
```carbon
808-
class Box(T:! type) {
809-
var ptr: T*;
810-
// ???
811-
}
812-
```
813-
814-
`Box(T)` should act like it has all the members of `T`:
815-
816-
```carbon
817-
class String {
818-
fn Search[self: Self](c: u8) -> i32;
819-
}
820-
var b: Box(String) = ...;
821-
var position: i32 = b.Search(32);
822-
```
823-
824-
There are two ingredients to make this work:
825-
826-
- We need some way to make `b.Search` be equivalent to `b.(String.Search)` not
827-
`b.(Box(String).Search)`.
828-
- We need the act of binding a method of `String` to a `Box(String)` value
829-
work by dereferencing the pointer `b.ptr`.
830-
831-
#### `extend api`
832-
833-
For the first ingredient, we need a way to customize
834-
[simple member access](/docs/design/expressions/member_access.md) for the class
835-
`Box(T)`. Normally simple member access on a value like `b`
836-
[looks in the type of `b`](/docs/design/expressions/member_access.md#values).
837-
However, types can designate other entities to also look up in using the
838-
`extend` keyword (see [proposal #2760](/proposals/p2760.md#proposal)):
839-
840-
- `extend impl as I` adds lookup into the implementation of an interface `I`.
841-
- `extend base: B` adds lookup into the base class `B`.
842-
- `extend adapt C` adds lookup into the adapted class `C`.
843-
- [mixins are also planning](/proposals/p2760.md#future-work-mixins) on using
844-
an `extend` syntax
845-
846-
The natural choice is to add another way of using `extend` with consistent
847-
lookup rules as the other uses of `extend`, with conflicts handled as determined
848-
by leads issues
849-
[#2355](https://github.com/carbon-language/carbon-lang/issues/2355) and
850-
[#2745](https://github.com/carbon-language/carbon-lang/issues/2745). I propose
851-
`extend api T`:
852-
853-
```carbon
854-
class Box(T:! type) {
855-
var ptr: T*;
856-
extend api T;
857-
}
858-
```
859-
860-
This means that lookup into `Box(T)` also looks in the type `T` for members. In
861-
this way, `b.Search` will find `b.(String.Search)`. The extended type is
862-
required to be complete at the point of the `extend api` declaration, so these
863-
lookups into `T` can succeed.
864-
865-
The general rule for resolving ambiguity for `extend`, which we apply here as
866-
well, is that if lookup into `Box(T)` succeeds, then that result is used and no
867-
lookup into `T` is performed. If a class uses `extend` more than once, and finds
868-
the same name more than once, that is an ambiguity error that needs to be
869-
resolved by qualifying the name on use.
870-
871-
> **TODO:** Are these the right rules in the context of API evolution? See
872-
> [comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files#r1527084183).
873-
874-
> **Note:** This is an alternative to defining an `alias` for each member of the
875-
> extended type. This avoids having to repeat them, which is both lengthy and
876-
> could get out of sync as the class evolves. The `extend api` approach works
877-
> even if, as in this example, we don't know the names of the members of the
878-
> type being extended.
879-
880-
Like other uses of `extend`, an `extend api` declaration do not have access
881-
control modifiers and only operate on public names.
882-
883-
> **Future:** We might want `extend api` to also get (some) interface
884-
> implementations, like `extend base` does. Or this may be provided by a
885-
> different mechanism, for example that allows customization of impl binding by
886-
> implementing an interface. See
887-
> [this comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files#r1527083149)
888-
> and
889-
> [2024-03-18 open discussion](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.23p8d6pqg1qp).
890-
891-
#### Binding to the members of another type
892-
893-
For the second ingredient, the binding interfaces already provide everything
894-
needed, we just need to make a parameterized implementation of them:
895-
896-
```carbon
897-
impl forall [T:! type, U:! ValueBind(T)]
898-
U as ValueBind(Box(T)) where .Result = U.Result {
899-
fn Op[self: Self](x: Box(T)) -> Result {
900-
// Uses `ValueBind(T).Op` based on type of `U`.
901-
return self.Op(*x.ptr);
902-
903-
// NOT `return x.ptr->(self)` since that attempts
904-
// to do reference binding not value binding.
905-
}
906-
}
907-
908-
impl forall [T:! type, U:! RefBind(T)]
909-
U as RefBind(Box(T)) where .Result = U.Result {
910-
fn Op[self: Self](p: Box(T)*) -> Result* {
911-
return self.Op(p->ptr);
912-
// Or equivalently:
913-
// return p->ptr->(self);
914-
}
915-
}
916-
```
917-
918-
A few observations:
919-
920-
- These declarations use `Box` to satisfy the orphan rule, and so this
921-
approach only works with `Box(T)` values, not types that can implicitly
922-
convert to `Box(T)`.
923-
- The implementation of the `Op` method is where we follow the pointer member
924-
`ptr` of `Box(T)` to get a `T` value that is compatible with the member
925-
being bound. This resolves the type mismatch that is introduced by allowing
926-
name resolution to find another type's members.
927-
- We have to be a little careful in the implementation of `ValueBind(Box(T))`
928-
to still use value binding even when we get a reference expression from
929-
dereferencing the pointer `ptr`.
930-
931-
With these two ingredients, `b.Search(32)` is equivalent to
932-
`b.(String.Search)(32)`, which is then equivalent to
933-
`b.ptr->(String.Search)(32)` or `b.ptr->Search(32)` (since `b.ptr` has type
934-
`String*`).
935-
936-
### Other uses of `extend api`
937-
938-
> TODO: Give the example of reference library type from
939-
> [this comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files#r1513712572).
940-
941-
> TODO: Show how to model other language constructs with `extend api`, custom
942-
> binding, and implicit (or explicit) conversions, as described in
943-
> [this comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files#r1527089564):
944-
>
945-
> - other uses of `extend`,
946-
> - inheritance,
947-
> - virtual dispatch, and
948-
> - mixins.
949-
950-
The [`extend api` mechanism](#extend-api), allowing lookup to find names from
951-
another type, has other uses beyond [member forwarding](#member-forwarding).
952-
953-
A class could extend the API of a class it implicitly converts to, see
954-
[inheritance and other implicit conversions](#inheritance-and-other-implicit-conversions).
955-
For example, imagine we have a class representing an integer in a restricted
956-
range that can implicitly convert to an integer value.
957-
958-
```carbon
959-
class InRange(Low:! i32, High:! i32) {
960-
var value: i32;
961-
impl as ImplicitAs(i32) {
962-
fn Convert[self: Self]() -> i32 { return self.value; }
963-
}
964-
extend api i32;
965-
}
966-
```
967-
968-
By including `extend api i32`, `InRange` gains support for any non-`addr`
969-
methods on `i32`, like perhaps `Abs`.
970-
971-
Or a class could have members specifically intended for use by another class, as
972-
[extension methods](https://github.com/carbon-language/carbon-lang/issues/1345)
973-
-- effectively acting as mixin except it can't add member variables.
974-
975-
The examples so far use `extend api` between two classes, but we also allow it
976-
with interfaces and named constraints.
977-
978-
For example, a class can `extend api` of an interface it
979-
[conditionally conforms to](/docs/design/generics/details.md#conditional-conformance),
980-
as in:
981-
982-
```carbon
983-
interface A {
984-
fn F();
985-
}
986-
class C(T:! type) {
987-
extend api A;
988-
}
989-
impl C(i32) as A { fn F() { ... } }
990-
```
991-
992-
Here, the class `C(T)` implements `A` for some values of `T`, such as `i32`.
993-
Rather than manually individually aliasing the members of `A` in `C`, to make
994-
them available as part of the API of `C`, so that `C(i32).F()` is valid, an
995-
`extend api` declaration includes all of them at once.
996-
997-
Another use case is that an interface can `extend api` of another interface. In
998-
this example,
999-
1000-
```carbon
1001-
interface A {
1002-
fn F();
1003-
}
1004-
interface B {
1005-
extend api A;
1006-
fn G();
1007-
}
1008-
```
1009-
1010-
`B.F` would be an alias for `A.F`, but without any implied
1011-
`require Self impls A`, in contrast with a plain `extend A`. So `extend I;` is
1012-
equivalent to `require Self impls I; extend api I;`.
1013-
1014-
Lastly, an interface could `extend api` a class. This could be done to add
1015-
something that acts like `final` functions to the interface, using
1016-
[extension methods](https://github.com/carbon-language/carbon-lang/issues/1345),
1017-
as in:
1018-
1019-
```carbon
1020-
class Helpers {
1021-
fn F[T:! type, self: T]() { DoStuffWith(self); }
1022-
// ...
1023-
}
1024-
interface Iface {
1025-
extend api Helpers;
1026-
fn G();
1027-
// ...
1028-
}
1029-
class C {
1030-
extend impl as Iface;
1031-
}
1032-
fn Test(c: C) {
1033-
// Calls Helpers.F from extended class Helpers
1034-
c.F();
1035-
}
1036-
```
1037-
1038-
Unlike `final` functions in an interface, this approach defines all the helpers
1039-
in a separate entity that could be used to extend more than one type.
1040-
1041757
### C++ operator overloading
1042758
1043-
C++ does not support a feature like `extend api` nor does it support customizing
1044-
the behavior of `x.y`. It does support customizing the behavior of `operator*`
1045-
and `operator->` which is frequently used to support smart pointers and
1046-
iterators. There is, however, nothing restricting the implementations of those
1047-
two operators to be consistent, so that `(*x).y` and `x->y` are the same.
759+
C++ does not support customizing the behavior of `x.y`. It does support
760+
customizing the behavior of `operator*` and `operator->` which is frequently
761+
used to support smart pointers and iterators. There is, however, nothing
762+
restricting the implementations of those two operators to be consistent, so that
763+
`(*x).y` and `x->y` are the same.
1048764
1049765
Carbon instead will only have a single interface for customizing dereference,
1050766
corresponding to `operator*` not `operator->`. All uses of `x->y` will be
@@ -1263,28 +979,3 @@ types, not all compile-time values. The
1263979
was that types are special because their members are defined by their values,
1264980
not by their type
1265981
([which is always `type`](https://github.com/carbon-language/carbon-lang/pull/2360)).
1266-
1267-
### Allow `extend api` of an incomplete type
1268-
1269-
We considered allowing `extend api T` with `T` an incomplete type. This has some
1270-
disadvantages:
1271-
1272-
- `T` must be complete before the first time name lookup into it is performed.
1273-
Actually avoiding name lookup into `T` is very difficult, though, since you
1274-
need to be very careful to fully qualify (using `package.`) names used in
1275-
the class definition, except those names defined within the class itself.
1276-
- Other uses of `extend`, such as `extend base: T` require the type to be
1277-
complete.
1278-
- We did not have a use case for using `extend api` with an incomplete type.
1279-
- Requiring complete types forces types to be defined in a total order,
1280-
preventing cycles (`A` extends `B` extends `C` extends `A`).
1281-
1282-
This was discussed in the
1283-
[proposal review](https://github.com/carbon-language/carbon-lang/pull/3720/files#r1507988547)
1284-
and
1285-
[the #typesystem channel on Discord](https://discord.com/channels/655572317891461132/708431657849585705/1217499991329738852).
1286-
1287-
### Make some name resolutions cases unambiguous with `extend api`
1288-
1289-
TODO: See
1290-
[comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files#r1513671030)

0 commit comments

Comments
 (0)