Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// A dependency-injection container as a library ships it: the service's type is
// the call's own type argument, and nothing else names it.
export class Container {
get<T>(id: symbol): T {
return undefined as unknown as T;
}
bind<T>(id: symbol): Container {
return this;
}
}

// A generic keyed store, the library-side twin of Map.
export class Registry<V> {
lookup(key: string): V | undefined {
return undefined;
}
}
88 changes: 88 additions & 0 deletions graph/test/typescript/cases/80-generic-return-receiver/src/use.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// A receiver whose type is what a GENERIC or computed call returns. Each shape is
// statically typed by the compiler, so each `.m()` below has one declared target.

export interface Warrior { fight(): string; }
export class Ninja implements Warrior { fight(): string { return "ninja"; } }

export class User {
touch(): string { return "t"; }
save(): string { return "s"; }
}

export interface Shape { area(): number; }
export class Sq implements Shape { constructor(private s: number) {} area(): number { return this.s * this.s; } }
export class Circ implements Shape { constructor(private r: number) {} area(): number { return 3 * this.r * this.r; } }

// ── the gap ──────────────────────────────────────────────────────────────────
// `Map<string, User>` built with `new`, read with `.get(k)!`.
export function recvMapGet(): string {
const m = new Map<string, User>();
return m.get("x")!.touch();
}

// A function-typed PARAMETER with its own type parameter, called with a written argument.
export function recvGeneric(get: <T>() => T): string {
return get<User>().touch();
}

// An index-signature object whose values are factories; the element is CALLED.
const factories: { [k: string]: (n: number) => Shape } = {
sq: (n) => new Sq(n),
circ: (n) => new Circ(n),
};
export function totalArea(kind: string, n: number): number {
return factories[kind](n).area();
}

// The same through a declared Record alias.
const makers: Record<string, (n: number) => Shape> = { sq: (n) => new Sq(n) };
export function recordArea(kind: string, n: number): number {
return makers[kind](n).area();
}

// An inline index signature whose value is NOT a function: the element is a Shape.
const shapes: { [k: string]: Shape } = { sq: new Sq(1) };
export function indexValue(kind: string): number {
return shapes[kind].area();
}

// T bound from the ARGUMENT of a generic function-typed parameter.
export function recvGenericArg(pick: <T>(x: T) => T, u: User): string {
return pick(u).save();
}

// ── controls: already typed, must not change ────────────────────────────────
export function ctlDeclared(): string {
const u: User = JSON.parse("{}");
return u.save();
}
export function ctlCast(): string {
const u = JSON.parse("{}") as User;
return u.save();
}
// ── controls: nothing names the type, so these must STAY unresolved ─────────
// The written type argument is `any`: T is untyped, and `.touch()` is a name only.
export function ctlGenericUnbound(get: <T>() => T): string {
return get<any>().touch();
}
// An index signature over `any`: the element is untyped.
const anyBag: { [k: string]: any } = {};
export function ctlAnyIndex(kind: string): number {
return anyBag[kind].area();
}

// ── client -> library ────────────────────────────────────────────────────────
import { Container, Registry } from "../lib/container";

const container = new Container();
const WARRIOR = Symbol.for("Warrior");
container.bind<Warrior>(WARRIOR);

export function run(): string {
const w = container.get<Warrior>(WARRIOR);
return w.fight();
}

