semantifold 0.11.0 → 0.13.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.
- package/README.md +2 -1
- package/build/src/backends/c.d.ts +2 -1
- package/build/src/backends/c.d.ts.map +1 -1
- package/build/src/backends/c.js +26 -5
- package/build/src/backends/cpp.js +3 -1
- package/build/src/backends/effects.js +3 -2
- package/build/src/backends/java.js +33 -14
- package/build/src/backends/javascript.js +83 -18
- package/build/src/backends/ordered-expressions.d.ts +19 -7
- package/build/src/backends/ordered-expressions.d.ts.map +1 -1
- package/build/src/backends/ordered-expressions.js +46 -11
- package/build/src/backends/php.js +33 -14
- package/build/src/backends/ruby.js +37 -12
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +92 -37
- package/build/src/backends/typescript.js +83 -18
- package/build/src/frontends/c.d.ts +34 -1
- package/build/src/frontends/c.d.ts.map +1 -1
- package/build/src/frontends/c.js +128 -21
- package/build/src/frontends/java.d.ts +8 -0
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +77 -33
- package/build/src/frontends/javascript-typescript.d.ts +2 -0
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +118 -37
- package/build/src/frontends/php.d.ts +8 -0
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +68 -29
- package/build/src/frontends/ruby.d.ts +3 -0
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +103 -30
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +16 -1
- package/build/src/semantic/effects.d.ts.map +1 -1
- package/build/src/semantic/effects.js +18 -6
- package/build/src/semantic/lifetime.js +28 -4
- package/build/src/semantic/provenance.d.ts.map +1 -1
- package/build/src/semantic/provenance.js +26 -7
- package/build/src/semantic/types.d.ts +76 -16
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +23 -7
- package/build/src/semantic/validate.d.ts.map +1 -1
- package/build/src/semantic/validate.js +106 -35
- package/docs/adr/20261026-typed-error-handling-extension.md +55 -0
- package/docs/adr-numeric-tower.md +156 -0
- package/docs/architecture.md +1 -1
- package/docs/c.md +1 -1
- package/docs/language-support.md +10 -9
- package/docs/testing.md +8 -5
- package/package.json +1 -1
- package/src/backends/c.js +23 -4
- package/src/backends/cpp.js +1 -0
- package/src/backends/effects.js +2 -1
- package/src/backends/java.js +33 -14
- package/src/backends/javascript.js +83 -18
- package/src/backends/ordered-expressions.js +46 -10
- package/src/backends/php.js +33 -14
- package/src/backends/ruby.js +36 -12
- package/src/backends/shared.js +89 -42
- package/src/backends/typescript.js +83 -18
- package/src/frontends/c.js +135 -19
- package/src/frontends/java.js +82 -33
- package/src/frontends/javascript-typescript.js +130 -38
- package/src/frontends/php.js +68 -27
- package/src/frontends/ruby.js +107 -28
- package/src/language-registry.js +15 -0
- package/src/semantic/effects.js +17 -5
- package/src/semantic/lifetime.js +29 -3
- package/src/semantic/provenance.js +27 -12
- package/src/semantic/types.js +24 -6
- package/src/semantic/validate.js +105 -36
package/src/frontends/ruby.js
CHANGED
|
@@ -49,7 +49,7 @@ import {missingType, SemantifoldDiagnostic, unsupportedSyntax} from "../diagnost
|
|
|
49
49
|
import {blockReferencesProtectedEntry} from "../semantic/effects.js"
|
|
50
50
|
import {locationFromOffsets, moduleLocation, utf8ByteOffsetToUtf16Offset} from "../semantic/location.js"
|
|
51
51
|
import {withAdaptedOperation} from "../semantic/operators.js"
|
|
52
|
-
import {withParserRanges} from "../semantic/provenance.js"
|
|
52
|
+
import {setParserRanges, withParserRanges} from "../semantic/provenance.js"
|
|
53
53
|
import {hasOnlyUnicodeScalars, scalarType} from "../semantic/scalars.js"
|
|
54
54
|
import {isTask034ResourceProbe} from "../semantic/capabilities.js"
|
|
55
55
|
import {hasExactEffectSupport} from "../backends/effects.js"
|
|
@@ -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>, classes: Map<string, import("../semantic/types.js").ClassDeclaration>, 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>, 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"],
|
|
@@ -969,15 +969,38 @@ function convertStatement(node, comments, context, filename, source) {
|
|
|
969
969
|
* @param {RubyConversionContext} context - Typed lexical context.
|
|
970
970
|
* @param {string} filename - Source filename.
|
|
971
971
|
* @param {string} source - Complete source.
|
|
972
|
-
* @returns {import("../semantic/types.js").RaiseStatement} Semantic raise.
|
|
972
|
+
* @returns {import("../semantic/types.js").RaiseStatement | import("../semantic/types.js").RethrowStatement} Semantic raise or rethrow.
|
|
973
973
|
*/
|
|
974
974
|
function convertRubyRaise(node, context, filename, source) {
|
|
975
975
|
const location = nodeLocation(node, filename, source)
|
|
976
976
|
const arguments_ = node.arguments_?.arguments_ ?? []
|
|
977
|
+
|
|
978
|
+
if (arguments_.length == 0 && context.currentCatchBindingNode) {
|
|
979
|
+
return withParserRanges({
|
|
980
|
+
binding: context.currentCatchBindingNode.name,
|
|
981
|
+
bindingNode: context.currentCatchBindingNode,
|
|
982
|
+
kind: /** @type {const} */ ("RethrowStatement"),
|
|
983
|
+
location
|
|
984
|
+
}, {keyword: prismLocation(node.messageLoc ?? node.location, filename, source)})
|
|
985
|
+
}
|
|
986
|
+
if (arguments_.length == 1 && arguments_[0] instanceof LocalVariableReadNode &&
|
|
987
|
+
context.catchBindingNames?.has(arguments_[0].name) && context.bindings.get(arguments_[0].name)?.kind == "ErrorType") {
|
|
988
|
+
const bindingNode = context.catchBindingNodes?.get(arguments_[0].name)
|
|
989
|
+
|
|
990
|
+
if (!bindingNode) return unsupportedSyntax("ruby", "raise of a catch binding without an exact handler binding", location)
|
|
991
|
+
return withParserRanges({
|
|
992
|
+
binding: arguments_[0].name,
|
|
993
|
+
bindingNode,
|
|
994
|
+
kind: /** @type {const} */ ("RethrowStatement"),
|
|
995
|
+
location
|
|
996
|
+
}, {keyword: prismLocation(node.messageLoc ?? node.location, filename, source)})
|
|
997
|
+
}
|
|
998
|
+
if (node.block || node.callOperatorLoc || arguments_.length != 2) {
|
|
999
|
+
return unsupportedSyntax("ruby", "raise other than exact declared error and message", location)
|
|
1000
|
+
}
|
|
977
1001
|
const errorNode = arguments_[0]
|
|
978
1002
|
|
|
979
|
-
if (
|
|
980
|
-
!(errorNode instanceof ConstantReadNode) && !(errorNode instanceof ConstantPathNode) ||
|
|
1003
|
+
if (!(errorNode instanceof ConstantReadNode) && !(errorNode instanceof ConstantPathNode) ||
|
|
981
1004
|
!context.errorNames.has(constantPathName(errorNode))) {
|
|
982
1005
|
return unsupportedSyntax("ruby", "raise other than exact declared error and message", location)
|
|
983
1006
|
}
|
|
@@ -997,48 +1020,104 @@ function convertRubyRaise(node, context, filename, source) {
|
|
|
997
1020
|
}
|
|
998
1021
|
|
|
999
1022
|
/**
|
|
1000
|
-
* Converts one exact
|
|
1023
|
+
* Converts one exact Prism ensure clause body.
|
|
1024
|
+
* @param {import("@ruby/prism").EnsureNode} node - Prism ensure clause.
|
|
1025
|
+
* @param {import("@ruby/prism/src/deserialize.js").Comment[]} comments - Prism comments.
|
|
1026
|
+
* @param {RubyConversionContext} context - Typed lexical context.
|
|
1027
|
+
* @param {string} filename - Source filename.
|
|
1028
|
+
* @param {string} source - Complete source.
|
|
1029
|
+
* @returns {import("../semantic/types.js").Block} Semantic finally block.
|
|
1030
|
+
*/
|
|
1031
|
+
function convertRubyEnsure(node, comments, context, filename, source) {
|
|
1032
|
+
// A completion block gets its own binding scope, like every other block in convertRubyTry: a local
|
|
1033
|
+
// declaration inside `ensure` must not leak back into the enclosing conversion context.
|
|
1034
|
+
return convertBlock(node.statements ?? null, comments, {...context, bindings: new Map(context.bindings)}, filename, source,
|
|
1035
|
+
nodeLocation(node, filename, source))
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/**
|
|
1039
|
+
* Converts one begin block with ordered exact typed rescues and an optional ensure completion block.
|
|
1001
1040
|
* @param {BeginNode} node - Prism begin node.
|
|
1002
1041
|
* @param {import("@ruby/prism/src/deserialize.js").Comment[]} comments - Parser-owned comments.
|
|
1003
1042
|
* @param {RubyConversionContext} context - Typed lexical context.
|
|
1004
1043
|
* @param {string} filename - Source filename.
|
|
1005
1044
|
* @param {string} source - Complete source.
|
|
1006
|
-
* @returns {import("../semantic/types.js").TryStatement} Semantic exact handler.
|
|
1045
|
+
* @returns {import("../semantic/types.js").TryStatement} Semantic exact handler chain.
|
|
1007
1046
|
*/
|
|
1008
1047
|
function convertRubyTry(node, comments, context, filename, source) {
|
|
1009
1048
|
const location = nodeLocation(node, filename, source)
|
|
1010
|
-
const handler = node.rescueClause
|
|
1011
1049
|
|
|
1012
|
-
if (
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1050
|
+
if (node.elseClause) return unsupportedSyntax("ruby", "begin else clause", nodeLocation(node.elseClause, filename, source))
|
|
1051
|
+
/** @type {import("../semantic/types.js").CatchHandler[]} */
|
|
1052
|
+
const handlers = []
|
|
1053
|
+
const seenHandlerNames = new Set(context.catchBindingNames ?? [])
|
|
1054
|
+
let handler = node.rescueClause
|
|
1055
|
+
|
|
1056
|
+
if (!(handler instanceof RescueNode)) {
|
|
1057
|
+
if (node.ensureClause) {
|
|
1058
|
+
return withParserRanges({
|
|
1059
|
+
body: convertBlock(node.statements, comments, context, filename, source, location),
|
|
1060
|
+
handlers,
|
|
1061
|
+
kind: /** @type {const} */ ("TryStatement"),
|
|
1062
|
+
location,
|
|
1063
|
+
finallyBody: convertRubyEnsure(node.ensureClause, comments, context, filename, source)
|
|
1064
|
+
}, {ensure: nodeLocation(node.ensureClause, filename, source), try: location})
|
|
1065
|
+
}
|
|
1066
|
+
return unsupportedSyntax("ruby", "begin without rescue or ensure", location)
|
|
1017
1067
|
}
|
|
1018
|
-
const errorNode = handler.exceptions[0]
|
|
1019
|
-
const declaration = /** @type {import("../semantic/types.js").ErrorDeclaration} */ (context.errorNames.get(constantPathName(errorNode)))
|
|
1020
|
-
const caughtLocation = nodeLocation(errorNode, filename, source)
|
|
1021
|
-
const catchType = withParserRanges({declarationId: /** @type {string} */ (declaration.id), kind: /** @type {const} */ ("ErrorType")}, {
|
|
1022
|
-
type: caughtLocation
|
|
1023
|
-
})
|
|
1024
1068
|
const bodyContext = {...context, bindings: new Map(context.bindings)}
|
|
1025
|
-
|
|
1069
|
+
while (handler instanceof RescueNode) {
|
|
1070
|
+
if (handler.exceptions.length != 1 ||
|
|
1071
|
+
!(handler.exceptions[0] instanceof ConstantReadNode) && !(handler.exceptions[0] instanceof ConstantPathNode) ||
|
|
1072
|
+
!context.errorNames.has(constantPathName(handler.exceptions[0])) || !(handler.reference instanceof LocalVariableTargetNode)) {
|
|
1073
|
+
return unsupportedSyntax("ruby", "rescue without one exact typed exception and binding", nodeLocation(handler, filename, source))
|
|
1074
|
+
}
|
|
1075
|
+
if (seenHandlerNames.has(handler.reference.name)) {
|
|
1076
|
+
return unsupportedSyntax("ruby", `rescue binding '${handler.reference.name}' shadows an outer handler binding`,
|
|
1077
|
+
nodeLocation(handler.reference, filename, source))
|
|
1078
|
+
}
|
|
1079
|
+
seenHandlerNames.add(handler.reference.name)
|
|
1080
|
+
const errorNode = handler.exceptions[0]
|
|
1081
|
+
const declaration = /** @type {import("../semantic/types.js").ErrorDeclaration} */ (context.errorNames.get(constantPathName(errorNode)))
|
|
1082
|
+
const caughtLocation = nodeLocation(errorNode, filename, source)
|
|
1083
|
+
const catchType = withParserRanges({declarationId: /** @type {string} */ (declaration.id), kind: /** @type {const} */ ("ErrorType")}, {
|
|
1084
|
+
type: caughtLocation
|
|
1085
|
+
})
|
|
1086
|
+
const catchContext = {...context, bindings: new Map(context.bindings), catchBindingNames: new Set(seenHandlerNames),
|
|
1087
|
+
catchBindingNodes: new Map(context.catchBindingNodes)}
|
|
1026
1088
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
body: convertBlock(node.statements, comments, bodyContext, filename, source, location),
|
|
1030
|
-
catchBinding: withParserRanges({
|
|
1089
|
+
catchContext.bindings.set(handler.reference.name, catchType)
|
|
1090
|
+
const catchBindingNode = withParserRanges({
|
|
1031
1091
|
kind: /** @type {const} */ ("CatchBinding"),
|
|
1032
1092
|
location: nodeLocation(handler.reference, filename, source),
|
|
1033
1093
|
mutable: /** @type {const} */ (false),
|
|
1034
1094
|
name: handler.reference.name,
|
|
1035
1095
|
type: catchType
|
|
1036
|
-
}, {name: nodeLocation(handler.reference, filename, source)})
|
|
1037
|
-
|
|
1038
|
-
|
|
1096
|
+
}, {name: nodeLocation(handler.reference, filename, source)})
|
|
1097
|
+
|
|
1098
|
+
catchContext.catchBindingNodes.set(handler.reference.name, catchBindingNode)
|
|
1099
|
+
catchContext.currentCatchBindingNode = catchBindingNode
|
|
1100
|
+
handlers.push(withParserRanges({
|
|
1101
|
+
catchBinding: catchBindingNode,
|
|
1102
|
+
catchBody: convertBlock(handler.statements, comments, catchContext, filename, source, nodeLocation(handler, filename, source)),
|
|
1103
|
+
catchType,
|
|
1104
|
+
kind: /** @type {const} */ ("CatchHandler"),
|
|
1105
|
+
location: nodeLocation(handler, filename, source)
|
|
1106
|
+
}, {catch: nodeLocation(handler, filename, source)}))
|
|
1107
|
+
handler = handler.subsequent
|
|
1108
|
+
}
|
|
1109
|
+
const semantic = /** @type {import("../semantic/types.js").TryStatement} */ (withParserRanges({
|
|
1110
|
+
body: convertBlock(node.statements, comments, bodyContext, filename, source, location),
|
|
1111
|
+
handlers,
|
|
1039
1112
|
kind: /** @type {const} */ ("TryStatement"),
|
|
1040
1113
|
location
|
|
1041
|
-
}, {
|
|
1114
|
+
}, {try: location}))
|
|
1115
|
+
|
|
1116
|
+
if (node.ensureClause) {
|
|
1117
|
+
semantic.finallyBody = convertRubyEnsure(node.ensureClause, comments, context, filename, source)
|
|
1118
|
+
setParserRanges(semantic, {ensure: nodeLocation(node.ensureClause, filename, source), try: location})
|
|
1119
|
+
}
|
|
1120
|
+
return semantic
|
|
1042
1121
|
}
|
|
1043
1122
|
|
|
1044
1123
|
/**
|
package/src/language-registry.js
CHANGED
|
@@ -571,6 +571,21 @@ const records = [
|
|
|
571
571
|
artifactMultiplicity: "multiple",
|
|
572
572
|
check: {factory: createCCheckPlan, stages: ["compile", "link"], toolchains: ["clang"]},
|
|
573
573
|
defaultFilename: "program.c",
|
|
574
|
+
features: {
|
|
575
|
+
closedRecords: false,
|
|
576
|
+
conditionControlledLoops: false,
|
|
577
|
+
conditionalExpressions: false,
|
|
578
|
+
stringInterpolation: false,
|
|
579
|
+
effectfulCapabilitiesAndResources: false,
|
|
580
|
+
generalFunctionsAndCalls: true,
|
|
581
|
+
immutableCollections: false,
|
|
582
|
+
optionalValues: false,
|
|
583
|
+
orderedListIteration: false,
|
|
584
|
+
orderedMapIteration: false,
|
|
585
|
+
referenceClasses: false,
|
|
586
|
+
typedErrors: false,
|
|
587
|
+
typeParametersAndGenerics: false
|
|
588
|
+
},
|
|
574
589
|
frontend: parseC,
|
|
575
590
|
id: "c",
|
|
576
591
|
mediaType: "text/x-c",
|
package/src/semantic/effects.js
CHANGED
|
@@ -379,10 +379,19 @@ function escapingBlockFailures(block, functionSummaries, methodSummaries, capabi
|
|
|
379
379
|
|
|
380
380
|
for (const statement of block.statements) {
|
|
381
381
|
if (statement.kind == "TryStatement") {
|
|
382
|
-
|
|
382
|
+
let body = escapingBlockFailures(statement.body, functionSummaries, methodSummaries, capabilityFailureIds)
|
|
383
383
|
|
|
384
|
-
|
|
385
|
-
|
|
384
|
+
for (const handler of statement.handlers ?? []) {
|
|
385
|
+
const caughtId = handler.catchType?.declarationId
|
|
386
|
+
|
|
387
|
+
if (typeof caughtId == "string" && body.delete(caughtId)) {
|
|
388
|
+
addFailures(body, escapingBlockFailures(handler.catchBody, functionSummaries, methodSummaries, capabilityFailureIds))
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (statement.finallyBody) {
|
|
392
|
+
// A completion block that can raise replaces the pending completion on the paths where it does,
|
|
393
|
+
// but pending failures still escape on the paths where it completes normally, so both are reachable.
|
|
394
|
+
addFailures(body, escapingBlockFailures(statement.finallyBody, functionSummaries, methodSummaries, capabilityFailureIds))
|
|
386
395
|
}
|
|
387
396
|
addFailures(result, body)
|
|
388
397
|
continue
|
|
@@ -639,7 +648,9 @@ function forEachBlockExpression(block, visitor, postorder) {
|
|
|
639
648
|
visit(statement.condition); forEachBlockExpression(statement.body, visitor, postorder)
|
|
640
649
|
} else if (statement.kind == "RaiseStatement") visit(statement.error.message)
|
|
641
650
|
else if (statement.kind == "TryStatement") {
|
|
642
|
-
forEachBlockExpression(statement.body, visitor, postorder)
|
|
651
|
+
forEachBlockExpression(statement.body, visitor, postorder)
|
|
652
|
+
for (const handler of statement.handlers) forEachBlockExpression(handler.catchBody, visitor, postorder)
|
|
653
|
+
if (statement.finallyBody) forEachBlockExpression(statement.finallyBody, visitor, postorder)
|
|
643
654
|
}
|
|
644
655
|
}
|
|
645
656
|
|
|
@@ -694,7 +705,8 @@ function statementReferencesProtectedEntry(statement) {
|
|
|
694
705
|
case "RaiseStatement":
|
|
695
706
|
return expressionReferencesProtectedEntry(statement.error.message)
|
|
696
707
|
case "TryStatement":
|
|
697
|
-
return blockReferencesProtectedEntry(statement.body) || blockReferencesProtectedEntry(
|
|
708
|
+
return blockReferencesProtectedEntry(statement.body) || statement.handlers.some((handler) => blockReferencesProtectedEntry(handler.catchBody)) ||
|
|
709
|
+
(statement.finallyBody ? blockReferencesProtectedEntry(statement.finallyBody) : false)
|
|
698
710
|
default:
|
|
699
711
|
return false
|
|
700
712
|
}
|
package/src/semantic/lifetime.js
CHANGED
|
@@ -123,6 +123,10 @@ function analyzeStatement(statement, state, fail, locals, errorEffects) {
|
|
|
123
123
|
? {errorId: statement.error.error.declarationId, kind: "raise", location: statement.location, state: flow.state}
|
|
124
124
|
: {...flow, location: statement.location})
|
|
125
125
|
}
|
|
126
|
+
if (statement.kind == "RethrowStatement") {
|
|
127
|
+
// A rethrow re-raises the caught binding's exact nominal type, so an enclosing handler must see it.
|
|
128
|
+
return [{errorId: statement.bindingNode?.type?.declarationId, kind: "raise", location: statement.location, state: cloneState(state)}]
|
|
129
|
+
}
|
|
126
130
|
if (statement.kind == "IfStatement") {
|
|
127
131
|
const conditions = evaluateExpression(statement.condition, cloneState(state), fail, errorEffects)
|
|
128
132
|
/** @type {LifetimeExit[]} */
|
|
@@ -142,10 +146,32 @@ function analyzeStatement(statement, state, fail, locals, errorEffects) {
|
|
|
142
146
|
if (statement.kind == "TryStatement") {
|
|
143
147
|
const bodyExits = analyzeBlock(statement.body, [{kind: "normal", state: cloneState(state)}], fail, errorEffects)
|
|
144
148
|
/** @type {LifetimeExit[]} */
|
|
145
|
-
|
|
149
|
+
let result = bodyExits
|
|
150
|
+
|
|
151
|
+
for (const handler of statement.handlers ?? []) {
|
|
152
|
+
const caughtId = handler.catchType?.declarationId
|
|
153
|
+
const caught = result.filter((flow) => flow.kind == "raise" && flow.errorId == caughtId)
|
|
154
|
+
|
|
155
|
+
if (caught.length == 0) continue
|
|
156
|
+
result = result.filter((flow) => flow.kind != "raise" || flow.errorId != caughtId)
|
|
157
|
+
for (const flow of caught) {
|
|
158
|
+
result.push(...analyzeBlock(handler.catchBody, [{kind: "normal", state: cloneState(flow.state)}], fail, errorEffects))
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (statement.finallyBody) {
|
|
162
|
+
/** @type {LifetimeExit[]} */
|
|
163
|
+
const afterFinally = []
|
|
164
|
+
|
|
165
|
+
for (const flow of result) {
|
|
166
|
+
const finallyExits = analyzeBlock(statement.finallyBody, [{kind: "normal", state: cloneState(flow.state)}], fail, errorEffects)
|
|
146
167
|
|
|
147
|
-
|
|
148
|
-
|
|
168
|
+
for (const finallyExit of finallyExits) {
|
|
169
|
+
afterFinally.push(finallyExit.kind == "normal"
|
|
170
|
+
? {...flow, location: statement.location}
|
|
171
|
+
: {...finallyExit, location: statement.location})
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
result = afterFinally
|
|
149
175
|
}
|
|
150
176
|
return mergeEquivalentNormals(result, statement.location, fail)
|
|
151
177
|
}
|
|
@@ -580,8 +580,14 @@ export function semanticEntries(module) {
|
|
|
580
580
|
visit(node.body, `${path}/body`, location)
|
|
581
581
|
} else if (node.kind == "RaiseStatement") {
|
|
582
582
|
visit(node.error, `${path}/error`, location)
|
|
583
|
+
} else if (node.kind == "RethrowStatement") {
|
|
584
|
+
visit(node.bindingNode, `${path}/bindingNode`, location)
|
|
585
|
+
visit(node.bindingNode, `${path}/bindingNode/name`, location)
|
|
583
586
|
} else if (node.kind == "TryStatement") {
|
|
584
587
|
visit(node.body, `${path}/body`, location)
|
|
588
|
+
node.handlers.forEach((child, index) => visit(child, `${path}/handlers/${index}`, location))
|
|
589
|
+
if (node.finallyBody) visit(node.finallyBody, `${path}/finallyBody`, location)
|
|
590
|
+
} else if (node.kind == "CatchHandler") {
|
|
585
591
|
visit(node.catchType, `${path}/catchType`, location)
|
|
586
592
|
visit(node.catchBinding, `${path}/catchBinding`, location)
|
|
587
593
|
visit(node.catchBody, `${path}/catchBody`, location)
|
|
@@ -895,19 +901,28 @@ function resolveSymbols(module, records) {
|
|
|
895
901
|
visitType(statement.error.error, `${statementPath}/error/error`)
|
|
896
902
|
reference(statement.error, errorSymbols.get(statement.error.error.declarationId), "construct", `${statementPath}/error`)
|
|
897
903
|
visitExpression(statement.error.message, scope, `${statementPath}/error/message`)
|
|
904
|
+
} else if (statement.kind == "RethrowStatement") {
|
|
905
|
+
const symbol = scope.get(statement.binding)
|
|
906
|
+
|
|
907
|
+
if (symbol) reference(statement, symbol, "read", statementPath)
|
|
898
908
|
} else if (statement.kind == "TryStatement") {
|
|
899
909
|
visitBlock(statement.body, scope, `${statementPath}/body`)
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
910
|
+
for (const [index, handler] of statement.handlers.entries()) {
|
|
911
|
+
const handlerPath = `${statementPath}/handlers/${index}`
|
|
912
|
+
|
|
913
|
+
visitType(handler.catchType, `${handlerPath}/catchType`)
|
|
914
|
+
visitType(handler.catchBinding.type, `${handlerPath}/catchBinding/type`)
|
|
915
|
+
const catchScope = new Map(scope)
|
|
916
|
+
|
|
917
|
+
catchScope.set(handler.catchBinding.name, declare(
|
|
918
|
+
handler.catchBinding,
|
|
919
|
+
handler.catchBinding.name,
|
|
920
|
+
"catch",
|
|
921
|
+
`${handlerPath}/catchBinding`
|
|
922
|
+
))
|
|
923
|
+
visitBlock(handler.catchBody, catchScope, `${handlerPath}/catchBody`)
|
|
924
|
+
}
|
|
925
|
+
if (statement.finallyBody) visitBlock(statement.finallyBody, scope, `${statementPath}/finallyBody`)
|
|
911
926
|
}
|
|
912
927
|
}
|
|
913
928
|
}
|
|
@@ -1005,7 +1020,7 @@ function resolveSymbols(module, records) {
|
|
|
1005
1020
|
|
|
1006
1021
|
/**
|
|
1007
1022
|
* Attaches one resolved symbol reference.
|
|
1008
|
-
* @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").ErrorType | import("./types.js").ErrorConstruction} node - Reference node.
|
|
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.
|
|
1009
1024
|
* @param {string | undefined} symbolId - Resolved symbol.
|
|
1010
1025
|
* @param {"type" | "construct" | "member" | "read" | "write" | "call"} role - Reference role.
|
|
1011
1026
|
* @param {string} path - Reference occurrence path.
|
package/src/semantic/types.js
CHANGED
|
@@ -1229,6 +1229,15 @@
|
|
|
1229
1229
|
* @property {SemanticNodeSourceProvenance} [sourceProvenance] - Parser-owned raise range.
|
|
1230
1230
|
*/
|
|
1231
1231
|
|
|
1232
|
+
/**
|
|
1233
|
+
* @typedef RethrowStatement
|
|
1234
|
+
* @property {"RethrowStatement"} kind - Re-propagation of the exact caught binding in scope.
|
|
1235
|
+
* @property {string} binding - Name of the lexically visible immutable catch binding being re-raised.
|
|
1236
|
+
* @property {CatchBinding} bindingNode - Exact catch binding node being re-raised.
|
|
1237
|
+
* @property {SourceLocation} location - Complete rethrow source location.
|
|
1238
|
+
* @property {SemanticNodeSourceProvenance} [sourceProvenance] - Parser-owned rethrow range.
|
|
1239
|
+
*/
|
|
1240
|
+
|
|
1232
1241
|
/**
|
|
1233
1242
|
* @typedef CatchBinding
|
|
1234
1243
|
* @property {"CatchBinding"} kind - Exact-handler binding discriminator.
|
|
@@ -1240,18 +1249,27 @@
|
|
|
1240
1249
|
*/
|
|
1241
1250
|
|
|
1242
1251
|
/**
|
|
1243
|
-
* @typedef
|
|
1244
|
-
* @property {"
|
|
1245
|
-
* @property {Block} body - Protected body.
|
|
1252
|
+
* @typedef CatchHandler
|
|
1253
|
+
* @property {"CatchHandler"} kind - One exact nominal handler within an ordered handler list.
|
|
1246
1254
|
* @property {ErrorType} catchType - Exact caught nominal error type.
|
|
1247
1255
|
* @property {CatchBinding} catchBinding - Immutable handler-local binding.
|
|
1248
1256
|
* @property {Block} catchBody - Handler body.
|
|
1257
|
+
* @property {SourceLocation} location - Complete handler source location.
|
|
1258
|
+
* @property {SemanticNodeSourceProvenance} [sourceProvenance] - Parser-owned catch ranges.
|
|
1259
|
+
*/
|
|
1260
|
+
|
|
1261
|
+
/**
|
|
1262
|
+
* @typedef TryStatement
|
|
1263
|
+
* @property {"TryStatement"} kind - One body, an ordered list of exact typed handlers, and an optional completion block.
|
|
1264
|
+
* @property {Block} body - Protected body.
|
|
1265
|
+
* @property {CatchHandler[]} handlers - Ordered exact handlers, evaluated top to bottom with exact nominal matching.
|
|
1266
|
+
* @property {Block} [finallyBody] - Completion block executed on every normal and abrupt completion of the try.
|
|
1249
1267
|
* @property {SourceLocation} location - Complete try/handler source location.
|
|
1250
|
-
* @property {SemanticNodeSourceProvenance} [sourceProvenance] - Parser-owned try/catch ranges.
|
|
1268
|
+
* @property {SemanticNodeSourceProvenance} [sourceProvenance] - Parser-owned try/catch/finally ranges.
|
|
1251
1269
|
*/
|
|
1252
1270
|
|
|
1253
1271
|
/** @typedef {LocalDeclaration | AssignmentStatement} LocalStatement */
|
|
1254
|
-
/** @typedef {LocalStatement | PrivateFieldWriteStatement | ExpressionStatement | IfStatement | ForEachStatement | ForEachMapStatement | WhileStatement | BreakStatement | ContinueStatement | ReturnStatement | PrintStatement | RaiseStatement | TryStatement} Statement */
|
|
1272
|
+
/** @typedef {LocalStatement | PrivateFieldWriteStatement | ExpressionStatement | IfStatement | ForEachStatement | ForEachMapStatement | WhileStatement | BreakStatement | ContinueStatement | ReturnStatement | PrintStatement | RaiseStatement | RethrowStatement | TryStatement} Statement */
|
|
1255
1273
|
|
|
1256
1274
|
/**
|
|
1257
1275
|
* @typedef Block
|
|
@@ -1468,7 +1486,7 @@
|
|
|
1468
1486
|
* @property {StdlibFacadeProgramDescriptor} [stdlibFacades] - Complete selected source-language facade closure.
|
|
1469
1487
|
*/
|
|
1470
1488
|
|
|
1471
|
-
/** @typedef {SemanticModule | SemanticProgramModule | SemanticImport | SemanticExport | RecordDeclaration | ClassDeclaration | ErrorDeclaration | EffectCapabilityDeclaration | EffectResourceDeclaration | EffectFailureDeclaration | EffectOperationDeclaration | RecordField | PrivateField | ConstructorDeclaration | MethodDeclaration | FunctionDeclaration | TypeParameter | Parameter | ValueBinding | CatchBinding | Block | Statement | EntryPoint | Expression | ErrorConstruction | ErrorType | MapEntry | SemanticValueType | FunctionReturnTypeReference} SemanticNode */
|
|
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 */
|
|
1472
1490
|
/** @typedef {SemanticNode} SemanticNodeWithoutLocations */
|
|
1473
1491
|
|
|
1474
1492
|
export {}
|
package/src/semantic/validate.js
CHANGED
|
@@ -8,7 +8,7 @@ import {parserRangeFor} from "./provenance.js"
|
|
|
8
8
|
import {validateEffectGraph} from "./effects.js"
|
|
9
9
|
import {validateResourceLifetimes} from "./lifetime.js"
|
|
10
10
|
|
|
11
|
-
const task005Languages = new Set(["php", "ruby", "javascript", "typescript", "java", "dart", "zig"])
|
|
11
|
+
const task005Languages = new Set(["php", "ruby", "javascript", "typescript", "java", "c", "dart", "zig"])
|
|
12
12
|
|
|
13
13
|
/** @typedef {Map<string, import("./types.js").RecordDeclaration>} RecordRegistry */
|
|
14
14
|
/** @typedef {Map<string, import("./types.js").ClassDeclaration>} ClassRegistry */
|
|
@@ -160,7 +160,7 @@ function validateBlockShape(block, detail, fail) {
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
for (const statement of block.statements) {
|
|
163
|
-
if (!["AssignmentStatement", "BreakStatement", "ContinueStatement", "ExpressionStatement", "ForEachMapStatement", "ForEachStatement", "IfStatement", "LocalDeclaration", "PrivateFieldWriteStatement", "PrintStatement", "RaiseStatement", "ReturnStatement", "TryStatement", "WhileStatement"].includes(statement.kind)) {
|
|
163
|
+
if (!["AssignmentStatement", "BreakStatement", "ContinueStatement", "ExpressionStatement", "ForEachMapStatement", "ForEachStatement", "IfStatement", "LocalDeclaration", "PrivateFieldWriteStatement", "PrintStatement", "RaiseStatement", "RethrowStatement", "ReturnStatement", "TryStatement", "WhileStatement"].includes(statement.kind)) {
|
|
164
164
|
fail(`${detail} statement ${statement.kind}`, statement.location)
|
|
165
165
|
}
|
|
166
166
|
if (statement.kind == "IfStatement") {
|
|
@@ -172,7 +172,18 @@ function validateBlockShape(block, detail, fail) {
|
|
|
172
172
|
}
|
|
173
173
|
if (statement.kind == "TryStatement") {
|
|
174
174
|
validateBlockShape(statement.body, "try body", fail)
|
|
175
|
-
|
|
175
|
+
if (!Array.isArray(statement.handlers) || (statement.handlers.length == 0 && !statement.finallyBody)) {
|
|
176
|
+
fail(`${detail} try without an ordered handler list`, statement.location)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for (const handler of statement.handlers ?? []) {
|
|
180
|
+
if (!handler || handler.kind != "CatchHandler" ||
|
|
181
|
+
Object.keys(handler).filter((key) => key != "sourceProvenance").sort().join(",") != "catchBinding,catchBody,catchType,kind,location") {
|
|
182
|
+
fail(`${detail} malformed catch handler`, handler?.location ?? statement.location)
|
|
183
|
+
}
|
|
184
|
+
validateBlockShape(handler.catchBody, "catch body", fail)
|
|
185
|
+
}
|
|
186
|
+
if (statement.finallyBody) validateBlockShape(statement.finallyBody, "finally body", fail)
|
|
176
187
|
}
|
|
177
188
|
}
|
|
178
189
|
}
|
|
@@ -310,7 +321,8 @@ function prepareLoopIdentities(module, normalizeOperations, fail) {
|
|
|
310
321
|
if (statement.alternate) visit(statement.alternate, owner, active)
|
|
311
322
|
} else if (statement.kind == "TryStatement") {
|
|
312
323
|
visit(statement.body, owner, active)
|
|
313
|
-
|
|
324
|
+
for (const handler of statement.handlers ?? []) visit(handler.catchBody, owner, active)
|
|
325
|
+
if (statement.finallyBody) visit(statement.finallyBody, owner, active)
|
|
314
326
|
}
|
|
315
327
|
}
|
|
316
328
|
}
|
|
@@ -1086,44 +1098,87 @@ function validateBlock(block, scope, returnType, functions, records, errors, cal
|
|
|
1086
1098
|
normal = false
|
|
1087
1099
|
continue
|
|
1088
1100
|
}
|
|
1089
|
-
if (statement.kind == "
|
|
1090
|
-
const
|
|
1101
|
+
if (statement.kind == "RethrowStatement") {
|
|
1102
|
+
const binding = findBinding(statement.binding, scope)
|
|
1091
1103
|
|
|
1092
|
-
if (!
|
|
1093
|
-
fail("
|
|
1104
|
+
if (!binding || binding.mutable !== false || binding.type.kind != "ErrorType") {
|
|
1105
|
+
fail("INVALID_RETHROW", "Rethrow requires a catch binding in scope.", statement.location)
|
|
1094
1106
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1107
|
+
raises.add(/** @type {import("./types.js").ErrorType} */ (binding.type).declarationId)
|
|
1108
|
+
normal = false
|
|
1109
|
+
continue
|
|
1110
|
+
}
|
|
1111
|
+
if (statement.kind == "TryStatement") {
|
|
1112
|
+
if (!Array.isArray(statement.handlers) || (statement.handlers.length == 0 && !statement.finallyBody)) {
|
|
1113
|
+
fail("INVALID_ERROR_HANDLER", "Try requires at least one exact typed handler or a finally block.", statement.location)
|
|
1099
1114
|
}
|
|
1100
1115
|
const bodyAssignedOuterBindings = outerMutableBindingsAssignedBy(statement.body, scope)
|
|
1101
1116
|
const assignedOuterBindings = new Set(bodyAssignedOuterBindings)
|
|
1102
1117
|
|
|
1103
|
-
|
|
1118
|
+
for (const handler of statement.handlers) outerMutableBindingsAssignedBy(handler.catchBody, scope, assignedOuterBindings)
|
|
1119
|
+
if (statement.finallyBody) outerMutableBindingsAssignedBy(statement.finallyBody, scope, assignedOuterBindings)
|
|
1104
1120
|
const bodyScope = createScope(scope, statement.body.statements)
|
|
1105
1121
|
const bodyFlow = validateBlock(statement.body, bodyScope, returnType, functions, records, errors, callEffects, fail, normalizeOperations, loopContext)
|
|
1106
|
-
const catchScope = createScope(scope, statement.catchBody.statements)
|
|
1107
1122
|
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1123
|
+
let remainingRaises = new Set(bodyFlow.raises)
|
|
1124
|
+
let normalCompletion = bodyFlow.normal
|
|
1125
|
+
const handlerRaises = new Set()
|
|
1126
|
+
const seenHandlerNames = new Set()
|
|
1112
1127
|
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
mutable: false,
|
|
1116
|
-
type: bindingType
|
|
1117
|
-
}, roleLocation(statement.catchBinding, "name"), catchScope, fail)
|
|
1118
|
-
const catchFlow = validateBlock(statement.catchBody, catchScope, returnType, functions, records, errors, callEffects, fail, normalizeOperations, loopContext)
|
|
1128
|
+
for (const handler of statement.handlers) {
|
|
1129
|
+
const caughtType = validateErrorType(handler.catchType, handler.location, errors, fail)
|
|
1119
1130
|
|
|
1120
|
-
|
|
1121
|
-
|
|
1131
|
+
if (!handler.catchBinding || handler.catchBinding.kind != "CatchBinding" || handler.catchBinding.mutable !== false) {
|
|
1132
|
+
fail("INVALID_ERROR_HANDLER", "Catch requires one immutable typed binding.", handler.catchBinding?.location ?? handler.location)
|
|
1133
|
+
}
|
|
1134
|
+
const bindingType = validateErrorType(handler.catchBinding.type, handler.catchBinding.location, errors, fail)
|
|
1135
|
+
|
|
1136
|
+
if (bindingType.declarationId != caughtType.declarationId) {
|
|
1137
|
+
fail("INVALID_ERROR_HANDLER", "Catch binding type must exactly match its handler type.", handler.catchBinding.location)
|
|
1138
|
+
}
|
|
1139
|
+
if (!remainingRaises.has(caughtType.declarationId)) {
|
|
1140
|
+
fail("UNREACHABLE_HANDLER", `Handler for '${errors.get(caughtType.declarationId)?.name}' cannot be reached.`,
|
|
1141
|
+
typeLocation(handler.catchType, handler.location))
|
|
1142
|
+
}
|
|
1143
|
+
if (seenHandlerNames.has(handler.catchBinding.name)) {
|
|
1144
|
+
fail("DUPLICATE_BINDING", `Duplicate or shadowed binding '${handler.catchBinding.name}'.`, handler.catchBinding.location)
|
|
1145
|
+
}
|
|
1146
|
+
seenHandlerNames.add(handler.catchBinding.name)
|
|
1147
|
+
remainingRaises.delete(caughtType.declarationId)
|
|
1148
|
+
const catchScope = createScope(scope, handler.catchBody.statements)
|
|
1149
|
+
|
|
1150
|
+
for (const binding of bodyAssignedOuterBindings) {
|
|
1151
|
+
binding.knownValue = undefined
|
|
1152
|
+
catchScope.presenceProofs.delete(binding)
|
|
1153
|
+
}
|
|
1154
|
+
declareBinding(handler.catchBinding.name, {
|
|
1155
|
+
knownValue: undefined,
|
|
1156
|
+
mutable: false,
|
|
1157
|
+
type: bindingType
|
|
1158
|
+
}, roleLocation(handler.catchBinding, "name"), catchScope, fail)
|
|
1159
|
+
const catchFlow = validateBlock(handler.catchBody, catchScope, returnType, functions, records, errors, callEffects, fail,
|
|
1160
|
+
normalizeOperations, loopContext)
|
|
1161
|
+
|
|
1162
|
+
normalCompletion = normalCompletion || catchFlow.normal
|
|
1163
|
+
addAll(handlerRaises, catchFlow.raises)
|
|
1164
|
+
}
|
|
1165
|
+
const pendingRaises = new Set(remainingRaises)
|
|
1166
|
+
|
|
1167
|
+
addAll(pendingRaises, handlerRaises)
|
|
1168
|
+
if (statement.finallyBody) {
|
|
1169
|
+
const finallyScope = createScope(scope, statement.finallyBody.statements)
|
|
1170
|
+
const finallyFlow = validateBlock(statement.finallyBody, finallyScope, returnType, functions, records, errors, callEffects,
|
|
1171
|
+
fail, normalizeOperations, loopContext)
|
|
1172
|
+
// `raises` is the enclosing block's accumulator. A completion block replaces only this try's
|
|
1173
|
+
// pending completion; escapes contributed by earlier statements of the enclosing block survive
|
|
1174
|
+
// whichever way the completion block completes.
|
|
1175
|
+
if (finallyFlow.normal) addAll(raises, pendingRaises)
|
|
1176
|
+
addAll(raises, finallyFlow.raises)
|
|
1177
|
+
normal = finallyFlow.normal ? normalCompletion : false
|
|
1178
|
+
} else {
|
|
1179
|
+
addAll(raises, pendingRaises)
|
|
1180
|
+
normal = normalCompletion
|
|
1122
1181
|
}
|
|
1123
|
-
bodyFlow.raises.delete(caughtType.declarationId)
|
|
1124
|
-
addAll(raises, bodyFlow.raises)
|
|
1125
|
-
addAll(raises, catchFlow.raises)
|
|
1126
|
-
normal = bodyFlow.normal || catchFlow.normal
|
|
1127
1182
|
for (const binding of assignedOuterBindings) {
|
|
1128
1183
|
binding.knownValue = undefined
|
|
1129
1184
|
scope.presenceProofs.delete(binding)
|
|
@@ -1271,14 +1326,27 @@ function syntacticBlockEffects(block, callEffects, methodEffects = new Map()) {
|
|
|
1271
1326
|
}
|
|
1272
1327
|
continue
|
|
1273
1328
|
}
|
|
1329
|
+
if (kind == "RethrowStatement") {
|
|
1330
|
+
const bindingNode = Reflect.get(statement, "bindingNode")
|
|
1331
|
+
const bindingType = bindingNode && typeof bindingNode == "object" ? Reflect.get(bindingNode, "type") : undefined
|
|
1332
|
+
const declarationId = bindingType && typeof bindingType == "object" ? Reflect.get(bindingType, "declarationId") : undefined
|
|
1333
|
+
|
|
1334
|
+
if (typeof declarationId == "string") result.add(declarationId)
|
|
1335
|
+
continue
|
|
1336
|
+
}
|
|
1274
1337
|
if (kind == "TryStatement") {
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
const
|
|
1338
|
+
let bodyEffects = syntacticBlockEffects(Reflect.get(statement, "body"), callEffects, methodEffects)
|
|
1339
|
+
|
|
1340
|
+
for (const handler of Reflect.get(statement, "handlers") ?? []) {
|
|
1341
|
+
if (!handler || typeof handler != "object") continue
|
|
1342
|
+
const catchType = Reflect.get(handler, "catchType")
|
|
1343
|
+
const caughtId = catchType && typeof catchType == "object" ? Reflect.get(catchType, "declarationId") : undefined
|
|
1278
1344
|
|
|
1279
|
-
|
|
1280
|
-
|
|
1345
|
+
if (typeof caughtId == "string" && bodyEffects.delete(caughtId)) {
|
|
1346
|
+
addAll(bodyEffects, syntacticBlockEffects(Reflect.get(handler, "catchBody"), callEffects, methodEffects))
|
|
1347
|
+
}
|
|
1281
1348
|
}
|
|
1349
|
+
addAll(bodyEffects, syntacticBlockEffects(Reflect.get(statement, "finallyBody"), callEffects, methodEffects))
|
|
1282
1350
|
addAll(result, bodyEffects)
|
|
1283
1351
|
continue
|
|
1284
1352
|
}
|
|
@@ -3046,7 +3114,8 @@ function outerMutableBindingsAssignedBy(block, outerScope, assigned = new Set())
|
|
|
3046
3114
|
outerMutableBindingsAssignedBy(statement.body, outerScope, assigned)
|
|
3047
3115
|
} else if (statement.kind == "TryStatement") {
|
|
3048
3116
|
outerMutableBindingsAssignedBy(statement.body, outerScope, assigned)
|
|
3049
|
-
|
|
3117
|
+
for (const handler of statement.handlers ?? []) outerMutableBindingsAssignedBy(handler.catchBody, outerScope, assigned)
|
|
3118
|
+
if (statement.finallyBody) outerMutableBindingsAssignedBy(statement.finallyBody, outerScope, assigned)
|
|
3050
3119
|
}
|
|
3051
3120
|
}
|
|
3052
3121
|
|