mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-07 08:33:50 +08:00
Fix the species trace, deep release, iteration bound and optional length findings
This commit is contained in:
@@ -1481,13 +1481,11 @@ export function emitClassShapes(
|
||||
tracedFields.forEach((f, i) => {
|
||||
tr.push(...traceFieldVisitLines(struct, f.index, i, llvmCommentText(f.name)));
|
||||
});
|
||||
// An Array subclass instance's elements, when they are cycle-capable.
|
||||
const tracedElements =
|
||||
cls.arrayElem !== undefined &&
|
||||
traceAdapter(host, { kind: "array", elem: cls.arrayElem }) !== null;
|
||||
if (tracedElements) {
|
||||
host.declare(`declare void @scr_arr_trace_v(ptr, ptr, ptr)`);
|
||||
tr.push(` call void @scr_arr_trace_v(ptr %o, ptr %visit, ptr %ctx) ; the elements`);
|
||||
// An Array subclass instance's elements, when its current element
|
||||
// storage is cycle-capable (a species result can retype it).
|
||||
if (cls.arrayElem !== undefined) {
|
||||
host.declare(`declare void @scr_arr_subclass_trace(ptr, ptr, ptr)`);
|
||||
tr.push(` call void @scr_arr_subclass_trace(ptr %o, ptr %visit, ptr %ctx) ; the elements`);
|
||||
}
|
||||
tr.push(` ret void`, `}`, ``);
|
||||
defs.push(...tr);
|
||||
@@ -1508,9 +1506,9 @@ export function emitClassShapes(
|
||||
if (meta.threadSlot) gf.push(...slotForgetLines(host, struct, meta, "gfw"));
|
||||
if (cls.arrayElem !== undefined) {
|
||||
// The elements: traced ones are the collector's, the others are
|
||||
// released with the storage.
|
||||
host.declare(`declare void @scr_arr_subclass_gc_drop(ptr, i1 zeroext)`);
|
||||
gf.push(` call void @scr_arr_subclass_gc_drop(ptr %o, i1 ${tracedElements})`);
|
||||
// released with the storage (the same test as the trace's).
|
||||
host.declare(`declare void @scr_arr_subclass_gc_drop(ptr)`);
|
||||
gf.push(` call void @scr_arr_subclass_gc_drop(ptr %o)`);
|
||||
}
|
||||
gf.push(...emitObjectFree(host, true), ` ret void`, `}`, ``);
|
||||
defs.push(...gf);
|
||||
|
||||
@@ -12,6 +12,7 @@ import { isJsSourceFile, locOf } from "../../program.js";
|
||||
import { stripTypeWrappers } from "../typed-arguments.js";
|
||||
import { dynUndefinedExpr, nodeThrowExpr } from "../../../ir/build.js";
|
||||
import { lowerArrayFromCall, lowerArrayOfCall } from "../containers/array-construction.js";
|
||||
import { boundedArrayHofHelper } from "../containers/array-callbacks.js";
|
||||
import { storedClassValueType, type ClassInfo } from "../lower-classes.js";
|
||||
import {
|
||||
BOOL,
|
||||
@@ -34,6 +35,18 @@ export function arrayClassBase(
|
||||
lowerer: Lowerer,
|
||||
heritage: ts.ExpressionWithTypeArguments,
|
||||
): { elem: IrType } {
|
||||
const decl = heritage.parent?.parent;
|
||||
const self =
|
||||
decl && ts.isClassLikeDeclaration(decl)
|
||||
? lowerer.checker.getSymbolAtLocation(decl.name ?? decl)
|
||||
: undefined;
|
||||
const element = heritage.typeArguments?.nodes[0];
|
||||
if (
|
||||
self &&
|
||||
element &&
|
||||
mentionsClass(lowerer, lowerer.checker.getTypeAtLocation(element), self, new Set())
|
||||
)
|
||||
lowerer.unsupported("SC1090", heritage, "classes that extend an Array of their own instances");
|
||||
const mapped = lowerer.mapTypeOf(lowerer.checker.getTypeAtLocation(heritage));
|
||||
// `extends Array` in JavaScript reads `any[]`: dyn elements.
|
||||
if (isJsSourceFile(ts.getSourceFileOfNode(heritage)) && mapped?.kind !== "array")
|
||||
@@ -47,6 +60,26 @@ export function arrayClassBase(
|
||||
return { elem: mapped!.kind === "array" ? mapped!.elem : DYN };
|
||||
}
|
||||
|
||||
/** Whether `type` is the class `self`'s instance type, or holds it through
|
||||
* unions, intersections or type arguments (`Group | null`, `Group[]`,
|
||||
* `Map<string, Group>`): the class's array type would contain itself.
|
||||
* Another class's members do not count, its instances are references. */
|
||||
function mentionsClass(
|
||||
lowerer: Lowerer,
|
||||
type: ts.Type,
|
||||
self: ts.Symbol,
|
||||
seen: Set<ts.Type>,
|
||||
): boolean {
|
||||
if (seen.has(type)) return false;
|
||||
seen.add(type);
|
||||
if (type.getSymbol() === self) return true;
|
||||
if (type.isUnionType() || type.isIntersectionType())
|
||||
return ts.constituentTypes(type).some((part) => mentionsClass(lowerer, part, self, seen));
|
||||
return lowerer.checker
|
||||
.getTypeArguments(type as ts.TypeReference)
|
||||
.some((argument) => mentionsClass(lowerer, argument, self, seen));
|
||||
}
|
||||
|
||||
/** The class declared `extends Array` at the root of `info`'s chain, or null. */
|
||||
export function arrayRootOf(info: ClassInfo | null | undefined): ClassInfo | null {
|
||||
for (let c = info ?? null; c; c = c.base) if (c.extendsArray) return c;
|
||||
@@ -349,6 +382,15 @@ export function arrayClassFieldsFn(lowerer: Lowerer, info: ClassInfo): IrFunctio
|
||||
* take those arguments: they must be none, a forwarding rest (dyn or
|
||||
* number), or a number first parameter whose others may be omitted (whose
|
||||
* no-argument form throws). */
|
||||
/** A constructor parameter the species length argument can fill: a
|
||||
* number, a checked-dynamic value, or a union with a number arm
|
||||
* (`length?: number`). */
|
||||
function takesLength(lowerer: Lowerer, type: IrType): boolean {
|
||||
if (type.kind === "f64" || type.kind === "dyn") return true;
|
||||
if (type.kind !== "union") return false;
|
||||
return lowerer.unions.get(type.unionId)?.arms.some((arm) => arm.kind === "f64") === true;
|
||||
}
|
||||
|
||||
export function arrayClassSpeciesFn(lowerer: Lowerer, info: ClassInfo): IrFunction | null {
|
||||
const elem = arrayRootOf(info)!.extendsArray!.elem;
|
||||
const arrT = arrayOf(elem);
|
||||
@@ -381,31 +423,35 @@ export function arrayClassSpeciesFn(lowerer: Lowerer, info: ClassInfo): IrFuncti
|
||||
withLength = [{ kind: "arrayLit", elems: [n()], type: params[0]!.type, loc } as IrExpr];
|
||||
without = [{ kind: "arrayLit", elems: [], type: params[0]!.type, loc } as IrExpr];
|
||||
} else if (
|
||||
params[0]!.mode === "required" &&
|
||||
(params[0]!.type.kind === "f64" || params[0]!.type.kind === "dyn") &&
|
||||
(params[0]!.mode === "required" || params[0]!.mode === "omittable") &&
|
||||
takesLength(lowerer, params[0]!.type) &&
|
||||
params.slice(1).every((p) => p.mode === "omittable")
|
||||
) {
|
||||
const rest = params
|
||||
.slice(1)
|
||||
.map((p) =>
|
||||
lowerer.coerceToExpected(
|
||||
{ kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc } as IrExpr,
|
||||
p.type,
|
||||
),
|
||||
);
|
||||
// A dyn first parameter (a JavaScript `constructor(size)`) takes the
|
||||
// length as a number, and undefined in the no-argument form.
|
||||
if (params[0]!.type.kind === "dyn") without = [dynUndefinedExpr(loc), ...rest.map((e) => e)];
|
||||
withLength = [
|
||||
params[0]!.type.kind === "dyn" ? { kind: "dynFrom", value: n(), type: DYN, loc } : n(),
|
||||
...params
|
||||
.slice(1)
|
||||
.map((p) =>
|
||||
lowerer.coerceToExpected(
|
||||
// An omitted argument is what a call without it passes: the
|
||||
// parameter's default, or undefined.
|
||||
const blame = info.decl ?? null;
|
||||
const omitted = (p: (typeof params)[number]): IrExpr =>
|
||||
p.callDefault ??
|
||||
(blame
|
||||
? lowerer.undefinedArgFor(p.type, loc, blame)
|
||||
: lowerer.coerceToExpected(
|
||||
{ kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc } as IrExpr,
|
||||
p.type,
|
||||
),
|
||||
),
|
||||
));
|
||||
const rest = params.slice(1).map(omitted);
|
||||
// A dyn first parameter (a JavaScript `constructor(size)`) and an
|
||||
// optional or defaulted one take the length as a number, and their
|
||||
// omitted value in the no-argument form.
|
||||
if (params[0]!.type.kind === "dyn" || params[0]!.mode === "omittable")
|
||||
without = [
|
||||
params[0]!.type.kind === "dyn" ? dynUndefinedExpr(loc) : omitted(params[0]!),
|
||||
...rest,
|
||||
];
|
||||
withLength = [
|
||||
params[0]!.type.kind === "dyn"
|
||||
? { kind: "dynFrom", value: n(), type: DYN, loc }
|
||||
: lowerer.coerceToExpected(n(), params[0]!.type),
|
||||
...params.slice(1).map(omitted),
|
||||
];
|
||||
}
|
||||
if (withLength === null) return null;
|
||||
@@ -517,14 +563,17 @@ export function lowerWithArraySpecies(
|
||||
const receiverRef: IrExpr = { kind: "varRef", localId: local.id, type: receiver.type, loc };
|
||||
if (mode.callback) {
|
||||
// The species constructor runs before the first callback: the
|
||||
// arguments are evaluated in order, then the instance is created (with
|
||||
// the receiver's length for map, 0 otherwise), then the method runs and
|
||||
// its elements move into the instance.
|
||||
if (
|
||||
result.kind !== "call" ||
|
||||
result.args[0]?.kind !== "varRef" ||
|
||||
result.args[0].localId !== local.id
|
||||
)
|
||||
// arguments are evaluated in order, the receiver's length is read, the
|
||||
// instance is created (with that length for map, 0 otherwise), then
|
||||
// the method runs to that length and its elements move into the
|
||||
// instance.
|
||||
const bounded =
|
||||
result.kind === "call" &&
|
||||
result.args[0]?.kind === "varRef" &&
|
||||
result.args[0].localId === local.id
|
||||
? boundedArrayHofHelper(lowerer, result.callee)
|
||||
: null;
|
||||
if (bounded === null || result.kind !== "call")
|
||||
lowerer.unsupported(
|
||||
"SC1090",
|
||||
call,
|
||||
@@ -537,9 +586,25 @@ export function lowerWithArraySpecies(
|
||||
stmts.push({ kind: "varDecl", localId: held.id, init: arg, loc });
|
||||
args.push({ kind: "varRef", localId: held.id, type: arg.type, loc });
|
||||
}
|
||||
const bound = lowerer.declareHiddenLocal("%speciesLength", F64);
|
||||
stmts.push({
|
||||
kind: "varDecl",
|
||||
localId: bound.id,
|
||||
init: {
|
||||
kind: "arrIntrinsic",
|
||||
method: "length",
|
||||
receiver: receiverRef,
|
||||
args: [],
|
||||
type: F64,
|
||||
loc,
|
||||
},
|
||||
loc,
|
||||
});
|
||||
const boundRef: IrExpr = { kind: "varRef", localId: bound.id, type: F64, loc };
|
||||
args.push(boundRef);
|
||||
const instance = lowerer.declareHiddenLocal("%speciesInstance", receiver.type);
|
||||
const length: IrExpr = mode.resultLength
|
||||
? { kind: "arrIntrinsic", method: "length", receiver: receiverRef, args: [], type: F64, loc }
|
||||
? boundRef
|
||||
: { kind: "numLit", value: 0, type: F64, loc };
|
||||
stmts.push({
|
||||
kind: "varDecl",
|
||||
@@ -561,7 +626,7 @@ export function lowerWithArraySpecies(
|
||||
fn: "array.speciesFinish",
|
||||
args: [
|
||||
{ kind: "varRef", localId: instance.id, type: receiver.type, loc },
|
||||
{ ...result, args },
|
||||
{ ...result, callee: bounded, args },
|
||||
{ kind: "boolLit", value: mode.setLength, type: BOOL, loc },
|
||||
],
|
||||
type: result.type,
|
||||
|
||||
@@ -3171,3 +3171,33 @@ export function lowerTuplePredicateCall(
|
||||
loc,
|
||||
};
|
||||
}
|
||||
|
||||
/** The `%arr.<method>` helper `name` (map, flatMap or filter) taking its
|
||||
* iteration bound as a last parameter: the loop runs to the bound instead
|
||||
* of the receiver's length when it starts. ArraySpeciesCreate reads the
|
||||
* receiver's length before the species constructor runs, so a constructor
|
||||
* that appends to the receiver does not extend the loop
|
||||
* (lowerWithArraySpecies). Null when `name` is no such helper. */
|
||||
export function boundedArrayHofHelper(lowerer: Lowerer, name: string): string | null {
|
||||
const info = lowerer.arrHofInfo.get(name);
|
||||
if (!info || info.method === "forEach") return null;
|
||||
const bounded = `${name}.bounded`;
|
||||
if (lowerer.liftedFnNamed(bounded)) return bounded;
|
||||
const fn = lowerer.liftedFnNamed(name);
|
||||
if (!fn) return null;
|
||||
let replaced = false;
|
||||
const body = fn.body.map((stmt): IrStmt => {
|
||||
if (replaced || stmt.kind !== "varDecl" || stmt.localId !== "n.0") return stmt;
|
||||
replaced = true;
|
||||
return { ...stmt, init: varRef("bound.0", F64, stmt.loc) };
|
||||
});
|
||||
if (!replaced) return null;
|
||||
lowerer.liftedFns.push({
|
||||
...fn,
|
||||
name: bounded,
|
||||
params: [...fn.params, { localId: "bound.0", name: "bound", type: F64 }],
|
||||
locals: [...fn.locals, { id: "bound.0", name: "bound", type: F64, mutable: false }],
|
||||
body,
|
||||
});
|
||||
return bounded;
|
||||
}
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/7730-array-subclass-retyped-trace.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/7731-array-subclass-deep-release.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/7732-array-subclass-species-bound.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/7733-array-subclass-optional-length.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -1868,8 +1868,21 @@ const ScrArrClass *scr_arr_class_of(const ScrArr *a) {
|
||||
return scr_arr_ext_of((ScrArr *)a)->cls;
|
||||
}
|
||||
|
||||
void scr_arr_subclass_gc_drop(ScrArr *a, bool traced) {
|
||||
if (!traced) scr_arr_release_contents(a);
|
||||
/* Whether an Array subclass instance's elements are the collector's: its
|
||||
* current element ops trace them. A species result can retype an empty
|
||||
* instance's storage (scr_arr_species_move), so this follows the storage,
|
||||
* not the element type the class declares. */
|
||||
static bool scr_arr_subclass_elements_traced(const ScrArr *a) {
|
||||
const ScrArrOps *ops = scr_arr_ops(a);
|
||||
return ops != NULL && ops->trace != NULL;
|
||||
}
|
||||
|
||||
void scr_arr_subclass_trace(void *a, ScrTraceVisit visit, void *ctx) {
|
||||
if (scr_arr_subclass_elements_traced((ScrArr *)a)) scr_arr_trace_v(a, visit, ctx);
|
||||
}
|
||||
|
||||
void scr_arr_subclass_gc_drop(ScrArr *a) {
|
||||
if (!scr_arr_subclass_elements_traced(a)) scr_arr_release_contents(a);
|
||||
scr_arr_free_parts(a);
|
||||
a->data = NULL;
|
||||
a->len = a->cap = a->filled = 0;
|
||||
@@ -1906,7 +1919,7 @@ void scr_arr_release(ScrArr *a) {
|
||||
/* An Array subclass instance: its class tears it down. */
|
||||
if ((a->spare & SCR_ARR_HAS_EXT) && scr_arr_ext_of(a)->cls) {
|
||||
if (a->cyc) scr_cyc_dead(a);
|
||||
scr_arr_ext_of(a)->cls->destroy(a);
|
||||
scr_rc_destroy(a, scr_arr_ext_of(a)->cls->destroy);
|
||||
return;
|
||||
}
|
||||
/* Nothing to tear down: free the storage without scr_rc_destroy's
|
||||
|
||||
@@ -185,7 +185,7 @@ typedef struct ScrArrClass {
|
||||
const char *name;
|
||||
/* Destroys an instance whose count reached zero: releases the class's own
|
||||
* fields, then scr_arr_subclass_drop, then frees the instance. */
|
||||
void (*destroy)(ScrArr *a);
|
||||
void (*destroy)(void *a);
|
||||
/* The class's own fields as a dyn object in declaration order (the array
|
||||
* is passed owned, +1), or NULL when the class declares none. util.inspect
|
||||
* and Object.keys list them after the elements. */
|
||||
@@ -503,7 +503,10 @@ void scr_arr_subclass_init(ScrArr *a, uint8_t elem, const ScrArrOps *ops, const
|
||||
bool cyc);
|
||||
/* The collector's teardown of an Array subclass instance's storage: the
|
||||
* elements are released unless they are the collector's (`traced`). */
|
||||
void scr_arr_subclass_gc_drop(ScrArr *a, bool traced);
|
||||
void scr_arr_subclass_gc_drop(ScrArr *a);
|
||||
/* The collector trace of an Array subclass instance's elements: visits
|
||||
* them when its current element ops trace them. */
|
||||
void scr_arr_subclass_trace(void *a, ScrTraceVisit visit, void *ctx);
|
||||
/* super(...args) in a class that extends Array (the Array constructor's
|
||||
* length-or-elements rule); `args` is borrowed. */
|
||||
void scr_arr_subclass_construct(ScrArr *a, ScrArr *args, bool dyn_elems);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// A cycle-capable class that extends Array (its elements and its own field
|
||||
// can close a cycle) whose map results take other element types: the
|
||||
// collector traces and tears down each instance by the elements it holds.
|
||||
// Garbage cycles make the collector run while the results are alive.
|
||||
class Holder {
|
||||
group: Group | null = null;
|
||||
}
|
||||
|
||||
class Group extends Array<Holder | null> {
|
||||
owner: Holder | null = null;
|
||||
}
|
||||
|
||||
function churn(rounds: number): number {
|
||||
let made = 0;
|
||||
for (let i = 0; i < rounds; i++) {
|
||||
const holder = new Holder();
|
||||
const group = new Group();
|
||||
group.owner = holder;
|
||||
holder.group = group;
|
||||
group.push(holder);
|
||||
made++;
|
||||
}
|
||||
return made;
|
||||
}
|
||||
|
||||
const groups = new Group();
|
||||
groups.push(new Holder(), null, new Holder());
|
||||
const numbers = groups.map((_, i) => i * 1.5 + 1e6);
|
||||
const labels = groups.map((_, i) => `label-${i}`);
|
||||
console.log(churn(20000));
|
||||
console.log(numbers instanceof Group, numbers.length, numbers.join(","));
|
||||
console.log(labels instanceof Group, labels.join(","));
|
||||
console.log(churn(20000), numbers[2], labels[1]);
|
||||
@@ -0,0 +1,17 @@
|
||||
// Releasing a long chain of instances of a class that extends Array, each
|
||||
// holding the next one, tears them down without growing the native stack.
|
||||
class Link extends Array<number> {
|
||||
next: Link | null = null;
|
||||
}
|
||||
|
||||
let head: Link | null = new Link();
|
||||
let tail = head;
|
||||
for (let i = 0; i < 300000; i++) {
|
||||
const link = new Link();
|
||||
link.push(i);
|
||||
tail.next = link;
|
||||
tail = link;
|
||||
}
|
||||
console.log(tail.length, tail[0]);
|
||||
head = null;
|
||||
console.log("released", head);
|
||||
@@ -0,0 +1,32 @@
|
||||
// map, filter and flatMap read the receiver's length before the species
|
||||
// constructor runs: elements the constructor appends to the receiver are
|
||||
// not visited.
|
||||
let watched: Growing | null = null;
|
||||
|
||||
class Growing extends Array<number> {
|
||||
constructor(...args: number[]) {
|
||||
super(...args);
|
||||
if (watched !== null) watched.push(watched.length * 100);
|
||||
}
|
||||
}
|
||||
|
||||
const source = Growing.from([1, 2, 3]);
|
||||
watched = source;
|
||||
const visits: number[] = [];
|
||||
const doubled = source.map((value) => {
|
||||
visits.push(value);
|
||||
return value * 2;
|
||||
});
|
||||
console.log(doubled.join(","), visits.join(","), source.join(","));
|
||||
visits.length = 0;
|
||||
const kept = source.filter((value) => {
|
||||
visits.push(value);
|
||||
return value > 1;
|
||||
});
|
||||
console.log(kept.join(","), visits.join(","), source.length);
|
||||
visits.length = 0;
|
||||
const spread = source.flatMap((value) => {
|
||||
visits.push(value);
|
||||
return [value];
|
||||
});
|
||||
console.log(spread.length, visits.length, source.length);
|
||||
@@ -0,0 +1,26 @@
|
||||
// A class that extends Array whose constructor's length parameter is
|
||||
// optional or has a default takes the species length argument: map,
|
||||
// filter and slice construct their results through it.
|
||||
class Optional extends Array<number> {
|
||||
constructor(length?: number) {
|
||||
super(length ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
class Defaulted extends Array<string> {
|
||||
tag: string;
|
||||
constructor(length = 0, tag = "none") {
|
||||
super(length);
|
||||
this.tag = tag;
|
||||
}
|
||||
}
|
||||
|
||||
const optional = Optional.from([1, 2, 3]);
|
||||
const mapped = optional.map((value) => value * 10);
|
||||
console.log(mapped instanceof Optional, mapped.join(","), optional.slice(1) instanceof Optional);
|
||||
console.log(optional.filter((value) => value !== 2).join(","));
|
||||
|
||||
const defaulted = new Defaulted(2).fill("x");
|
||||
const sliced = defaulted.slice(0, 1);
|
||||
if (sliced instanceof Defaulted) console.log(sliced.length, sliced.tag);
|
||||
console.log(defaulted.map((s) => s + "!").join(","));
|
||||
@@ -1,6 +1,7 @@
|
||||
// Array subclasses that do not compile yet, each refused where it is
|
||||
// declared: a generic subclass, a subclass of a class that extends Array,
|
||||
// and a class that declares its own Symbol.species.
|
||||
// a class that declares its own Symbol.species, and a class whose elements
|
||||
// are its own instances.
|
||||
class Bag<T> extends Array<T> {
|
||||
firstOr(fallback: T): T {
|
||||
return this.length > 0 ? this[0]! : fallback;
|
||||
@@ -16,6 +17,9 @@ class Speciated extends Array<number> {
|
||||
}
|
||||
}
|
||||
|
||||
class Nested extends Array<Nested | null> {}
|
||||
|
||||
console.log(new Bag<number>().firstOr(1));
|
||||
console.log(new Derived().length);
|
||||
console.log(new Speciated().length);
|
||||
console.log(new Nested().length);
|
||||
|
||||
@@ -1,41 +1,55 @@
|
||||
array-subclass-fences.ts:4:22 - error SC1090: a generic class that extends Array is not supported yet
|
||||
array-subclass-fences.ts:5:22 - error SC1090: a generic class that extends Array is not supported yet
|
||||
|
||||
3 | // and a class that declares its own Symbol.species.
|
||||
4 | class Bag<T> extends Array<T> {
|
||||
4 | // are its own instances.
|
||||
5 | class Bag<T> extends Array<T> {
|
||||
| ^~~~~~~~
|
||||
5 | firstOr(fallback: T): T {
|
||||
6 | firstOr(fallback: T): T {
|
||||
|
||||
array-subclass-fences.ts:11:23 - error SC1090: extending 'Base', a class that extends Array (one level of Array subclass compiles) is not supported yet
|
||||
array-subclass-fences.ts:12:23 - error SC1090: extending 'Base', a class that extends Array (one level of Array subclass compiles) is not supported yet
|
||||
|
||||
10 | class Base extends Array<number> {}
|
||||
11 | class Derived extends Base {}
|
||||
11 | class Base extends Array<number> {}
|
||||
12 | class Derived extends Base {}
|
||||
| ^~~~
|
||||
12 |
|
||||
13 |
|
||||
|
||||
array-subclass-fences.ts:14:3 - error SC1090: Symbol.species on a class that extends Array (its methods' results construct the class itself) is not supported yet
|
||||
array-subclass-fences.ts:15:3 - error SC1090: Symbol.species on a class that extends Array (its methods' results construct the class itself) is not supported yet
|
||||
|
||||
13 | class Speciated extends Array<number> {
|
||||
14 | static get [Symbol.species](): ArrayConstructor {
|
||||
14 | class Speciated extends Array<number> {
|
||||
15 | static get [Symbol.species](): ArrayConstructor {
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
15 | return Array;
|
||||
16 | return Array;
|
||||
|
||||
array-subclass-fences.ts:19:13 - error SC1090: constructing values other than classes declared in the program is not supported yet
|
||||
array-subclass-fences.ts:20:22 - error SC1090: classes that extend an Array of their own instances are not supported yet
|
||||
|
||||
18 |
|
||||
19 | console.log(new Bag<number>().firstOr(1));
|
||||
19 |
|
||||
20 | class Nested extends Array<Nested | null> {}
|
||||
| ^~~~~~~~~~~~~~~~~~~~
|
||||
21 |
|
||||
|
||||
array-subclass-fences.ts:22:13 - error SC1090: constructing values other than classes declared in the program is not supported yet
|
||||
|
||||
21 |
|
||||
22 | console.log(new Bag<number>().firstOr(1));
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
20 | console.log(new Derived().length);
|
||||
23 | console.log(new Derived().length);
|
||||
|
||||
array-subclass-fences.ts:20:13 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet
|
||||
array-subclass-fences.ts:23:13 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet
|
||||
|
||||
19 | console.log(new Bag<number>().firstOr(1));
|
||||
20 | console.log(new Derived().length);
|
||||
22 | console.log(new Bag<number>().firstOr(1));
|
||||
23 | console.log(new Derived().length);
|
||||
| ^~~~~~~~~~~~~
|
||||
21 | console.log(new Speciated().length);
|
||||
24 | console.log(new Speciated().length);
|
||||
|
||||
array-subclass-fences.ts:21:13 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet
|
||||
array-subclass-fences.ts:24:13 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet
|
||||
|
||||
20 | console.log(new Derived().length);
|
||||
21 | console.log(new Speciated().length);
|
||||
23 | console.log(new Derived().length);
|
||||
24 | console.log(new Speciated().length);
|
||||
| ^~~~~~~~~~~~~~~
|
||||
22 |
|
||||
25 | console.log(new Nested().length);
|
||||
|
||||
array-subclass-fences.ts:25:13 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet
|
||||
|
||||
24 | console.log(new Speciated().length);
|
||||
25 | console.log(new Nested().length);
|
||||
| ^~~~~~~~~~~~
|
||||
26 |
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 11000, "san": 9000 }
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 11000, "san": 9000 }
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 11000, "san": 9000 }
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 11000, "san": 9000 }
|
||||
Reference in New Issue
Block a user