slow.monster

STUDIO HAZE · topaz

Topaz source

Open by house policy. Topaz is our own language.

Topaz 5.20 · 56 files

compiler/crates/topaz_self_frontend/topaz/checker/bodies_captures.tpz

Topaz · 31517 B · Original file

1// Function-body checking, initialization order, and closure capture derivation.
2// Expression recursion enters through CheckerService; this leaf owns body
3// sequencing and returns indexes used by the checker facade.
5import types {
6 PreviewResolvedReference,
7 PreviewTypedCall,
8 PreviewTypedCapture,
9 ResolveResult,
10 SyntaxNode,
12import checker.model {
13 CaptureTarget,
14 CheckerService,
15 CheckerState,
16 FunctionDefinition,
17 ModuleSyntax,
18 TYPE_BOUND_INDEX_STRIDE,
19 constructor,
20 emptyType,
21 functionType,
22 primitive,
24import checker.type_algebra {
25 flattenType,
26 hasHole,
27 joinedTypes,
28 projectionType,
29 stripProjectionTypes,
31import checker.syntax_index {
32 addDiagnostic,
33 addNode,
34 attribute,
35 bindSymbol,
36 boolAttribute,
37 child,
38 children,
39 containsString,
40 declarationFor,
41 definitionNameKey,
42 emptyStringIntMap,
43 moduleIdentityAt,
44 referenceFor,
45 referenceTargetsPendingFunction,
46 replaceNodeType,
47 scopeFor,
48 scopeKey,
49 spanKey,
50 text,
51 typedNodeIndex,
52 unwrapExport,
54import checker.protocols_methods { functionDeclaration }
55import checker.members_capabilities { bindingIndex }
56import checker.patterns { patternBindingNodes }
57import checker.call_facts { checkExpressionExpected }
58import checker.expression_core { blockTerminates }
59import checker.expression_control { inferExpression }
60import checker.statements {
61 checkBlock,
62 checkBlockExpected,
63 checkStatement,
64 constantExpressionShape,
65 foldConstantInteger,
66 precheckTopLevelBindings,
69function checkRecordDefaults(service: CheckerService, state: CheckerState, module: ModuleSyntax) -> () {
70 for statement in module.root.children {
71 let item = unwrapExport(statement)
72 if item.kind == "statement/record" {
73 for field in children(item, "fields") {
74 match child(field, "default") {
75 case Some(found) => {
76 let _ = inferExpression(
77 service,
78 state,
79 module.moduleIndex,
80 module.source,
81 found,
82 )
83 }
84 case None => ()
85 }
86 }
87 }
88 }
89 ()
92function checkFunctionDefault(
93 state: CheckerState,
94 moduleIndex: int,
95 node: SyntaxNode,
96) -> () {
97 if !constantExpressionShape(node) {
98 addDiagnostic(
99 state,
100 moduleIndex,
101 "TPZ5001",
102 "`const` initializers must be constant expressions (§4)",
103 "",
104 node,
105 )
106 return ()
107 }
108 let _ = foldConstantInteger(state, moduleIndex, node)
109 ()
112function callsPendingReturnSince(
113 state: CheckerState,
114 firstCall: int,
115) -> bool {
116 let mut index = firstCall
117 while index < state.calls.length {
118 match state.calls.get(index) {
119 case Some(call) => match referenceFor(
120 state,
121 call.moduleIndex,
122 call.calleeLo,
123 call.calleeHi,
124 ) {
125 case Some(reference) => if referenceTargetsPendingFunction(
126 state,
127 reference,
128 ) {
129 return true
130 }
131 case None => ()
132 }
133 case None => ()
134 }
135 index += 1
136 }
137 false
140export function checkFunctionDeclaration(
141 service: CheckerService,
142 state: CheckerState,
143 module: ModuleSyntax,
144 declaration: SyntaxNode,
145) -> () {
146 let nameNode = match child(declaration, "name") {
147 case Some(found) => found
148 case None => declaration
149 }
150 let mut exported = false
151 for published in state.resolved.exports {
152 if published.moduleIndex == module.moduleIndex && published.namespace == "value" &&
153 published.declarationLo == nameNode.lo && published.declarationHi == nameNode.hi {
154 exported = true
155 }
156 }
157 let mut seenBounds: Array<string> = []
158 for bound in children(declaration, "typeParameterBounds") {
159 let protocol = text(module.source, bound)
160 if exported && protocol != "Eq" && protocol != "Order" && protocol != "Show" && protocol != "JSON" {
161 addDiagnostic(state, module.moduleIndex, "TPZ5524",
162 "exported functions cannot expose module-local protocol bound `{protocol}`", "", bound)
163 }
164 let key = "{bound.index / TYPE_BOUND_INDEX_STRIDE}:{protocol}"
165 if containsString(seenBounds, key) {
166 addDiagnostic(state, module.moduleIndex, "TPZ5523",
167 "protocol bound `{protocol}` is repeated for the same type parameter", "", bound)
168 } else {
169 seenBounds.push(key)
170 }
171 }
172 let definitionIndex = match state.functionIndexes.get(spanKey(
173 module.moduleIndex,
174 nameNode.lo,
175 nameNode.hi,
176 )) {
177 case Some(index) => index
178 case None => -1
179 }
180 match state.functions.get(definitionIndex) {
181 case Some(definition) => {
182 let mut fixedIndex = 0
183 for parameter in children(declaration, "parameters") {
184 let name = match child(parameter, "name") {
185 case Some(found) => found
186 case None => parameter
187 }
188 let parameterType = if boolAttribute(parameter, "variadic") {
189 constructor("Array", [match definition.variadic.get(0) {
190 case Some(found) => found
191 case None => emptyType("unknown")
192 }])
193 } else {
194 match definition.parameterTypes.get(fixedIndex) {
195 case Some(found) => found
196 case None => emptyType("unknown")
197 }
198 }
199 if !boolAttribute(parameter, "variadic") {
200 fixedIndex += 1
201 }
202 bindSymbol(
203 state,
204 module.moduleIndex,
205 name.lo,
206 name.hi,
207 text(module.source, name),
208 parameterType,
209 false,
210 )
211 addNode(state, module.moduleIndex, "binding", name, parameterType)
212 match child(parameter, "default") {
213 case Some(found) => {
214 let _ = checkExpressionExpected(
215 service,
216 state,
217 module.moduleIndex,
218 module.source,
219 found,
220 parameterType,
221 )
222 checkFunctionDefault(state, module.moduleIndex, found)
223 }
224 case None => ()
225 }
226 }
227 match child(declaration, "body") {
228 case Some(body) => {
229 let inferredResult = definition.result.kind == "unknown"
230 let firstCall = state.calls.length
231 let mut activeFunctions = state.activeFunctions
232 activeFunctions.push(definitionIndex)
233 let mut loopFloors = state.loopFloors
234 loopFloors.push(state.loopFrames.length)
235 let mut expectedReturns = state.expectedReturns
236 expectedReturns.push(definition.result)
237 let mut returnJoins = state.returnJoins
238 returnJoins.push([])
239 let bodyResult = if inferredResult {
240 checkBlock(
241 service,
242 state,
243 module.moduleIndex,
244 module.source,
245 body,
246 )
247 } else {
248 checkBlockExpected(
249 service,
250 state,
251 module.moduleIndex,
252 module.source,
253 body,
254 definition.result,
255 )
256 }
257 let mut returns = match returnJoins.get(returnJoins.length - 1) {
258 case Some(found) => found
259 case None => []
260 }
261 returnJoins.pop()
262 expectedReturns.pop()
263 loopFloors.pop()
264 activeFunctions.pop()
265 if inferredResult {
266 match child(body, "tail") {
267 case Some(_) => returns.push(bodyResult)
268 case None => if !blockTerminates(body) {
269 returns.push(primitive("unit"))
270 }
271 }
272 let mut result = if returns.length == 0 {
273 primitive("unit")
274 } else {
275 let mut unresolved = false
276 for found in returns {
277 if hasHole(found) {
278 unresolved = true
279 }
280 }
281 if unresolved {
282 emptyType("unknown")
283 } else {
284 joinedTypes(state, returns, true)
285 }
286 }
287 if projectionType(result) {
288 addDiagnostic(
289 state,
290 module.moduleIndex,
291 "TPZ5022",
292 "cannot infer a return type for `{definition.name}`: it returns a destructured generic value with no nameable type — annotate the return type",
293 "",
294 nameNode,
295 )
296 result = stripProjectionTypes(result)
297 }
298 if result.kind == "unknown" &&
299 callsPendingReturnSince(state, firstCall) {
300 addDiagnostic(
301 state,
302 module.moduleIndex,
303 "TPZ5022",
304 "`{definition.name}` is (mutually) recursive: declare its return type (§7)",
305 "",
306 nameNode,
307 )
308 }
309 let mut pending = state.pendingFunctions
310 pending[definitionIndex] = false
311 let patched = FunctionDefinition {
312 ...definition,
313 result: result,
314 }
315 let mut functions = state.functions
316 functions[definitionIndex] = patched
317 let functionValue = functionType(
318 definition.parameterTypes,
319 definition.variadic,
320 result,
321 )
322 bindSymbol(
323 state,
324 module.moduleIndex,
325 nameNode.lo,
326 nameNode.hi,
327 definition.name,
328 functionValue,
329 false,
330 )
331 replaceNodeType(
332 state,
333 module.moduleIndex,
334 "declaration",
335 nameNode,
336 functionValue,
337 )
338 }
339 }
340 case None => ()
341 }
342 // Publish the declaration fact after checking its body. Signatures used
343 // during recursion are already available in symbols and functions.
344 addNode(
345 state,
346 module.moduleIndex,
347 "declaration",
348 nameNode,
349 functionType(
350 definition.parameterTypes,
351 definition.variadic,
352 definition.result,
353 ),
354 )
355 }
356 case None => ()
357 }
358 ()
361function staticTopLevelItem(item: SyntaxNode) -> bool {
362 item.kind == "statement/function" ||
363 item.kind == "statement/impl" ||
364 item.kind == "statement/record" ||
365 item.kind == "statement/enum" ||
366 item.kind == "statement/newtype" ||
367 item.kind == "statement/type-alias" ||
368 item.kind == "statement/protocol" ||
369 item.kind == "statement/const" ||
370 item.kind == "statement/import"
373function checkImplBodies(
374 service: CheckerService,
375 state: CheckerState,
376 module: ModuleSyntax,
377 statement: SyntaxNode,
378) -> () {
379 let item = unwrapExport(statement)
380 if item.kind != "statement/impl" {
381 return ()
382 }
383 for method in children(item, "methods") {
384 match child(method, "declaration") {
385 case Some(declaration) => checkFunctionDeclaration(
386 service,
387 state,
388 module,
389 declaration,
390 )
391 case None => ()
392 }
393 }
394 ()
397function topLevelRuntimeBindingIndexes(
398 module: ModuleSyntax,
399) -> Map<int, int> {
400 let mut indexes: Map<int, int> = Map.new()
401 let mut index = 0
402 for statement in module.root.children {
403 let item = unwrapExport(statement)
404 match functionDeclaration(item) {
405 case Some(declaration) => match child(declaration, "name") {
406 case Some(name) => indexes.insert(name.lo, index)
407 case None => ()
408 }
409 case None => if item.kind == "statement/let" {
410 match child(item, "pattern") {
411 case Some(pattern) => for binding in patternBindingNodes(pattern) {
412 indexes.insert(binding.lo, index)
413 }
414 case None => ()
415 }
416 } else {
417 ()
418 }
419 }
420 index += 1
421 }
422 indexes
425function initializationReferenceViolation(
426 state: CheckerState,
427 module: ModuleSyntax,
428 runtimeBindingIndexes: Map<int, int>,
429 cutoff: int,
430 node: SyntaxNode,
431) -> bool {
432 match referenceFor(state, module.moduleIndex, node.lo, node.hi) {
433 case Some(reference) => if reference.targetModuleIndex == module.moduleIndex {
434 match runtimeBindingIndexes.get(reference.targetLo) {
435 case Some(target) => target >= cutoff
436 case None => false
437 }
438 } else {
439 false
440 }
441 case None => false
442 }
445function initializationChildIsImmediate(
446 parent: SyntaxNode,
447 found: SyntaxNode,
448) -> bool {
449 // Conditional children are delayed under the same DIRECT rule as branch,
450 // arm, loop, lambda, function, defer, and concurrent bodies.
451 let kind = parent.kind
452 if kind == "statement/function" || kind == "statement/defer" ||
453 kind == "statement/break" || kind == "statement/continue" ||
454 kind == "expression/lambda" || kind == "expression/loop" {
455 return false
456 }
457 if kind == "statement/while" || kind == "expression/if" {
458 return found.field == "condition"
459 }
460 if kind == "expression/match" {
461 return found.field == "scrutinee"
462 }
463 if kind == "expression/for" || kind == "comprehension-clause/for" {
464 return found.field == "iterator"
465 }
466 if kind == "expression/comprehension" {
467 return found.field == "clauses" && found.index == 0 &&
468 found.kind == "comprehension-clause/for"
469 }
470 if kind == "expression/concurrent" {
471 return found.field == "timeout"
472 }
473 if kind == "expression/binary" && found.field == "right" {
474 let operator = attribute(parent, "operator")
475 return operator != "and" && operator != "or" && operator != "coalesce"
476 }
477 if kind == "expression/call" && found.field == "arguments" {
478 match child(parent, "callee") {
479 case Some(callee) => return callee.kind != "expression/optional-access"
480 case None => ()
481 }
482 }
483 if kind == "statement/let" || kind == "statement/const" ||
484 kind == "statement/return" {
485 return found.field == "value"
486 }
487 if kind == "statement/assign" {
488 return found.field == "target" || found.field == "value"
489 }
490 if kind == "statement/using" {
491 return found.field == "value" || found.field == "body"
492 }
493 if kind.startsWith("statement/") {
494 return kind == "statement/expression"
495 }
496 true
499function initializationViolation(
500 state: CheckerState,
501 module: ModuleSyntax,
502 runtimeBindingIndexes: Map<int, int>,
503 cutoff: int,
504 node: SyntaxNode,
505) -> Option<SyntaxNode> {
506 if node.kind == "expression/identifier" {
507 return if initializationReferenceViolation(
508 state,
509 module,
510 runtimeBindingIndexes,
511 cutoff,
512 node,
513 ) {
514 Some(node)
515 } else {
516 None
517 }
518 }
519 for found in node.children {
520 if initializationChildIsImmediate(node, found) {
521 match initializationViolation(
522 state,
523 module,
524 runtimeBindingIndexes,
525 cutoff,
526 found,
527 ) {
528 case Some(violation) => return Some(violation)
529 case None => ()
530 }
531 }
532 }
533 None
536function reportInitializationViolation(
537 state: CheckerState,
538 module: ModuleSyntax,
539 violation: Option<SyntaxNode>,
540) -> () {
541 match violation {
542 case Some(found) => addDiagnostic(
543 state,
544 module.moduleIndex,
545 "TPZ5002",
546 "`{text(module.source, found)}` is not bound",
547 "",
548 found,
549 )
550 case None => ()
551 }
554function checkTopLevelInitializationOrder(
555 state: CheckerState,
556 module: ModuleSyntax,
557) -> () {
558 let runtimeBindingIndexes = topLevelRuntimeBindingIndexes(module)
559 let mut cutoff = 0
560 for statement in module.root.children {
561 let item = unwrapExport(statement)
562 if item.kind == "statement/let" {
563 match child(item, "value") {
564 case Some(found) => reportInitializationViolation(
565 state,
566 module,
567 initializationViolation(
568 state,
569 module,
570 runtimeBindingIndexes,
571 cutoff,
572 found,
573 ),
574 )
575 case None => ()
576 }
577 } else if item.kind == "statement/using" {
578 match child(item, "value") {
579 case Some(found) => reportInitializationViolation(
580 state,
581 module,
582 initializationViolation(
583 state,
584 module,
585 runtimeBindingIndexes,
586 cutoff,
587 found,
588 ),
589 )
590 case None => ()
591 }
592 match child(item, "body") {
593 case Some(found) => reportInitializationViolation(
594 state,
595 module,
596 initializationViolation(
597 state,
598 module,
599 runtimeBindingIndexes,
600 cutoff,
601 found,
602 ),
603 )
604 case None => ()
605 }
606 } else if item.kind == "statement/assign" {
607 for field in ["target", "value"] {
608 match child(item, field) {
609 case Some(found) => reportInitializationViolation(
610 state,
611 module,
612 initializationViolation(
613 state,
614 module,
615 runtimeBindingIndexes,
616 cutoff,
617 found,
618 ),
619 )
620 case None => ()
621 }
622 }
623 } else if item.kind == "statement/while" {
624 match child(item, "condition") {
625 case Some(found) => reportInitializationViolation(
626 state,
627 module,
628 initializationViolation(
629 state,
630 module,
631 runtimeBindingIndexes,
632 cutoff,
633 found,
634 ),
635 )
636 case None => ()
637 }
638 } else if item.kind == "statement/expression" ||
639 item.kind == "statement/return" {
640 for found in item.children {
641 reportInitializationViolation(
642 state,
643 module,
644 initializationViolation(
645 state,
646 module,
647 runtimeBindingIndexes,
648 cutoff,
649 found,
650 ),
651 )
652 }
653 }
654 cutoff += 1
655 }
656 ()
659function checkModuleBodies(service: CheckerService, state: CheckerState, module: ModuleSyntax) -> () {
660 checkRecordDefaults(service, state, module)
661 for statement in module.root.children {
662 checkImplBodies(service, state, module, statement)
663 match functionDeclaration(unwrapExport(statement)) {
664 case Some(declaration) => checkFunctionDeclaration(
665 service,
666 state,
667 module,
668 declaration,
669 )
670 case None => if !staticTopLevelItem(unwrapExport(statement)) {
671 checkStatement(
672 service,
673 state,
674 module.moduleIndex,
675 module.source,
676 statement,
677 )
678 }
679 }
680 }
681 checkTopLevelInitializationOrder(state, module)
682 ()
685export function checkFunctionBodies(service: CheckerService, state: CheckerState) -> () {
686 // The resolver supplies dependency order, so inferred exported bindings
687 // are available before a consumer checks its own initializers.
688 let ordered = state.modules
689 for module in ordered {
690 precheckTopLevelBindings(service, state, module)
691 }
692 for module in ordered {
693 checkModuleBodies(service, state, module)
694 }
695 ()
698function captureScopeAncestors(
699 state: CheckerState,
700 indexes: Map<string, Set<int>>,
701 moduleIndex: int,
702 ordinal: int,
703) -> Set<int> {
704 let key = scopeKey(moduleIndex, ordinal)
705 match indexes.get(key) {
706 case Some(found) => return found
707 case None => ()
708 }
709 let mut ancestors: Set<int> = set {}
710 let mut current = ordinal
711 while current >= 0 {
712 ancestors.add(current)
713 match scopeFor(state, moduleIndex, current) {
714 case Some(scope) => {
715 current = scope.parentOrdinal
716 }
717 case None => {
718 current = -1
719 }
720 }
721 }
722 let mut target = indexes
723 target.insert(key, ancestors)
724 ancestors
727function namespaceCaptureTarget(
728 state: CheckerState,
729 reference: PreviewResolvedReference,
730) -> Option<CaptureTarget> {
731 let segments = reference.name.split(".")
732 if segments.length < 2 {
733 return None
734 }
735 let alias = match segments.get(0) {
736 case Some(found) => found
737 case None => return None
738 }
739 let index = match state.namespaceImportIndexes.get(
740 definitionNameKey(reference.moduleIndex, alias),
741 ) {
742 case Some(found) => found
743 case None => return None
744 }
745 let declaration = match state.resolved.declarations.get(index) {
746 case Some(found) => found
747 case None => return None
748 }
749 let unknown = emptyType("unknown")
750 Some(CaptureTarget {
751 declaration: declaration,
752 name: declaration.name,
753 typeValue: flattenType(unknown),
754 ambient: true,
755 })
758function captureTargetForReference(
759 state: CheckerState,
760 reference: PreviewResolvedReference,
761) -> Option<CaptureTarget> {
762 if reference.namespace != "value" {
763 return None
764 }
765 if reference.role == "namespace-member" {
766 return namespaceCaptureTarget(state, reference)
767 }
768 if reference.targetModuleIndex != reference.moduleIndex {
769 return None
770 }
771 let bindingIndex = typedNodeIndex(
772 state,
773 reference.targetModuleIndex,
774 "binding",
775 reference.targetLo,
776 reference.targetHi,
777 )
778 let declarationIndex = typedNodeIndex(
779 state,
780 reference.targetModuleIndex,
781 "declaration",
782 reference.targetLo,
783 reference.targetHi,
784 )
785 let declaredIndex = if bindingIndex > declarationIndex {
786 bindingIndex
787 } else {
788 declarationIndex
789 }
790 let observedIndex = if declaredIndex >= 0 {
791 declaredIndex
792 } else {
793 typedNodeIndex(
794 state,
795 reference.moduleIndex,
796 "expression",
797 reference.lo,
798 reference.hi,
799 )
800 }
801 let typedNode = match state.nodes.get(observedIndex) {
802 case Some(found) => found
803 case None => return None
804 }
805 let declaration = match declarationFor(
806 state,
807 reference.targetModuleIndex,
808 reference.targetLo,
809 reference.targetHi,
810 ) {
811 case Some(found) => found
812 case None => return None
813 }
814 Some(CaptureTarget {
815 declaration: declaration,
816 name: reference.name,
817 typeValue: typedNode.typeValue,
818 ambient: typedNode.ambient,
819 })
822function capturesForReference(
823 state: CheckerState,
824 scopeAncestorIndexes: Map<string, Set<int>>,
825 reference: PreviewResolvedReference,
826) -> Array<PreviewTypedCapture> {
827 let mut captures: Array<PreviewTypedCapture> = []
828 let target = match captureTargetForReference(state, reference) {
829 case Some(found) => found
830 case None => return captures
831 }
832 let declarationAncestors = captureScopeAncestors(
833 state,
834 scopeAncestorIndexes,
835 reference.moduleIndex,
836 target.declaration.scopeOrdinal,
837 )
838 let mut current = reference.scopeOrdinal
839 while current >= 0 {
840 match scopeFor(state, reference.moduleIndex, current) {
841 case Some(scope) => {
842 if (scope.kind == "function" || scope.kind == "lambda") &&
843 !declarationAncestors.contains(scope.ordinal) {
844 captures.push(PreviewTypedCapture {
845 moduleIndex: reference.moduleIndex,
846 closureLo: scope.lo,
847 closureHi: scope.hi,
848 referenceLo: reference.lo,
849 referenceHi: reference.hi,
850 declarationLo: target.declaration.lo,
851 declarationHi: target.declaration.hi,
852 name: target.name,
853 typeValue: target.typeValue,
854 ambient: target.ambient,
855 })
856 }
857 current = scope.parentOrdinal
858 }
859 case None => {
860 current = -1
861 }
862 }
863 }
864 captures
867function capturesAreSame(
868 left: PreviewTypedCapture,
869 right: PreviewTypedCapture,
870) -> bool {
871 left.moduleIndex == right.moduleIndex &&
872 left.closureLo == right.closureLo &&
873 left.closureHi == right.closureHi &&
874 left.referenceLo == right.referenceLo &&
875 left.referenceHi == right.referenceHi &&
876 left.declarationLo == right.declarationLo &&
877 left.declarationHi == right.declarationHi &&
878 left.name == right.name
881function sortAndDeduplicateCaptures(state: CheckerState) -> () {
882 let mut target = state.captures
883 target.sortBy((capture) => capture.name)
884 target.sortBy((capture) => capture.declarationHi)
885 target.sortBy((capture) => capture.declarationLo)
886 target.sortBy((capture) => capture.referenceHi)
887 target.sortBy((capture) => capture.referenceLo)
888 target.sortBy((capture) => capture.closureHi)
889 target.sortBy((capture) => capture.closureLo)
890 target.sortBy((capture) => capture.moduleIndex)
891 target.sortBy((capture) => moduleIdentityAt(state, capture.moduleIndex))
892 let mut index = 1
893 while index < target.length {
894 let previous = match target.get(index - 1) {
895 case Some(value) => value
896 case None => return ()
897 }
898 let current = match target.get(index) {
899 case Some(value) => value
900 case None => return ()
901 }
902 if capturesAreSame(previous, current) {
903 target.removeAt(index)
904 } else {
905 index += 1
906 }
907 }
910export function deriveCaptures(state: CheckerState) -> () {
911 let mut scopeAncestorIndexes: Map<string, Set<int>> = Map.new()
912 let mut captures = state.captures
913 for reference in state.resolved.references {
914 for capture in capturesForReference(
915 state,
916 scopeAncestorIndexes,
917 reference,
918 ) {
919 captures.push(capture)
920 }
921 }
922 sortAndDeduplicateCaptures(state)
923 ()
926function lispexRuleFactoryCallIndexes(
927 calls: Array<PreviewTypedCall>,
928) -> Map<string, Array<int>> {
929 let mut indexes: Map<string, Array<int>> = Map.new()
930 let mut index = 0
931 for call in calls {
932 if call.arguments.length == 0 &&
933 call.targetIdentity.startsWith("topaz.lispex-rule-handle/v1:") {
934 let key = definitionNameKey(call.moduleIndex, call.targetIdentity)
935 match indexes.get(key) {
936 case Some(found) => {
937 let mut targets = found
938 targets.push(index)
939 }
940 case None => indexes.insert(key, [index])
941 }
942 }
943 index += 1
944 }
945 indexes
948export function diagnoseLispexRuleFactoryEscapes(state: CheckerState) -> () {
949 let callIndexes = lispexRuleFactoryCallIndexes(state.calls)
950 for reference in state.resolved.references {
951 if reference.namespace != "value" ||
952 reference.targetModule != "std.lispex.rules" ||
953 reference.targetName == "" {
954 continue
955 }
956 let expected = "topaz.lispex-rule-handle/v1:{reference.targetName}"
957 let mut direct = false
958 match callIndexes.get(definitionNameKey(reference.moduleIndex, expected)) {
959 case Some(indexes) => for index in indexes {
960 match state.calls.get(index) {
961 case Some(call) => if reference.lo >= call.calleeLo &&
962 reference.hi <= call.calleeHi {
963 direct = true
964 }
965 case None => ()
966 }
967 }
968 case None => ()
969 }
970 if !direct {
971 addDiagnostic(
972 state,
973 reference.moduleIndex,
974 "TPZ5500",
975 "a generated Lispex rule factory may only appear as the direct callee of a zero-argument call",
976 "",
977 SyntaxNode {
978 kind: "identifier",
979 lo: reference.lo,
980 hi: reference.hi,
981 field: "",
982 index: 0,
983 attributes: [],
984 children: [],
985 },
986 )
987 }
988 }
989 ()
992record DeclarationIndexes {
993 spans: Map<string, int>,
994 valuesByModule: Map<string, Array<int>>,
995 namespaceImports: Map<string, int>,
998export record CheckerIndexes {
999 declarations: Map<string, int>,
1000 valueDeclarationsByModule: Map<string, Array<int>>,
1001 namespaceImports: Map<string, int>,
1002 references: Map<string, int>,
1003 scopes: Map<string, int>,
1006function declarationIndexes(resolved: ResolveResult) -> DeclarationIndexes {
1007 let mut spans = emptyStringIntMap()
1008 let mut valuesByModule: Map<string, Array<int>> = Map.new()
1009 let mut namespaceImports = emptyStringIntMap()
1010 let mut index = 0
1011 for declaration in resolved.declarations {
1012 let span = spanKey(declaration.moduleIndex, declaration.lo, declaration.hi)
1013 match spans.get(span) {
1014 case Some(_) => ()
1015 case None => spans.insert(span, index)
1017 if declaration.namespace == "value" {
1018 let key = "{declaration.moduleIndex}"
1019 match valuesByModule.get(key) {
1020 case Some(found) => {
1021 let mut targets = found
1022 targets.push(index)
1024 case None => valuesByModule.insert(key, [index])
1027 if declaration.namespace == "module" &&
1028 declaration.declarationKind == "namespace-import" {
1029 let name = definitionNameKey(declaration.moduleIndex, declaration.name)
1030 match namespaceImports.get(name) {
1031 case Some(_) => ()
1032 case None => namespaceImports.insert(name, index)
1035 index += 1
1037 DeclarationIndexes {
1038 spans: spans,
1039 valuesByModule: valuesByModule,
1040 namespaceImports: namespaceImports,
1044function referenceSpanIndexes(resolved: ResolveResult) -> Map<string, int> {
1045 let mut indexes = emptyStringIntMap()
1046 let mut index = 0
1047 for reference in resolved.references {
1048 let key = spanKey(reference.moduleIndex, reference.lo, reference.hi)
1049 match indexes.get(key) {
1050 case Some(_) => ()
1051 case None => indexes.insert(key, index)
1053 index += 1
1055 indexes
1058function scopeOrdinalIndexes(resolved: ResolveResult) -> Map<string, int> {
1059 let mut indexes = emptyStringIntMap()
1060 let mut index = 0
1061 for scope in resolved.scopes {
1062 let key = scopeKey(scope.moduleIndex, scope.ordinal)
1063 match indexes.get(key) {
1064 case Some(_) => ()
1065 case None => indexes.insert(key, index)
1067 index += 1
1069 indexes
1072export function checkerIndexes(resolved: ResolveResult) -> CheckerIndexes {
1073 let declarations = declarationIndexes(resolved)
1074 CheckerIndexes {
1075 declarations: declarations.spans,
1076 valueDeclarationsByModule: declarations.valuesByModule,
1077 namespaceImports: declarations.namespaceImports,
1078 references: referenceSpanIndexes(resolved),
1079 scopes: scopeOrdinalIndexes(resolved),
1083export function initialCheckerState(
1084 resolved: ResolveResult,
1085 modules: Array<ModuleSyntax>,
1086 indexes: CheckerIndexes,
1087) -> CheckerState {
1088 CheckerState {
1089 resolved: resolved,
1090 modules: modules,
1091 declarationIndexes: indexes.declarations,
1092 valueDeclarationIndexesByModule: indexes.valueDeclarationsByModule,
1093 namespaceImportIndexes: indexes.namespaceImports,
1094 referenceIndexes: indexes.references,
1095 scopeIndexes: indexes.scopes,
1096 symbolIndexes: emptyStringIntMap(),
1097 nodeIndexes: emptyStringIntMap(),
1098 callIndexes: emptyStringIntMap(),
1099 functionIndexes: emptyStringIntMap(),
1100 functionNameIndexes: emptyStringIntMap(),
1101 receiverMethodIndexes: emptyStringIntMap(),
1102 protocolIndexes: emptyStringIntMap(),
1103 conformanceIndexes: emptyStringIntMap(),
1104 conformanceTypeIndexes: emptyStringIntMap(),
1105 recordIndexes: emptyStringIntMap(),
1106 enumIndexes: emptyStringIntMap(),
1107 newtypeIndexes: emptyStringIntMap(),
1108 aliasIndexes: emptyStringIntMap(),
1109 symbols: [],
1110 records: [],
1111 enums: [],
1112 newtypes: [],
1113 aliases: [],
1114 functions: [],
1115 receiverMethods: [],
1116 protocols: [],
1117 pendingFunctions: [],
1118 functionAliases: emptyStringIntMap(),
1119 conformances: [],
1120 typeParameters: [],
1121 activeFunctions: [],
1122 expectedReturns: [],
1123 returnJoins: [],
1124 expectedExpressions: [],
1125 bareBindingExpressions: [],
1126 literalJoinContexts: [],
1127 branchJoinContexts: [],
1128 statementExpressionContexts: [],
1129 typeCapabilitySuppressions: [],
1130 loopFrames: [],
1131 loopFloors: [],
1132 pipeValues: [],
1133 nodes: [],
1134 calls: [],
1135 captures: [],
1136 diagnostics: [],
1137 inferenceSolutions: Map.new(),
1138 nextInference: 0,

SHA256 cc512c8ffbff50688063ea9078fd6afaa80670d55795e844aa7d7376e2a2b613