@scriptc/compiler 0.0.32 → 0.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend/cc-cache.test.js +430 -1
- package/dist/backend/cc-cache.test.js.map +1 -1
- package/dist/backend/cc.d.ts +25 -0
- package/dist/backend/cc.js +818 -88
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-exprs.js +79 -3
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-shapes.js +37 -4
- package/dist/backend/emission/emit-shapes.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +23 -7
- package/dist/backend/emission/emitter.js +171 -27
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/emission/may-throw.js +7 -4
- package/dist/backend/emission/may-throw.js.map +1 -1
- package/dist/backend/ffi-callbacks.d.ts +38 -1
- package/dist/backend/ffi-callbacks.js +76 -3
- package/dist/backend/ffi-callbacks.js.map +1 -1
- package/dist/backend/library-identity.d.ts +22 -0
- package/dist/backend/library-identity.js +103 -0
- package/dist/backend/library-identity.js.map +1 -0
- package/dist/backend/llvm/emitter.d.ts +3 -0
- package/dist/backend/llvm/emitter.js +278 -17
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/shapes.d.ts +1 -0
- package/dist/backend/llvm/shapes.js +25 -1
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/mangle.d.ts +1 -0
- package/dist/backend/mangle.js +3 -0
- package/dist/backend/mangle.js.map +1 -1
- package/dist/backend/object-localize.d.ts +5 -5
- package/dist/backend/object-localize.js +19 -16
- package/dist/backend/object-localize.js.map +1 -1
- package/dist/backend/object-localize.test.js +37 -13
- package/dist/backend/object-localize.test.js.map +1 -1
- package/dist/diagnostics/diagnostic.js +2 -0
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/ffi/profile.d.ts +17 -5
- package/dist/ffi/profile.js +169 -22
- package/dist/ffi/profile.js.map +1 -1
- package/dist/frontend/input-tracker.d.ts +82 -0
- package/dist/frontend/input-tracker.js +380 -0
- package/dist/frontend/input-tracker.js.map +1 -0
- package/dist/frontend/input-tracker.test.d.ts +1 -0
- package/dist/frontend/input-tracker.test.js +137 -0
- package/dist/frontend/input-tracker.test.js.map +1 -0
- package/dist/frontend/lowering/lower-builtins.js +2 -8
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +1 -0
- package/dist/frontend/lowering/lower-calls.js +244 -163
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +3 -0
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.js +113 -28
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +67 -1
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +45 -26
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +1 -1
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +48 -3
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +79 -29
- package/dist/frontend/lowering/lowerer.js +429 -87
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/npm-static-rewrite.js +25 -26
- package/dist/frontend/npm-static-rewrite.js.map +1 -1
- package/dist/frontend/npm-static.js +29 -30
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm-static.test.d.ts +1 -0
- package/dist/frontend/npm-static.test.js +15 -0
- package/dist/frontend/npm-static.test.js.map +1 -0
- package/dist/frontend/npm.js +5 -17
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/program.js +2 -6
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/resolve.js +8 -29
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/ts7/checker.d.ts +2 -0
- package/dist/frontend/ts7/checker.js +20 -3
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/ts7/program.js +17 -18
- package/dist/frontend/ts7/program.js.map +1 -1
- package/dist/frontend/types.d.ts +41 -0
- package/dist/frontend/types.js +147 -8
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +275 -41
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +42 -12
- package/dist/ir/nodes.js +7 -1
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +1 -1
- package/dist/ir/validate.js +104 -1
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/early-cache.d.ts +65 -0
- package/dist/library/early-cache.js +380 -0
- package/dist/library/early-cache.js.map +1 -0
- package/dist/library/early-cache.test.d.ts +1 -0
- package/dist/library/early-cache.test.js +528 -0
- package/dist/library/early-cache.test.js.map +1 -0
- package/dist/library/int-infer.js +14 -0
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +21 -4
- package/dist/library/int-infer.test.js.map +1 -1
- package/dist/library/profile.d.ts +3 -0
- package/dist/library/profile.js +9 -1
- package/dist/library/profile.js.map +1 -1
- package/dist/library/semantic-source.d.ts +13 -0
- package/dist/library/semantic-source.js +257 -0
- package/dist/library/semantic-source.js.map +1 -0
- package/dist/library/semantic-source.test.d.ts +1 -0
- package/dist/library/semantic-source.test.js +113 -0
- package/dist/library/semantic-source.test.js.map +1 -0
- package/dist/library/sidecar.d.ts +6 -0
- package/dist/library/sidecar.js +11 -0
- package/dist/library/sidecar.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +1 -1
|
@@ -428,6 +428,7 @@ export declare function spreadNeedsRuntimeArity(shapes: readonly ParamShape[], a
|
|
|
428
428
|
* is-not-a-function where the kind's prototype lacks the name, and
|
|
429
429
|
* fences LOUDLY on real-but-unimplemented pairs. */
|
|
430
430
|
export declare const DYN_DISPATCH_METHODS: Set<string>;
|
|
431
|
+
export declare function lowerDynDispatchMethodCall(L: Lowerer, call: ts.CallExpression, access: ts.PropertyAccessExpression, recv: IrExpr, arrayReceiver: boolean): IrExpr | null;
|
|
431
432
|
/** Radix-free `.toString()` on a PRIMITIVE receiver: numbers take the
|
|
432
433
|
* STATIC JS-exact number formatter — the same `toString` node templates
|
|
433
434
|
* and String(n) lower to (Number::toString with radix 10 IS that
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* generic instantiation (bounded by MAX_GENERIC_INSTANCES). */
|
|
5
5
|
import * as ts from "../ts7/adapter.js";
|
|
6
6
|
import { lowerGenMethodCall } from "./lower-generators.js";
|
|
7
|
-
import { BOOL, CAUGHT, DYN, F64, JSVAL, STRING, SYMBOL_T, UNDEFINED_T, VOID, arrayOf, canBoxFuncIntoDyn, canConvertToDyn, canDynCheckTo, canMarshalTypedFuncIntoIsland, ffiClassType, ffiSourceParamTypes, funcOf, isFfiCallbackParam, isFfiContextParam, isUnitType, shapeHasAccessorSlots, typeEquals } from "../../ir/nodes.js";
|
|
7
|
+
import { BOOL, CAUGHT, DYN, F64, JSVAL, STRING, SYMBOL_T, UNDEFINED_T, VOID, arrayOf, canBoxFuncIntoDyn, canConvertToDyn, canDynCheckTo, canMarshalTypedFuncIntoIsland, ffiClassType, ffiSourceParamTypes, funcOf, isFfiCallbackParam, isFfiContextParam, isFfiReleaseParam, isUnitType, shapeHasAccessorSlots, typeEquals } from "../../ir/nodes.js";
|
|
8
8
|
import { isJsSourceFile, locOf } from "../program.js";
|
|
9
9
|
import { isGenericCallableMemberType, typeKey } from "../types.js";
|
|
10
10
|
import { PoisonError, dynFallbackType, dynUndefinedExpr, importCallHandleType, jsFuncNameOf, newFnCtx, nodeThrowExpr } from "./lowerer.js";
|
|
@@ -931,6 +931,7 @@ export function internGenericInstance(L, blame, info, params, returnType, makeBi
|
|
|
931
931
|
info.instances.set(key, inst);
|
|
932
932
|
L.instantiationQueue.push({ info, inst });
|
|
933
933
|
}
|
|
934
|
+
L.noteGenericInstanceDemand(inst);
|
|
934
935
|
return inst;
|
|
935
936
|
}
|
|
936
937
|
/** The type parameters of `info` declared with a `keyof` CONSTRAINT
|
|
@@ -2276,7 +2277,11 @@ function ffiSourceParams(binding) {
|
|
|
2276
2277
|
return binding.params.filter((param) => !isFfiContextParam(param));
|
|
2277
2278
|
}
|
|
2278
2279
|
function ffiParamDisplay(param) {
|
|
2279
|
-
return isFfiCallbackParam(param)
|
|
2280
|
+
return isFfiCallbackParam(param)
|
|
2281
|
+
? `callback '${param.callback.id}'`
|
|
2282
|
+
: isFfiReleaseParam(param)
|
|
2283
|
+
? `release callback '${param.callback.release}'`
|
|
2284
|
+
: `class '${param}'`;
|
|
2280
2285
|
}
|
|
2281
2286
|
/** Callback arguments flow from native code into TypeScript. Their source
|
|
2282
2287
|
* declarations therefore need to cover the whole scalar domain represented
|
|
@@ -2302,12 +2307,23 @@ function ffiCallbackInputDiagnostic(L, descriptor, callbackType) {
|
|
|
2302
2307
|
!typeEquals(mapped, ffiClassType(nativeClass))) {
|
|
2303
2308
|
continue;
|
|
2304
2309
|
}
|
|
2305
|
-
|
|
2310
|
+
if (nativeClass === "bytes")
|
|
2311
|
+
continue;
|
|
2312
|
+
const domain = nativeClass === "bool"
|
|
2313
|
+
? "boolean"
|
|
2314
|
+
: nativeClass === "cstring" || nativeClass === "string"
|
|
2315
|
+
? "string"
|
|
2316
|
+
: "number";
|
|
2306
2317
|
const coversDomain = nativeClass === "bool"
|
|
2307
2318
|
? (paramType.flags & ts.TypeFlags.Boolean) !== 0
|
|
2308
|
-
:
|
|
2319
|
+
: nativeClass === "cstring" || nativeClass === "string"
|
|
2320
|
+
? (paramType.flags & ts.TypeFlags.String) !== 0
|
|
2321
|
+
: (paramType.flags & ts.TypeFlags.Number) !== 0;
|
|
2309
2322
|
if (!coversDomain) {
|
|
2310
|
-
|
|
2323
|
+
const descriptorName = isFfiCallbackParam(descriptor)
|
|
2324
|
+
? descriptor.callback.id
|
|
2325
|
+
: descriptor.callback.release;
|
|
2326
|
+
return (`callback '${descriptorName}' parameter ${i + 1} is '${L.checker.typeToString(paramType)}', ` +
|
|
2311
2327
|
`but native class '${nativeClass}' may supply any ${domain}; declare it as '${domain}'`);
|
|
2312
2328
|
}
|
|
2313
2329
|
}
|
|
@@ -2379,7 +2395,7 @@ function ffiDeclarationDiagnostic(L, binding, symbol, loc) {
|
|
|
2379
2395
|
if ((paramType.flags & ts.TypeFlags.Never) !== 0) {
|
|
2380
2396
|
return signatureDiag(binding.name, `parameter ${i + 1} is 'never', an uninhabited TypeScript type that cannot describe a native ABI parameter`, loc);
|
|
2381
2397
|
}
|
|
2382
|
-
if (isFfiCallbackParam(sourceParam)) {
|
|
2398
|
+
if (isFfiCallbackParam(sourceParam) || isFfiReleaseParam(sourceParam)) {
|
|
2383
2399
|
const callbackDiagnostic = ffiCallbackInputDiagnostic(L, sourceParam, paramType);
|
|
2384
2400
|
if (callbackDiagnostic !== null) {
|
|
2385
2401
|
return signatureDiag(binding.name, callbackDiagnostic, loc);
|
|
@@ -2602,7 +2618,45 @@ export function lowerFfiCall(L, expr) {
|
|
|
2602
2618
|
signatureError(`this call passes ${expr.arguments.length} argument(s), but the native binding requires exactly ${expectedParams.length}`);
|
|
2603
2619
|
}
|
|
2604
2620
|
const expectedReturn = ffiClassType(binding.returns);
|
|
2605
|
-
const
|
|
2621
|
+
const sourceParams = ffiSourceParams(binding);
|
|
2622
|
+
const args = expr.arguments.map((arg, i) => {
|
|
2623
|
+
const sourceParam = sourceParams[i];
|
|
2624
|
+
const expected = expectedParams[i];
|
|
2625
|
+
const lowered = L.lowerExprExpecting(arg, expected);
|
|
2626
|
+
// Retained identity is the runtime closure pointer. A coercion adapter
|
|
2627
|
+
// would be freshly allocated at registration and release sites, so an
|
|
2628
|
+
// assignable-but-different function shape (notably `() => number` into
|
|
2629
|
+
// `() => void`) cannot honestly participate in explicit release. The
|
|
2630
|
+
// adapter set comes from the mint sites themselves (Lowerer's
|
|
2631
|
+
// freshClosureAdapters), not name-prefix matching, so a new coercion
|
|
2632
|
+
// helper cannot silently slip past this guard.
|
|
2633
|
+
if (isFfiReleaseParam(sourceParam) ||
|
|
2634
|
+
(isFfiCallbackParam(sourceParam) && sourceParam.callback.lifetime === "retained")) {
|
|
2635
|
+
if (lowered.kind === "dynCheck" ||
|
|
2636
|
+
(lowered.kind === "call" && L.freshClosureAdapters.has(lowered.callee))) {
|
|
2637
|
+
signatureError(`retained callback argument ${i + 1} must have the exact manifest function type; ` +
|
|
2638
|
+
`an implicit function adapter would change its release identity`);
|
|
2639
|
+
}
|
|
2640
|
+
// An inline function value at a RELEASE site can never match:
|
|
2641
|
+
// lifted lambdas always carry a captures list (even an empty one),
|
|
2642
|
+
// so both backends mint a fresh closure per evaluation of the
|
|
2643
|
+
// expression — the release argument is a pointer no registration
|
|
2644
|
+
// holds, a guaranteed runtime trap. Declared functions stay valid
|
|
2645
|
+
// here — their value is the interned immortal closure (captures
|
|
2646
|
+
// undefined), one pointer for every mention. Registration sites
|
|
2647
|
+
// still accept literals: an unnameable registration is simply
|
|
2648
|
+
// permanent, released by the exit teardown (the live-at-exit
|
|
2649
|
+
// fixture shape), and hides no matching failure.
|
|
2650
|
+
if (isFfiReleaseParam(sourceParam) &&
|
|
2651
|
+
lowered.kind === "closure" &&
|
|
2652
|
+
L.liftedFns.some((f) => f.name === lowered.fnName && f.captures !== undefined)) {
|
|
2653
|
+
signatureError(`retained callback argument ${i + 1} cannot be an inline function value; ` +
|
|
2654
|
+
`each evaluation creates a fresh closure no registration holds — ` +
|
|
2655
|
+
`pass the same named value used to register`);
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
return lowered;
|
|
2659
|
+
});
|
|
2606
2660
|
return {
|
|
2607
2661
|
kind: "ffiCall",
|
|
2608
2662
|
import: binding.name,
|
|
@@ -4155,7 +4209,8 @@ function lowerDynReceiverMethodCall(L, call, access) {
|
|
|
4155
4209
|
// take the ordinary typed paths, but there is no static home for an
|
|
4156
4210
|
// any-elemented array). Typed receivers keep their own lowerings.
|
|
4157
4211
|
const recvTs = L.typeOf(access.expression);
|
|
4158
|
-
const
|
|
4212
|
+
const arrayReceiver = L.checker.isArrayType(recvTs);
|
|
4213
|
+
const anyArray = arrayReceiver &&
|
|
4159
4214
|
((L.checker.getTypeArguments(recvTs)[0]?.flags ?? 0) &
|
|
4160
4215
|
(ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0;
|
|
4161
4216
|
let recv;
|
|
@@ -4234,21 +4289,9 @@ function lowerDynReceiverMethodCall(L, call, access) {
|
|
|
4234
4289
|
// unimplemented methods throw a LOUD not-supported Error; names the
|
|
4235
4290
|
// kind's prototype lacks throw Node's "x.y is not a function"; OBJ
|
|
4236
4291
|
// receivers call the own member.
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
}
|
|
4241
|
-
const args = call.arguments.map((a) => L.lowerExprExpecting(a, DYN));
|
|
4242
|
-
return {
|
|
4243
|
-
kind: "dynInvoke",
|
|
4244
|
-
recv,
|
|
4245
|
-
method: access.name.text,
|
|
4246
|
-
calleeName: access.getText(),
|
|
4247
|
-
args,
|
|
4248
|
-
type: DYN,
|
|
4249
|
-
loc: locOf(call),
|
|
4250
|
-
};
|
|
4251
|
-
}
|
|
4292
|
+
const dispatched = lowerDynDispatchMethodCall(L, call, access, recv, arrayReceiver);
|
|
4293
|
+
if (dispatched)
|
|
4294
|
+
return dispatched;
|
|
4252
4295
|
// Names NO dyn-representable prototype declares: the member can only
|
|
4253
4296
|
// be an OWN property, so "read the member, call it" IS Node's
|
|
4254
4297
|
// semantics for every possible dyn value — `handlers.onDone(x)` on a
|
|
@@ -4359,6 +4402,30 @@ export const DYN_DISPATCH_METHODS = new Set([
|
|
|
4359
4402
|
"request", "sendTrailers", "priority", "settings", "goaway", "ping",
|
|
4360
4403
|
"additionalHeaders", "altsvc", "origin",
|
|
4361
4404
|
]);
|
|
4405
|
+
export function lowerDynDispatchMethodCall(L, call, access, recv, arrayReceiver) {
|
|
4406
|
+
const method = access.name.text;
|
|
4407
|
+
if (!DYN_DISPATCH_METHODS.has(method) || call.questionDotToken || access.questionDotToken)
|
|
4408
|
+
return null;
|
|
4409
|
+
if (call.arguments.some((arg) => ts.isSpreadElement(arg))) {
|
|
4410
|
+
L.unsupported("SC1090", call, "spread arguments in calls through 'unknown' values");
|
|
4411
|
+
}
|
|
4412
|
+
const predicate = method === "filter" && call.arguments[0]
|
|
4413
|
+
? L.lowerExpr(call.arguments[0])
|
|
4414
|
+
: null;
|
|
4415
|
+
if (arrayReceiver && predicate?.type.kind === "func" && predicate.type.ret.kind === "void") {
|
|
4416
|
+
L.unsupported("SC1090", call.arguments[0], "'.filter()' with a void-returning predicate (the callback return value is erased before its truthiness can be tested)");
|
|
4417
|
+
}
|
|
4418
|
+
const args = call.arguments.map((arg, i) => i === 0 && predicate ? L.coerceInto(arg, predicate, DYN) : L.lowerExprExpecting(arg, DYN));
|
|
4419
|
+
return {
|
|
4420
|
+
kind: "dynInvoke",
|
|
4421
|
+
recv,
|
|
4422
|
+
method,
|
|
4423
|
+
calleeName: access.getText(),
|
|
4424
|
+
args,
|
|
4425
|
+
type: DYN,
|
|
4426
|
+
loc: locOf(call),
|
|
4427
|
+
};
|
|
4428
|
+
}
|
|
4362
4429
|
/** STR_METHODS ∪ the regex-form names, MINUS everything Array (or any
|
|
4363
4430
|
* other dyn kind's prototype) also declares. */
|
|
4364
4431
|
const DYN_STRING_ONLY_METHODS = new Set([
|
|
@@ -6343,48 +6410,8 @@ export function recordKeysArrayCall(L, receiver, argIr, shape, loc) {
|
|
|
6343
6410
|
helper = `%obj.keys.${L.arrHofHelpers.size}`;
|
|
6344
6411
|
const ref = { kind: "varRef", localId: "r.0", type: argIr, loc };
|
|
6345
6412
|
const outRef = { kind: "varRef", localId: "out.0", type: resultT, loc };
|
|
6346
|
-
const body = [
|
|
6347
|
-
{ kind: "varDecl", localId: "out.0", init: { kind: "arrayLit", elems: [], type: resultT, loc }, loc },
|
|
6348
|
-
];
|
|
6349
|
-
const order = shape.declaredOrder ?? shape.fields.map((f) => f.name);
|
|
6350
|
-
for (const name of order) {
|
|
6351
|
-
const f = shape.fields.find((x) => x.name === name);
|
|
6352
|
-
const pushStmt = {
|
|
6353
|
-
kind: "exprStmt",
|
|
6354
|
-
expr: {
|
|
6355
|
-
kind: "arrIntrinsic",
|
|
6356
|
-
method: "push",
|
|
6357
|
-
receiver: outRef,
|
|
6358
|
-
args: [{ kind: "strLit", value: f.name, type: STRING, loc }],
|
|
6359
|
-
type: F64,
|
|
6360
|
-
loc,
|
|
6361
|
-
},
|
|
6362
|
-
loc,
|
|
6363
|
-
};
|
|
6364
|
-
// Undefined-armed fields: the push is guarded by a tag test (the
|
|
6365
|
-
// key exists exactly when the arm is not undefined).
|
|
6366
|
-
const utag = f.type.kind === "union" ? L.armTag(f.type.unionId, UNDEFINED_T) : -1;
|
|
6367
|
-
body.push(utag >= 0 && f.type.kind === "union"
|
|
6368
|
-
? {
|
|
6369
|
-
kind: "if",
|
|
6370
|
-
cond: {
|
|
6371
|
-
kind: "unionIsTag",
|
|
6372
|
-
unionId: f.type.unionId,
|
|
6373
|
-
tag: utag,
|
|
6374
|
-
negated: true,
|
|
6375
|
-
value: { kind: "recordGet", obj: ref, shapeId: argIr.shapeId, field: f.name, type: f.type, loc },
|
|
6376
|
-
type: BOOL,
|
|
6377
|
-
loc,
|
|
6378
|
-
},
|
|
6379
|
-
then: [pushStmt],
|
|
6380
|
-
else_: null,
|
|
6381
|
-
loc,
|
|
6382
|
-
}
|
|
6383
|
-
: pushStmt);
|
|
6384
|
-
}
|
|
6385
|
-
body.push({ kind: "return", value: outRef, loc });
|
|
6386
6413
|
L.arrHofHelpers.set(key, helper);
|
|
6387
|
-
|
|
6414
|
+
const fn = {
|
|
6388
6415
|
name: helper,
|
|
6389
6416
|
params: [{ localId: "r.0", name: "r", type: argIr }],
|
|
6390
6417
|
returnType: resultT,
|
|
@@ -6392,9 +6419,56 @@ export function recordKeysArrayCall(L, receiver, argIr, shape, loc) {
|
|
|
6392
6419
|
{ id: "r.0", name: "r", type: argIr, mutable: true },
|
|
6393
6420
|
{ id: "out.0", name: "out", type: resultT, mutable: false },
|
|
6394
6421
|
],
|
|
6395
|
-
body,
|
|
6422
|
+
body: [],
|
|
6396
6423
|
loc,
|
|
6397
|
-
}
|
|
6424
|
+
};
|
|
6425
|
+
const finalize = () => {
|
|
6426
|
+
const current = L.shapes.get(argIr.shapeId) ?? shape;
|
|
6427
|
+
const body = [
|
|
6428
|
+
{ kind: "varDecl", localId: "out.0", init: { kind: "arrayLit", elems: [], type: resultT, loc }, loc },
|
|
6429
|
+
];
|
|
6430
|
+
const order = current.declaredOrder ?? current.fields.map((f) => f.name);
|
|
6431
|
+
for (const name of order) {
|
|
6432
|
+
const f = current.fields.find((x) => x.name === name);
|
|
6433
|
+
const pushStmt = {
|
|
6434
|
+
kind: "exprStmt",
|
|
6435
|
+
expr: {
|
|
6436
|
+
kind: "arrIntrinsic",
|
|
6437
|
+
method: "push",
|
|
6438
|
+
receiver: outRef,
|
|
6439
|
+
args: [{ kind: "strLit", value: f.name, type: STRING, loc }],
|
|
6440
|
+
type: F64,
|
|
6441
|
+
loc,
|
|
6442
|
+
},
|
|
6443
|
+
loc,
|
|
6444
|
+
};
|
|
6445
|
+
// Undefined-armed fields: the push is guarded by a tag test (the
|
|
6446
|
+
// key exists exactly when Object.keys would list it).
|
|
6447
|
+
const utag = f.type.kind === "union" ? L.armTag(f.type.unionId, UNDEFINED_T) : -1;
|
|
6448
|
+
body.push(utag >= 0 && f.type.kind === "union"
|
|
6449
|
+
? {
|
|
6450
|
+
kind: "if",
|
|
6451
|
+
cond: {
|
|
6452
|
+
kind: "unionIsTag",
|
|
6453
|
+
unionId: f.type.unionId,
|
|
6454
|
+
tag: utag,
|
|
6455
|
+
negated: true,
|
|
6456
|
+
value: { kind: "recordGet", obj: ref, shapeId: argIr.shapeId, field: f.name, type: f.type, loc },
|
|
6457
|
+
type: BOOL,
|
|
6458
|
+
loc,
|
|
6459
|
+
},
|
|
6460
|
+
then: [pushStmt],
|
|
6461
|
+
else_: null,
|
|
6462
|
+
loc,
|
|
6463
|
+
}
|
|
6464
|
+
: pushStmt);
|
|
6465
|
+
}
|
|
6466
|
+
body.push({ kind: "return", value: outRef, loc });
|
|
6467
|
+
fn.body = body;
|
|
6468
|
+
};
|
|
6469
|
+
finalize();
|
|
6470
|
+
L.shapeOrderHelperFinalizers.push(finalize);
|
|
6471
|
+
L.liftedFns.push(fn);
|
|
6398
6472
|
}
|
|
6399
6473
|
return { kind: "call", callee: helper, args: [receiver], type: resultT, loc };
|
|
6400
6474
|
}
|
|
@@ -7104,109 +7178,15 @@ function lowerObjectStaticCall(L, call, access) {
|
|
|
7104
7178
|
L.badType(call, L.typeOf(call));
|
|
7105
7179
|
valueT = tupleShape.fields.find((f) => f.name === "1").type;
|
|
7106
7180
|
}
|
|
7107
|
-
const order = shape.declaredOrder ?? shape.fields.map((f) => f.name);
|
|
7108
7181
|
const key = `obj.${member}:${argIr.shapeId}:${typeKey(resultT)}`;
|
|
7109
7182
|
let helper = L.arrHofHelpers.get(key);
|
|
7110
7183
|
if (!helper) {
|
|
7111
7184
|
helper = `%obj.${member}.${L.arrHofHelpers.size}`;
|
|
7112
7185
|
const recT = argIr;
|
|
7113
7186
|
const ref = { kind: "varRef", localId: "r.0", type: recT, loc };
|
|
7114
|
-
const body = [
|
|
7115
|
-
{ kind: "varDecl", localId: "out.0", init: { kind: "arrayLit", elems: [], type: resultT, loc }, loc },
|
|
7116
|
-
];
|
|
7117
7187
|
const outRef = { kind: "varRef", localId: "out.0", type: resultT, loc };
|
|
7118
|
-
for (const name of order) {
|
|
7119
|
-
const f = shape.fields.find((x) => x.name === name);
|
|
7120
|
-
const raw = { kind: "recordGet", obj: ref, shapeId: argIr.shapeId, field: f.name, type: f.type, loc };
|
|
7121
|
-
// The pushed element per member; null when the field's value
|
|
7122
|
-
// cannot flow into the result element type.
|
|
7123
|
-
const elemOf = (value, vt) => {
|
|
7124
|
-
if (!valueT)
|
|
7125
|
-
return null;
|
|
7126
|
-
if (typeEquals(vt, valueT))
|
|
7127
|
-
return value;
|
|
7128
|
-
if (valueT.kind === "union" && vt.kind !== "union") {
|
|
7129
|
-
const tag = L.armTag(valueT.unionId, vt);
|
|
7130
|
-
if (tag >= 0) {
|
|
7131
|
-
return { kind: "unionWrap", unionId: valueT.unionId, tag, value, type: valueT, loc };
|
|
7132
|
-
}
|
|
7133
|
-
}
|
|
7134
|
-
return null;
|
|
7135
|
-
};
|
|
7136
|
-
// Undefined-armed fields: the push is guarded by a tag test, and
|
|
7137
|
-
// the pushed value is the narrowed non-undefined arm.
|
|
7138
|
-
let guardUndefTag = null;
|
|
7139
|
-
let value = raw;
|
|
7140
|
-
let vt = f.type;
|
|
7141
|
-
if (f.type.kind === "union") {
|
|
7142
|
-
const undefTag = L.armTag(f.type.unionId, UNDEFINED_T);
|
|
7143
|
-
if (undefTag >= 0) {
|
|
7144
|
-
guardUndefTag = undefTag;
|
|
7145
|
-
const arms = L.unions.get(f.type.unionId)?.arms ?? [];
|
|
7146
|
-
const others = arms.filter((a) => a.kind !== "undefinedT");
|
|
7147
|
-
if (typeEquals(f.type, valueT ?? f.type)) {
|
|
7148
|
-
// The field union IS the result union (single-field shapes):
|
|
7149
|
-
// push the raw box — but then the undefined skip must NOT
|
|
7150
|
-
// narrow. Handled below via vt === valueT.
|
|
7151
|
-
value = raw;
|
|
7152
|
-
vt = f.type;
|
|
7153
|
-
}
|
|
7154
|
-
else if (others.length === 1) {
|
|
7155
|
-
vt = others[0];
|
|
7156
|
-
// A UNIT other arm (`null | undefined` fields — the mixed-
|
|
7157
|
-
// defaults spread idiom; undefined was filtered above, so
|
|
7158
|
-
// the unit is null): units carry no payload, so the guarded
|
|
7159
|
-
// push writes the unit LITERAL — unionNarrow to a unit arm
|
|
7160
|
-
// (and unionWrap of a narrowed unit) is malformed IR; the
|
|
7161
|
-
// literal is the one legal unit spelling.
|
|
7162
|
-
value = isUnitType(vt)
|
|
7163
|
-
? { kind: "unitLit", unit: "null", type: vt, loc }
|
|
7164
|
-
: { kind: "unionNarrow", unionId: f.type.unionId, tag: L.armTag(f.type.unionId, vt), value: raw, type: vt, loc };
|
|
7165
|
-
}
|
|
7166
|
-
else {
|
|
7167
|
-
L.unsupported("SC1090", call, `Object.${member} over '${L.fmt(argIr)}' (field '${f.name}' is a multi-arm union that ` +
|
|
7168
|
-
"cannot re-tag into the result element type — read the fields directly)");
|
|
7169
|
-
}
|
|
7170
|
-
}
|
|
7171
|
-
else if (!typeEquals(f.type, valueT ?? f.type)) {
|
|
7172
|
-
L.unsupported("SC1090", call, `Object.${member} over '${L.fmt(argIr)}' (field '${f.name}' is a union that cannot ` +
|
|
7173
|
-
"re-tag into the result element type — read the fields directly)");
|
|
7174
|
-
}
|
|
7175
|
-
}
|
|
7176
|
-
const coerced = elemOf(value, vt);
|
|
7177
|
-
if (!coerced) {
|
|
7178
|
-
L.unsupported("SC1090", call, `Object.${member} over '${L.fmt(argIr)}' (field '${f.name}' of type '${L.fmt(f.type)}' ` +
|
|
7179
|
-
`cannot flow into the '${L.fmt(valueT)}' result element — read the fields directly)`);
|
|
7180
|
-
}
|
|
7181
|
-
const pushed = member === "values"
|
|
7182
|
-
? coerced
|
|
7183
|
-
: {
|
|
7184
|
-
kind: "recordLit",
|
|
7185
|
-
fields: [
|
|
7186
|
-
{ name: "0", value: { kind: "strLit", value: f.name, type: STRING, loc } },
|
|
7187
|
-
{ name: "1", value: coerced },
|
|
7188
|
-
],
|
|
7189
|
-
type: tupleT,
|
|
7190
|
-
loc,
|
|
7191
|
-
};
|
|
7192
|
-
const pushStmt = {
|
|
7193
|
-
kind: "exprStmt",
|
|
7194
|
-
expr: { kind: "arrIntrinsic", method: "push", receiver: outRef, args: [pushed], type: F64, loc },
|
|
7195
|
-
loc,
|
|
7196
|
-
};
|
|
7197
|
-
body.push(guardUndefTag !== null && f.type.kind === "union"
|
|
7198
|
-
? {
|
|
7199
|
-
kind: "if",
|
|
7200
|
-
cond: { kind: "unionIsTag", unionId: f.type.unionId, tag: guardUndefTag, negated: true, value: raw, type: BOOL, loc },
|
|
7201
|
-
then: [pushStmt],
|
|
7202
|
-
else_: null,
|
|
7203
|
-
loc,
|
|
7204
|
-
}
|
|
7205
|
-
: pushStmt);
|
|
7206
|
-
}
|
|
7207
|
-
body.push({ kind: "return", value: outRef, loc });
|
|
7208
7188
|
L.arrHofHelpers.set(key, helper);
|
|
7209
|
-
|
|
7189
|
+
const fn = {
|
|
7210
7190
|
name: helper,
|
|
7211
7191
|
params: [{ localId: "r.0", name: "r", type: recT }],
|
|
7212
7192
|
returnType: resultT,
|
|
@@ -7214,9 +7194,110 @@ function lowerObjectStaticCall(L, call, access) {
|
|
|
7214
7194
|
{ id: "r.0", name: "r", type: recT, mutable: true },
|
|
7215
7195
|
{ id: "out.0", name: "out", type: resultT, mutable: false },
|
|
7216
7196
|
],
|
|
7217
|
-
body,
|
|
7197
|
+
body: [],
|
|
7218
7198
|
loc,
|
|
7219
|
-
}
|
|
7199
|
+
};
|
|
7200
|
+
const finalize = () => {
|
|
7201
|
+
const current = L.shapes.get(argIr.shapeId) ?? shape;
|
|
7202
|
+
const body = [
|
|
7203
|
+
{ kind: "varDecl", localId: "out.0", init: { kind: "arrayLit", elems: [], type: resultT, loc }, loc },
|
|
7204
|
+
];
|
|
7205
|
+
const order = current.declaredOrder ?? current.fields.map((f) => f.name);
|
|
7206
|
+
for (const name of order) {
|
|
7207
|
+
const f = current.fields.find((x) => x.name === name);
|
|
7208
|
+
const raw = { kind: "recordGet", obj: ref, shapeId: argIr.shapeId, field: f.name, type: f.type, loc };
|
|
7209
|
+
// The pushed element per member; null when the field's value
|
|
7210
|
+
// cannot flow into the result element type.
|
|
7211
|
+
const elemOf = (value, vt) => {
|
|
7212
|
+
if (!valueT)
|
|
7213
|
+
return null;
|
|
7214
|
+
if (typeEquals(vt, valueT))
|
|
7215
|
+
return value;
|
|
7216
|
+
if (valueT.kind === "union" && vt.kind !== "union") {
|
|
7217
|
+
const tag = L.armTag(valueT.unionId, vt);
|
|
7218
|
+
if (tag >= 0) {
|
|
7219
|
+
return { kind: "unionWrap", unionId: valueT.unionId, tag, value, type: valueT, loc };
|
|
7220
|
+
}
|
|
7221
|
+
}
|
|
7222
|
+
return null;
|
|
7223
|
+
};
|
|
7224
|
+
// Undefined-armed fields: the push is guarded by a tag test, and
|
|
7225
|
+
// the pushed value is the narrowed non-undefined arm.
|
|
7226
|
+
let guardUndefTag = null;
|
|
7227
|
+
let value = raw;
|
|
7228
|
+
let vt = f.type;
|
|
7229
|
+
if (f.type.kind === "union") {
|
|
7230
|
+
const undefTag = L.armTag(f.type.unionId, UNDEFINED_T);
|
|
7231
|
+
if (undefTag >= 0) {
|
|
7232
|
+
guardUndefTag = undefTag;
|
|
7233
|
+
const arms = L.unions.get(f.type.unionId)?.arms ?? [];
|
|
7234
|
+
const others = arms.filter((a) => a.kind !== "undefinedT");
|
|
7235
|
+
if (typeEquals(f.type, valueT ?? f.type)) {
|
|
7236
|
+
// The field union IS the result union (single-field shapes):
|
|
7237
|
+
// push the raw box — but then the undefined skip must NOT
|
|
7238
|
+
// narrow. Handled below via vt === valueT.
|
|
7239
|
+
value = raw;
|
|
7240
|
+
vt = f.type;
|
|
7241
|
+
}
|
|
7242
|
+
else if (others.length === 1) {
|
|
7243
|
+
vt = others[0];
|
|
7244
|
+
// A UNIT other arm (`null | undefined` fields — the mixed-
|
|
7245
|
+
// defaults spread idiom; undefined was filtered above, so
|
|
7246
|
+
// the unit is null): units carry no payload, so the guarded
|
|
7247
|
+
// push writes the unit LITERAL — unionNarrow to a unit arm
|
|
7248
|
+
// (and unionWrap of a narrowed unit) is malformed IR; the
|
|
7249
|
+
// literal is the one legal unit spelling.
|
|
7250
|
+
value = isUnitType(vt)
|
|
7251
|
+
? { kind: "unitLit", unit: "null", type: vt, loc }
|
|
7252
|
+
: { kind: "unionNarrow", unionId: f.type.unionId, tag: L.armTag(f.type.unionId, vt), value: raw, type: vt, loc };
|
|
7253
|
+
}
|
|
7254
|
+
else {
|
|
7255
|
+
L.unsupported("SC1090", call, `Object.${member} over '${L.fmt(argIr)}' (field '${f.name}' is a multi-arm union that ` +
|
|
7256
|
+
"cannot re-tag into the result element type — read the fields directly)");
|
|
7257
|
+
}
|
|
7258
|
+
}
|
|
7259
|
+
else if (!typeEquals(f.type, valueT ?? f.type)) {
|
|
7260
|
+
L.unsupported("SC1090", call, `Object.${member} over '${L.fmt(argIr)}' (field '${f.name}' is a union that cannot ` +
|
|
7261
|
+
"re-tag into the result element type — read the fields directly)");
|
|
7262
|
+
}
|
|
7263
|
+
}
|
|
7264
|
+
const coerced = elemOf(value, vt);
|
|
7265
|
+
if (!coerced) {
|
|
7266
|
+
L.unsupported("SC1090", call, `Object.${member} over '${L.fmt(argIr)}' (field '${f.name}' of type '${L.fmt(f.type)}' ` +
|
|
7267
|
+
`cannot flow into the '${L.fmt(valueT)}' result element — read the fields directly)`);
|
|
7268
|
+
}
|
|
7269
|
+
const pushed = member === "values"
|
|
7270
|
+
? coerced
|
|
7271
|
+
: {
|
|
7272
|
+
kind: "recordLit",
|
|
7273
|
+
fields: [
|
|
7274
|
+
{ name: "0", value: { kind: "strLit", value: f.name, type: STRING, loc } },
|
|
7275
|
+
{ name: "1", value: coerced },
|
|
7276
|
+
],
|
|
7277
|
+
type: tupleT,
|
|
7278
|
+
loc,
|
|
7279
|
+
};
|
|
7280
|
+
const pushStmt = {
|
|
7281
|
+
kind: "exprStmt",
|
|
7282
|
+
expr: { kind: "arrIntrinsic", method: "push", receiver: outRef, args: [pushed], type: F64, loc },
|
|
7283
|
+
loc,
|
|
7284
|
+
};
|
|
7285
|
+
body.push(guardUndefTag !== null && f.type.kind === "union"
|
|
7286
|
+
? {
|
|
7287
|
+
kind: "if",
|
|
7288
|
+
cond: { kind: "unionIsTag", unionId: f.type.unionId, tag: guardUndefTag, negated: true, value: raw, type: BOOL, loc },
|
|
7289
|
+
then: [pushStmt],
|
|
7290
|
+
else_: null,
|
|
7291
|
+
loc,
|
|
7292
|
+
}
|
|
7293
|
+
: pushStmt);
|
|
7294
|
+
}
|
|
7295
|
+
body.push({ kind: "return", value: outRef, loc });
|
|
7296
|
+
fn.body = body;
|
|
7297
|
+
};
|
|
7298
|
+
finalize();
|
|
7299
|
+
L.shapeOrderHelperFinalizers.push(finalize);
|
|
7300
|
+
L.liftedFns.push(fn);
|
|
7220
7301
|
}
|
|
7221
7302
|
return { kind: "call", callee: helper, args: [receiver], type: resultT, loc };
|
|
7222
7303
|
}
|