In a generic context, we don't in general know whether the type being
subscripted or indexed implements IndirectIndexWith
or BindToRef
. If we
perform a rewrite to the corresponding interfaces in a generic context, the
behavior can depend on which interfaces we happen to know the type implements,
and the behavior can change sharply if we learn more about the interfaces
implemented by the type. This knowledge can even arise implicitly in some cases.
class Slice {}
class Element {}
class Member {}
impl Slice as IndirectIndexWith(i32) where .Result = Element;
impl forall [T:! type] Member as BindToValue(T) where .Result = i32 {
fn Op[self: Self](x: T) -> i32 {
return 1;
}
}
impl forall [T:! type] Member as BindToRef(T) where .Result = i32 {
fn Op[self: Self](x: T) -> i32 {
return 2;
}
}
class Generic(T:! type) {}
fn F[I:! IndexWith(i32)](unused g: Generic(I), i: I) -> i32 {
return i[0].({} as Member);
}
fn Run() {
Print("{0}", F({}, {} as Slice));
}
The behavior here depends on whether I
is known to implement IndirectIndexWith(i32)
.
- If not, then
i[0]
is a value expression, the binding usesBindToValue
, and the program prints 1. - If so, then
i[0]
is a reference expression, the binding usesBindToRef
, and the program prints 2.
In the above, we don't know that I
implements IndirectIndexWith(i32)
, so the program prints 1. But if we make a seemingly-unrelated change to Generic
:
-class Generic(T:! type) {}
+class Generic(T:! IndirectIndexWith(i32)) {}
then F
picks up an implicit constraint that T
implements
IndirectIndexWith(i32)
then the program now prints 2.