@tsonic/frontend 0.0.59 → 0.0.61

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.
Files changed (39) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/ir/binding/index.d.ts +17 -0
  3. package/dist/ir/binding/index.d.ts.map +1 -1
  4. package/dist/ir/binding/index.js +41 -7
  5. package/dist/ir/binding/index.js.map +1 -1
  6. package/dist/ir/converters/expressions/access.d.ts.map +1 -1
  7. package/dist/ir/converters/expressions/access.js +94 -11
  8. package/dist/ir/converters/expressions/access.js.map +1 -1
  9. package/dist/ir/this-parameter-inference-rewrap.test.d.ts +14 -0
  10. package/dist/ir/this-parameter-inference-rewrap.test.d.ts.map +1 -0
  11. package/dist/ir/this-parameter-inference-rewrap.test.js +110 -0
  12. package/dist/ir/this-parameter-inference-rewrap.test.js.map +1 -0
  13. package/dist/ir/this-parameter-inference.test.d.ts +13 -0
  14. package/dist/ir/this-parameter-inference.test.d.ts.map +1 -0
  15. package/dist/ir/this-parameter-inference.test.js +165 -0
  16. package/dist/ir/this-parameter-inference.test.js.map +1 -0
  17. package/dist/ir/type-system/internal/handle-types.d.ts +2 -0
  18. package/dist/ir/type-system/internal/handle-types.d.ts.map +1 -1
  19. package/dist/ir/type-system/internal/type-converter/references.d.ts.map +1 -1
  20. package/dist/ir/type-system/internal/type-converter/references.js +96 -2
  21. package/dist/ir/type-system/internal/type-converter/references.js.map +1 -1
  22. package/dist/ir/type-system/type-system.d.ts +4 -0
  23. package/dist/ir/type-system/type-system.d.ts.map +1 -1
  24. package/dist/ir/type-system/type-system.js +103 -28
  25. package/dist/ir/type-system/type-system.js.map +1 -1
  26. package/dist/ir/validation/soundness-gate.d.ts.map +1 -1
  27. package/dist/ir/validation/soundness-gate.js +9 -1
  28. package/dist/ir/validation/soundness-gate.js.map +1 -1
  29. package/dist/program/bindings.d.ts +17 -0
  30. package/dist/program/bindings.d.ts.map +1 -1
  31. package/dist/program/bindings.js +137 -36
  32. package/dist/program/bindings.js.map +1 -1
  33. package/dist/types/diagnostic.d.ts +1 -1
  34. package/dist/types/diagnostic.d.ts.map +1 -1
  35. package/dist/types/diagnostic.js.map +1 -1
  36. package/dist/validation/static-safety.d.ts.map +1 -1
  37. package/dist/validation/static-safety.js +20 -0
  38. package/dist/validation/static-safety.js.map +1 -1
  39. package/package.json +3 -3
@@ -14,7 +14,7 @@
14
14
  import * as ts from "typescript";
15
15
  import { substituteIrType as irSubstitute, } from "../types/ir-substitution.js";
16
16
  import { inferNumericKindFromRaw } from "../types/numeric-helpers.js";
17
- import { getBinaryResultKind, TSONIC_TO_NUMERIC_KIND } from "../types/numeric-kind.js";
17
+ import { getBinaryResultKind, TSONIC_TO_NUMERIC_KIND, } from "../types/numeric-kind.js";
18
18
  // ═══════════════════════════════════════════════════════════════════════════
19
19
  // BUILTIN NOMINAL MAPPING
20
20
  // ═══════════════════════════════════════════════════════════════════════════
