semantifold 0.13.0 → 0.15.0

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 (66) hide show
  1. package/README.md +2 -1
  2. package/build/src/backends/effects.js +3 -1
  3. package/build/src/backends/php.d.ts.map +1 -1
  4. package/build/src/backends/php.js +56 -3
  5. package/build/src/backends/python.js +11 -7
  6. package/build/src/backends/scalars.js +2 -2
  7. package/build/src/backends/shared.d.ts +2 -1
  8. package/build/src/backends/shared.d.ts.map +1 -1
  9. package/build/src/backends/shared.js +210 -8
  10. package/build/src/backends/writer.d.ts +18 -4
  11. package/build/src/backends/writer.d.ts.map +1 -1
  12. package/build/src/backends/writer.js +40 -4
  13. package/build/src/frontends/java.d.ts +1 -1
  14. package/build/src/frontends/java.d.ts.map +1 -1
  15. package/build/src/frontends/java.js +14 -11
  16. package/build/src/frontends/javascript-typescript.d.ts +1 -1
  17. package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
  18. package/build/src/frontends/javascript-typescript.js +17 -17
  19. package/build/src/frontends/php.d.ts +5 -1
  20. package/build/src/frontends/php.d.ts.map +1 -1
  21. package/build/src/frontends/php.js +159 -31
  22. package/build/src/frontends/python.d.ts.map +1 -1
  23. package/build/src/frontends/python.js +37 -7
  24. package/build/src/frontends/ruby.d.ts +1 -1
  25. package/build/src/frontends/ruby.d.ts.map +1 -1
  26. package/build/src/frontends/ruby.js +12 -9
  27. package/build/src/frontends/types.d.ts +9 -2
  28. package/build/src/frontends/types.d.ts.map +1 -1
  29. package/build/src/frontends/types.js +26 -9
  30. package/build/src/language-registry.d.ts.map +1 -1
  31. package/build/src/language-registry.js +30 -6
  32. package/build/src/mapping.d.ts.map +1 -1
  33. package/build/src/mapping.js +4 -2
  34. package/build/src/semantic/effects.d.ts.map +1 -1
  35. package/build/src/semantic/effects.js +3 -1
  36. package/build/src/semantic/provenance.d.ts.map +1 -1
  37. package/build/src/semantic/provenance.js +37 -4
  38. package/build/src/semantic/types.d.ts +140 -9
  39. package/build/src/semantic/types.d.ts.map +1 -1
  40. package/build/src/semantic/types.js +37 -6
  41. package/build/src/semantic/validate.d.ts +31 -2
  42. package/build/src/semantic/validate.d.ts.map +1 -1
  43. package/build/src/semantic/validate.js +347 -28
  44. package/docs/adr-bounded-subtyping.md +180 -0
  45. package/docs/goals.md +1 -1
  46. package/docs/language-support.md +5 -4
  47. package/docs/testing.md +2 -2
  48. package/package.json +1 -1
  49. package/src/backends/effects.js +1 -0
  50. package/src/backends/php.js +56 -2
  51. package/src/backends/python.js +10 -4
  52. package/src/backends/scalars.js +1 -1
  53. package/src/backends/shared.js +211 -7
  54. package/src/backends/writer.js +41 -3
  55. package/src/frontends/java.js +13 -10
  56. package/src/frontends/javascript-typescript.js +16 -16
  57. package/src/frontends/php.js +175 -31
  58. package/src/frontends/python.js +39 -5
  59. package/src/frontends/ruby.js +11 -8
  60. package/src/frontends/types.js +26 -8
  61. package/src/language-registry.js +29 -5
  62. package/src/mapping.js +3 -1
  63. package/src/semantic/effects.js +1 -0
  64. package/src/semantic/provenance.js +34 -3
  65. package/src/semantic/types.js +39 -5
  66. package/src/semantic/validate.js +378 -27
@@ -5,8 +5,8 @@ import PythonLanguage from "tree-sitter-python"
5
5
  import {missingType, parseFailure, SemantifoldDiagnostic, unsupportedSyntax} from "../diagnostic.js"
6
6
  import {locationFromOffsets, moduleLocation, utf8ByteOffsetToUtf16Offset} from "../semantic/location.js"
7
7
  import {withAdaptedOperation} from "../semantic/operators.js"
8
- import {withParserRanges} from "../semantic/provenance.js"
9
- import {hasOnlyUnicodeScalars} from "../semantic/scalars.js"
8
+ import {setParserRanges, withParserRanges} from "../semantic/provenance.js"
9
+ import {hasOnlyUnicodeScalars, voidType} from "../semantic/scalars.js"
10
10
  import {requireSourceScalarType} from "./scalars.js"
11
11
 
12
12
  const parser = new Parser()
@@ -171,6 +171,31 @@ function convertType(node, subject, ownerLocation, filename, source) {
171
171
  return requireSourceScalarType("python", children[0].text, subject, ownerLocation, typeLocation)
172
172
  }
173
173
 
174
+ /**
175
+ * Converts one exact scalar or Task 005 `None` function-return annotation.
176
+ * @param {import("tree-sitter").SyntaxNode | null} node - Return type node, if present.
177
+ * @param {string} subject - Typed function return.
178
+ * @param {import("../semantic/types.js").SourceLocation} ownerLocation - Owning function.
179
+ * @param {string} filename - Source filename.
180
+ * @param {string} source - Complete source.
181
+ * @returns {import("../semantic/types.js").SemanticFunctionReturnType} Semantic return type.
182
+ */
183
+ function convertReturnType(node, subject, ownerLocation, filename, source) {
184
+ if (!node) return missingType("python", subject, ownerLocation)
185
+ const typeLocation = nodeLocation(node, filename, source)
186
+ const children = node.namedChildren.filter((child) => child.type != "comment")
187
+
188
+ if (node.type == "type" && children.length == 1 && children[0].type == "none" && node.text == "None") {
189
+ const type = voidType()
190
+
191
+ setParserRanges(type, {type: typeLocation})
192
+
193
+ return type
194
+ }
195
+
196
+ return convertType(node, subject, ownerLocation, filename, source)
197
+ }
198
+
174
199
  /**
175
200
  * Finds exactly one anonymous operator token directly owned by a parser node.
176
201
  * @param {import("tree-sitter").SyntaxNode} node - Operation node.
@@ -434,7 +459,8 @@ function convertStatement(node, context, visible, filename, source) {
434
459
  if (node.type == "return_statement") {
435
460
  const expressions = node.namedChildren.filter((child) => child.type != "comment")
436
461
 
437
- if (expressions.length != 1) return unsupportedSyntax("python", "return without one expression", location)
462
+ if (expressions.length > 1) return unsupportedSyntax("python", "return with more than one expression", location)
463
+ if (expressions.length == 0) return {kind: "ReturnStatement", location}
438
464
 
439
465
  return {expression: convertExpression(expressions[0], filename, source), kind: "ReturnStatement", location}
440
466
  }
@@ -444,8 +470,16 @@ function convertStatement(node, context, visible, filename, source) {
444
470
 
445
471
  if (expressions.length != 1) return unsupportedSyntax("python", node.type, location)
446
472
  if (expressions[0].type == "assignment") return convertAssignment(expressions[0], context, visible, filename, source)
473
+ if (expressions[0].type == "call") {
474
+ const calleeNode = requiredField(expressions[0], "function", filename, source)
475
+
476
+ if (calleeNode.type == "identifier" && calleeNode.text == "print") return convertPrint(expressions[0], filename, source, location)
477
+ const call = /** @type {import("../semantic/types.js").CallExpression} */ (convertExpression(expressions[0], filename, source))
478
+
479
+ return {expression: call, kind: /** @type {const} */ ("ExpressionStatement"), location}
480
+ }
447
481
 
