Sfoglia il codice sorgente

Member binding operators (#3720)

Define the binding operation used to compute the result of `x.y`,
`p->y`, `x.(C.y)`, and `p->(C.y)` as calling a method from
user-implementable interfaces.

---------

Co-authored-by: Josh L <josh11b@users.noreply.github.com>
Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
josh11b 1 anno fa
parent
commit
679d57df78
1 ha cambiato i file con 1209 aggiunte e 0 eliminazioni
  1. 1209 0
      proposals/p3720.md

+ 1209 - 0
proposals/p3720.md

@@ -0,0 +1,1209 @@
+# Member binding operators
+
+<!--
+Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+Exceptions. See /LICENSE for license information.
+SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+-->
+
+[Pull request](https://github.com/carbon-language/carbon-lang/pull/3720)
+
+<!-- toc -->
+
+## Table of contents
+
+-   [Abstract](#abstract)
+-   [Problem](#problem)
+-   [Background](#background)
+-   [Proposal](#proposal)
+-   [Details](#details)
+    -   [Inheritance and other implicit conversions](#inheritance-and-other-implicit-conversions)
+    -   [Data fields](#data-fields)
+    -   [Generic type of a class member](#generic-type-of-a-class-member)
+        -   [Methods](#methods)
+        -   [Fields](#fields)
+    -   [C++ pointer to member](#c-pointer-to-member)
+    -   [Instance interface members](#instance-interface-members)
+    -   [Non-instance interface members](#non-instance-interface-members)
+    -   [C++ operator overloading](#c-operator-overloading)
+-   [Future work](#future-work)
+    -   [Future: tuple indexing](#future-tuple-indexing)
+    -   [Future: properties](#future-properties)
+    -   [Future: building block for language features such as API extension](#future-building-block-for-language-features-such-as-api-extension)
+-   [Rationale](#rationale)
+-   [Alternatives considered](#alternatives-considered)
+    -   [Swap the member binding interface parameters](#swap-the-member-binding-interface-parameters)
+    -   [Member binding to references produces a value that wraps a pointer](#member-binding-to-references-produces-a-value-that-wraps-a-pointer)
+    -   [Separate interface for compile-time member binding instead of type member binding](#separate-interface-for-compile-time-member-binding-instead-of-type-member-binding)
+    -   [Non-instance members are idempotent under member binding](#non-instance-members-are-idempotent-under-member-binding)
+    -   [Separate `Result` types for `BindToValue` and `BindToRef`](#separate-result-types-for-bindtovalue-and-bindtoref)
+    -   [`BindToValue` is a subtype of `BindToRef`](#bindtovalue-is-a-subtype-of-bindtoref)
+    -   [Directly rewrite all calls to interface member functions to method call intrinsics](#directly-rewrite-all-calls-to-interface-member-functions-to-method-call-intrinsics)
+
+<!-- tocstop -->
+
+## Abstract
+
+Define the member binding operation used to compute the result of `x.y`, `p->y`,
+`x.(C.y)`, and `p->(C.y)` as calling a method from user-implementable
+interfaces.
+
+## Problem
+
+What happens when member binding is performed between an object instance and a
+member of its type? We'd like to define the semantics in a way that is simple,
+orthogonal, supports the use cases from C++, allows users to express their
+intent in code in a natural and predictable way that is consistent with other
+Carbon constructs, and is consistent with Carbon's goals.
+
+Consider a class with a method and a field:
+
+```carbon
+class C {
+  fn M[self: Self]();
+  var f: i32;
+}
+var x: C = {.f = 2};
+```
+
+The expressions `C.M` and `C.f` correspond roughly to
+[C++ pointers to members](https://en.cppreference.com/w/cpp/language/pointer#Pointers_to_members).
+They may be used to access the members of `x` using Carbon's
+[compound member syntax](/docs/design/expressions/member_access.md), as in
+`x.(C.M)` or `x.(C.f)`. What is their type? Can they be passed to a function
+separately from the instance of `C` to bind with it?
+
+The expression `x.M` on the other hand doesn't have a trivial correspondence in
+C++ despite being a useful to bind a specific instance and produce a stand-alone
+callable object. We would like a model that allows `x.M` to be meaningful in a
+way that is consistent with the existing meaning of `x.f` and generalizes well
+across different kinds of methods and callables.
+
+Another issue is how we clearly delineate the `self` associated with a method
+signature as separate from the `self` of function values.
+
+## Background
+
+Member access has been specified in two proposals:
+
+-   [Proposal #989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989)
+-   [Proposal #2360: Types are values of type `type`](https://github.com/carbon-language/carbon-lang/pull/2360)
+
+The results of these proposals is recorded in the
+["qualified names and member access" design document](/docs/design/expressions/member_access.md).
+Notably, there is the process of
+[instance binding](/docs/design/expressions/member_access.md#instance-binding),
+that can convert a method into a bound method. This is described as an
+uncustomizable process, with the members of classes being non-first-class names.
+
+With
+[proposal #3646: Tuples and tuple indexing](https://github.com/carbon-language/carbon-lang/pull/3646),
+tuple indexing also uses the member-access syntax, except with numeric names for
+the fields.
+
+The currently accepted proposals for functions, most notably
+[Proposal #2875: Functions, function types, and function calls](https://github.com/carbon-language/carbon-lang/pull/2875),
+don't support all of the different function signatures for the `Call` interface.
+For example, it does not support `addr self` or explicit compile-time
+parameters. That is out of scope of this proposal, and will be addressed
+separately, and means that `addr self` methods won't be considered here. The
+difference between functions and methods, however, is in scope.
+
+Other languages, such as
+[C#](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/using-delegates)
+and [D](https://tour.dlang.org/tour/en/basics/delegates), have constructs that
+represent bound and unbound methods, such as "delegates".
+
+## Proposal
+
+We propose that Carbon defines the compound member access operator, specifically
+`x.(y)`, in terms of rewrites to invoking an interface method, like other
+operators. There are three different interfaces used, depending on whether `x`
+is a value expression, a reference expression, or a facet:
+
+```carbon
+// This determines the type of the result of member binding. It is
+// a separate interface shared by `BindToValue` and `BindToRef` to
+// ensure they produce the same result type. We don't want the
+// type of an expression to depend on the expression category
+// of the arguments.
+interface Bind(T:! type) {
+  let Result:! type;
+}
+
+// For a value expression `x` with type `T` and an expression
+// `y` of type `U`, `x.(y)` is `y.((U as BindToValue(T)).Op)(x)`
+interface BindToValue(T:! type) {
+  extend Bind(T);
+  fn Op[self: Self](x: T) -> Result;
+}
+
+// For a reference expression `x` using a member binding `var x: T`
+// and an expression `y` of type `U`, `x.(y)` is
+// `*y.((U as BindToRef(T)).Op)(&x)`
+interface BindToRef(T:! type) {
+  extend Bind(T);
+  fn Op[self: Self](p: T*) -> Result*;
+}
+
+// For a facet value, which includes all type values, `T` and
+// an expression `y` of type `U`, `T.(y)` is
+// `y.((U as BindToType(T)).Op)()`.
+interface BindToType(T:! type) {
+  let Result:! type;
+  fn Op[self: Self]() -> Result;
+}
+```
+
+> **Note:** `BindToType` is its own interface since the members of a type are
+> defined by their values, not by their type. Observe that this means that a
+> generic function might not use `BindToType` on a symbolic value that was not
+> known to be a facet, where it would use `BindToType` on the concrete value.
+
+The other member access operators -- `x.y`, `x->y`, and `x->(y)` -- are defined
+by how they rewrite into the `x.(y)` form using these two rules:
+
+-   `x.y` is interpreted using the existing
+    [member resolution rules](/docs/design/expressions/member_access.md#member-resolution).
+    For example, `x.y` is treated as `x.(T.y)` for non-type values `x` with type
+    `T`.
+    -   Simple member access of a facet `T`, as in `T.y`, is not rewritten into
+        the `T.(`\_\_\_`)` form.
+-   `x->y` and `x->(y)` are interpreted as `(*x).y` and `(*x).(y)` respectively.
+
+## Details
+
+To use instance members of a class, we need to go through the additional step of
+_member binding_. Consider a class `C`:
+
+```carbon
+class C {
+  fn F[self: Self]() -> i32 { return self.x + 5; }
+  fn Static() -> i32 { return 2; }
+  var x: i32;
+}
+```
+
+Each member of `C` with a distinct name will have a corresponding type (like
+`__TypeOf_C_F`) and value of that type (like `__C_F`). There are two more types
+for each member function (either static class function or method), though, that
+[adapt](/docs/design/generics/terminology.md#adapting-a-type) `C` and represent
+the type of binding that member with either a `C` value or variable.
+
+```carbon
+class __TypeOf_C_F {}
+let __C_F:! __TypeOf_C_F = {};
+class __Binding_C_F {
+  adapt C;
+}
+
+// and similarly for Static
+```
+
+These are the types that result from
+[instance binding](/docs/design/expressions/member_access.md#instance-binding)
+an instance of `C` with these member names. They define the bound method value
+and bound method type of [proposal #2875](/proposals/p2875.md#bound-methods).
+For example,
+
+```carbon
+let v: C = {.x = 3};
+Assert(v.F() == 8);
+Assert(v.Static() == 2);
+var r: C = {.x = 4};
+Assert(r.F() == 9);
+Assert(r.Static() == 2);
+```
+
+is interpreted as:
+
+```carbon
+let v: C = {.x = 3};
+Assert((v as __Binding_C_F).(Call(()).Op)() == 8);
+Assert((v as __Binding_C_Static).(Call(()).Op)() == 2);
+var r: C = {.x = 4};
+Assert((r as __Binding_C_F).(Call(()).Op)() == 9);
+Assert((r as __Binding_C_Static).(Call(()).Op)() == 2);
+```
+
+How does this arise?
+
+1. First the simple member access is resolved using the type of the receiver: \
+   `v.F` -> `v.(C.F)`, `v.Static` -> `v.(C.Static)`, `r.F` -> `r.(C.F)`, `r.Static`
+   -> `r.(C.Static)`. \
+   Note that `C.F` is `__C_F` with type `__TypeOf_C_F`, and `C.Static` is
+   `__C_Static` with type `__TypeOf_C_Static`.
+2. It then looks at the expression to the left of the `.`:
+    - If it is a facet value, the "member binding to type" (`BindToType`)
+      operator is applied.
+    - If it is a reference expression, the "member binding to reference"
+      (`BindToRef`) operator is applied.
+    - If it is a value expression, the "member binding to value" (`BindToValue`)
+      operator is applied.
+3. The result of the member binding has a type that implements the call
+   interface.
+
+> **Note:** The current wording in
+> [member_access.md](/docs/design/expressions/member_access.md) says that
+> `v.(C.Static)` and `r.(C.Static)` are both invalid, because they don't perform
+> member name lookup, instance binding, nor impl lookup -- the `v.` and `r.`
+> portions are redundant. That rule is removed by this proposal.
+>
+> Instead, tools such as linters can highlight such code as suspicious on a
+> best-effort basis, particularly when the issue is contained in a single
+> expression. Such tools may still allow code that performs the same operation
+> across multiple statements, as in:
+>
+> ```carbon
+> let M:! auto = C.Static;
+> v.(M)();
+> r.(M)();
+> ```
+>
+> Note that if `M` is an overloaded name, it could be an instance member in some
+> cases and a non-instance member in others, depending on the arguments passed.
+> This is another reason to delegate this to linters analyzing a whole
+> expression on a best-effort basis, rather than a strict rule just about member
+> binding.
+
+The member binding operators are defined using three dedicated interfaces --
+`BindToValue`, `BindToRef`, and `BindToType` --
+[as defined in the "proposal" section](#proposal). These member binding
+operations are implemented for the types of the class members:
+
+```carbon
+impl __TypeOf_C_F as BindToValue(C)
+    where .Result = __Binding_C_F {
+  fn Op[unused self: Self](x: C) -> __Binding_C_F {
+    return x as __Binding_C_F;
+  }
+}
+
+// Note that the `Result` type has to match, since
+// it is an associated type in the `Bind(C)` interface
+// that both `BindToValue(C)` and `BindToRef(C)` extend.
+impl __TypeOf_C_F as BindToRef(C)
+    where .Result = __Binding_C_F {
+  fn Op[unused self: Self](p: C*) -> __Binding_C_F* {
+    return p as __Binding_C_F*;
+  }
+}
+```
+
+> **Note:** `BindToType` is used for
+> [non-instance interface members](#non-instance-interface-members).
+
+Those implementations are how we get from `__C_F` with type `__TypeOf_C_F` to
+`v as __Binding_C_F` or `&r as __Binding_C_F*`, conceptually following these
+steps:
+
+```carbon
+// `v` is a value and so uses `BindToValue`
+v.F() == v.(C.F)()
+      == v.(__C_F)()
+      == __C_F.((__TypeOf_C_F as BindToValue(C)).Op)(v)()
+      == (v as __Binding_C_F)()
+
+// `r` is a reference expression and so uses `BindToRef`
+r.F() == r.(C.F)()
+      == r.(__C_F)()
+      == (*__C_F.((__TypeOf_C_F as BindToRef(C)).Op)(&r))()
+      == (*(&r as __Binding_C_F*))()
+```
+
+However, to avoid recursive application of these same rules, we need to avoid
+expressing this in terms of evaluating `__C_F.(`...`)`. Instead the third step
+uses an intrinsic compiler primitive, as in:
+
+```carbon
+// `v` is a value and so uses `BindToValue`
+v.F() == v.(C.F)()
+      == v.(__C_F)()
+      == inlined_method_call_compiler_intrinsic(
+              <function body (__TypeOf_C_F as BindToValue(C)).Op overload 0>,
+              __C_F, (v))()
+      == (v as __Binding_C_F)()
+
+// `r` is a reference expression and so uses `BindToRef`
+r.F() == r.(C.F)()
+      == r.(__C_F)()
+      == (*inlined_method_call_compiler_intrinsic(
+              <function body (__TypeOf_C_F as BindToRef(C)).Op overload 0>,
+              __C_F, (&r)))()
+      == (*(&r as __Binding_C_F*))()
+```
+
+At this point we have resolved the member binding, and are left with an
+expression of type `__Binding_C_F` followed by `()`. In the first case, that
+expression is a value expression. In the second case, it is a reference
+expression.
+
+The last ingredient is the implementation of the call interfaces for these bound
+types.
+
+```carbon
+// Member binding with `C.F` produces something with type
+// `__Binding_C_F` whether it is a value or reference
+// expression. Since `C.F` takes `self: Self` it can be
+// used in both cases.
+impl __Binding_C_F as Call(()) with .Result = i32 {
+  fn Op[self: Self]() -> i32 {
+    // Calls `(self as C).(C.F)()`, but without triggering
+    // member binding again.
+    return inlined_method_call_compiler_intrinsic(
+        <function body C.F overload 0>, self as C, ());
+  }
+}
+
+// `C.Static` works the same as `C.F`, except it also
+// implements the call interfaces on `__TypeOf_C_Static`.
+// This allows `C.Static()` to work, in addition to
+// `v.Static()` and `r.Static()`.
+impl __Binding_C_Static as Call(()) with .Result = i32 {
+  // Other implementations of `Call(())` are the same.
+  fn Op[unused self: Self]() -> i32 {
+    // Calls `C.Static()`, without triggering member binding again.
+    return inlined_call_compiler_intrinsic(
+               <function body C.Static overload 0>, ());
+  }
+}
+impl __TypeOf_C_Static as Call(()) where .Result = i32;
+```
+
+Going back to `v.F()` and `r.F()`, after member binding the next step is to
+resolve the call. As described in
+[proposal #2875](https://github.com/carbon-language/carbon-lang/pull/2875), this
+call is rewritten to an invocation of the `Op` method of the `Call(())`
+interface, using the implementations just defined. Note:
+
+-   Passing `*(&r as __Binding_C_F*)` to the `self` parameter of `Call(()).Op`
+    converts the reference expression to a value. Note that mutating
+    (`addr self`) methods are [out of scope for this proposal](#background).
+-   The `Call` interface is special. We don't
+    [rewrite](#instance-interface-members) calls to `Call(__).Op` to avoid
+    infinite recursion.
+
+```carbon
+v.F() == (v as __Binding_C_F)()
+      == (v as __Binding_C_F).((__Binding_C_F as Call(())).Op)()
+      == inlined_method_call_compiler_intrinsic(
+            <function body (__Binding_C_F as Call(())).Op overload 0>,
+            v as __Binding_C_F, ());
+      == inlined_method_call_compiler_intrinsic(
+             <function body C.F overload 0>,
+             (v as __Binding_C_F) as C, ())
+      == inlined_method_call_compiler_intrinsic(
+             <function body C.F overload 0>, v, ())
+
+r.F() == (*(&r as __Binding_C_F*))()
+      == (*(&r as __Binding_C_F*)).((__Binding_C_F as Call(())).Op)()
+      == inlined_method_call_compiler_intrinsic(
+            <function body (__Binding_C_F as Call(())).Op overload 0>,
+            *(&r as __Binding_C_F*) <as value expression>, ());
+      == inlined_method_call_compiler_intrinsic(
+             <function body C.F overload 0>,
+             *(&r as __Binding_C_F*) as C, ())
+      == inlined_method_call_compiler_intrinsic(
+             <function body C.F overload 0>,
+             r <as value expression>, ())
+```
+
+> **Note:** This rewrite results in compiler intrinsics for calling. This is to
+> show that no more rewrites are applied.
+
+### Inheritance and other implicit conversions
+
+Now consider methods of a base class:
+
+```carbon
+base class B {
+  fn F[self: Self]();
+  virtual fn V[self: Self]();
+}
+
+class D {
+  extend base: B;
+  impl fn V[self: Self]();
+}
+
+var d: D = {}
+d.(B.F)();
+d.(B.V)();
+```
+
+To allow this to work, we need the implementation of the member binding
+interfaces to allow implicit conversions:
+
+```carbon
+impl [T:! ImplicitAs(B)] __TypeOf_B_F as BindToValue(T)
+    where .Result = __Binding_B_F {
+  fn Op[self: Self](x: T) -> __Binding_B_F {
+    return (x as B) as __Binding_B_F;
+  }
+}
+
+impl [T:! type where .Self* impls ImplicitAs(B*)]
+    __TypeOf_B_F as BindToRef(T)
+    where .Result = __Binding_B_F {
+  fn Op[self: Self](p: T*) -> __Binding_B_F* {
+    return (p as B*) as __Binding_B_F*;
+  }
+}
+```
+
+This matches the expected semantics of method calls, even for methods of final
+classes.
+
+Note that the implementation of the member binding interfaces is where the
+`Self` type of a method is used. If that type is different from the class it is
+being defined in, as considered in
+[#1345](https://github.com/carbon-language/carbon-lang/issues/1345), that will
+be reflected in the member binding implementations.
+
+```carbon
+class C {
+  // Note: not `self: Self` or `self: C`!
+  fn G[self: Different]();
+}
+
+let c: C = {};
+// `c.G()` is only allowed if there is an implicit
+// conversion from `C` to `Different`.
+
+let d: Different = {};
+// Allowed:
+d.(C.G)();
+```
+
+results in an implementation using `Different` instead of `C`:
+
+```carbon
+// `C.G` will only member bind to values that can implicitly convert
+// to type `Different`.
+impl [T:! ImplicitAs(Different)] __TypeOf_C_G as BindToValue(T)
+    where .Result = __Binding_C_G;
+```
+
+### Data fields
+
+The same `BindToValue` and `BindToRef` operations allow us to define access to
+the data fields in an object, without any additional changes.
+
+For example, given a class with a data member `m` with type `i32`:
+
+```carbon
+class C {
+  var m: i32;
+}
+```
+
+we want the usual operations to work, with `x.m` equivalent to `x.(C.m)`:
+
+```carbon
+let v: C = {.m = 4};
+var x: C = {.m = 3};
+x.m += 5;
+Assert(x.(C.m) == v.m + v.(C.m));
+```
+
+To accomplish this we will, as before, associate an empty (stateless or
+zero-sized) type with the `m` member of `C`, that just exists to support the
+member binding operation. However, this time the result type of member binding
+is simply `i32`, the type of the variable, instead of a new, dedicated type.
+
+```carbon
+class __TypeOf_C_m {}
+let __C_m:! __TypeOf_C_m = {};
+
+impl __TypeOf_C_m as BindToValue(C) where .Result = i32 {
+  fn Op[self: Self](x: C) -> i32 {
+    // Effectively performs `x.m`, but without triggering member binding again.
+    return value_compiler_intrinsic(x, __OffsetOf_C_m, i32)
+  }
+}
+
+impl __TypeOf_C_m as BindToRef(C) where .Result = i32 {
+  fn Op[self: Self](p: C*) -> i32* {
+    // Effectively performs `&p->m`, but without triggering member binding again,
+    // by doing something like `((p as byte*) + __OffsetOf_C_m) as i32*`
+    return offset_compiler_intrinsic(p, __OffsetOf_C_m, i32);
+  }
+}
+```
+
+These definitions give us the desired semantics:
+
+```carbon
+// For value `v` with type `T` and `y` of type `U`,
+// `v.(y)` is `y.((U as BindToValue(T)).Op)(v)`
+v.m == v.(C.m)
+    == v.(__C_m)
+    == v.(__C_m as (__TypeOf_C_m as BindToValue(C)))
+    == __C_m.((__TypeOf_C_m as BindToValue(C)).Op)(v)
+    == value_compiler_intrinsic(v, __OffsetOf_C_m, i32)
+
+// For reference expression `var x: T` and `y` of type `U`,
+// `x.(y)` is `*y.(U as BindToRef(T)).Op(&x)`
+x.m == x.(C.m)
+    == x.(__C_m)
+    == *__C_m.((__TypeOf_C_m as BindToRef(C)).Op)(&x)
+    == *offset_compiler_intrinsic(&x, __OffsetOf_C_m, i32)
+// Note that this requires `x` to be a reference expression,
+// so `&x` is valid, and produces a reference expression,
+// since it is the result of dereferencing a pointer.
+```
+
+The fields of [tuple types](/docs/design/tuples.md) and
+[struct types](/docs/design/classes.md#struct-types) operate the same way.
+
+```carbon
+let t_let: (i32, i32) = (3, 6);
+Assert(t_let.(((i32, i32) as type).0) == 3);
+
+var t_var: (i32, i32) = (4, 8);
+Assert(t_var.(((i32, i32) as type).1) == 8);
+t_var.(((i32, i32) as type).1) = 9;
+Assert(t_var.1 == 9);
+
+let s_let: {.x: i32, .y: i32} = {.x = 5, .y = 10};
+Assert(s_let.({.x: i32, .y: i32}.x) == 5);
+
+var s_var: {.x: i32, .y: i32} = {.x = 6, .y = 12};
+Assert(s_var.({.x: i32, .y: i32}.y) == 12);
+s_var.({.x: i32, .y: i32}.y) = 13;
+Assert(s_var.y == 13);
+```
+
+For example, `{.x: i32, .y: i32}.x` is a value `__Struct_x_i32_y_i32_Field_x`,
+analogous to `__C_m`, of a type `__TypeOf_Struct_x_i32_y_i32_Field_x` (that is
+zero-sized / has no state), analogous to `__TypeOf_C_m`, that implements the
+member binding interfaces for any type that implicitly converts to
+`{.x: i32, .y: i32}`.
+
+Note that for tuples, the `as type` is needed since `(i32, i32)` on its own is a
+tuple, not a type. In particular `(i32, i32)` is not the type of `t_let` or
+`t_var`. `(i32, i32).0` is just `i32`, and isn't the name of the first element
+of an `(i32, i32)` tuple.
+
+### Generic type of a class member
+
+Given the above, we can now write a constraint on a symbolic parameter to match
+the names of an unbound class member. There are a two cases: methods and fields.
+
+#### Methods
+
+Restricting to value methods, since mutating (`addr self`) methods are
+[out of scope for this proposal](#background), the receiver object may be passed
+by value. To be able to call the method, we must include a restriction that the
+result of `BindToValue` implements `Call(())`:
+
+```carbon
+// `m` can be any method object that implements `Call(())` once bound.
+fn CallMethod
+    [T:! type, M:! BindToValue(T) where .Result impls Call(())]
+    (x: T, m: M) -> auto {
+  // `x.(m)` is rewritten to a call to `BindToValue(T).Op`. The
+  // constraint on `M` ensures the result implements `Call(())`.
+  return x.(m)();
+}
+```
+
+This will work with any value method or static class function. This will also
+work with inheritance and virtual methods, using
+[the support for implicit conversions of self](#inheritance-and-other-implicit-conversions).
+
+```carbon
+base class X {
+  virtual fn V[self: Self]() -> i32 { return 1; }
+  fn B[self: Self]() -> i32 { return 0; }
+}
+class Y {
+  extend base: X;
+  impl fn V[self: Self]() -> i32 { return 2; }
+}
+class Z {
+  extend base: X;
+  impl fn V[self: Self]() -> i32 { return 3; }
+}
+
+var (x: X, y: Y, z: Z);
+
+// Respects inheritance
+Assert(CallMethod(x, X.B) == 0);
+Assert(CallMethod(y, X.B) == 0);
+Assert(CallMethod(z, X.B) == 0);
+
+// Respects method overriding
+Assert(CallMethod(x, X.V) == 1);
+Assert(CallMethod(y, X.V) == 2);
+Assert(CallMethod(z, X.V) == 3);
+```
+
+#### Fields
+
+Fields can be accessed, given the type of the field
+
+```carbon
+fn GetField
+    [T:! type, F:! BindToValue(T) where .Result = i32]
+    (x: T, f: F) -> i32 {
+  // `x.(f)` is rewritten to `f.((F as BindToValue(T)).Op)(x)`,
+  // and `(F as BindToValue(T)).Op` is a method on `f` with
+  // return type `i32` by the constraint on `F`.
+  return x.(f);
+}
+
+fn SetField
+    [T:! type, F:! BindToRef(T) where .Result = i32]
+    (x: T*, f: F, y: i32) {
+  // `x->(f)` is rewritten to `(*x).(f)`, which then
+  // becomes: `*f.((F as BindToRef(T)).Op)(&*x)`
+  // The constraint `F` says the return type of
+  // `(F as BindToRef(T)).Op` is `i32*`, which is
+  // dereferenced to get an `i32` reference expression
+  // which may then be assigned.
+  x->(f) = y;
+}
+
+class C {
+  var m: i32;
+  var n: i32;
+}
+var c: C = {.m = 5, .n = 6};
+Assert(GetField(c, C.m) == 5);
+Assert(GetField(c, C.n) == 6);
+SetField(&c, C.m, 42);
+SetField(&c, C.n, 12);
+Assert(GetField(c, C.m) == 42);
+Assert(GetField(c, C.n) == 12);
+```
+
+### C++ pointer to member
+
+In [the generic type of member section](#generic-type-of-a-class-member), the
+names of members, such as `D.K`, `X.B`, `X.V`, and `C.n`, refer to zero-sized /
+stateless objects where all the offset information is encoded in the type.
+However, the definitions of `CallMethod`, `SetField`, and `GetField` do not
+depend on that fact and will be usable with objects, such as C++
+pointers-to-members, that include the offset information in the runtime object
+state. So we can define member binding implementations for them so that they may
+be used with Carbon's `.(`**`)` and `->(`**`)` operators.
+
+For example, this is how we expect C++ code to call the above Carbon functions:
+
+```cpp
+struct C {
+  int F() const { return m + 1; }
+  int m;
+};
+
+int main() {
+  // pointer to data member `m` of class C
+  int C::* p = &C::m;
+  C c = {2};
+  assert(c.*p == 2);
+  assert(Carbon::GetField(c, p) == 2);
+  Carbon::SetField(&c, p, 4);
+  assert(c.m == 4);
+  // pointer to method `F` of class C
+  int (C::*q)() const = &C::F;
+  assert(Carbon::CallMethod(&c, q) == 5);
+}
+```
+
+### Instance interface members
+
+Instance members of an interface, such as methods, can use this framework. For
+example, given these declarations:
+
+```carbon
+interface I {
+  fn F[self: Self]();
+}
+class C {
+  impl as I;
+}
+let c: C = {};
+```
+
+Then `I.F` is its own value with its own type:
+
+```carbon
+class __TypeOf_I_F {}
+let __I_F:! __TypeOf_I_F = {};
+```
+
+That type implements `BindToValue` for any type that implements the interface
+`I`:
+
+```carbon
+class __Binding_I_F(T:! I) {
+  adapt T;
+}
+impl forall [T:! I] __TypeOf_I_F as BindToValue(T)
+    where .Result = __Binding_I_F(T) {
+  fn Op[self: Self](x: T) -> __Binding_I_F(T) {
+    // Valid since `__Binding_I_F(T)` adapts `T`:
+    return x as __Binding_I_F(T);
+  }
+}
+```
+
+The actual dispatch to the `I.F` method of `C` happens in the implementation of
+the `Call` interface of this adapter type that is the result of member binding
+to a value. So, this implementation of `C as I`:
+
+```carbon
+impl C as I {
+  fn F[self: Self]() {
+    Fanfare(self);
+  }
+}
+```
+
+Results in this implementation:
+
+```carbon
+impl __Binding_I_F(C) as Call(()) where .Result = () {
+  fn Op[self: Self]() {
+    inlined_method_call_compiler_intrinsic(
+        <function body (C as I).F overload 0>, self as C, ());
+  }
+}
+```
+
+A call such as `c.(I.F)()` goes through these rewrites:
+
+```carbon
+c.(I.F)() == c.(__I_F)()
+          == __I_F.((__TypeOf_I_F as BindToValue(C)).Op)(c)()
+          == (c as __Binding_I_F(C))()
+          == (c as __Binding_I_F(C)).((__Binding_I_F(C) as Call(())).Op)()
+```
+
+Which results in invoking the above implementation that will ultimately call
+`Fanfare(c)`.
+
+> **Note:** The `Call` interface gets special treatment and does not get these
+> rewrites to avoid recursing forever.
+
+### Non-instance interface members
+
+Non-instance members use the `BindToType` interface instead. For example, if `G`
+is a non-instance function of an interface `J`:
+
+```carbon
+interface J {
+  fn G();
+}
+impl C as J;
+```
+
+Again the member is given its own type and value:
+
+```carbon
+class __TypeOf_J_G {}
+let __J_G:! __TypeOf_J_G = {};
+```
+
+Since this is a non-instance member, this type implements `BindToType` instead
+of `BindToValue`:
+
+```carbon
+class __TypeBinding_J_G(T:! J) {}
+impl forall [T:! J] __TypeOf_J_G as BindToType(T)
+    where .Result = __TypeBinding_J_G(T) {
+  fn Op[self: Self]() -> __TypeBinding_J_G(T) {
+    return {};
+  }
+}
+```
+
+So, this implementation of `C as J`:
+
+```carbon
+impl C as J {
+  fn G() {
+    Fireworks();
+  }
+}
+```
+
+Results in this implementation:
+
+```carbon
+impl __TypeBinding_J_G(C) as Call(()) where .Result = () {
+  fn Op[self: Self]() {
+    Fireworks();
+  }
+}
+```
+
+A call such as `C.(J.G)()` goes through these rewrites:
+
+```carbon
+C.(J.G)() == C.(__J_G)()
+          == __J_G.((__TypeOf_J_G as BindToType(C)).Op)()()
+          == ({} as __TypeBinding_J_G(C))()
+          == (({} as __TypeBinding_J_G(C)) as Call(())).Op()
+```
+
+Which calls the above implementation that calls `Fireworks()`.
+
+> **Note:** Member binding for non-instance members doesn't work with
+> `BindToValue`, we need `BindToType`. Otherwise there is no way to get the
+> value `C` into the result type. Furthermore, we want `BindToType`
+> implementation no matter which facet of the type is used in the code.
+
+### C++ operator overloading
+
+C++ does not support customizing the behavior of `x.y`. It does support
+customizing the behavior of `operator*` and `operator->` which is frequently
+used to support smart pointers and iterators. There is, however, nothing
+restricting the implementations of those two operators to be consistent, so that
+`(*x).y` and `x->y` are the same.
+
+Carbon instead will only have a single interface for customizing dereference,
+corresponding to `operator*` not `operator->`. All uses of `x->y` will be
+rewritten to use `(*x).y` instead. This may cause some friction when porting C++
+code where those operators are not consistent. If the C++ code is just missing
+the definition of `operator*` corresponding to an `operator->`, a workaround
+would be just to define `operator*`.
+
+Other cases of divergence between those operators should be rare, since that is
+both surprising to users and for the common case of iterators, violates the C++
+requirements. If necessary, we can in the future introduce a specific construct
+just for C++ interop that invokes the C++ arrow operator, such as
+`CppArrowOperator(x)`, that returns a pointer.
+
+**Context:** This was discuseed in
+[2024-02-29 open discussion](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.5vj8ohrvqjqh)
+and in
+[a comment on this proposal](https://github.com/carbon-language/carbon-lang/pull/3720/files#r1507917882).
+
+## Future work
+
+### Future: tuple indexing
+
+We can reframe the use of the compound member access syntax for tuple fields as
+an implementation of member binding of tuples with compile-time integer
+expressions. The specifics of how this works will be resolved later, once we
+address how compile-time interacts with interfaces.
+
+### Future: properties
+
+If there was a way to implement the member binding operator to only produce
+values, even when the expression to the left of the `.` was a reference
+expression, then that could be used to implement read-only properties. This
+would support something like:
+
+```carbon
+let Pi: f64 = 3.1415926535897932384626433832795;
+
+class Circle {
+  var radius: f64;
+  read_property area -> f64 {
+    return Pi * self.radius * self.radius;
+  }
+}
+
+let c: Circle = {.radius = 2};
+Assert(NearlyEqual(c.area, 4 * Pi));
+```
+
+In this example, the member binding of `c` of type `Circle` to `Circle.area`
+would perform the computation and return the result as an `f64`.
+
+If there was some way to customize the result of member binding, this could be
+extended to support other kinds of properties, such as mutable properties that
+use `get` and `set` methods to access and mutate the value. The main obstacle to
+any support for properties with member binding is how the customization would be
+done. The most natural way to support this customization would be to have
+multiple interfaces. The compiler would try them in a specified order and use
+the one it found first. This has the downside of the possibility of different
+behavior in a checked generic context where only some of the implementations are
+visible. Our choice to
+[make the result type the same `Result` associated type of the `Bind` interface](#proposal)
+independent of whether the `BindToValue` or `BindToRef` interface is used makes
+this less concerning. Only the phase of the result, not the type, would depend
+on which implementations were found, similar to
+[how indexing works](/docs/design/expressions/indexing.md).
+
+### Future: building block for language features such as API extension
+
+We should be able to express other language features, such as API extension, in
+terms of customized member binding, plus possibly some new language primitives.
+This should be explored in a future proposal.
+
+## Rationale
+
+This proposal is about:
+
+-   Orthogonality: separating the member binding process as a distinct and
+    independent step of using the members of a type.
+-   Being consistent with our overall strategy for defining operators in terms
+    of interface implementations.
+-   Allows member-binding-related functionality to be defined through
+    [library APIs](/docs/project/principles/library_apis_only.md).
+-   Increases uniformity by making member names into ordinary values with types.
+-   Adds expressiveness, enabling member forwarding, passing a member as an
+    argument, and other use cases.
+
+These benefits advance Carbon's goals including:
+
+-   [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem):
+    by making it easier to reason about more Carbon entities within Carbon
+    itself, and reducing the number of different concepts that have to be
+    modeled.
+-   [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write):
+    through increased consistency, uniformity, and expressiveness.
+-   [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code):
+    by adding support for pointer-to-member constructs.
+
+## Alternatives considered
+
+### Swap the member binding interface parameters
+
+We considered instead making the receiver object the `Self` type of the
+interface, and using the member type as the parameter to the interface. This
+would have the advantage of matching the order that they appear in the source,
+consistent with other operators.
+
+> **Alternative:**
+>
+> ```carbon
+> // For value `x` with type `T` and `y` of type `U`,
+> // `x.(y)` is `x.((T as ValueBind(U)).Op)(y)`
+> interface ValueBind(U:! type) {
+>   extend Bind(U);
+>   fn Op[self: Self](x: U) -> Result;
+> }
+>
+> // For reference expression `var x: T` and `y` of type `U`,
+> // `x.(y)` is `*x.((T as RefBind(U)).Op)(y)`
+> interface RefBind(U:! type) {
+>   extend Bind(U);
+>   fn Op[addr self: Self*](x: U) -> Result*;
+> }
+> ```
+
+This had some disadvantages however:
+
+-   The binding property is more associated with the member than the receiver.
+-   Some patterns are more awkward in the alternative syntax.
+
+As an example of this last point, consider a function that takes multiple (or
+even a variadic list) methods to call on a receiver object. With the proposed
+approach, each method type is constrained:
+
+```carbon
+// `m1`, `m2`, and `m3` are methods on class `T`.
+fn Call3Methods[T:! type,
+                M1:! BindToValue(T) where .Result impls Call(()),
+                M2:! BindToValue(T) where .Result impls Call(()),
+                M3:! BindToValue(T) where .Result impls Call(())]
+    (x: T, m1: M1, m2: M2, m3: M3) -> auto;
+```
+
+With the alternative, the type of the receiver would be constrained, and the
+deduced types would be written in a different order:
+
+> **Alternative:**
+>
+> ```carbon
+> // `m1`, `m2`, and `m3` are methods on class `T`.
+> fn Call3MethodsAlternative1
+>     [M1:! type, M2:! type, M3:! type,
+>      T:! ValueBid(M1) & ValueBind(M2) & ValueBind(M3)
+>          where .(ValueBind(M1).Result) impls Call(())
+>            and .(ValueBind(M2).Result) impls Call(())
+>            and .(ValueBind(M3).Result) impls Call(())]
+>     (x: T, m1: M1, m2: M2, m3: M3) -> auto;
+> ```
+
+Or, the constraints can be moved to the method types at the cost of additional
+length:
+
+> **Alternative:**
+>
+> ```carbon
+> // `m1`, `m2`, and `m3` are methods on class `T`.
+> fn Call3MethodsAlternative2
+>     [T:! type,
+>      M1:! type where T impls (ValueBind(.Self) where .Result impls Call(())),
+>      M2:! type where T impls (ValueBind(.Self) where .Result impls Call(())),
+>      M3:! type where T impls (ValueBind(.Self) where .Result impls Call(()))]
+>     (x: T, m1: M1, m2: M2, m3: M3) -> auto;
+> ```
+
+### Member binding to references produces a value that wraps a pointer
+
+Consider a mutating method on a class:
+
+```carbon
+class Counter {
+  var count: i32 = 0;
+  fn Increment[addr self: Self*]() {
+    self->count += 1;
+  }
+}
+
+var c: Counter = {};
+```
+
+This proposal says `c.Increment` is a reference expression with a type that
+adapts `Counter`. For `c.Increment()` to affect the value of `c.count`, there
+needs to be some way for the `Call` operator to mutate `c`. The current
+definition of `Call` takes `self` by value, though, so this doesn't work.
+Addressing this is [out of scope of the current proposal](#background).
+
+We could instead make `c.Increment` be a value holding `&c`. That would allow
+`Call` to work even when taking `self` by value. This is the solution likely
+implied by the current [proposal #2875](/proposals/p2875.md#bound-methods),
+though that proposal does not say what the bound method type is at all. It
+leaves two other problems, however:
+
+-   We will still need a way to support function objects that are mutated by
+    calling them. This comes up, for example, with C++ types that define
+    `operator()`.
+-   We want the proposed behavior when member binding a [field](#data-fields).
+
+As a result, it would be better to evaluate this alternative later as part of
+considering mutation in calls.
+
+### Separate interface for compile-time member binding instead of type member binding
+
+The first proposed way to handle
+[non-instance interface members](#non-instance-interface-members) was in
+[the #typesystem channel on Discord on 2024-03-07](https://discord.com/channels/655572317891461132/708431657849585705/1215405895752618134).
+The suggestion was to have a `CompileBind` interface used for any compile-time
+value to the left of the `.`. It would have access to the value, which is needed
+when accessing the members of a type.
+
+We eventually concluded that the special treatment was specifically needed for
+types, not all compile-time values. The
+[insight](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.1k8r5fhfwyh6)
+was that types are special because their members are defined by their values,
+not by their type
+([which is always `type`](https://github.com/carbon-language/carbon-lang/pull/2360)).
+
+### Non-instance members are idempotent under member binding
+
+In the current proposal, member binding of non-instance members results in an
+adapter type, the same as an instance member. For example,
+
+```carbon
+class C {
+  fn Static() -> i32;
+}
+```
+
+is translated into something like:
+
+> **Current proposal:**
+>
+> ```carbon
+> class __TypeOf_C_Static {}
+> let __C_Static:! __TypeOf_C_Static = {};
+>
+> class __Binding_C_Static {
+>   adapt C;
+> }
+>
+> impl __TypeOf_C_Static as BindToValue(C)
+>     where .Result = __Binding_C_Static;
+>
+> impl __TypeOf_C_Static as BindToRef(C)
+>     where .Result = __Binding_C_Static;
+> ```
+
+An alternative is that member binding of a non-instance member is idempotent, so
+there is no `__Binding_C_Static` type and `BindToValue(C)` results in a value of
+type `__TypeOf_C_Static` instead:
+
+> **Alternative:**
+>
+> ```carbon
+> class __TypeOf_C_Static {}
+> // Might need to be a `var` instead?
+> let __C_Static:! __TypeOf_C_Static = {};
+>
+> impl __TypeOf_C_Static as BindToValue(C)
+>     where .Result = __TypeOf_C_Static;
+> impl __TypeOf_C_Static as BindToRef(C)
+>     where .Result = __TypeOf_C_Static;
+> ```
+
+There are a few concerns with this alternative:
+
+-   This is less consistent with the instance member case.
+-   There would be a discontinuity when adding an instance overload to a name
+    that was previously only a non-instance member.
+-   Member binding to a reference is trickier, since it would have to return the
+    address of an object of type `__TypeOf_C_Static`. Perhaps a global variable?
+-   The current proposal rejects `v.(v.(C.Static))`, which is desirable.
+
+This was discussed in
+[this comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files#r1513681915).
+
+### Separate `Result` types for `BindToValue` and `BindToRef`
+
+An earlier iteration of this proposal had separate `Result` associated types for
+`BindToValue` and `BindToRef`, as in:
+
+```carbon
+interface BindToValue(T:! type) {
+  let Result:! type;
+  fn Op[self: Self](x: T) -> Result;
+}
+
+interface BindToRef(T:! type) {
+  let Result:! type;
+  fn Op[self: Self](p: T*) -> Result*;
+}
+```
+
+However, this results in the type of a member binding depending on the what
+[category](/docs/design/values.md#expression-categories) the expression to the
+left of the dot has. This could change the interpretation of code using
+[indexing](/docs/design/expressions/indexing.md), such as an expression like
+`a[b].F()`, when the type of `a` is changed from or to a checked generic. This
+is because the the expression is legal as long as the type of `a` implements
+`IndexWith(typeof(b))`, but category of `a[b]` depends on whether the type of
+`a` is known to implement `IndirectIndexWith(typeof(b))`.
+
+To avoid this problem, we make the result type of the member binding the same
+whether it is binding to a value or reference. See
+[this comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files/99fb69aaaa24bd502d71cd4259f37cfa8346b244#r1597317217).
+
+### `BindToValue` is a subtype of `BindToRef`
+
+We could make `BindToValue` be a subtype of `BindToRef`, as suggested in
+[this comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files/99fb69aaaa24bd502d71cd4259f37cfa8346b244#r1597317217).
+This would be a step beyond just saying they have to have the same `Result`
+type, which is achieved by that type being defined in the `Bind` interface they
+both extend.
+
+This approach would rule out the use case where value binding _computes_ a new
+value rather than returning an existing one -- that is, a read-only property.
+That use case isn't currently well supported by this proposal -- while you can
+make `x.ComputeSize` work when `x` is a value expression, you can't make it work
+when `x` is a reference expression. However, that use case can be supported with
+the approach described in [future work](#future-properties).
+
+### Directly rewrite all calls to interface member functions to method call intrinsics
+
+In this proposal, the `Call` interface is given special treatment, in that
+invoking its method is rewritten into a primitive operation rather than going
+through the customizable member binding that other interfaces use. This is
+described in the [Details](#details) and
+[Instance interface members](#instance-interface-members) sections.
+
+In a comment on #3720
+([1](https://github.com/carbon-language/carbon-lang/pull/3720#discussion_r1597324225),
+[2](https://github.com/carbon-language/carbon-lang/pull/3720/files#r1597324225)),
+we considered the possibility that invoking any interface member would be
+directly rewritten into a primitive operation. We realized the downside of this
+approach in
+[open discussion on 2024-05-16](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.3qlpp5e56u46),
+that this would not allow interface members to support overloading.