Skip to content

Latest commit

 

History

History
138 lines (106 loc) · 4.49 KB

indexing.md

File metadata and controls

138 lines (106 loc) · 4.49 KB

Indexing

Table of contents

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

References