448
- return convertPrint(expressions[0], filename, source, location)
482
+ return unsupportedSyntax("python", "expression statement other than assignment, print, or direct call", location)
449
483
  }
450
484
 
451
485
  return unsupportedSyntax("python", node.type, location)
@@ -611,7 +645,7 @@ function convertFunction(node, context, filename, source) {
611
645
  location,
612
646
  name,
613
647
  parameters,
614
- returnType: convertType(returnNode, `Function '${name}' return`, location, filename, source)
648
+ returnType: convertReturnType(returnNode, `Function '${name}' return`, location, filename, source)
615
649
  }, {name: nodeLocation(nameNode, filename, source)})
616
650
  }
617
651
 
@@ -61,7 +61,7 @@ const referenceMethodHooks = new Set([
61
61
  "respond_to_missing?", "send", "singleton_class", "size"
62
62
  ])
63
63
  /** @typedef {{name: string, nameLocation: import("../semantic/types.js").SourceLocation, parameters: import("../semantic/types.js").Parameter[], returnType: import("../semantic/types.js").SemanticFunctionReturnType, typeParameters?: import("../semantic/types.js").TypeParameter[], location: import("../semantic/types.js").SourceLocation}} RubyFunctionSignature */
64
- /** @typedef {{bindings: Map<string, import("../semantic/types.js").SemanticBindingType>, catchBindingNames?: Set<string>, classes: Map<string, import("../semantic/types.js").ClassDeclaration>, catchBindingNodes?: Map<string, import("../semantic/types.js").CatchBinding>, currentCatchBindingNode?: import("../semantic/types.js").CatchBinding, currentClass?: import("../semantic/types.js").ClassDeclaration, errorNames: Map<string, import("../semantic/types.js").ErrorDeclaration>, errors: Map<string, import("../semantic/types.js").ErrorDeclaration>, functions: Map<string, RubyFunctionSignature>, orderedMapDeclarations?: Set<object>, records: Map<string, import("../semantic/types.js").RecordDeclaration>, recordNames: Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>, loopDepth?: number, returnType?: import("../semantic/types.js").SemanticFunctionReturnType, typeParameters?: Map<string, import("../semantic/types.js").TypeParameter>}} RubyConversionContext */
64
+ /** @typedef {{bindings: Map<string, import("../semantic/types.js").SemanticBindingType>, catchBindingNames?: Set<string>, classes: Map<string, import("../semantic/types.js").ClassDeclaration>, catchBindingNodes?: Map<string, import("../semantic/types.js").CatchBinding>, currentCatchBindingNode?: import("../semantic/types.js").CatchBinding, currentClass?: import("../semantic/types.js").ClassDeclaration, errorNames: Map<string, import("../semantic/types.js").ErrorDeclaration>, errors: Map<string, import("../semantic/types.js").ErrorDeclaration>, functions: Map<string, RubyFunctionSignature>, orderedMapDeclarations?: Set<object>, records: Map<string, import("../semantic/types.js").RecordDeclaration>, recordNames: Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>, loopDepth?: number, returnType?: import("../semantic/types.js").SemanticFunctionReturnType, typeParameters?: Map<string, import("../semantic/types.js").TypeParameter>}} RubyConversionContext */
65
65
  const rubyBinaryOperations = new Map([
66
66
  ["+", "Add"],
67
67
  ["-", "Subtract"],
@@ -357,6 +357,9 @@ function convertExpression(node, filename, source, context, expectedType, preser
357
357
  reference: referenceType(/** @type {string} */ (declaration.id), nodeLocation(node.receiver, filename, source))},
358
358
  {class: nodeLocation(node.receiver, filename, source)})
359
359
  }
360
+ if (declaration.kind == "InterfaceDeclaration") {
361
+ return unsupportedSyntax("ruby", "construction of an interface", nodeLocation(node.receiver, filename, source))
362
+ }
360
363
  if (declaration.kind == "EffectResourceDeclaration") {
361
364
  return unsupportedSyntax("ruby", "owned resource construction", nodeLocation(node.receiver, filename, source))
362
365
  }