export function viaRegistry(r: Registry<User>): string {
return r.lookup("x")!.touch();
}
1 change: 1 addition & 0 deletions graph/test/typescript/expected/13-receiver-forms.edges
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L22 -> Node#vis
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L23 -> Holder#make()
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L23 -> Node#visit()
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L24 -> Node#visit()
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L25 -> Node#visit()
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L26 -> Node#visit()
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L27 -> Node#visit()
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L28 -> Node#visit()
Expand Down
1 change: 1 addition & 0 deletions graph/test/typescript/expected/13-receiver-forms.lib.edges
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L22 -> Node#vis
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L23 -> Holder#make()
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L23 -> Node#visit()
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L24 -> Node#visit()
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L25 -> Node#visit()
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L26 -> Node#visit()
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L27 -> Node#visit()
known_edge METHOD_CALL receivers#drive(Holder,Node | undefined) @L28 -> Node#visit()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
ambiguous_unknown METHOD_PARAM 1 Parse [TYPE] -> -
known_edge FIELD_TYPE 0 Schema [FIELD] -> Internals
known_edge METHOD_PARAM 0 schema [METHOD_PARAM] -> Schema
known_edge METHOD_TYPE_PARAM_BOUND 0 - [TYPE_PARAMETER] -> Schema
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
73-assigned-interface-members [types]
precision 1.0000 (3 correct, 0 wrong)
recall 0.7500 (3 of 4 the compiler resolved)
sites 6 resolved 5 (83.3%)
tiers ambiguous_unknown=1 known_edge=5
contexts FIELD_TYPE=1 METHOD_PARAM=3 METHOD_TYPE_PARAM_BOUND=1 VARIABLE_TYPE=1
not scored: 2 rows whose target is not a client declaration
sites 5 resolved 5 (100.0%)
tiers known_edge=5
contexts FIELD_TYPE=1 METHOD_PARAM=2 METHOD_TYPE_PARAM_BOUND=1 VARIABLE_TYPE=1
not scored: 1 rows whose target is not a client declaration
MISSING Parse USES Schema
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
ambiguous_unknown AS_TARGET 0 store [EXPRESSION] -> -
ambiguous_unknown TYPE_ARGUMENT 2 LoggerImpl [TYPE] -> -
ambiguous_unknown TYPE_ELEMENT 1 store [METHOD_PARAM] -> -
known_edge CONDITIONAL_TRUE 2 Mutate [TYPE] -> Mutate
known_edge FIELD_TYPE 0 StoreApi [FIELD] -> SetStateInternal
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
79-function-typed-parameter-dispatch [types]
precision 1.0000 (12 correct, 0 wrong)
recall 1.0000 (12 of 12 the compiler resolved)
sites 32 resolved 27 (84.4%)
tiers ambiguous_unknown=5 known_edge=27
contexts AS_TARGET=1 CONDITIONAL_TRUE=1 FIELD_TYPE=1 METHOD_PARAM=6 METHOD_RETURN=2 TYPE_ARGUMENT=8 TYPE_ELEMENT=11 VARIABLE_TYPE=2
not scored: 5 rows whose target is not a client declaration
sites 30 resolved 27 (90.0%)
tiers ambiguous_unknown=3 known_edge=27
contexts AS_TARGET=1 CONDITIONAL_TRUE=1 FIELD_TYPE=1 METHOD_PARAM=6 METHOD_RETURN=2 TYPE_ARGUMENT=6 TYPE_ELEMENT=11 VARIABLE_TYPE=2
not scored: 3 rows whose target is not a client declaration
34 changes: 34 additions & 0 deletions graph/test/typescript/expected/80-generic-return-receiver.edges
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
ambiguous_unknown CONSTRUCTOR_CALL use#<module-init>() @L77 -> -
ambiguous_unknown CONSTRUCTOR_CALL use#recvMapGet() @L19 -> -
ambiguous_unknown METHOD_CALL use#<module-init>() @L78 -> -
ambiguous_unknown METHOD_CALL use#<module-init>() @L79 -> -
ambiguous_unknown METHOD_CALL use#ctlAnyIndex(string) @L71 -> -
ambiguous_unknown METHOD_CALL use#ctlCast() @L60 -> -
ambiguous_unknown METHOD_CALL use#ctlDeclared() @L56 -> -
ambiguous_unknown METHOD_CALL use#ctlGenericUnbound(() =) @L66 -> -
ambiguous_unknown METHOD_CALL use#recvMapGet() @L20 -> -
ambiguous_unknown METHOD_CALL use#run() @L82 -> -
ambiguous_unknown METHOD_CALL use#run() @L83 -> -
ambiguous_unknown METHOD_CALL use#viaRegistry(Registry) @L87 -> -
known_edge CONSTRUCTOR_CALL use#<arrow@30>(?) @L30 -> Sq#<new>(number)
known_edge CONSTRUCTOR_CALL use#<arrow@31>(?) @L31 -> Circ#<new>(number)
known_edge CONSTRUCTOR_CALL use#<arrow@38>(?) @L38 -> Sq#<new>(number)
known_edge CONSTRUCTOR_CALL use#<module-init>() @L44 -> Sq#<new>(number)
known_edge FUNCTION_CALL use#ctlGenericUnbound(() =) @L66 -> use#<arrow@65>()
known_edge FUNCTION_CALL use#recvGeneric(() =) @L25 -> use#<arrow@24>()
known_edge FUNCTION_CALL use#recvGenericArg((x: T) =,User) @L51 -> use#<arrow@50>(T)
known_edge METHOD_CALL use#ctlCast() @L61 -> User#save()
known_edge METHOD_CALL use#ctlDeclared() @L57 -> User#save()
known_edge METHOD_CALL use#recordArea(string,number) @L40 -> use#<arrow@38>(number)
known_edge METHOD_CALL use#recvGeneric(() =) @L25 -> User#touch()
known_edge METHOD_CALL use#recvGenericArg((x: T) =,User) @L51 -> User#save()
known_edge METHOD_CALL use#totalArea(string,number) @L34 -> use#<arrow@29>(number)
multi_inferred METHOD_CALL use#indexValue(string) @L46 -> Circ#area()
multi_inferred METHOD_CALL use#indexValue(string) @L46 -> Shape#area()
multi_inferred METHOD_CALL use#indexValue(string) @L46 -> Sq#area()
multi_inferred METHOD_CALL use#recordArea(string,number) @L40 -> Circ#area()
multi_inferred METHOD_CALL use#recordArea(string,number) @L40 -> Shape#area()
multi_inferred METHOD_CALL use#recordArea(string,number) @L40 -> Sq#area()
multi_inferred METHOD_CALL use#totalArea(string,number) @L34 -> Circ#area()
multi_inferred METHOD_CALL use#totalArea(string,number) @L34 -> Shape#area()
multi_inferred METHOD_CALL use#totalArea(string,number) @L34 -> Sq#area()
14 changes: 14 additions & 0 deletions graph/test/typescript/expected/80-generic-return-receiver.entries
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
── entry_point (13) ──
exported_from_entry_module use#ctlAnyIndex use.ts:70
exported_from_entry_module use#ctlCast use.ts:59
exported_from_entry_module use#ctlDeclared use.ts:55
exported_from_entry_module use#ctlGenericUnbound use.ts:65
exported_from_entry_module use#indexValue use.ts:45
exported_from_entry_module use#recordArea use.ts:39
exported_from_entry_module use#recvGeneric use.ts:24
exported_from_entry_module use#recvGenericArg use.ts:50
exported_from_entry_module use#recvMapGet use.ts:18
exported_from_entry_module use#run use.ts:81
exported_from_entry_module use#totalArea use.ts:33
exported_from_entry_module use#viaRegistry use.ts:86
unimported_module use#<module> use.ts:1
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
nominal use#Shape.area -> use#Circ.area
nominal use#Shape.area -> use#Sq.area
nominal use#Warrior.fight -> use#Ninja.fight
value_call use#recordArea -> use#<arrow>@38
value_call use#totalArea -> use#<arrow>@30
value_call use#totalArea -> use#<arrow>@31
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
known_edge read Circ#area() -> Circ#r
known_edge read Sq#area() -> Sq#s
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
80-generic-return-receiver [fields]
precision 1.0000 (2 correct, 0 wrong)
recall 1.0000 (2 of 2 the compiler resolved)
sites 4 resolved 4 (100.0%)
tiers known_edge=4
access read=4
not scored: 0 rows whose target is not a client declaration
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Accepted gaps for 80-generic-return-receiver — each line is an edge the TypeScript compiler
# resolves and this engine does not. A NEW missing edge fails the suite;
# a line here that STARTS working also fails, so the debt cannot rot.
# The receiver's type is the return of a LIBRARY generic: with no library there is no
# signature to read it from. run and viaRegistry resolve in the .lib pass.
use#run() -> Warrior#fight()
use#viaRegistry(Registry) -> User#touch()
# `Map` is the standard library's, which no case pass loads; it resolves on a real index
# given the TypeScript lib as a library.
use#recvMapGet() -> User#touch()
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
ambiguous_unknown CONSTRUCTOR_CALL use#recvMapGet() @L19 -> -
ambiguous_unknown METHOD_CALL use#<module-init>() @L78 -> -
ambiguous_unknown METHOD_CALL use#ctlAnyIndex(string) @L71 -> -
ambiguous_unknown METHOD_CALL use#ctlCast() @L60 -> -
ambiguous_unknown METHOD_CALL use#ctlDeclared() @L56 -> -
ambiguous_unknown METHOD_CALL use#ctlGenericUnbound(() =) @L66 -> -
ambiguous_unknown METHOD_CALL use#recvMapGet() @L20 -> -
boundary_lib CONSTRUCTOR_CALL use#<module-init>() @L77 -> Container#<new>()
boundary_lib METHOD_CALL use#<module-init>() @L79 -> Container#bind(symbol)
boundary_lib METHOD_CALL use#run() @L82 -> Container#get(symbol)
boundary_lib METHOD_CALL use#viaRegistry(Registry) @L87 -> Registry#lookup(string)
known_edge CONSTRUCTOR_CALL use#<arrow@30>(?) @L30 -> Sq#<new>(number)
known_edge CONSTRUCTOR_CALL use#<arrow@31>(?) @L31 -> Circ#<new>(number)
known_edge CONSTRUCTOR_CALL use#<arrow@38>(?) @L38 -> Sq#<new>(number)
known_edge CONSTRUCTOR_CALL use#<module-init>() @L44 -> Sq#<new>(number)
known_edge FUNCTION_CALL use#ctlGenericUnbound(() =) @L66 -> use#<arrow@65>()
known_edge FUNCTION_CALL use#recvGeneric(() =) @L25 -> use#<arrow@24>()
known_edge FUNCTION_CALL use#recvGenericArg((x: T) =,User) @L51 -> use#<arrow@50>(T)
known_edge METHOD_CALL use#ctlCast() @L61 -> User#save()
known_edge METHOD_CALL use#ctlDeclared() @L57 -> User#save()
known_edge METHOD_CALL use#recordArea(string,number) @L40 -> use#<arrow@38>(number)
known_edge METHOD_CALL use#recvGeneric(() =) @L25 -> User#touch()
known_edge METHOD_CALL use#recvGenericArg((x: T) =,User) @L51 -> User#save()
known_edge METHOD_CALL use#totalArea(string,number) @L34 -> use#<arrow@29>(number)
known_edge METHOD_CALL use#viaRegistry(Registry) @L87 -> User#touch()
multi_inferred METHOD_CALL use#indexValue(string) @L46 -> Circ#area()
multi_inferred METHOD_CALL use#indexValue(string) @L46 -> Shape#area()
multi_inferred METHOD_CALL use#indexValue(string) @L46 -> Sq#area()
multi_inferred METHOD_CALL use#recordArea(string,number) @L40 -> Circ#area()
multi_inferred METHOD_CALL use#recordArea(string,number) @L40 -> Shape#area()
multi_inferred METHOD_CALL use#recordArea(string,number) @L40 -> Sq#area()
multi_inferred METHOD_CALL use#run() @L83 -> Ninja#fight()
multi_inferred METHOD_CALL use#run() @L83 -> Warrior#fight()
multi_inferred METHOD_CALL use#totalArea(string,number) @L34 -> Circ#area()
multi_inferred METHOD_CALL use#totalArea(string,number) @L34 -> Shape#area()
multi_inferred METHOD_CALL use#totalArea(string,number) @L34 -> Sq#area()
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
nominal use#Shape.area -> use#Circ.area
nominal use#Shape.area -> use#Sq.area
nominal use#Warrior.fight -> use#Ninja.fight
value_call use#recordArea -> use#<arrow>@38
value_call use#totalArea -> use#<arrow>@30
value_call use#totalArea -> use#<arrow>@31
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Accepted gaps for 80-generic-return-receiver — each line is an edge the TypeScript compiler
# resolves and this engine does not. A NEW missing edge fails the suite;
# a line here that STARTS working also fails, so the debt cannot rot.
# `Map` is the standard library's, which no case pass loads; it resolves on a real index
# given the TypeScript lib as a library.
use#recvMapGet() -> User#touch()
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
oracle=23 engine=29 agree=22 missing=1 (known 1, NEW 0) extra=7
known use#recvMapGet() -> User#touch()
extra use#indexValue(string) -> Circ#area()
extra use#indexValue(string) -> Sq#area()
extra use#recordArea(string,number) -> Circ#area()
extra use#recordArea(string,number) -> Sq#area()
extra use#run() -> Ninja#fight()
extra use#totalArea(string,number) -> Circ#area()
extra use#totalArea(string,number) -> Sq#area()
10 changes: 10 additions & 0 deletions graph/test/typescript/expected/80-generic-return-receiver.oracle
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
oracle=19 engine=22 agree=16 missing=3 (known 3, NEW 0) extra=6
known use#recvMapGet() -> User#touch()
known use#run() -> Warrior#fight()
known use#viaRegistry(Registry) -> User#touch()
extra use#indexValue(string) -> Circ#area()
extra use#indexValue(string) -> Sq#area()
extra use#recordArea(string,number) -> Circ#area()
extra use#recordArea(string,number) -> Sq#area()
extra use#totalArea(string,number) -> Circ#area()
extra use#totalArea(string,number) -> Sq#area()
16 changes: 16 additions & 0 deletions graph/test/typescript/expected/80-generic-return-receiver.type-use
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
ambiguous_unknown METHOD_PARAM 0 use [METHOD_PARAM] -> -
ambiguous_unknown OBJECT_CREATION_TYPE 0 use [EXPRESSION] -> -
ambiguous_unknown VARIABLE_TYPE 0 use [VARIABLE] -> -
known_edge AS_TARGET 0 use [EXPRESSION] -> User
known_edge IMPLEMENTS_INTERFACE 0 Circ [HERITAGE] -> Shape
known_edge IMPLEMENTS_INTERFACE 0 Ninja [HERITAGE] -> Warrior
known_edge IMPLEMENTS_INTERFACE 0 Sq [HERITAGE] -> Shape
known_edge INDEX_SIGNATURE_VALUE 1 use [VARIABLE] -> Shape
known_edge METHOD_PARAM 0 use [METHOD_PARAM] -> User
known_edge METHOD_RETURN 2 use [VARIABLE] -> Shape
known_edge METHOD_TYPE_ARGUMENT 0 use [EXPRESSION] -> User
known_edge METHOD_TYPE_ARGUMENT 0 use [EXPRESSION] -> Warrior
known_edge OBJECT_CREATION_TYPE 0 use [EXPRESSION] -> Circ
known_edge OBJECT_CREATION_TYPE 0 use [EXPRESSION] -> Sq
known_edge TYPE_ARGUMENT 1 use [METHOD_PARAM] -> User
known_edge VARIABLE_TYPE 0 use [VARIABLE] -> User
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
80-generic-return-receiver [types]
precision 1.0000 (8 correct, 0 wrong)
recall 1.0000 (8 of 8 the compiler resolved)
sites 22 resolved 18 (81.8%)
tiers ambiguous_unknown=4 known_edge=18
contexts AS_TARGET=1 IMPLEMENTS_INTERFACE=3 INDEX_SIGNATURE_VALUE=1 METHOD_PARAM=2 METHOD_RETURN=2 METHOD_TYPE_ARGUMENT=4 OBJECT_CREATION_TYPE=6 TYPE_ARGUMENT=1 VARIABLE_TYPE=2
not scored: 4 rows whose target is not a client declaration
14 changes: 14 additions & 0 deletions graph/typescript/engine/expression-resolution/expr-type.dl
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,20 @@ expr_element_type(e, prov, t) :- expr_type(e, _, qt),
expr_element_type(e, prov, t) :- expr_type(e, _, qt),
index_signature_in_scope(qt, f),
field_type(f, prov, t).
// …and the same signature written INLINE — `const reg: { [k: string]: Handler }` —
// where the qualifier has a SHAPE rather than a named type. And where the signature's
// value is itself a function type, `{ [k: string]: (n: number) => Shape }`, the element
// is that signature: `factories[kind](n).area()` is a call on it, and without the shape
// the call had no candidate and `.area()` no receiver type.
expr_element_type(e, prov, t) :- expr_shape(e, qs),
index_signature_in_scope(qs, f),
field_type(f, prov, t).
expr_element_shape(e, sh) :- expr_type(e, _, qt),
index_signature_in_scope(qt, f),
field_shape(f, sh).
expr_element_shape(e, sh) :- expr_shape(e, qs),
index_signature_in_scope(qs, f),
field_shape(f, sh).

