diff --git a/proposals/p3802.md b/proposals/p3802.md new file mode 100644 index 0000000000000..0a3ecef450114 --- /dev/null +++ b/proposals/p3802.md @@ -0,0 +1,1251 @@ +# `extend api` + + + +[Pull request](https://github.com/carbon-language/carbon-lang/pull/3802) + + + +## Table of contents + +- [Abstract](#abstract) +- [Problem](#problem) +- [Background](#background) +- [Proposal](#proposal) +- [Details](#details) + - [Use case: Member forwarding](#use-case-member-forwarding) + - [Use case: with implicit conversion](#use-case-with-implicit-conversion) + - [Use case: extension methods](#use-case-extension-methods) + - [`extend api` with interfaces](#extend-api-with-interfaces) + - [Modeling other language constructs](#modeling-other-language-constructs) + - [Modeling `extend impl`](#modeling-extend-impl) + - [Modeling `extend base`](#modeling-extend-base) + - [Modeling virtual dispatch](#modeling-virtual-dispatch) + - [Modeling `extend adapt`](#modeling-extend-adapt) + - [Modeling `extend` in interfaces and named constraints](#modeling-extend-in-interfaces-and-named-constraints) + - [Future work: modeling mixins](#future-work-modeling-mixins) + - [Future work: customizing impl binding](#future-work-customizing-impl-binding) + - [Future work: Reference library type](#future-work-reference-library-type) +- [Rationale](#rationale) +- [Alternatives considered](#alternatives-considered) + - [Allow `extend api` of an incomplete type](#allow-extend-api-of-an-incomplete-type) + - [Make some name resolutions cases unambiguous with `extend api`](#make-some-name-resolutions-cases-unambiguous-with-extend-api) + + + +## Abstract + +Allow types to `extend api` other types, adding the names from the other type to +its namespace, for forwarding and delegation use cases. + +## Problem + +Carbon has a number of constructs using the `extend` keyword that allow a type +to extend its API by the API of another type in various ways. For example, a +class can use `extend base` to inherit from a base class, or an interface can +`extend` another interface. One component of this is including the names of the +extended type in the extending type. + +This capability is useful on its own, particularly when combined with the +ability customize how binding works for a type, as introduced in +[proposal #3720](https://github.com/carbon-language/carbon-lang/pull/3720), for +example [member forwarding](#use-case-member-forwarding). + +It also allows most of the current `extend` constructs to be expressed as +[a rewrite into more primitive language feature](#modeling-other-language-constructs). +This is a useful strategy in general: + +- It gives a concrete way of explaining the language semantics. +- It means that the rules governing different language constructs are + consistent, which helps user learning and understanding, and allows the + implementation to be smaller due to reuse of logic. +- It gives users additional expressive power through the unbundling of + capabilities the user might want to use individually, and the ability to + combine the primitives in new ways. + +**Non-goal:** This proposal does not address how one type can inherit the +_interface implementations_ of another type. This occurs with `extend adapt` and +is planned with `extend base`. Controlling that capability is +[future work](#future-work-customizing-impl-binding). + +## Background + +This proposal builds upon a number of previous proposals: + +- [Proposal #553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) + introduced interface extension and a way for classes to include the API of + implemented interfaces. +- [Proposal #731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731) + defined extending adapters. +- [#777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777) + defined how a class could extend a base class. +- [Proposal 2760: Consistent `class` and `interface` syntax](https://github.com/carbon-language/carbon-lang/pull/2760) + changed the syntax to consistently use the `extend` keyword for any language + construct that involves delegating name lookup to another type. +- [Proposal #3720: Member binding operators](https://github.com/carbon-language/carbon-lang/pull/3720) + introduced customization of how member binding works, as in `x.y` or + `x.(y)`. This proposal was originally part of #3720, but was split out. + +## Proposal + +We propose adding a declaration in a type definition to find names defined in +another type, [`extend api T`](#extend-api): + +```carbon +class Extended { + fn F[self: Self](); + fn G[self: Self](); +} + +class Extending { + fn G[self: Self](); + extend api Extended; + impl as ImplicitAs(Extended); +} + +var e: Extending; +``` + +This means that lookup into `Extending` also looks in the type `Extended` for +members. In this way, `e.F` will find `e.(Extended.F)`. Note that does not make +`e.F()` actually work unless `Extended.F` was somehow legal to call on an value +of type `Extending`. In the example, this is accomplished by defining an +implicit conversion from `Extending` to `Extended`. + +Type definitions here include `class`, `interface`, and `constraint` +definitions. + +The lookup rules for `extend api` are consistent with the other uses of +`extend`, with conflicts handled as determined by leads issues +[#2355](https://github.com/carbon-language/carbon-lang/issues/2355) and +[#2745](https://github.com/carbon-language/carbon-lang/issues/2745). This means +`Extending` is equivalent to: + +```carbon +class Extending { + fn G[self: Self](); + alias F = Extended.F; + impl as ImplicitAs(Extended); +} +``` + +The extended type is required to be complete at the point of the `extend api` +declaration, so the lookups that will be performed in the the containing class +definition into the extended type can succeed. + +## Details + +Normally simple member access on a value +[looks in the type of that value](/docs/design/expressions/member_access.md#values). +However, classes can designate other entities to also look up in using the +`extend` keyword (see [proposal #2760](/proposals/p2760.md#proposal)): + +- `extend impl as I` adds lookup into the implementation of an interface `I`. +- `extend base: B` adds lookup into the base class `B`. +- `extend adapt C` adds lookup into the adapted class `C`. +- [mixins are also planning](/proposals/p2760.md#future-work-mixins) on using + an `extend` syntax + +Interfaces and named constraints also +[use the `extend` keyword](/docs/design/generics/details.md#interface-extension) +to indicate that lookups should be performed in another constraint, and to add a +requirement that `Self` implements the other constraint. + +For some use cases, we want to be able to add to name lookup into a type without +making other changes to the type, for which we introduce `extend api`, with +consistent lookup rules as the other uses of `extend`, with conflicts handled as +determined by leads issues +[#2355](https://github.com/carbon-language/carbon-lang/issues/2355) and +[#2745](https://github.com/carbon-language/carbon-lang/issues/2745). + +The extended type is required to be complete at the point of the `extend api` +declaration, so these lookups into `T` can succeed. + +The general rule for resolving ambiguity for `extend`, which we apply here as +well, is that if lookup into the extended type (`Box(T)` in +[the member forwarding example](#use-case-member-forwarding)) succeeds, then +that result is used and no lookup into the extending type (`T` in +[the member forwarding example](#use-case-member-forwarding)) is performed. If a +class uses `extend` more than once, and finds the same name more than once as a +result, that is an ambiguity error that needs to be resolved by qualifying the +name on use. + +> **Future work:** Are these the right rules in the context of API evolution? +> See +> [comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files/97004ac6748a4f39e664d7409401df1811adc25c#r1527084183). + +> **Note:** This is an alternative to defining an `alias` for each member of the +> extended type. This avoids having to repeat them, which is both lengthy and +> could get out of sync as the class evolves. The `extend api` approach works +> even if, as in this example, we don't know the names of the members of the +> type being extended. + +Like other uses of `extend`, an `extend api` declaration does not have access +control modifiers and only operates on public names. + +### Use case: Member forwarding + +Consider a class that we want to act like it has the members of another type. +For example, a type `Box(T)` that has a pointer to a `T` object allocated on the +heap: + +```carbon +class Box(T:! type) { + var ptr: T*; + // ??? +} +``` + +`Box(T)` should act like it has all the members of `T`: + +```carbon +class String { + fn Search[self: Self](c: u8) -> i32; +} +var b: Box(String) = ...; +var position: i32 = b.Search(32); +``` + +There are two ingredients to make this work: + +- We need some way to make `b.Search` be equivalent to `b.(String.Search)` not + `b.(Box(String).Search)`. +- We need the act of binding a method of `String` to a `Box(String)` value to + work by dereferencing the pointer `b.ptr`. + +For the first ingredient, we use `extend api` to customize +[simple member access](/docs/design/expressions/member_access.md) for the class +`Box(T)`: + +```carbon +class Box(T:! type) { + var ptr: T*; + extend api T; +} +``` + +This means that lookup into `Box(T)` also looks in the type `T` for members. In +this way, `b.Search` will find `b.(String.Search)`. + +For the second ingredient, the binding interfaces from +[proposal #3720: "Member binding operators"](https://github.com/carbon-language/carbon-lang/pull/3720) +already provide everything needed, we just need to make a parameterized +implementation of them: + +```carbon +impl forall [T:! type, U:! BindToValue(T)] + U as BindToValue(Box(T)) where .Result = U.Result { + fn Op[self: Self](x: Box(T)) -> Result { + // Uses `BindToValue(T).Op` based on type of `U`. + return self.Op(*x.ptr); + + // NOT `return x.ptr->(self)` since that attempts + // to do reference binding not value binding. + } +} + +impl forall [T:! type, U:! BindToRef(T)] + U as BindToRef(Box(T)) where .Result = U.Result { + fn Op[self: Self](p: Box(T)*) -> Result* { + return self.Op(p->ptr); + // Or equivalently: + // return p->ptr->(self); + } +} +``` + +A few observations: + +- These declarations use `Box` to satisfy the orphan rule, and so this + approach only works with `Box(T)` values, not types that can implicitly + convert to `Box(T)`. +- The implementation of the `Op` method is where we follow the pointer member + `ptr` of `Box(T)` to get a `T` value that is compatible with the member + being bound. This resolves the type mismatch that is introduced by allowing + name resolution to find another type's members. +- We have to be a little careful in the implementation of + `BindToValue(Box(T))` to still use value binding even when we get a + reference expression from dereferencing the pointer `ptr`. + +With these two ingredients, `b.Search(32)` is equivalent to +`b.(String.Search)(32)` due to `extend api`, which then uses the custom binding +implementation to get something equivalent to `b.ptr->(String.Search)(32)`, +which is the same as `b.ptr->Search(32)` since `b.ptr` has type `String*`. + +### Use case: with implicit conversion + +A class could extend the API of a class it implicitly converts to. For example, +imagine we have a class representing an integer in a restricted range that can +implicitly convert to an integer value, see +[inheritance and other implicit conversions from proposal #3720](p3720.md#inheritance-and-other-implicit-conversions). + +```carbon +class I32InRange(Low:! i32, High:! i32) { + var value: i32; + impl as ImplicitAs(i32) { + fn Convert[self: Self]() -> i32 { return self.value; } + } + extend api i32; +} +``` + +By including `extend api i32`, `I32InRange` gains support for any non-`addr` +methods on `i32`, like perhaps `Abs`. The method `i32.Abs` has a `self` type of +`i32`, which an `I32InRange` value can implicitly convert to. + +### Use case: extension methods + +A class could have members specifically intended for use by another class, as +[extension methods](https://github.com/carbon-language/carbon-lang/issues/1345) +-- effectively acting as mixin except it can't add member variables. + +```carbon +class ThreeExtension { + fn Three() -> i32 { + return 3; + } +} + +class EmptyExtension(template T:! type) { + fn Empty[self: T]() -> bool { + return self.Size() == 0; + } +} + +class RangeOfI32 { + var low: i32; + var high: i32; + + // Adds `Three` class function + extend api ThreeExtension; + // Roughly equivalent to: + // `alias Three = ThreeExtension.Three;` + + fn Size[self: Self]() -> i32 { + return self.high - self.low; + } + + // Adds an `Empty` method. + extend api EmptyExtension(RangeOfI32); +} + +// Because of `extend api`, `RangeOfI32.Three` is equivalent to +// `ThreeExtension.Three`. +Assert(RangeOfI32.Three() == 3); + +var r: RangeOfI32 = {.low = 2, .high = 5}; +// Because of `extend api`, `r.Empty` is equivalent to +// `r.(EmptyExtension(RangeOfI32).Empty)` +Assert(r.Empty() == false); +``` + +### `extend api` with interfaces + +The examples so far use `extend api` between two classes, but we also allow it +with interfaces and named constraints. + +For example, a class can `extend api` of an interface it +[conditionally conforms to](/docs/design/generics/details.md#conditional-conformance), +as in: + +```carbon +interface A { + fn F(); +} +class C(T:! type) { + extend api A; +} +impl C(i32) as A { fn F() { ... } } +``` + +Here, the class `C(T)` implements `A` for some values of `T`, such as `i32`. +Rather than manually individually aliasing the members of `A` in `C`, to make +them available as part of the API of `C`, so that `C(i32).F()` is valid, an +`extend api` declaration includes all of them at once. + +Another use case is that an interface can `extend api` of another interface. In +this example, + +```carbon +interface A { + fn F(); +} +interface B { + extend api A; + fn G(); +} +``` + +`B.F` would be an alias for `A.F`, but without any implied +`require Self impls A`, +[in contrast with a plain `extend A`](#modeling-extend-in-interfaces-and-named-constraints). + +Lastly, an interface could `extend api` a class. This could be done to add +something that acts like `final` functions to the interface, using +[extension methods](https://github.com/carbon-language/carbon-lang/issues/1345), +as [can be done with classes](#use-case-extension-methods): + +```carbon +class Helpers { + fn F[T:! type, self: T]() { DoStuffWith(self); } + // ... +} +interface Iface { + extend api Helpers; + fn G(); + // ... +} +class C { + extend impl as Iface; +} +fn Test(c: C) { + // Calls Helpers.F from extended class Helpers + c.F(); +} +``` + +This is approximately equivalent to using an interface with `final` functions: + +```carbon +interface Helpers { + final fn F[self: Self]() { DoStuffWith(self); } + // ... +} +interface Iface { + extend Helpers; + fn G(); + // ... +} +``` + +This relies on the fact that implementing `Iface` will also implement extended +interfaces like `Helpers`. + +### Modeling other language constructs + +[This comment on proposal #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files/97004ac6748a4f39e664d7409401df1811adc25c#r1527089564) +observed that `extend api` could be used to model other language constructs +using `extend`. + +#### Modeling `extend impl` + +```carbon +interface I { ... } +class C { + extend impl as I; +} +``` + +is equivalent to: + +```carbon +interface I { ... } +class C { + impl as I; + extend api I; +} +``` + +#### Modeling `extend base` + +[Class inheritance](/docs/design/classes.md#inheritance) can be modelled using +`extend api`, outside of planned +[interface implementation inheritance](#future-work-customizing-impl-binding). +This is done by the extending class: + +- using `extend api` of the base class; +- having a data member holding the base class; +- defining + [implicit conversion to the base class](#use-case-with-implicit-conversion); +- [modeling virtual dispatch](#modeling-virtual-dispatch). + +In this example, + +```carbon +base class B { + var i: i32; + + fn Get[self: Self]() -> i32 { + return self.i; + } + fn Set[addr self: Self*](x: i32) { + self->i = x; + } +} + +class D { + extend base B; + var increment_by: i32; + + fn Make(init: i32) -> D { + return {.base = {.i = init}, .increment_by = 1}; + } + + fn Increment[addr self: Self*]() { + self->Set(self->i + self->increment_by); + } +} + +var d: D = D.Make(2); +``` + +The class `D` could be replaced by: + +```carbon +class D { + extend api B; + var __base: B; + var increment_by: i32; + + fn Make(init: i32) -> D { + return {.__base = {.i = init}, .increment_by = 1}; + } + + impl as ImplicitAs(B) { + fn Convert[self: Self]() -> B { return self.__base; } + } + impl D* as ImplicitAs(B*) { + fn Convert[self: D*]() -> B* { return &self->__base; } + } + + fn Increment[addr self: Self*]() { + // Unchanged + self->Set(self->i + self->increment_by); + + // This finds the names `Set` and `i` from `B` using + // `extend api`. Those names are usable due to the + // implicit conversions defined above. + } +} +``` + +Note that +[implicit conversions are sufficient for member binding to work, no additional member binding implementations are needed](p3720.md#inheritance-and-other-implicit-conversions). + +**Future work:** Currently the Carbon design doesn't include base-to-derived +conversions. When they are added, it would be good to also have a way to model +them for classes that use `extend api` instead of `extend base`. + +##### Modeling virtual dispatch + +To support virtual dispatch, virtual functions need to be collected into their +own [vtable](https://en.wikipedia.org/wiki/Virtual_method_table) class and a +vtable pointer stored. For example, + +```carbon +base class VB { + var i: i32; + virtual fn F[self: Self]() -> i32 { return 0; } + fn Make(init: i32) -> partial VB { + return {.i = init}; + } +} + +class VD { + extend base VB; + impl fn F[self: Self]() -> i32 { return self.G(); } + virtual fn G[self: Self]() -> i32 { return 1; } + fn Make() -> VD { + return {.base = VB.Make()}; + } +} +``` + +Could be modeled without virtual functions, assuming we have a couple of other +features: + +- A way to represent a function pointer (written `fnty` below) +- A way to perform an unsafe cast (written `UnsafeCast` below) + +```carbon +class VB; + +// Type of `VB`'s vtable +base class __VBVTableType { + // Placeholder syntax for a function pointer + var F: fnty (__self: VB) -> i32; +} + +// Represents `partial VB` +base class __partial_VB { + var __vptr: __VBVTableType*; + var i: i32; + extend api __VBVTableType; + fn Make(init: i32) -> __partial_VB { + return {.__vptr = UnsafeCast(0, __VBVTableType*), + .i = init}; + } +} + +base class VB { + extend base __partial_VB; +} + +// Represents the specific vtable of `VB` +var __VBVTable: __VBVTableType = + {.F = fn(__self: VB) -> i32 { return 0; }}; + +// Converting a `partial VB` value to `VB` sets +// the vptr to the specific `VB` vtable +impl __partial_VB as ImplicitAs(VB) { + fn Convert[self: __partial_VB]() -> VB { + return {.__vptr = &__VBVTable, .i = self.i}; + } +} + +// Represents the binding of a `VB` value or object +// with `__VBVTableType.F`. +class __VB_F { + adapt VB; + impl as Call(()) where .Result = i32 { + fn Op[self: Self](_: ()) -> i32 { + return (self as VB).__vptr->F(self as VB); + } + } +} + +// `__VBVTableType.F` may be bound to any type that +// can implicitly convert to VB. +impl forall [T:! ImplicitAs(VB)] + __VBVTableType.F as BindToValue(T) + where .Result = __VB_F; + fn Op[self: Self](x: T) -> Result { + return (x as VB) as __VB_F; + } +} +impl forall [T:! type where .Self* impls ImplicitAs(VB*)] + __VBVTableType.F as BindToRef(T) + where .Result = __VB_F; + fn Op[self: Self](p: T*) -> Result* { + return (p as VB*) as __VB_F*; + } +} + +class VD; + +// Represents the type of `VD`'s vtable. +// This can be final since `VD` is final. +class __VDVTableType { + extend base __VBVTableType; + var G: fnty (__self: VD) -> i32; +} + +// No need to support a separate `partial VD` +// since `VD` is final. +class VD { + extend base VB; + extend api __VDVTableType; + fn Make() -> VD; +} + +// Represents the specific vtable of `VD`. +var __VDVTable: __VDVTableType = + {.F = fn(__self: VB) -> i32 { + // Cast `__self` to `VD` is safe due to + // this implementation only used on `VD` + // instances due to virtual dispatch. + return UnsafeCast(__self, VD).G(); + }, + .G = fn(__self: VD) -> i32 { return 1; } + }; + +// Constructing a `VD` value or object sets +// the vptr to point at `VD`'s vtable. +fn VD.Make() -> VD { + var __base: __partial_VB = VB.Make(); + __base.__vptr = &__VDVTable; + return {.base = __base}; +} + +// Represents the binding of a `VD` value or object +// with `__VDVTableType.G`. Like `__VB_F` above, +// except we need to cast the `__vptr` member. +class __VD_G { + adapt VD; + impl as Call(()) where .Result = i32 { + fn Op[self: Self](_: ()) -> i32 { + let vptr: auto = + UnsafeCast((self as VD).__vptr, __VDVTableType*); + return vptr->G(self as VD); + } + } +} + +// `__VDVTableType.G` may be bound to any type that can +// implicitly convert to `VD`, just like `F` above. +impl forall [T:! ImplicitAs(VD)] + __VDVTableType.G as BindToValue(T) + where .Result = __VD_G; + fn Op[self: Self](x: T) -> Result { + return (x as VD) as __VD_G; + } +} +impl forall [T:! type where .Self* impls ImplicitAs(VD*)] + __VDVTableType.G as BindToRef(T) + where .Result = __VD_G; + fn Op[self: Self](p: T*) -> Result* { + return (p as VD*) as __VD_G*; + } +} +``` + +And then separately, the `extend base` could be removed, +[as before](#modeling-extend-base). + +> **Note:** This ignores the specifics of the ABI. In particular, Carbon may +> match C++'s ABI, which includes dynamic `std::type_info` and additional data +> to support multiple inheritance, to ease interop. For this reason, the +> specifics of how `extend base` is desugared is not specified, and is not +> something users can rely on. + +The above is quite involved, but shows that in principle, once various features +of Carbon are filled in, that inheritance and dynamic dispatch could be +implemented from scratch to allow the details to be customized. Even without +that, this proposal does make the name lookup consistent across all uses of +`extend`. That is, `extend base` works like `extend api` plus a hypothetical +`base` feature without `extend` that worked like `extend base` but didn't +introduce any names into the class' scope. + +> **Note:** This desugaring suggests that name lookup finds virtual functions +> through an `extend`. +> [Leads issue #2355: Name conflicts between base and derived classes](https://github.com/carbon-language/carbon-lang/issues/2355) +> specifies that name conflicts with virtual methods are forbidden, rather than +> the `extend` behavior being discussed in +> [leads issue #2745: Name conflicts beyond inheritance](https://github.com/carbon-language/carbon-lang/issues/2745). +> We include this additional rule about name conflicts from #2355, observing +> that it is stricter than the `extend` rules being considered, but otherwise +> compatible. + +#### Modeling `extend adapt` + +First, let us consider `adapt` without `extend`. For example, + +```carbon +class A { ... } +class B { + adapt A; +} +``` + +is equivalent to: + +```carbon +class A { ... } +class B { + var __a: A; +} +impl B as As(A) { + fn Convert[self: B]() -> A { + return self.__a; + } +} +impl A as As(B) { + fn Convert[self: A]() -> B { + return {.__a = self} as B; + } +} +impl forall [T:! BindToValue(A)] + T as BindToValue(B) where .Result = T.Result { + fn Op[self: Self](x: B) -> Result { + return self.Op(x as A); + } +} +impl forall [T:! BindToRef(A)] + T as BindToRef(B) where .Result = T.Result { + fn Op[self: Self](p: B*) -> Result* { + return self.Op(&p->__a); + } +} +``` + +In addition, there are `as` conversions that convert reference expressions to +reference expressions, which is currently not expressible by implementing +interfaces. + +Note that the blanket implementations of the binding operator interfaces allow +methods of `A` to be used on values and objects of type `B`, as in: + +```carbon +class A { + fn F[self: Self](x: Self) -> Self; + fn G[self: Self](y: Self) -> Self; +} + +class B { + adapt A; + alias AG = A.G; +} + +var a: A = {}; +var b_ref: B = {}; +let b_val: B = {}; + +// Allowed: binds `b_val` to `A.F`. Since `b_val` is a value expression, +// it uses `A.F as BindToValue(B)`, so this is equivalent to +// `(b_val as A).(A.F)(a)`. +b_val.(A.F)(a); + +// Allowed: binds `b_ref` to `A.F`. Since `b_ref` is a reference +// expression, it uses `A.F as BindToRef(B)`, so this is equivalent to: +// `(&((&b_ref)->__a))->(A.F)(a)`. +b_ref.(A.F)(a); + +// These also work, and show how aliases can be used to add methods of +// `A` to `B`. +b_val.AG(a); +b_ref.AG(a); +``` + +Notice how custom binding implementation adapts the type of `self`, but not any +other `Self` type used in the signature. So `b_ref.(A.F)` is a function that +takes and returns `A` values, not `B` values. + +Now consider adding the `extend` modifier before `adapt`, as in: + +```carbon +class A { + fn F[self: Self](x: Self) -> Self; + fn G[self: Self](y: Self) -> Self; +} + +class B { + extend adapt A; +} +``` + +The `extend` does two things: + +- It adds `extend api A;` to class `B`. Due to the custom binding + implementation provided by `adapt A`, this allows calling the methods + defined on `A` on values and objects of type `B`. + +- For an interface `I`, when no implementation `B as I` is found, it will + attempt to adapt the implementation `A as I`, as described in + [the existing "extending adapter" section of the design](/docs/design/generics/details.md#extending-adapter). + Note though that the the `Self` types in signatures in members of `I` + **are** changed to `B`, as required to be an implementation of `B as I`, + though this is + [not always possible](/docs/design/generics/details.md#adapter-compatibility). + Allowing the user to do this directly is + [future work](#future-work-customizing-impl-binding). + +#### Modeling `extend` in interfaces and named constraints + +The `extend` declaration in interfaces and named constraints is equivalent to a +requirement that the extended type is implemented plus an `extend api` to add +the names of the extended type to the extending type. For example, + +```carbon +interface A { ... } +interface B { + extend A; +} +``` + +is equivalent to: + +```carbon +interface A { ... } +interface B { + require Self impls A; + extend api A; +} +``` + +#### Future work: modeling mixins + +Mixins are planned feature of Carbon. +[Leads issue #995: "Generics external impl versus extends"](https://github.com/carbon-language/carbon-lang/issues/995) +and +[proposal #2760: "Consistent `class` and `interface` syntax](p2760.md#future-work-mixins) +give a syntax for including a mixin in a class using the `extend` keyword. The +declaration `extend m: M;` includes a mixin of type `M` in the class, using the +name `m` to reference it. The +["Mixin vision" document](https://docs.google.com/document/d/1ewVySLnOniph4UJR30TrCsrPhNkWOCR8kgj_F_KJiA8/edit?tab=t.0) +describes the thinking about mixins more comprehensively as of Q1 2023, and has +more background material. + +Example: + +```carbon +interface I { + fn F[self: Self]() -> i32; +} + +mixin Mix for I { + inject fn G[self: Self]() -> i32{ + return self.F() + self.plus - self.minus; + } + + fn Make(p: i32, m: i32) -> Mix { + return {.plus = p, .minus = m}; + } + + inject var plus: i32; + private var minus: i32; +} + +class C { + impl as I { + fn F[self: Self]() -> i32 { return 1; } + } + extend m: Mix = Mix.Make(); +} +``` + +could be roughly represented by: + +```carbon +// Unchanged +interface I { + fn F[self: Self]() -> i32; +} + +// Represents a type that has a mixin of type `Mix`. +interface HasMix; + +// Represents the mixin `Mix` when mixed into type `__Self`. +class Mix(__Self:! I & HasMix(.Self)); + +// A type that has a mixin of type `Mix` can turn a +// `Self` value/object into a `Mix(Self)` value/object. +interface HasMix { + require Self impls I; + fn GetMixinValue[self: Self]() -> Mix(Self); + fn GetMixinRef[addr self: Self*]() -> Mix(Self)*; +} + +class Mix(__Self:! I & HasMix) { + fn G[self: __Self]() -> i32 { + return self.F() + + self.GetMixinValue().plus + - self.GetMixinValue().minus; + } + + fn Make(p: i32, m: i32) -> Self { + return {.plus = p, .minus = m}; + } + + var plus: i32; + private var minus: i32; +} + +class __Mix_Injected(__Self:! I & HasMix) { + alias G = Mix(__Self).G; + alias plus = Mix(__Self).plus; + // These might not be able to be aliases, instead: + // let template G:! auto = Mix(__Self).G; + // let template plus:! auto = Mix(__Self).plus; +} + +// Allows classes that mixin `Mix` to bind to `plus`. +impl forall [__Self:! I & HasMix] + Mix(__Self).plus as BindToValue(__Self) where .Result = i32 { + fn Op[self: Self](x: __Self) -> Result { + return x.GetMixinValue().plus; + } +} +impl forall [__Self:! I & HasMix] + Mix(__Self).plus as BindToRef(__Self) where .Result = i32 { + fn Op[self: Self](p: __Self*) -> Result* { + return &p->GetMixinRef()->plus; + } +} + +// Classes that mixin `Mix` are translated into classes with a +// `Mix(Self)` field, implement the `HasMix` interface, and +// include the names from `__Mix_Injected(Self)` using +// `extend api`. +class C { + impl as I { + fn F[self: Self]() -> i32 { return 1; } + } + var m: Mix(Self) = Mix(Self).Make(); + impl as HasMix { + fn GetMixinValue[self: Self]() -> Mix(Self) { + return self.m; + } + fn GetMixinRef[addr self: Self*]() -> Mix(Self)* { + return &self->m; + } + } + extend api __Mix_Injected(Self); +} +``` + +> **Note:** If a class uses the same mixin twice, it will need to use distinct +> adapters for each. The adapters will have implementations of `HasMix` that +> return the corresponding mixin member. For example: +> +> ```carbon +> class C { +> impl as I; +> extend m1: Mix; +> extend m2: Mix; +> } +> ``` +> +> would be translated to: +> +> ```carbon +> class __C_m1; +> class __C_m2; +> class C { +> impl as I; +> var m1: Mix(__C_m1); +> var m2: Mix(__C_m2); +> } +> class __C_m1 { +> adapt C; +> impl as HasMix { +> fn GetMixinValue[self: Self]() -> Mix(Self) { +> return (self as C).m1; +> } +> fn GetMixinRef[addr self: Self*]() -> Mix(Self)* { +> return &(self as C*)->m1; +> } +> } +> } +> class __C_m2 { +> adapt C; +> impl as HasMix { +> fn GetMixinValue[self: Self]() -> Mix(Self) { +> return (self as C).m2; +> } +> fn GetMixinRef[addr self: Self*]() -> Mix(Self)* { +> return &(self as C*)->m2; +> } +> } +> } +> ``` +> +> Though observe we can't inject the same names more than once. This is +> reflected in the fact that the adapter classes `__C_m1` and `__C_m2` are what +> are implementing `HasMix`, not `C`. + +Of course this is just speculation until such time as we have an accepted mixins +proposal. + +### Future work: customizing impl binding + +We might want a mechanism to also get (some) interface implementations from an +extended type. This already occurs for `extend adapt`, from +[proposal #731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731), +and refined by +[open discussion on 2023-04-06](https://docs.google.com/document/d/1gnJBTfY81fZYvI_QXjwKk1uQHYBNHGqRLI2BS_cYYNQ/edit?resourcekey=0-ql1Q1WvTcDvhycf8LbA9DQ&tab=t.0#heading=h.x4syhdu36xdc) +and [PR #3231](https://github.com/carbon-language/carbon-lang/pull/3231). That +open discussion expressed that interface implementations would also be inherited +with `extend base`, though there are more cases where you can't use a base class +implementation for the derived class than with adapters. + +For example, the mechanism might allow customization of impl binding by +implementing an interface. See +[this comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files/97004ac6748a4f39e664d7409401df1811adc25c#r1527083149) +and +[2024-03-18 open discussion](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.23p8d6pqg1qp). +It is unclear how that would work, since the interfaces that would be +implemented by `extend base` and `extend adapt` are different, and +[the rules for conversions allowed by adapters are complex](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#adapter-compatibility). + +### Future work: Reference library type + +The `Box` type that was used as +[an example of member forwarding](#use-case-member-forwarding) preserves the +expression category -- you can only call mutating operations on a `Box` +reference expression, not on a `Box` value expression. For a library reference +type that would simulate the C++-built-in reference type, we instead want to +permit value binding for anything where `T` permits reference binding. This +could be done using `extend api` with some additional pieces: + +- We need `BindToRef` to fall back to `BindToValue`, as suggested in + [proposal #3720's future work](p3720.md#future-properties). This would allow + binding to a reference expression to produce a value, which is important in + this case since we don't have an object we can return the address of. +- We need support for a `RefCall` interface that is like `Call` but uses + `addr self`. This would handle mutation of the callable, and would be used + to implement `addr self` methods. There would be a blanket `final` + implementation of `RefCall` for any object implementing `Call`, so what + function you get would not change based on knowing more about what its type + implements (implying that there is no way to override a method solely on + category of the receiver). + +It would also use a different set of binding implementations: + +```carbon +class Ref(T:! type) { + var ptr: T*; + extend api T; +} + +match_first { + +// . produces a value expression of type `Ref(R)`. +impl forall [T:! type, U:! BindToRef(T)] + U as BindToValue(Ref(T)) where .Result = Ref(U.Result) { + fn Op[self: Self](p: Ref(T)) -> Result { + return {.ptr = self.Op(p->ptr)}; + } +} + +// . produces a value expression of type `R`. +// A value binding is performed to turn the Ref(T) into a T. +impl forall [T:! type, U:! BindToValue(T)] + U as BindToValue(Ref(T)) where .Result = U.Result { + fn Op[self: Self](p: Ref(T)) -> Result { + return self.Op(*p.ptr); + } +} + +} + +// Support `Call`s of a , if `T` supports `Call` or `RefCall`. +match_first { + +impl forall [...each U:! type, T:! RefCall(... each U)] + Ref(T) as Call(... each U) where .Result = T.Result { + fn Op[self: Self](... each args: ... each U) -> Result { + return (*self.ptr)(... each args); + } +} + +impl forall [...each U:! type, T:! Call(... each U)] + Ref(T) as Call(... each U) where .Result = T.Result { + fn Op[self: Self](... each args: ... each U) -> Result { + return (*self.ptr)(... each args); + } +} + +} +``` + +For example, it could be used as: + +```carbon +class Counter { + fn Increment[addr self: Self*](); + var count: i32 = 0; +} + +fn F(r: Ref(Counter)) { + // OK, even though `r` is a value expression. + r.Increment(); + // - `r.Increment` is `r.(Ref(Counter).Increment)`. + // - Due to the `extend api T` in `Ref(T)`, this finds + // `r.(Counter.Increment)` + // - `Counter.Increment` implements `BindToRef(Counter)` + // so it also implement `BindToValue(Ref(Counter))`. + // - The result of binding to the value `r` has type + // `Ref(Result)` where `Result` is the bound member + // function type + // `(Counter.Increment as BindToRef(Counter)).Result`. + // - `Result` implement `RefCall()`, because + // `Counter.Increment` has signature + // `[addr self: Self*]()`, so `Ref(Result)` implements + // `Call()`. + + // OK, forms a reference to the `count` member. + let count_ref: Ref(i32) = r.count; + // count_ref.ptr == &(r.ptr->count) + + count_ref += 3 as i32; + // - Rewritten to + // `count_ref.(AddAssignWith(i32).Op)(3 as i32)`. + // - `Ref(i32)` can be bound to `AddAssignWith(i32).Op` + // and the result is callable, just like with + // `Ref(Counter)` and `Counter.Increment` in the + // `r.Increment()` example previously. +} +``` + +Note, however, `Ref(T)` doesn't preserve the interface implementations of `T`, +so `Ref(i32)` doesn't implement `AddAssignWith(i32)` despite the call to its +method working. See [future work](#future-work-customizing-impl-binding). + +This application of `extend api` and binding was first described in +[this comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files/37e967ff53e89e345eecb49ec79c6dfbe18a3c54#r1513712572). +It had some problems, which were addressed in +[this comment on #3802](https://github.com/carbon-language/carbon-lang/pull/3802/files/f9b63b9bbf45a3ab1000d9be8c98fb93711e12c8#r1602305497) +and +[open discussion on 2024-05-16](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.3qlpp5e56u46). + +## Rationale + +This proposal advances these [Carbon goals](/docs/project/goals.md): + +- [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem), + by making the language more orthogonal and providing a path to rewriting + constructs into more primitive features, as described in the + ["Problem" section](#problem). +- [Software and language evolution](/docs/project/goals.md#software-and-language-evolution), + by giving additional ways to express a single API, allowing some additional + freedom to evolve an implementation without changing clients. +- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), + due to the consistency across different uses of `extend`, as described in + the ["Problem" section](#problem). + +## Alternatives considered + +### Allow `extend api` of an incomplete type + +We considered allowing `extend api T` with `T` an incomplete type. This has some +disadvantages: + +- `T` must be complete before the first time name lookup into it is performed. + Actually avoiding name lookup into `T` is very difficult, though, since you + need to be very careful to fully qualify (using `package.`) names used in + the class definition, except those names defined within the class itself. +- Other uses of `extend`, such as `extend base: T` require the type to be + complete. +- We did not have a use case for using `extend api` with an incomplete type. +- Requiring complete types forces types to be defined in a total order, + preventing cycles (`A` extends `B` extends `C` extends `A`). + +This was discussed in the +[proposal review](https://github.com/carbon-language/carbon-lang/pull/3720/files/19774c5013706e5795a7952f0c6f08a73872a036#r1507988547) +and +[the #typesystem channel on Discord](https://discord.com/channels/655572317891461132/708431657849585705/1217499991329738852). + +### Make some name resolutions cases unambiguous with `extend api` + +In +[this comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files/97004ac6748a4f39e664d7409401df1811adc25c#r1513671030), +regarding this code + +```carbon +class A { + fn F(); +} +class B { + extend api A; + // Hides A.F + fn F(); +} +class C { + extend api A; + extend api B; +} +``` + +There was a question about whether we want `C.F` to be ambiguous, or to +unambiguously name `B.F`. The idea was that class `B` intentionally hid `A.F`, +and in C++, the corresponding rule for member name lookup in an inheritance +hierarchy makes this ambiguous if `A` is a non-virtual base, but unambiguously +picks `C.F` if `A` is a virtual base. If this use of `extend api` was more like +a virtual base situation, it could motivate making `C.F` refer unambiguously to +`B.F`. + +For now, we instead decided to go with the simpler rule: + +> We look in each `extend`, and if a name is found multiple times it is an +> ambiguity error that needs qualification to disambiguate. + +In addition to being simpler, there was also the fact we can always add a more +complicated rule in the future, based on need. For now the extra complexity +doesn't seem justified given how rare conflicting multiple `extend`s is expected +to be. This specific example was not very motivating since the ambiguity is +fixed by dropping `extend api A;` from `C`, though that may not apply in other +examples.