Bläddra i källkod

Design documentation for indexing (#2388)

Update the design documentation to reflect #2274.

The contents are largely copy-pasted from p2274.md with minor edits, but the "Open questions" section is new.
Geoff Romer 3 år sedan
förälder
incheckning
0bf4d11ced
2 ändrade filer med 141 tillägg och 1 borttagningar
  1. 3 1
      docs/design/README.md
  2. 138 0
      docs/design/expressions/indexing.md

+ 3 - 1
docs/design/README.md

@@ -3071,11 +3071,13 @@ The interfaces that correspond to each operator are given by:
         [`As(U)`](expressions/as_expressions.md#extensibility) interface
     -   Implicit conversions use
         [`ImplicitAs(U)`](expressions/implicit_conversions.md#extensibility)
+-   Indexing:
+    -   `x[y]` is rewritten to use the
+        [`IndexWith` or `IndirectIndexWith`](expressions/indexing.md) interface.
 -   **TODO:** [Assignment](#assignment-statements): `x = y`, `++x`, `x += y`,
     and so on
 -   **TODO:** Dereference: `*p`
 -   **TODO:** [Move](#move): `~x`
--   **TODO:** Indexing: `a[3]`
 -   **TODO:** Function call: `f(4)`
 
 The

+ 138 - 0
docs/design/expressions/indexing.md

@@ -0,0 +1,138 @@
+# Indexing
+
+<!--
+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
+-->
+
+<!-- toc -->
+
+## Table of contents
+
+-   [Overview](#overview)
+-   [Details](#details)
+    -   [Examples](#examples)
+-   [Open questions](#open-questions)
+    -   [Tuple indexing](#tuple-indexing)
+-   [Alternatives considered](#alternatives-considered)
+-   [References](#references)
+
+<!-- tocstop -->
+
+## Overview
+
+Carbon supports indexing using the conventional `a[i]` subscript syntax. When
+`a` is an l-value, the result of subscripting is always an l-value, but when `a`
+is an r-value, the result can be an l-value or an r-value, depending on which
+interface the type implements:
+
+-   If subscripting an r-value produces an r-value result, as with an array, the
+    type should implement `IndexWith`.
+-   If subscripting an r-value produces an l-value result, as with C++'s
+    `std::span`, the type should implement `IndirectIndexWith`.
+
+`IndirectIndexWith` is a subtype of `IndexWith`, and subscript expressions are
+rewritten to method calls on `IndirectIndexWith` if the type is known to
+implement that interface, or to method calls on `IndexWith` otherwise.
+
+`IndirectIndexWith` provides a final blanket `impl` of `IndexWith`, so a type
+can implement at most one of those two interfaces.
+
+## Details
+
+A subscript expression has the form "_lhs_ `[` _index_ `]`". As in C++, this
+syntax has the same precedence as `.`, `->`, and function calls, and associates
+left-to-right with all of them.
+
+Its semantics are defined in terms of the following interfaces:
+
+```
+interface IndexWith(SubscriptType:! Type) {
+  let ElementType:! Type;
+  fn At[me: Self](subscript: SubscriptType) -> ElementType;
+  fn Addr[addr me: Self*](subscript: SubscriptType) -> ElementType*;
+}
+
+interface IndirectIndexWith(SubscriptType:! Type) {
+  impl as IndexWith(SubscriptType);
+  fn Addr[me: Self](subscript: SubscriptType) -> ElementType*;
+}
+```
+
+A subscript expression where _lhs_ has type `T` and _index_ has type `I` is
+rewritten based on the value category of _lhs_ and whether `T` is known to
+implement `IndirectIndexWith(I)`:
+
+-   If `T` implements `IndirectIndexWith(I)`, the expression is rewritten to
+    "`*((` _lhs_ `).(IndirectIndexWith(I).Addr)(` _index_ `))`".
+-   Otherwise, if _lhs_ is an l-value, the expression is rewritten to "`*((`
+    _lhs_ `).(IndexWith(I).Addr)(` _index_ `))`".
+-   Otherwise, the expression is rewritten to "`(` _lhs_ `).(IndexWith(I).At)(`
+    _index_ `)`".
+
+`IndirectIndexWith` provides a blanket `final impl` for `IndexWith`:
+
+```
+final external impl forall
+    [SubscriptType:! Type, T:! IndirectIndexWith(SubscriptType)]
+    T as IndexWith(SubscriptType) {
+  let ElementType:! Type = T.(IndirectIndexWith(SubscriptType)).ElementType;
+  fn At[me: Self](subscript: SubscriptType) -> ElementType {
+    return *(me.(IndirectIndexWith(SubscriptType).Addr)(index));
+  }
+  fn Addr[addr me: Self*](subscript: SubscriptType) -> ElementType* {
+    return me->(IndirectIndexWith(SubscriptType).Addr)(index);
+  }
+}
+```
+
+Thus, a type that implements `IndirectIndexWith` need not, and cannot, provide
+its own definitions of `IndexWith.At` and `IndexWith.Addr`.
+
+### Examples
+
+An array type could implement subscripting like so:
+
+```
+class Array(template T:! Type) {
+  external impl as IndexWith(like i64) {
+    let ElementType:! Type = T;
+    fn At[me: Self](subscript: i64) -> T;
+    fn Addr[addr me: Self*](subscript: i64) -> T*;
+  }
+}
+```
+
+And a type such as `std::span` could look like this:
+
+```
+class Span(T:! Type) {
+  external impl as IndirectIndexWith(like i64) {
+    let ElementType:! Type = T;
+    fn Addr[me: Self](subscript: i64) -> T*;
+  }
+}
+```
+
+## Open questions
+
+### Tuple indexing
+
+It is not clear how tuple indexing will be modeled. When indexing a tuple, the
+index value must be a constant, and the type of the expression can depend on
+that value, but we don't yet have the tools to express those properties in a
+Carbon API.
+
+## Alternatives considered
+
+-   [Different subscripting syntaxes](/proposals/p2274.md#different-subscripting-syntaxes)
+-   [Multiple indices](/proposals/p2274.md#multiple-indices)
+-   [Read-only subscripting](/proposals/p2274.md#read-only-subscripting)
+-   [Rvalue-only subscripting](/proposals/p2274.md#rvalue-only-subscripting)
+-   [Map-like subscripting](/proposals/p2274.md#map-like-subscripting)
+
+## References
+
+-   Proposal
+    [#2274: Subscript syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2274)