@@ -817,7 +820,7 @@ function associatedComments(comments, node, source) {
817
820
  * @param {LocalVariableWriteNode} node - Local write.
818
821
  * @param {string} filename - Source filename.
819
822
  * @param {string} source - Complete source.
820
- * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
823
+ * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by source name.
821
824
  * @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
822
825
  * @returns {{immutable: boolean, type: import("../semantic/types.js").SemanticValueType} | undefined} Metadata when present.
823
826
  */
@@ -1301,7 +1304,7 @@ function isImmediateCommentGap(gap) {
1301
1304
  * @param {import("../semantic/types.js").SourceLocation} location - Source location.
1302
1305
  * @param {string} source - Complete parser input.
1303
1306
  * @param {import("../semantic/types.js").SourceLocation} [typeLocation] - Exact type comment token location.
1304
- * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
1307
+ * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} [recordNames] - Nominal declarations by source name.
1305
1308
  * @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
1306
1309
  * @returns {import("../semantic/types.js").SemanticValueType} Semantic type.
1307
1310
  */
@@ -1315,7 +1318,7 @@ function convertType(sourceType, subject, location, source, typeLocation = locat
1315
1318
  * @param {import("@ruby/prism/src/deserialize.js").Comment[]} comments - Prism comments.
1316
1319
  * @param {string} filename - Source filename.
1317
1320
  * @param {string} source - Complete source.
1318
- * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
1321
+ * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by source name.
1319
1322
  * @param {string} ownerId - Stable function identity.
1320
1323
  * @param {boolean} [allowBuiltinOutput] - Whether compiler authority admits the exact output facade function.
1321
1324
  * @returns {RubyFunctionSignature} Semantic signature.
@@ -1380,7 +1383,7 @@ function convertFunctionSignature(node, comments, filename, source, recordNames,
1380
1383
  * @param {DefNode} node - Prism definition.
1381
1384
  * @param {RubyFunctionSignature} signature - Converted signature.
1382
1385
  * @param {Map<string, RubyFunctionSignature>} functions - Module signatures.
1383
- * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
1386
+ * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by source name.
1384
1387
  * @param {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Record declarations by identity.
1385
1388
  * @param {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Reference classes by identity.
1386
1389
  * @param {Map<string, import("../semantic/types.js").ErrorDeclaration>} errorNames - Error declarations by source name.
@@ -1491,7 +1494,7 @@ function convertRubyError(node, declaration, filename, source) {
1491
1494
  * Converts the bounded Ruby reference-class profile.
1492
1495
  * @param {ClassNode} node - Prism class declaration.
1493
1496
  * @param {import("../semantic/types.js").ClassDeclaration} declaration - Predeclared class identity.
1494
- * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} nominalNames - Visible nominal declarations.
1497
+ * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} nominalNames - Visible nominal declarations.
1495
1498
  * @param {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Records by identity.
1496
1499
  * @param {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Classes by identity.
1497
1500
  * @param {Map<string, RubyFunctionSignature>} functions - Top-level signatures.
@@ -1607,7 +1610,7 @@ function convertRubyReferenceClass(node, declaration, nominalNames, records, cla
1607
1610
  * Converts one exact typed-reader/initializer/freeze Ruby record profile.
1608
1611
  * @param {ClassNode} node - Prism class declaration.
1609
1612
  * @param {import("../semantic/types.js").RecordDeclaration} declaration - Predeclared nominal identity.
1610
- * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by name.
1613
+ * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by name.
1611
1614
  * @param {import("@ruby/prism/src/deserialize.js").Comment[]} comments - Parser-owned comments.
1612
1615
  * @param {string} filename - Source filename.
1613
1616
  * @param {string} source - Complete source.
@@ -1894,7 +1897,7 @@ export function parseRuby({capabilities = [], filename, source, program}) {
1894
1897
  classDeclarations.findIndex((candidate) => candidate.name == declaration.name) != index)
1895
1898
 
1896
1899
  if (reopened) return unsupportedSyntax("ruby", `reference class reopening '${reopened.name}'`, reopened.location)
1897
- const recordNames = /** @type {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} */ (
1900
+ const recordNames = /** @type {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} */ (
1898
1901
  new Map(program?.records ?? []))
1899
1902
  for (const [name, declaration] of program?.classes ?? []) recordNames.set(name, declaration)
1900
1903
  for (const resource of capabilities.flatMap(({resources}) => resources)) recordNames.set(resource.name, resource)
@@ -55,6 +55,7 @@ export function iterationBindingType(type, location) {
55
55
  if (type.kind == "RecordType") return recordType(type.declarationId, location,
56
56
  type.arguments?.map((argument) => iterationBindingType(argument, location)))
57
57
  if (type.kind == "ReferenceType") return referenceType(type.declarationId, location)
58
+ if (type.kind == "InterfaceType") return interfaceType(type.declarationId, location)
58
59
  if (type.kind == "OwnedResourceType") return ownedResourceType(type.resourceId, location)
59
60
  if (type.kind == "OwnedReferenceType") return ownedReferenceType(type.declarationId, location)
60
61
 
@@ -166,6 +167,19 @@ export function referenceType(declarationId, location) {
166
167
  return type
167
168
  }
168
169
 
170
+ /**
171
+ * Builds one nominal interface type with a parser-owned type range.
172
+ * @param {string} declarationId - Stable interface declaration identity.
173
+ * @param {import("../semantic/types.js").SourceLocation} location - Complete type-expression range.
174
+ * @returns {import("../semantic/types.js").InterfaceType} Interface type.
175
+ */
176
+ export function interfaceType(declarationId, location) {
177
+ const type = {declarationId, kind: /** @type {const} */ ("InterfaceType")}
178
+
179
+ setParserRanges(type, {type: location})
180
+ return type
181
+ }
182
+
169
183
  /**
170
184
  * Builds one compiler-owned resource type with a parser-owned name range.
171
185
  * @param {string} resourceId - Stable capability resource identity.
@@ -392,7 +406,7 @@ function collectKnownSubstitutions(formal, actual, substitutions) {
392
406
  * @param {"javascript" | "php" | "ruby"} input.language - Comment profile.
393
407
  * @param {import("../semantic/types.js").SourceLocation} input.location - Exact type token range.
394
408
  * @param {import("../semantic/types.js").SourceLocation} input.ownerLocation - Owning declaration range.
395
- * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [input.records] - Available nominal declarations by source name.
409
+ * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} [input.records] - Available nominal declarations by source name.
396
410
  * @param {Map<string, import("../semantic/types.js").TypeParameter>} [input.typeParameters] - Declaration-scoped parameters by name.
397
411
  * @param {string} input.source - Complete parser input for exact subranges.
398
412
  * @param {string | undefined} input.sourceType - Exact comment-parser/Prism-owned type token.
@@ -451,9 +465,11 @@ export function documentedValueType({language, location, ownerLocation, records
451
465
 
452
466
  if (directRecord?.id) return directRecord.kind == "ClassDeclaration"
453
467
  ? referenceType(directRecord.id, location)
454
- : directRecord.kind == "EffectResourceDeclaration"
455
- ? ownedResourceType(directRecord.id, location)
456
- : recordType(directRecord.id, location)
468
+ : directRecord.kind == "InterfaceDeclaration"
469
+ ? interfaceType(directRecord.id, location)
470
+ : directRecord.kind == "EffectResourceDeclaration"
471
+ ? ownedResourceType(directRecord.id, location)
472
+ : recordType(directRecord.id, location)
457
473
  const directTypeParameter = typeParameters.get(unwrappedName)
458
474
 
459
475
  if (directTypeParameter?.id) return typeVariable(directTypeParameter.id, location)
@@ -484,7 +500,7 @@ class DocumentedTypeParser {
484
500
  * @param {string} text - Parser-owned type text.
485
501
  * @param {import("../semantic/types.js").SourceLocation} location - Whole token location.
486
502
  * @param {string} source - Complete source.
487
- * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} records - Available nominal declarations.
503
+ * @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} records - Available nominal declarations.
488
504
  * @param {Map<string, import("../semantic/types.js").TypeParameter>} typeParameters - Declaration-scoped parameters.
489
505
  */
490
506
  constructor(language, text, location, source, records, typeParameters) {
@@ -580,9 +596,11 @@ class DocumentedTypeParser {
580
596
  if (record?.id && this.text[this.offset] != "<" && this.text[this.offset] != "[") {
581
597
  const type = record.kind == "ClassDeclaration"
582
598
  ? referenceType(record.id, this.range(start, nameEnd))
583
- : record.kind == "EffectResourceDeclaration"
584
- ? ownedResourceType(record.id, this.range(start, nameEnd))
585
- : recordType(record.id, this.range(start, nameEnd))
599
+ : record.kind == "InterfaceDeclaration"
600
+ ? interfaceType(record.id, this.range(start, nameEnd))
601
+ : record.kind == "EffectResourceDeclaration"
602
+ ? ownedResourceType(record.id, this.range(start, nameEnd))
603
+ : recordType(record.id, this.range(start, nameEnd))
586
604
 
587
605
  if (this.language == "ruby" && this.consume("?")) {
588
606
  return optionalType(type, this.range(start, this.offset), this.typeRange(type, start, nameEnd))
@@ -169,6 +169,7 @@ export function createLanguageRegistry(candidateRecords) {
169
169
  }
170
170
  if (typeof candidate.roundTrip != "boolean") invalidRegistry(`Registry record '${id}' requires a Boolean round-trip declaration.`, id)
171
171
  const featuresCandidate = candidate.features ?? {
172
+ boundedSubtyping: false,
172
173
  closedRecords: false,
173
174
  conditionControlledLoops: false,
174
175
  conditionalExpressions: false,
@@ -185,7 +186,8 @@ export function createLanguageRegistry(candidateRecords) {
185
186
  }
186
187
 
187
188
  if (!isPlainObject(featuresCandidate) ||
188
- Object.keys(featuresCandidate).sort().join(",") != "closedRecords,conditionControlledLoops,conditionalExpressions,effectfulCapabilitiesAndResources,generalFunctionsAndCalls,immutableCollections,optionalValues,orderedListIteration,orderedMapIteration,referenceClasses,stringInterpolation,typeParametersAndGenerics,typedErrors" ||
189
+ Object.keys(featuresCandidate).sort().join(",") != "boundedSubtyping,closedRecords,conditionControlledLoops,conditionalExpressions,effectfulCapabilitiesAndResources,generalFunctionsAndCalls,immutableCollections,optionalValues,orderedListIteration,orderedMapIteration,referenceClasses,stringInterpolation,typeParametersAndGenerics,typedErrors" ||
190
+ typeof featuresCandidate.boundedSubtyping != "boolean" ||
189
191
  typeof featuresCandidate.closedRecords != "boolean" ||
190
192
  typeof featuresCandidate.conditionControlledLoops != "boolean" ||
191
193
  typeof featuresCandidate.conditionalExpressions != "boolean" ||
@@ -196,12 +198,14 @@ export function createLanguageRegistry(candidateRecords) {
196
198
  typeof featuresCandidate.orderedListIteration != "boolean" ||
197
199
  typeof featuresCandidate.orderedMapIteration != "boolean" ||
198
200
  typeof featuresCandidate.referenceClasses != "boolean" ||
201
+ (featuresCandidate.boundedSubtyping && !featuresCandidate.referenceClasses) ||
199
202
  typeof featuresCandidate.stringInterpolation != "boolean" ||
200
203
  typeof featuresCandidate.typedErrors != "boolean" ||
201
204
  typeof featuresCandidate.typeParametersAndGenerics != "boolean") {
202
205
  invalidRegistry(`Registry record '${id}' has an invalid feature declaration.`, id)
203
206
  }
204
207
  const features = deepFreeze(/** @type {import("./semantic/types.js").LanguageFeatureCapabilities} */ ({
208
+ boundedSubtyping: featuresCandidate.boundedSubtyping,
205
209
  closedRecords: featuresCandidate.closedRecords,
206
210
  conditionControlledLoops: featuresCandidate.conditionControlledLoops,
207
211
  conditionalExpressions: featuresCandidate.conditionalExpressions,
@@ -541,6 +545,22 @@ const records = [
541
545
  acceptance: {stages: ["parse", "generate", "compile", "execute"], toolchains: ["python"]},
542
546
  check: {factory: createPythonCheckPlan, stages: ["compile"], toolchains: ["python"]},
543
547
  defaultFilename: "program.py",
548
+ features: {
549
+ boundedSubtyping: false,
550
+ closedRecords: false,
551
+ conditionControlledLoops: false,
552
+ conditionalExpressions: false,
553
+ effectfulCapabilitiesAndResources: false,
554
+ generalFunctionsAndCalls: true,
555
+ immutableCollections: false,
556
+ optionalValues: false,
557
+ orderedListIteration: false,
558
+ orderedMapIteration: false,
559
+ referenceClasses: false,
560
+ stringInterpolation: false,
561
+ typedErrors: false,
562
+ typeParametersAndGenerics: false
563
+ },
544
564
  frontend: pythonFrontend,
545
565
  id: "python",
546
566
  mediaType: "text/x-python",
@@ -582,6 +602,7 @@ const records = [
582
602
  optionalValues: false,
583
603
  orderedListIteration: false,
584
604
  orderedMapIteration: false,
605
+ boundedSubtyping: false,
585
606
  referenceClasses: false,
586
607
  typedErrors: false,
587
608
  typeParametersAndGenerics: false
@@ -621,6 +642,7 @@ const records = [
621
642
  optionalValues: false,
622
643
  orderedListIteration: false,
623
644
  orderedMapIteration: false,
645
+ boundedSubtyping: false,
624
646
  referenceClasses: false,
625
647
  typedErrors: false,
626
648
  typeParametersAndGenerics: false
@@ -646,6 +668,7 @@ const records = [
646
668
  optionalValues: false,
647
669
  orderedListIteration: false,
648
670
  orderedMapIteration: false,
671
+ boundedSubtyping: false,
649
672
  referenceClasses: false,
650
673
  typedErrors: false,
651
674
  typeParametersAndGenerics: false
@@ -661,7 +684,7 @@ const records = [
661
684
  artifactMultiplicity: "multiple",
662
685
  binaryBackend: generateBrowserWasm,
663
686
  defaultFilename: "program.wasm",
664
- features: {closedRecords: false, conditionControlledLoops: false, conditionalExpressions: false, stringInterpolation: false, effectfulCapabilitiesAndResources: false, generalFunctionsAndCalls: false, immutableCollections: false, optionalValues: false, orderedListIteration: false, orderedMapIteration: false, referenceClasses: false, typedErrors: false, typeParametersAndGenerics: false},
687
+ features: {boundedSubtyping: false, closedRecords: false, conditionControlledLoops: false, conditionalExpressions: false, stringInterpolation: false, effectfulCapabilitiesAndResources: false, generalFunctionsAndCalls: false, immutableCollections: false, optionalValues: false, orderedListIteration: false, orderedMapIteration: false, referenceClasses: false, typedErrors: false, typeParametersAndGenerics: false},
665
688
  id: "wasm",
666
689
  mapping: {binaryRanges: true, richText: true, sourceMapV3: true},
667
690
  mediaType: "application/wasm",
@@ -671,7 +694,7 @@ const records = [
671
694
  acceptance: {stages: ["generate", "compile", "validate", "instantiate", "execute"], toolchains: ["android"]},
672
695
  applicationBackend: generateAndroidApplication,
673
696
  artifactMultiplicity: "multiple",
674
- features: {closedRecords: false, conditionControlledLoops: false, conditionalExpressions: false, stringInterpolation: false, effectfulCapabilitiesAndResources: false, generalFunctionsAndCalls: false, immutableCollections: false, optionalValues: false, orderedListIteration: false, orderedMapIteration: false, referenceClasses: false, typedErrors: false, typeParametersAndGenerics: false},
697
+ features: {boundedSubtyping: false, closedRecords: false, conditionControlledLoops: false, conditionalExpressions: false, stringInterpolation: false, effectfulCapabilitiesAndResources: false, generalFunctionsAndCalls: false, immutableCollections: false, optionalValues: false, orderedListIteration: false, orderedMapIteration: false, referenceClasses: false, typedErrors: false, typeParametersAndGenerics: false},
675
698
  id: "android",
676
699
  mapping: {binaryRanges: true, richText: true, sourceMapV3: true},
677
700
  roundTrip: false
@@ -680,7 +703,7 @@ const records = [
680
703
  acceptance: {stages: ["generate"], toolchains: []},
681
704
  applicationBackend: generateIosApplication,
682
705
  artifactMultiplicity: "multiple",
683
- features: {closedRecords: false, conditionControlledLoops: false, conditionalExpressions: false, stringInterpolation: false, effectfulCapabilitiesAndResources: false, generalFunctionsAndCalls: false, immutableCollections: false, optionalValues: false, orderedListIteration: false, orderedMapIteration: false, referenceClasses: false, typedErrors: false, typeParametersAndGenerics: false},
706
+ features: {boundedSubtyping: false, closedRecords: false, conditionControlledLoops: false, conditionalExpressions: false, stringInterpolation: false, effectfulCapabilitiesAndResources: false, generalFunctionsAndCalls: false, immutableCollections: false, optionalValues: false, orderedListIteration: false, orderedMapIteration: false, referenceClasses: false, typedErrors: false, typeParametersAndGenerics: false},
684
707
  id: "ios",
685
708
  mapping: {binaryRanges: true, richText: true, sourceMapV3: true},
686
709
  roundTrip: false
@@ -689,7 +712,7 @@ const records = [
689
712
  acceptance: {stages: ["generate", "restore", "compile", "validate", "instantiate", "execute"], toolchains: ["flutter", "android"]},
690
713
  applicationBackend: generateFlutterApplication,
691
714
  artifactMultiplicity: "multiple",
692
- features: {closedRecords: false, conditionControlledLoops: false, conditionalExpressions: false, stringInterpolation: false, effectfulCapabilitiesAndResources: false, generalFunctionsAndCalls: false, immutableCollections: false, optionalValues: false, orderedListIteration: false, orderedMapIteration: false, referenceClasses: false, typedErrors: false, typeParametersAndGenerics: false},
715
+ features: {boundedSubtyping: false, closedRecords: false, conditionControlledLoops: false, conditionalExpressions: false, stringInterpolation: false, effectfulCapabilitiesAndResources: false, generalFunctionsAndCalls: false, immutableCollections: false, optionalValues: false, orderedListIteration: false, orderedMapIteration: false, referenceClasses: false, typedErrors: false, typeParametersAndGenerics: false},
693
716
  id: "flutter",
694
717
  mapping: {binaryRanges: true, richText: true, sourceMapV3: true},
695
718
  roundTrip: false
@@ -712,6 +735,7 @@ function language(values) {
712
735
  return {
713
736
  artifactMultiplicity: "single",
714
737
  features: {
738
+ boundedSubtyping: String(values.id) == "php",
715
739
  closedRecords: ["php", "ruby", "javascript", "typescript", "java"].includes(String(values.id)),
716
740
  conditionControlledLoops: ["php", "ruby", "javascript", "typescript", "java"].includes(String(values.id)),
717
741
  conditionalExpressions: ["php", "ruby", "javascript", "typescript", "java"].includes(String(values.id)),
package/src/mapping.js CHANGED
@@ -764,7 +764,7 @@ export function validateMapping(value) {
764
764
  for (const symbol of mapping.symbols) {
765
765
  if (!symbol || typeof symbol != "object" || typeof symbol.id != "string" || symbol.id.length == 0 || symbolIds.has(symbol.id) ||
766
766
  typeof symbol.name != "string" || symbol.name.length == 0 ||
767
- !["capability", "resource", "failure", "operation", "record", "class", "error", "field", "constructor", "method", "function", "typeParameter", "parameter", "local", "iteration", "catch"].includes(symbol.kind) ||
767
+ !["capability", "resource", "failure", "operation", "record", "interface", "interfaceMethod", "class", "error", "field", "constructor", "method", "function", "typeParameter", "parameter", "local", "iteration", "catch"].includes(symbol.kind) ||
768
768
  !nodeIds.has(symbol.declarationNodeId) || !Array.isArray(symbol.references) ||
769
769
  symbol.semanticDeclarationId !== undefined && (typeof symbol.semanticDeclarationId != "string" || !(
770
770
  symbol.kind == "function" && /^(?:[a-z][a-z0-9._-]*#)?function:[0-9]+$/u.test(symbol.semanticDeclarationId) ||
@@ -774,6 +774,8 @@ export function validateMapping(value) {
774
774
  symbol.kind == "field" && /^(?:[a-z][a-z0-9._-]*#)?(?:record|class):[0-9]+:field:[0-9]+$/u.test(symbol.semanticDeclarationId) ||
775
775
  symbol.kind == "constructor" && /^(?:[a-z][a-z0-9._-]*#)?class:[0-9]+:constructor$/u.test(symbol.semanticDeclarationId) ||
776
776
  symbol.kind == "method" && /^(?:[a-z][a-z0-9._-]*#)?class:[0-9]+:method:[0-9]+$/u.test(symbol.semanticDeclarationId) ||
777
+ symbol.kind == "interface" && /^(?:[a-z][a-z0-9._-]*#)?interface:[0-9]+$/u.test(symbol.semanticDeclarationId) ||
778
+ symbol.kind == "interfaceMethod" && /^(?:[a-z][a-z0-9._-]*#)?interface:[0-9]+:method:[0-9]+$/u.test(symbol.semanticDeclarationId) ||
777
779
  symbol.kind == "typeParameter" && /^(?:[a-z][a-z0-9._-]*#)?(?:function|record):[0-9]+:type:[0-9]+$/u.test(symbol.semanticDeclarationId) ||
778
780
  symbol.kind == "capability" && /^(?:module:[0-9]+\/)?capability:[0-9]+$/u.test(symbol.semanticDeclarationId) ||
779
781
  symbol.kind == "resource" && /^(?:module:[0-9]+\/)?capability:[0-9]+\/resource:[0-9]+$/u.test(symbol.semanticDeclarationId) ||
@@ -326,6 +326,7 @@ function typeIdentity(type) {
326
326
  if (type.kind == "RecordType") return {arguments: type.arguments?.map((argument) =>
327
327
  /** @type {import("./types.js").SemanticTypeIdentity} */ (typeIdentity(argument))), declarationId: type.declarationId, kind: type.kind}
328
328
  if (type.kind == "ReferenceType") return {declarationId: type.declarationId, kind: type.kind}
329
+ if (type.kind == "InterfaceType") return {declarationId: type.declarationId, kind: type.kind}
329
330
  return {kind: type.kind, parameterId: type.parameterId}
330
331
  }
331
332
 
@@ -492,6 +492,7 @@ export function semanticEntries(module) {
492
492
  node.imports.forEach((child, index) => visit(child, `/imports/${index}`, location))
493
493
  }
494
494
  ;(node.records ?? []).forEach((child, index) => visit(child, `/records/${index}`, location))
495
+ ;(node.interfaces ?? []).forEach((child, index) => visit(child, `/interfaces/${index}`, location))
495
496
  if (!("sourceFilename" in node) || Reflect.get(node, "stdlibFacade") !== undefined) {
496
497
  ;(node.classes ?? []).forEach((child, index) => visit(child, `/classes/${index}`, location))
497
498
  }
@@ -512,7 +513,14 @@ export function semanticEntries(module) {
512
513
  } else if (node.kind == "EffectOperationDeclaration") {
513
514
  node.parameters.forEach((child, index) => visit(child.type, `${path}/parameters/${index}/type`, location))
514
515
  visit(node.returnType, `${path}/returnType`, location)
516
+ } else if (node.kind == "InterfaceDeclaration") {
517
+ (node.extends ?? []).forEach((child, index) => visit(child, `${path}/extends/${index}`, location))
518
+ node.methods.forEach((child, index) => visit(child, `${path}/methods/${index}`, location))
519
+ } else if (node.kind == "InterfaceMethodDeclaration") {
520
+ node.parameters.forEach((child, index) => visit(child.type, `${path}/parameters/${index}/type`, location))
521
+ visit(node.returnType, `${path}/returnType`, location)
515
522
  } else if (node.kind == "ClassDeclaration") {
523
+ (node.implements ?? []).forEach((child, index) => visit(child, `${path}/implements/${index}`, location))
516
524
  node.fields.forEach((child, index) => visit(child, `${path}/fields/${index}`, location))
517
525
  visit(node.constructor, `${path}/constructor`, location)
518
526
  node.methods.forEach((child, index) => visit(child, `${path}/methods/${index}`, location))
@@ -665,6 +673,10 @@ function resolveSymbols(module, records) {
665
673
  /** @type {Map<string, string>} */
666
674
  const fieldSymbols = new Map()
667
675
  /** @type {Map<string, string>} */
676
+ const interfaceSymbols = new Map()
677
+ /** @type {Map<string, string>} */
678
+ const interfaceMethodSymbols = new Map()
679
+ /** @type {Map<string, string>} */
668
680
  const classSymbols = new Map()
669
681
  /** @type {Map<string, string>} */
670
682
  const constructorSymbols = new Map()
@@ -724,11 +736,29 @@ function resolveSymbols(module, records) {
724
736
  if (declaration.id) errorSymbols.set(declaration.id, symbol)
725
737
  }
726
738
 
739
+ for (const [index, declaration] of (module.interfaces ?? []).entries()) {
740
+ const declarationPath = `/interfaces/${index}`
741
+ const interfaceSymbol = declare(declaration, declaration.name, "interface", declarationPath)
742
+
743
+ if (declaration.id) interfaceSymbols.set(declaration.id, interfaceSymbol)
744
+ for (const [extendsIndex, parent] of (declaration.extends ?? []).entries()) {
745
+ visitType(parent, `${declarationPath}/extends/${extendsIndex}`)
746
+ }
747
+ for (const [methodIndex, method] of declaration.methods.entries()) {
748
+ const symbol = declare(method, method.name, "interfaceMethod", `${declarationPath}/methods/${methodIndex}`)
749
+
750
+ if (method.id) interfaceMethodSymbols.set(method.id, symbol)
751
+ }
752
+ }
753
+
727
754
  for (const [index, declaration] of (module.classes ?? []).entries()) {
728
755
  const declarationPath = `/classes/${index}`
729
756
  const classSymbol = declare(declaration, declaration.name, "class", declarationPath)
730
757
 
731
758
  if (declaration.id) classSymbols.set(declaration.id, classSymbol)
759
+ for (const [implementsIndex, implemented] of (declaration.implements ?? []).entries()) {
760
+ visitType(implemented, `${declarationPath}/implements/${implementsIndex}`)
761
+ }
732
762
  for (const [fieldIndex, field] of declaration.fields.entries()) {
733
763
  const symbol = declare(field, field.name, "field", `${declarationPath}/fields/${fieldIndex}`)
734
764
 
@@ -806,7 +836,7 @@ function resolveSymbols(module, records) {
806
836
 
807
837
  /**
808
838
  * Declares one canonical symbol.
809
- * @param {import("./types.js").EffectCapabilityDeclaration | import("./types.js").EffectResourceDeclaration | import("./types.js").EffectFailureDeclaration | import("./types.js").EffectOperationDeclaration | import("./types.js").RecordDeclaration | import("./types.js").ClassDeclaration | import("./types.js").ErrorDeclaration | import("./types.js").RecordField | import("./types.js").PrivateField | import("./types.js").ConstructorDeclaration | import("./types.js").MethodDeclaration | import("./types.js").FunctionDeclaration | import("./types.js").TypeParameter | import("./types.js").Parameter | import("./types.js").LocalDeclaration | import("./types.js").ValueBinding | import("./types.js").CatchBinding} node - Declaration.
839
+ * @param {import("./types.js").EffectCapabilityDeclaration | import("./types.js").EffectResourceDeclaration | import("./types.js").EffectFailureDeclaration | import("./types.js").EffectOperationDeclaration | import("./types.js").RecordDeclaration | import("./types.js").ClassDeclaration | import("./types.js").InterfaceDeclaration | import("./types.js").InterfaceMethodDeclaration | import("./types.js").ErrorDeclaration | import("./types.js").RecordField | import("./types.js").PrivateField | import("./types.js").ConstructorDeclaration | import("./types.js").MethodDeclaration | import("./types.js").FunctionDeclaration | import("./types.js").TypeParameter | import("./types.js").Parameter | import("./types.js").LocalDeclaration | import("./types.js").ValueBinding | import("./types.js").CatchBinding} node - Declaration.
810
840
  * @param {string} name - Symbol name.
811
841
  * @param {import("./types.js").SemanticSymbolKind} kind - Symbol kind.
812
842
  * @param {string} path - Declaration occurrence path.
@@ -828,7 +858,7 @@ function resolveSymbols(module, records) {
828
858
  location,
829
859
  name,
830
860
  references: [],
831
- ...(["capability", "resource", "failure", "operation", "record", "class", "error", "field", "constructor", "method", "function", "typeParameter"].includes(kind)
861
+ ...(["capability", "resource", "failure", "operation", "record", "interface", "interfaceMethod", "class", "error", "field", "constructor", "method", "function", "typeParameter"].includes(kind)
832
862
  ? {semanticDeclarationId: /** @type {{id?: string}} */ (node).id}
833
863
  : {})
834
864
  })
@@ -1009,6 +1039,7 @@ function resolveSymbols(module, records) {
1009
1039
  for (const [index, argument] of (type.arguments ?? []).entries()) visitType(argument, `${path}/arguments/${index}`)
1010
1040
  }
1011
1041
  else if (type.kind == "ErrorType") reference(type, errorSymbols.get(type.declarationId), "type", path)
1042
+ else if (type.kind == "InterfaceType") reference(type, interfaceSymbols.get(type.declarationId), "type", path)
1012
1043
  else if (type.kind == "ReferenceType" || type.kind == "OwnedReferenceType") reference(type, classSymbols.get(type.declarationId), "type", path)
1013
1044
  else if (type.kind == "OwnedResourceType") reference(type, resourceSymbols.get(type.resourceId), "type", path)
1014
1045
  else if (type.kind == "ListType") visitType(type.elementType, `${path}/elementType`)
@@ -1020,7 +1051,7 @@ function resolveSymbols(module, records) {
1020
1051
 
1021
1052
  /**
1022
1053
  * Attaches one resolved symbol reference.
1023
- * @param {import("./types.js").IdentifierExpression | import("./types.js").ReceiverExpression | import("./types.js").CallExpression | import("./types.js").EffectCallExpression | import("./types.js").MethodCallExpression | import("./types.js").TypeVariableReference | import("./types.js").RecordType | import("./types.js").ReferenceType | import("./types.js").OwnedReferenceType | import("./types.js").OwnedResourceType | import("./types.js").RecordConstruction | import("./types.js").ReferenceConstruction | import("./types.js").MemberRead | import("./types.js").PrivateFieldRead | import("./types.js").PrivateFieldWriteStatement | import("./types.js").RethrowStatement | import("./types.js").ErrorType | import("./types.js").ErrorConstruction} node - Reference node.
1054
+ * @param {import("./types.js").IdentifierExpression | import("./types.js").ReceiverExpression | import("./types.js").CallExpression | import("./types.js").EffectCallExpression | import("./types.js").MethodCallExpression | import("./types.js").TypeVariableReference | import("./types.js").RecordType | import("./types.js").ReferenceType | import("./types.js").InterfaceType | import("./types.js").OwnedReferenceType | import("./types.js").OwnedResourceType | import("./types.js").RecordConstruction | import("./types.js").ReferenceConstruction | import("./types.js").MemberRead | import("./types.js").PrivateFieldRead | import("./types.js").PrivateFieldWriteStatement | import("./types.js").RethrowStatement | import("./types.js").ErrorType | import("./types.js").ErrorConstruction} node - Reference node.
1024
1055
  * @param {string | undefined} symbolId - Resolved symbol.
1025
1056
  * @param {"type" | "construct" | "member" | "read" | "write" | "call"} role - Reference role.
1026
1057
  * @param {string} path - Reference occurrence path.
@@ -8,8 +8,8 @@
8
8
  /** @typedef {SemanticTypeName | "void"} FunctionReturnTypeName */
9
9
  /** @typedef {"IntegerNegate" | "BooleanNot"} SemanticUnaryOperation */
10
10
  /** @typedef {"IntegerAdd" | "IntegerSubtract" | "IntegerMultiply" | "BooleanAnd" | "BooleanOr" | "IntegerEqual" | "IntegerNotEqual" | "BooleanEqual" | "BooleanNotEqual" | "StringEqual" | "StringNotEqual" | "IntegerLessThan" | "IntegerLessThanOrEqual" | "IntegerGreaterThan" | "IntegerGreaterThanOrEqual" | "StringConcat"} SemanticBinaryOperation */
11
- /** @typedef {"capability" | "resource" | "failure" | "operation" | "record" | "class" | "error" | "field" | "constructor" | "method" | "function" | "typeParameter" | "parameter" | "local" | "iteration" | "catch"} SemanticSymbolKind */
12
- /** @typedef {"record" | "class" | "error" | "function"} SemanticDeclarationKind */
11
+ /** @typedef {"capability" | "resource" | "failure" | "operation" | "record" | "interface" | "interfaceMethod" | "class" | "error" | "field" | "constructor" | "method" | "function" | "typeParameter" | "parameter" | "local" | "iteration" | "catch"} SemanticSymbolKind */
12
+ /** @typedef {"record" | "interface" | "class" | "error" | "function"} SemanticDeclarationKind */
13
13
  /** @typedef {"declaration" | "type" | "construct" | "member" | "read" | "write" | "call"} SemanticSymbolRole */
14
14
  /** @typedef {"parse" | "generate" | "restore" | "compile" | "link" | "validate" | "instantiate" | "execute"} AcceptanceStage */
15
15
  /** @typedef {"entry" | "source" | "manifest" | "support" | "mapping" | "resource" | "loader"} GeneratedArtifactRole */
@@ -186,6 +186,7 @@
186
186
  * @property {boolean} typedErrors - Task 011 nominal unchecked errors, raises, and exact typed catches.
187
187
  * @property {boolean} typeParametersAndGenerics - Task 012 invariant unbounded type parameters and closed generic applications.
188
188
  * @property {boolean} referenceClasses - Task 033 nominal reference classes, private instance state, exact constructors, and receiver methods.
189
+ * @property {boolean} boundedSubtyping - Task 048 nominal interfaces with exact implementation contracts and implicit upcast (requires referenceClasses).
189
190
  * @property {boolean} effectfulCapabilitiesAndResources - Task 034 authorized host effects, typed failures, and owned resources.
190
191
  */
191
192
 
@@ -700,7 +701,14 @@
700
701
  * @property {SemanticNodeSourceProvenance} [sourceProvenance] - Parser-owned type-name range.
701
702
  */
702
703
 
703
- /** @typedef {TypeReference | TypeVariableReference | ListType | MapType | OrderedMapType | OptionalType | RecordType | ReferenceType | OwnedResourceType | OwnedReferenceType} SemanticValueType */
704
+ /**
705
+ * @typedef InterfaceType
706
+ * @property {"InterfaceType"} kind - Nominal bounded-subtyping interface type discriminator.
707
+ * @property {string} declarationId - Stable module-local interface declaration identity.
708
+ * @property {SemanticNodeSourceProvenance} [sourceProvenance] - Parser-owned type-name range.
709
+ */
710
+
711
+ /** @typedef {TypeReference | TypeVariableReference | ListType | MapType | OrderedMapType | OptionalType | RecordType | ReferenceType | OwnedResourceType | OwnedReferenceType | InterfaceType} SemanticValueType */
704
712
  /** @typedef {SemanticValueType | ErrorType} SemanticBindingType */
705
713
 
706
714
  /**
@@ -712,7 +720,7 @@
712
720
 
713
721
  /** @typedef {SemanticValueType | FunctionReturnTypeReference} SemanticFunctionReturnType */
714
722
 
715
- /** @typedef {SemanticTypeName | {kind: "TypeVariableReference", parameterId: string} | {kind: "ListType", elementType: SemanticTypeIdentity} | {kind: "MapType", keyType: SemanticTypeIdentity, valueType: SemanticTypeIdentity} | {kind: "OrderedMapType", keyType: SemanticTypeIdentity, valueType: SemanticTypeIdentity, order: "insertion"} | {kind: "OptionalType", valueType: SemanticTypeIdentity} | {kind: "RecordType", declarationId: string, arguments?: SemanticTypeIdentity[]} | {kind: "ReferenceType", declarationId: string} | {kind: "OwnedResourceType", resourceId: string} | {kind: "OwnedReferenceType", declarationId: string}} SemanticTypeIdentity */
723
+ /** @typedef {SemanticTypeName | {kind: "TypeVariableReference", parameterId: string} | {kind: "ListType", elementType: SemanticTypeIdentity} | {kind: "MapType", keyType: SemanticTypeIdentity, valueType: SemanticTypeIdentity} | {kind: "OrderedMapType", keyType: SemanticTypeIdentity, valueType: SemanticTypeIdentity, order: "insertion"} | {kind: "OptionalType", valueType: SemanticTypeIdentity} | {kind: "RecordType", declarationId: string, arguments?: SemanticTypeIdentity[]} | {kind: "ReferenceType", declarationId: string} | {kind: "OwnedResourceType", resourceId: string} | {kind: "OwnedReferenceType", declarationId: string} | {kind: "InterfaceType", declarationId: string}} SemanticTypeIdentity */
716
724
  /** @typedef {SemanticTypeIdentity | "void"} FunctionReturnTypeIdentity */
717
725
 
718
726
  /**
@@ -1345,10 +1353,33 @@
1345
1353
  * @property {Block} body - Method body.
1346
1354
  * @property {readonly ["host"]} [effects] - Inferred checked host-effect fact.
1347
1355
  * @property {string[]} [failureIds] - Ordered capability failures reachable through the method.
1356
+ * @property {{interfaceId: string, methodId: string}} [implements] - Exact satisfied interface member, required after validation when the method satisfies an interface contract.
1348
1357
  * @property {SourceLocation} location - Complete method location.
1349
1358
  * @property {SemanticNodeSourceProvenance} [sourceProvenance] - Parser-owned method-name range.
1350
1359
  */
1351
1360
 
1361
+ /**
1362
+ * @typedef InterfaceMethodDeclaration
1363
+ * @property {"InterfaceMethodDeclaration"} kind - Signature-only interface contract member.
1364
+ * @property {string} [id] - Stable interface-scoped method identity, required after validation.
1365
+ * @property {string} name - Source-visible method name.
1366
+ * @property {Parameter[]} parameters - Exact positional required parameters with explicit types.
1367
+ * @property {SemanticFunctionReturnType} returnType - Exact value or void return.
1368
+ * @property {SourceLocation} location - Complete interface method location.
1369
+ * @property {SemanticNodeSourceProvenance} [sourceProvenance] - Parser-owned method-name range.
1370
+ */
1371
+
1372
+ /**
1373
+ * @typedef InterfaceDeclaration
1374
+ * @property {"InterfaceDeclaration"} kind - Closed signature-only nominal interface with exact implementation contracts.
1375
+ * @property {string} [id] - Stable module-local interface identity, required after validation.
1376
+ * @property {string} name - Source-visible interface name.
1377
+ * @property {InterfaceType[]} [extends] - Direct interface parents in source order; omitted when empty.
1378
+ * @property {InterfaceMethodDeclaration[]} methods - Ordered closed signature set.
1379
+ * @property {SourceLocation} location - Complete interface location.
1380
+ * @property {SemanticNodeSourceProvenance} [sourceProvenance] - Parser-owned interface-name range.
1381
+ */
1382
+
1352
1383
  /**
1353
1384
  * @typedef ClassDeclaration
1354
1385
  * @property {"ClassDeclaration"} kind - Nominal reference class with private mutable state.
@@ -1357,6 +1388,7 @@
1357
1388
  * @property {PrivateField[]} fields - Ordered private instance state.
1358
1389
  * @property {ConstructorDeclaration} constructor - Sole exact constructor.
1359
1390
  * @property {MethodDeclaration[]} methods - Ordered receiver methods.
1391
+ * @property {InterfaceType[]} [implements] - Direct interface contracts in source order; omitted when empty.
1360
1392
  * @property {{kind: "ordinary"} | {kind: "ownedResource", fieldId: string, resourceId: string}} [ownership] - Explicit class ownership profile.
1361
1393
  * @property {SourceLocation} location - Complete class location.
1362
1394
  * @property {SemanticNodeSourceProvenance} [sourceProvenance] - Parser-owned class-name range.
@@ -1391,6 +1423,7 @@
1391
1423
  * @typedef SemanticModule
1392
1424
  * @property {"Module"} kind - Node discriminator.
1393
1425
  * @property {RecordDeclaration[]} [records] - Top-level nominal record declarations in source order; omitted when empty.
1426
+ * @property {InterfaceDeclaration[]} [interfaces] - Top-level nominal interface declarations in source order; omitted when empty.
1394
1427
  * @property {ClassDeclaration[]} [classes] - Top-level nominal reference classes in source order; omitted when empty.
1395
1428
  * @property {ErrorDeclaration[]} [errors] - Top-level nominal unchecked error declarations in source order; omitted when empty.
1396
1429
  * @property {EffectCapabilityDeclaration[]} [capabilities] - Compiler-authorized Task 034 declarations; omission is the legacy empty set.
@@ -1441,6 +1474,7 @@
1441
1474
  * @property {string} id - Stable caller-supplied logical module identity.
1442
1475
  * @property {string} sourceFilename - Explicit source filename; never an implicit read request.
1443
1476
  * @property {RecordDeclaration[]} [records] - Top-level nominal record declarations.
1477
+ * @property {InterfaceDeclaration[]} [interfaces] - Top-level nominal interface declarations.
1444
1478
  * @property {ClassDeclaration[]} [classes] - Compiler-owned facade reference classes.
1445
1479
  * @property {ErrorDeclaration[]} [errors] - Top-level nominal unchecked error declarations.
1446
1480
  * @property {FunctionDeclaration[]} functions - Top-level functions.
@@ -1486,7 +1520,7 @@
1486
1520
  * @property {StdlibFacadeProgramDescriptor} [stdlibFacades] - Complete selected source-language facade closure.
1487
1521
  */
1488
1522
 
1489
- /** @typedef {SemanticModule | SemanticProgramModule | SemanticImport | SemanticExport | RecordDeclaration | ClassDeclaration | ErrorDeclaration | EffectCapabilityDeclaration | EffectResourceDeclaration | EffectFailureDeclaration | EffectOperationDeclaration | RecordField | PrivateField | ConstructorDeclaration | MethodDeclaration | FunctionDeclaration | TypeParameter | Parameter | ValueBinding | CatchBinding | CatchHandler | Block | Statement | EntryPoint | Expression | ErrorConstruction | ErrorType | MapEntry | SemanticValueType | FunctionReturnTypeReference} SemanticNode */
1523
+ /** @typedef {SemanticModule | SemanticProgramModule | SemanticImport | SemanticExport | RecordDeclaration | InterfaceDeclaration | InterfaceMethodDeclaration | ClassDeclaration | ErrorDeclaration | EffectCapabilityDeclaration | EffectResourceDeclaration | EffectFailureDeclaration | EffectOperationDeclaration | RecordField | PrivateField | ConstructorDeclaration | MethodDeclaration | FunctionDeclaration | TypeParameter | Parameter | ValueBinding | CatchBinding | CatchHandler | Block | Statement | EntryPoint | Expression | ErrorConstruction | ErrorType | MapEntry | SemanticValueType | FunctionReturnTypeReference} SemanticNode */
1490
1524
  /** @typedef {SemanticNode} SemanticNodeWithoutLocations */
1491
1525
 
1492
1526
  export {}