You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -39,17 +35,14 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
39
35
-[Swap the bind interface parameters](#swap-the-bind-interface-parameters)
40
36
-[Reference binding produces a value that wraps a pointer](#reference-binding-produces-a-value-that-wraps-a-pointer)
41
37
-[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)
44
38
45
39
<!-- tocstop -->
46
40
47
41
## Abstract
48
42
49
43
Define the binding operation used to compute the result of `x.y`, `p->y`,
50
44
`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.
53
46
54
47
## Problem
55
48
@@ -157,43 +150,6 @@ by how they rewrite into the `x.(y)` form using these two rules:
157
150
the `T.(`\_\_\_`)` form.
158
151
-`x->y` and `x->(y)` are interpreted as `(*x).y` and `(*x).(y)` respectively.
159
152
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
-
197
153
## Details
198
154
199
155
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()`.
798
754
> result type. Furthermore, we want `TypeBind` implementation no matter which
799
755
> facet of the type is used in the code.
800
756
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 donot 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.
0 commit comments