@@ -157,6 +157,11 @@ export const createTypeSystem = (config) => {
157
157
  return undefined;
158
158
  // Convert parameter types from TypeNodes to IrTypes
159
159
  const parameterTypes = sigInfo.parameters.map((p) => (p.typeNode ? convertTypeNode(p.typeNode) : undefined));
160
+ // Convert a TypeScript `this:` parameter type (if present) to an IrType.
161
+ const thisParameterType = (() => {
162
+ const n = sigInfo.thisTypeNode;
163
+ return n ? convertTypeNode(n) : undefined;
164
+ })();
160
165
  // Extract parameter modes
161
166
  const parameterModes = sigInfo.parameters.map((p) => p.mode ?? "value");
162
167
  // Extract parameter names
@@ -179,7 +184,10 @@ export const createTypeSystem = (config) => {
179
184
  // identity captured in Binding and the (class) type parameters captured for the
180
185
  // constructor signature.
181
186
  if (isConstructor && sigInfo.declaringTypeTsName) {
182
- const typeArguments = typeParameters.map((tp) => ({ kind: "typeParameterType", name: tp.name }));
187
+ const typeArguments = typeParameters.map((tp) => ({
188
+ kind: "typeParameterType",
189
+ name: tp.name,
190
+ }));
183
191
  return {
184
192
  kind: "referenceType",
185
193
  name: sigInfo.declaringTypeTsName,
@@ -209,6 +217,7 @@ export const createTypeSystem = (config) => {
209
217
  }
210
218
  const rawSig = {
211
219
  parameterTypes,
220
+ thisParameterType,
212
221
  returnType,
213
222
  parameterModes,
214
223
  typeParameters,
@@ -602,14 +611,15 @@ export const createTypeSystem = (config) => {
602
611
  * Deterministically infer an expression's type using only:
603
612
  * - local lambda parameter environment
604
613
  * - declaration types (typeOfDecl)
605
- * - numeric literal lexeme rules
606
- *
607
- * This is intentionally small: it's used only to type lambda bodies for
608
- * initializer-driven generic inference (e.g., `Enumerable.select(..., x => x * 2)`).
609
- */
614
+ * - numeric literal lexeme rules
615
+ *
616
+ * This is intentionally small: it's used only to type lambda bodies for
617
+ * initializer-driven generic inference (e.g., `Enumerable.select(..., x => x * 2)`).
618
+ */
610
619
  const inferExpressionType = (expr, env) => {
611
620
  const unwrapped = unwrapParens(expr);
612
- if (ts.isAsExpression(unwrapped) || ts.isTypeAssertionExpression(unwrapped)) {
621
+ if (ts.isAsExpression(unwrapped) ||
622
+ ts.isTypeAssertionExpression(unwrapped)) {
613
623
  return convertTypeNode(unwrapped.type);
614
624
  }
615
625
  if (ts.isNonNullExpression(unwrapped)) {
@@ -790,13 +800,13 @@ export const createTypeSystem = (config) => {
790
800
  });
791
801
  const env = new Map();
792
802
  for (const p of parameters) {
793
- if (p.pattern.kind === "identifierPattern" &&
794
- p.pattern.name &&
795
- p.type) {
803
+ if (p.pattern.kind === "identifierPattern" && p.pattern.name && p.type) {
796
804
  env.set(p.pattern.name, p.type);
797
805
  }
798
806
  }
799
- const explicitReturnType = "type" in unwrapped && unwrapped.type ? convertTypeNode(unwrapped.type) : undefined;
807
+ const explicitReturnType = "type" in unwrapped && unwrapped.type
808
+ ? convertTypeNode(unwrapped.type)
809
+ : undefined;
800
810
  const expectedReturnType = expectedFnType?.returnType;
801
811
  const inferredReturnType = explicitReturnType ??
802
812
  (expectedReturnType && !containsTypeParameter(expectedReturnType)
@@ -1222,7 +1232,9 @@ export const createTypeSystem = (config) => {
1222
1232
  const effectiveReceiver = receiver.kind === "unionType"
1223
1233
  ? (() => {
1224
1234
  const nonNullish = receiver.types.filter((t) => t && !isNullishPrimitive(t));
1225
- return nonNullish.length === 1 && nonNullish[0] ? nonNullish[0] : receiver;
1235
+ return nonNullish.length === 1 && nonNullish[0]
1236
+ ? nonNullish[0]
1237
+ : receiver;
1226
1238
  })()
1227
1239
  : receiver;
1228
1240
  // 1. Normalize receiver to nominal form
@@ -1230,7 +1242,8 @@ export const createTypeSystem = (config) => {
1230
1242
  if (!normalized) {
1231
1243
  // Handle structural types (objectType)
1232
1244
  if (effectiveReceiver.kind === "objectType" ||
1233
- (effectiveReceiver.kind === "referenceType" && effectiveReceiver.structuralMembers)) {
1245
+ (effectiveReceiver.kind === "referenceType" &&
1246
+ effectiveReceiver.structuralMembers)) {
1234
1247
  return lookupStructuralMember(effectiveReceiver, memberName, site);
1235
1248
  }
1236
1249
  emitDiagnostic("TSN5203", `Cannot resolve member '${memberName}' on type`, site);
@@ -1333,7 +1346,9 @@ export const createTypeSystem = (config) => {
1333
1346
  if (!first)
1334
1347
  return undefined;
1335
1348
  // Strip assembly qualification (", Assembly, Version=..., ...") if present.
1336
- const withoutAsm = first.includes(",") ? (first.split(",")[0] ?? first) : first;
1349
+ const withoutAsm = first.includes(",")
1350
+ ? (first.split(",")[0] ?? first)
1351
+ : first;
1337
1352
  return withoutAsm.trim();
1338
1353
  };
1339
1354
  const getIndexerInfo = (receiver, _site) => {
@@ -1401,7 +1416,22 @@ export const createTypeSystem = (config) => {
1401
1416
  return true;
1402
1417
  }
1403
1418
  // Poison/any provides no deterministic information
1404
- if (argumentType.kind === "unknownType" || argumentType.kind === "anyType") {
1419
+ if (argumentType.kind === "unknownType" ||
1420
+ argumentType.kind === "anyType") {
1421
+ return true;
1422
+ }
1423
+ // Intersection argument types: unify through each constituent.
1424
+ //
1425
+ // This is required for airplane-grade extension method typing where the receiver
1426
+ // often has the form `TShape & <extension markers> & <method table>`.
1427
+ // Generic inference must still be able to infer through the real CLR shape in the intersection.
1428
+ if (argumentType.kind === "intersectionType") {
1429
+ for (const part of argumentType.types) {
1430
+ if (!part)
1431
+ continue;
1432
+ if (!tryUnify(parameterType, part))
1433
+ return false;
1434
+ }
1405
1435
  return true;
1406
1436
  }
1407
1437
  // Expression<TDelegate> wrapper: infer through the underlying delegate shape.
@@ -1419,12 +1449,14 @@ export const createTypeSystem = (config) => {
1419
1449
  // Without this, generic methods like:
1420
1450
  // Select<TResult>(selector: Func<TSource, TResult>)
1421
1451
  // cannot infer TResult from a lambda argument, causing TSN5201/TSN5202.
1422
- if (parameterType.kind === "referenceType" && argumentType.kind === "functionType") {
1452
+ if (parameterType.kind === "referenceType" &&
1453
+ argumentType.kind === "functionType") {
1423
1454
  const delegateFn = delegateToFunctionType(parameterType);
1424
1455
  if (delegateFn)
1425
1456
  return tryUnify(delegateFn, argumentType);
1426
1457
  }
1427
- if (parameterType.kind === "functionType" && argumentType.kind === "referenceType") {
1458
+ if (parameterType.kind === "functionType" &&
1459
+ argumentType.kind === "referenceType") {
1428
1460
  const delegateFn = delegateToFunctionType(argumentType);
1429
1461
  if (delegateFn)
1430
1462
  return tryUnify(parameterType, delegateFn);
@@ -1435,14 +1467,18 @@ export const createTypeSystem = (config) => {
1435
1467
  (parameterType.typeArguments?.length ?? 0) === 1 &&
1436
1468
  argumentType.kind === "arrayType") {
1437
1469
  const elementParam = parameterType.typeArguments?.[0];
1438
- return elementParam ? tryUnify(elementParam, argumentType.elementType) : true;
1470
+ return elementParam
1471
+ ? tryUnify(elementParam, argumentType.elementType)
1472
+ : true;
1439
1473
  }
1440
1474
  // Union parameter type: allow deterministic inference through common nullish unions.
1441
1475
  // Example: constructor(value: T | null) with argument of type T.
1442
1476
  if (parameterType.kind === "unionType") {
1443
1477
  const nonNullish = parameterType.types.filter((t) => t && !isNullishPrimitive(t));
1444
1478
  const nullish = parameterType.types.filter((t) => t && isNullishPrimitive(t));
1445
- const candidates = isNullishPrimitive(argumentType) ? nullish : nonNullish;
1479
+ const candidates = isNullishPrimitive(argumentType)
1480
+ ? nullish
1481
+ : nonNullish;
1446
1482
  if (candidates.length === 1) {
1447
1483
  const only = candidates[0];
1448
1484
  return only ? tryUnify(only, argumentType) : true;
@@ -1455,7 +1491,9 @@ export const createTypeSystem = (config) => {
1455
1491
  argumentType.name === "Array" &&
1456
1492
  (argumentType.typeArguments?.length ?? 0) === 1) {
1457
1493
  const elementArg = argumentType.typeArguments?.[0];
1458
- return elementArg ? tryUnify(parameterType.elementType, elementArg) : true;
1494
+ return elementArg
1495
+ ? tryUnify(parameterType.elementType, elementArg)
1496
+ : true;
1459
1497
  }
1460
1498
  // Same-kind structural unification
1461
1499
  if (parameterType.kind !== argumentType.kind) {
@@ -1550,7 +1588,6 @@ export const createTypeSystem = (config) => {
1550
1588
  }
1551
1589
  return tryUnify(parameterType.returnType, argFn.returnType);
1552
1590
  }
1553
- case "intersectionType":
1554
1591
  case "objectType":
1555
1592
  case "dictionaryType":
1556
1593
  // Conservative: only infer through these when shapes already match exactly.
@@ -1587,7 +1624,8 @@ export const createTypeSystem = (config) => {
1587
1624
  }
1588
1625
  if (type.kind === "functionType") {
1589
1626
  const paramsContain = type.parameters.some((p) => p.type ? containsMethodTypeParameter(p.type, unresolved) : false);
1590
- return paramsContain || containsMethodTypeParameter(type.returnType, unresolved);
1627
+ return (paramsContain ||
1628
+ containsMethodTypeParameter(type.returnType, unresolved));
1591
1629
  }
1592
1630
  if (type.kind === "unionType" || type.kind === "intersectionType") {
1593
1631
  return type.types.some((t) => t ? containsMethodTypeParameter(t, unresolved) : false);
@@ -1670,7 +1708,7 @@ export const createTypeSystem = (config) => {
1670
1708
  return score;
1671
1709
  };
1672
1710
  const tryResolveCallFromUnifiedCatalog = (declaringTypeTsName, declaringMemberName, query) => {
1673
- const { argumentCount, receiverType, explicitTypeArgs, argTypes, } = query;
1711
+ const { argumentCount, receiverType, explicitTypeArgs, argTypes } = query;
1674
1712
  if (!argTypes)
1675
1713
  return undefined;
1676
1714
  if (argTypes.length < argumentCount)
@@ -1810,6 +1848,7 @@ export const createTypeSystem = (config) => {
1810
1848
  }
1811
1849
  // 2. Start with raw types
1812
1850
  let workingParams = [...rawSig.parameterTypes];
1851
+ let workingThisParam = rawSig.thisParameterType;
1813
1852
  let workingReturn = rawSig.returnType;
1814
1853
  let workingPredicate = rawSig.typePredicate;
1815
1854
  // 3. Compute receiver substitution (class type params)
@@ -1819,6 +1858,9 @@ export const createTypeSystem = (config) => {
1819
1858
  const receiverSubst = computeReceiverSubstitution(receiverType, rawSig.declaringTypeTsName, rawSig.declaringMemberName);
1820
1859
  if (receiverSubst && receiverSubst.size > 0) {
1821
1860
  workingParams = workingParams.map((p) => p ? irSubstitute(p, receiverSubst) : undefined);
1861
+ if (workingThisParam) {
1862
+ workingThisParam = irSubstitute(workingThisParam, receiverSubst);
1863
+ }
1822
1864
  workingReturn = irSubstitute(workingReturn, receiverSubst);
1823
1865
  if (workingPredicate) {
1824
1866
  workingPredicate =
@@ -1849,9 +1891,38 @@ export const createTypeSystem = (config) => {
1849
1891
  }
1850
1892
  }
1851
1893
  // Source 2: Deterministic argument-driven unification
1852
- if (argTypes && argTypes.length > 0) {
1894
+ // 2a) Receiver-driven unification via TS `this:` parameter
1895
+ //
1896
+ // Method-table extension typing represents the receiver as an explicit `this:` parameter
1897
+ // in the `.d.ts` signature. Generic methods like:
1898
+ // ToArrayAsync<T>(this: IQueryable<T>, ...): Task<T[]>
1899
+ // must infer T from the receiver even when there are ZERO call arguments.
1900
+ //
1901
+ // This is airplane-grade determinism: we anchor inference to the selected TS signature’s
1902
+ // `this:` type and the IR receiver type (not TS structural tricks).
1903
+ if (receiverType && workingThisParam) {
1904
+ const receiverParamForInference = callSubst.size > 0
1905
+ ? irSubstitute(workingThisParam, callSubst)
1906
+ : workingThisParam;
1907
+ const inferredFromReceiver = inferMethodTypeArgsFromArguments(methodTypeParams, [receiverParamForInference], [receiverType]);
1908
+ if (inferredFromReceiver) {
1909
+ for (const [name, inferredType] of inferredFromReceiver) {
1910
+ const existing = callSubst.get(name);
1911
+ if (existing) {
1912
+ if (!typesEqual(existing, inferredType)) {
1913
+ emitDiagnostic("TSN5202", `Conflicting type argument inference for '${name}' (receiver)`, site);
1914
+ return poisonedCall(argumentCount, diagnostics.slice());
1915
+ }
1916
+ continue;
1917
+ }
1918
+ callSubst.set(name, inferredType);
1919
+ }
1920
+ }
1921
+ }
1922
+ // 2b) Argument-driven unification (run even when argTypes is empty).
1923
+ if (argTypes) {
1853
1924
  const paramsForInference = callSubst.size > 0
1854
- ? workingParams.map((p) => (p ? irSubstitute(p, callSubst) : undefined))
1925
+ ? workingParams.map((p) => p ? irSubstitute(p, callSubst) : undefined)
1855
1926
  : workingParams;
1856
1927
  const inferred = inferMethodTypeArgsFromArguments(methodTypeParams, paramsForInference, argTypes);
1857
1928
  if (!inferred) {
@@ -1897,7 +1968,8 @@ export const createTypeSystem = (config) => {
1897
1968
  const unresolved = new Set(methodTypeParams
1898
1969
  .map((tp) => tp.name)
1899
1970
  .filter((name) => !callSubst.has(name)));
1900
- if (unresolved.size > 0 && containsMethodTypeParameter(workingReturn, unresolved)) {
1971
+ if (unresolved.size > 0 &&
1972
+ containsMethodTypeParameter(workingReturn, unresolved)) {
1901
1973
  const fallback = argTypes && rawSig.declaringTypeTsName && rawSig.declaringMemberName
1902
1974
  ? tryResolveCallFromUnifiedCatalog(rawSig.declaringTypeTsName, rawSig.declaringMemberName, query)
1903
1975
  : undefined;
@@ -2237,7 +2309,10 @@ export const createTypeSystem = (config) => {
2237
2309
  */
2238
2310
  const expandAwaitedUtility = (type) => {
2239
2311
  if (type.kind === "unionType") {
2240
- return { kind: "unionType", types: type.types.map((t) => expandAwaitedUtility(t)) };
2312
+ return {
2313
+ kind: "unionType",
2314
+ types: type.types.map((t) => expandAwaitedUtility(t)),
2315
+ };
2241
2316
  }
2242
2317
  // Check for Promise<T>
2243
2318
  if (type.kind === "referenceType" &&