// ── `new C()` — the constructed type ───────────────────────────────────────
expr_type(e, prov, t) :- expr_kind("client", "NEW_EXPRESSION", _, e),
Expand Down
9 changes: 9 additions & 0 deletions graph/typescript/engine/resolution/type-resolution.dl
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,15 @@ ref_element_target(ref, prov, t) :- type_ref(rp, "TYPE_REFERENCE", _, tn, _, _,
ref_name_is_container(tn),
type_ref_parent(rp, ref, "0", child),
ref_type_target(child, prov, t).
// `Record<K, V>` read by key — `makers[kind](n)` — yields V, which is type argument 1,
// not 0, so it is kept off the container list: iterating a Record does not compile, and
// the list's promise is argument 0.
ref_element_target(ref, prov, t) :- type_ref(rp, "TYPE_REFERENCE", _, "Record", _, _, _, ref),
type_ref_parent(rp, ref, "1", child),
ref_type_target(child, prov, t).
ref_element_shape(ref, sh) :- type_ref(rp, "TYPE_REFERENCE", _, "Record", _, _, _, ref),
type_ref_parent(rp, ref, "1", child),
ref_shape_target(child, sh).
ref_name_is_container("Array").
ref_name_is_container("ReadonlyArray").
ref_name_is_container("Iterable").
Expand Down
Loading
Loading