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/call_plan.tpz

Topaz · 41830 B · Original file

1// Call-target selection and named, default, variadic, and spread planning.
2// Plans describe slot ownership only; call_complete performs inference and the
3// checker facade records the resulting typed call.
5import types { SyntaxNode }
6import checker_types { TypeValue }
7import checker.model {
8 CallArgumentPlan,
9 CallSignature,
10 CheckerService,
11 CheckerState,
12 FunctionDefinition,
13 TypeBinding,
14 UserProtocolCallTarget,
15 constructor,
16 emptyType,
17 literal,
18 primitive,
19 serviceInferExpression,
21import checker.syntax_index {
22 addDiagnostic,
23 addNode,
24 child,
25 children,
26 protocolMethodDefinition,
27 text,
29import checker.nominals { formType }
30import checker.protocols_methods {
31 enumDefinitionForType,
32 receiverMethodCallSignature,
33 referenceTargetType,
35import checker.members_capabilities {
36 bindTypeVariable,
37 builtinNamespaceFamily,
38 typeArgument,
40import checker.call_facts {
41 builtinCallTypeParameters,
42 builtinFunction,
43 builtinNamespaceObject,
44 defaultedCallSignature,
45 recordInferredExpression,
46 requiredCallSignature,
47 userProtocolObject,
49import checker.expression_core {
50 contextualMemberObjectType,
51 inferIdentifierExpression,
52 inferOptionalMemberAccess,
53 inferPlainMemberAccess,
54 optionalReceiver,
57export function nominalConstructorCallResult(
58 state: CheckerState,
59 moduleIndex: int,
60 source: Bytes,
61 callee: SyntaxNode,
62) -> Option<TypeValue> {
63 if callee.kind == "expression/identifier" {
64 match referenceTargetType(state, moduleIndex, callee) {
65 case Some(found) => if found.kind == "newtype" {
66 return Some(found)
67 }
68 case None => ()
69 }
70 }
71 if callee.kind == "expression/member" {
72 let object = match child(callee, "object") {
73 case Some(found) => found
74 case None => return None
75 }
76 let field = match child(callee, "field") {
77 case Some(found) => text(source, found)
78 case None => return None
79 }
80 let enumType = match referenceTargetType(
81 state,
82 moduleIndex,
83 object,
84 ) {
85 case Some(found) => found
86 case None => return None
87 }
88 match enumDefinitionForType(state, moduleIndex, enumType) {
89 case Some(definition) => for variant in definition.variants {
90 if variant.name == field && variant.payloads.length > 0 {
91 return Some(enumType)
92 }
93 }
94 case None => ()
95 }
96 }
97 None
100export function declaredCallSignature(
101 definition: FunctionDefinition,
102) -> CallSignature {
103 CallSignature {
104 parameterNames: definition.parameterNames,
105 parameterDefaults: definition.parameterDefaults,
106 parameterCount: definition.parameterTypes.length,
107 variadic: definition.variadic.length > 0,
108 positionalOnlyIdentity: "",
109 }
112function positionalOnlyCallSignature(
113 identity: string,
114 parameterNames: Array<string>,
115) -> CallSignature {
116 CallSignature {
117 ...requiredCallSignature(parameterNames, false),
118 positionalOnlyIdentity: identity,
119 }
122function collectionStaticCallSignature(
123 namespace: string,
124 name: string,
125) -> Option<CallSignature> {
126 if (namespace == "Array" || namespace == "Set") && name == "of" {
127 return Some(requiredCallSignature([], true))
128 }
129 if namespace == "Map" {
130 return match name {
131 case "new" => Some(requiredCallSignature([], false))
132 case "ofEntries" => Some(requiredCallSignature(["entries"], false))
133 case _ => None
134 }
135 }
136 if namespace == "Bytes" {
137 return match name {
138 case "empty" => Some(requiredCallSignature([], false))
139 case "encodeUtf8" | "fromHex" | "fromBase64" =>
140 Some(requiredCallSignature(["s"], false))
141 case "fromArray" => Some(requiredCallSignature(["values"], false))
142 case "concat" => Some(requiredCallSignature(["a", "b"], false))
143 case _ => None
144 }
145 }
146 if namespace == "ByteBuffer" {
147 return match name {
148 case "allocate" => Some(defaultedCallSignature(
149 ["length", "value"],
150 [false, true],
151 ))
152 case "fromBytes" => Some(requiredCallSignature(["value"], false))
153 case _ => None
154 }
155 }
156 None
159function numericBinaryStaticCallSignature(
160 namespace: string,
161 name: string,
162) -> Option<CallSignature> {
163 if namespace == "Math" {
164 return match name {
165 case "sqrt" | "abs" | "floor" | "ceil" | "round" |
166 "sin" | "cos" | "tan" | "isNaN" | "isFinite" =>
167 Some(requiredCallSignature(["x"], false))
168 case "parseFloat" => Some(requiredCallSignature(["s"], false))
169 case "min" | "max" => Some(requiredCallSignature(["a", "b"], false))
170 case _ => None
171 }
172 }
173 if namespace == "Codec" {
174 return match name {
175 case "zstdCompress" => Some(defaultedCallSignature(
176 ["bytes", "level"],
177 [false, true],
178 ))
179 case "gzipCompress" | "gzipDecompress" | "deflateCompress" |
180 "deflateFixedCompress" | "zlibFixedCompress" |
181 "reedSolomon255223Protect" | "deflateDecompress" |
182 "zstdDecompress" => Some(requiredCallSignature(["bytes"], false))
183 case _ => None
184 }
185 }
186 if namespace == "Hash" {
187 return match name {
188 case "sha256" | "sha512" | "crc32" =>
189 Some(requiredCallSignature(["data"], false))
190 case "hmacSha256" => Some(requiredCallSignature(
191 ["key", "message"],
192 false,
193 ))
194 case _ => None
195 }
196 }
197 None
200function hostStaticCallSignature(
201 namespace: string,
202 name: string,
203) -> Option<CallSignature> {
204 if namespace == "FS" {
205 return match name {
206 case "readText" | "readBytes" | "list" =>
207 Some(requiredCallSignature(["path"], false))
208 case "writeText" => Some(requiredCallSignature(
209 ["path", "text"],
210 false,
211 ))
212 case "writeBytes" => Some(requiredCallSignature(
213 ["path", "bytes"],
214 false,
215 ))
216 case _ => None
217 }
218 }
219 if namespace == "Encoding" {
220 return match name {
221 case "utf8Encode" | "hexDecode" | "base64Decode" =>
222 Some(requiredCallSignature(["text"], false))
223 case "utf8Decode" | "hexEncode" | "base64Encode" =>
224 Some(requiredCallSignature(["bytes"], false))
225 case _ => None
226 }
227 }
228 if namespace == "Cli" {
229 return match name {
230 case "hasFlag" | "option" | "options" =>
231 Some(requiredCallSignature(["args", "name"], false))
232 case "positionals" => Some(requiredCallSignature(["args"], false))
233 case _ => None
234 }
235 }
236 if namespace == "Path" &&
237 (name == "from" || name == "cwdRelative" || name == "project") {
238 return Some(requiredCallSignature(["text"], false))
239 }
240 if namespace == "Regex" && name == "compile" {
241 return Some(requiredCallSignature(["pattern"], false))
242 }
243 None
246function structuredDataStaticCallSignature(
247 namespace: string,
248 name: string,
249) -> Option<CallSignature> {
250 if namespace == "CSV" {
251 return match name {
252 case "parse" | "parseWithHeader" =>
253 Some(requiredCallSignature(["text"], false))
254 case "stringify" => Some(requiredCallSignature(["rows"], false))
255 case "stringifyWithHeader" => Some(requiredCallSignature(
256 ["rows", "columns"],
257 false,
258 ))
259 case _ => None
260 }
261 }
262 if namespace == "TOML" {
263 return match name {
264 case "parse" => Some(requiredCallSignature(["text"], false))
265 case "stringify" | "toJson" | "fromJson" =>
266 Some(requiredCallSignature(["value"], false))
267 case _ => None
268 }
269 }
270 if namespace == "JSON" {
271 return match name {
272 case "parseAs" | "parse" =>
273 Some(requiredCallSignature(["text"], false))
274 case "decode" | "stringify" =>
275 Some(requiredCallSignature(["value"], false))
276 case _ => None
277 }
278 }
279 None
282function scalarTestStaticCallSignature(
283 namespace: string,
284 name: string,
285) -> Option<CallSignature> {
286 if namespace == "URL" && name == "parse" {
287 return Some(requiredCallSignature(["text"], false))
288 }
289 if namespace == "Date" {
290 return match name {
291 case "fromYmd" => Some(requiredCallSignature(
292 ["year", "month", "day"],
293 false,
294 ))
295 case "parseIso" => Some(requiredCallSignature(["text"], false))
296 case _ => None
297 }
298 }
299 if namespace == "BigInt" {
300 return match name {
301 case "fromInt" => Some(requiredCallSignature(["n"], false))
302 case "parse" => Some(requiredCallSignature(["text", "radix"], false))
303 case _ => None
304 }
305 }
306 if namespace == "Decimal" {
307 return match name {
308 case "fromInt" => Some(requiredCallSignature(["n"], false))
309 case "parse" => Some(requiredCallSignature(["text"], false))
310 case _ => None
311 }
312 }
313 if namespace == "Test" {
314 return match name {
315 case "assert" => Some(defaultedCallSignature(
316 ["condition", "message"],
317 [false, true],
318 ))
319 case "assertEq" | "assertNe" => Some(requiredCallSignature(
320 ["actual", "expected"],
321 false,
322 ))
323 case "assertContains" => Some(requiredCallSignature(
324 ["text", "needle"],
325 false,
326 ))
327 case "assertOk" | "assertErr" | "assertSome" | "assertNone" =>
328 Some(requiredCallSignature(["value"], false))
329 case "assertGolden" => Some(requiredCallSignature(
330 ["path", "actual"],
331 false,
332 ))
333 case _ => None
334 }
335 }
336 None
339function staticBuiltinCallSignature(
340 namespace: string,
341 name: string,
342) -> Option<CallSignature> {
343 match builtinNamespaceFamily(namespace) {
344 case "collection" => collectionStaticCallSignature(namespace, name)
345 case "numeric-binary" => numericBinaryStaticCallSignature(namespace, name)
346 case "host" => hostStaticCallSignature(namespace, name)
347 case "structured-data" => structuredDataStaticCallSignature(namespace, name)
348 case "scalar" => scalarTestStaticCallSignature(namespace, name)
349 case "protocol" => if namespace == "Show" && name == "show" {
350 Some(positionalOnlyCallSignature("Show.show", ["value"]))
351 } else if namespace == "Eq" && name == "equals" {
352 Some(positionalOnlyCallSignature("Eq.equals", ["a", "b"]))
353 } else if namespace == "Order" && name == "compare" {
354 Some(positionalOnlyCallSignature("Order.compare", ["a", "b"]))
355 } else {
356 None
357 }
358 case _ => None
359 }
362export function userProtocolCallTarget(
363 state: CheckerState,
364 moduleIndex: int,
365 source: Bytes,
366 callee: SyntaxNode,
367) -> Option<UserProtocolCallTarget> {
368 if callee.kind != "expression/member" {
369 return None
370 }
371 let object = match child(callee, "object") {
372 case Some(found) => found
373 case None => return None
374 }
375 let field = match child(callee, "field") {
376 case Some(found) => text(source, found)
377 case None => return None
378 }
379 match userProtocolObject(state, moduleIndex, source, object) {
380 case Some(definition) => match protocolMethodDefinition(definition, field) {
381 case Some(method) => Some(UserProtocolCallTarget {
382 protocolName: definition.name,
383 method: method,
384 })
385 case None => None
386 }
387 case None => None
388 }
391export function userProtocolCallSignature(
392 target: UserProtocolCallTarget,
393) -> CallSignature {
394 positionalOnlyCallSignature(
395 "{target.protocolName}.{target.method.name}",
396 target.method.parameterNames,
397 )
400function containerReceiverCallSignature(
401 receiver: TypeValue,
402 name: string,
403) -> Option<CallSignature> {
404 if receiver.kind != "constructor" {
405 return None
406 }
407 if receiver.name == "Option" {
408 return match name {
409 case "okOr" => Some(requiredCallSignature(["error"], false))
410 case "okOrElse" | "map" | "flatMap" =>
411 Some(requiredCallSignature(["f"], false))
412 case _ => None
413 }
414 }
415 if receiver.name == "Result" {
416 return match name {
417 case "map" | "flatMap" => Some(requiredCallSignature(["f"], false))
418 case _ => None
419 }
420 }
421 if receiver.name == "Array" {
422 return match name {
423 case "get" => Some(requiredCallSignature(["i"], false))
424 case "slice" => Some(requiredCallSignature(["start", "end"], false))
425 case "map" | "sortedBy" | "filter" | "sortBy" | "retain" =>
426 Some(requiredCallSignature(["f"], false))
427 case "join" => Some(requiredCallSignature(["sep"], false))
428 case "reduce" => Some(requiredCallSignature(["initial", "f"], false))
429 case "indexOf" | "push" => Some(requiredCallSignature(["x"], false))
430 case "insert" => Some(requiredCallSignature(
431 ["index", "value"],
432 false,
433 ))
434 case "removeAt" => Some(requiredCallSignature(["index"], false))
435 case "sorted" | "pop" | "clear" | "reverse" | "sort" =>
436 Some(requiredCallSignature([], false))
437 case _ => None
438 }
439 }
440 if receiver.name == "Map" {
441 return match name {
442 case "insert" => Some(requiredCallSignature(["k", "v"], false))
443 case "get" | "remove" | "containsKey" =>
444 Some(requiredCallSignature(["k"], false))
445 case "getOr" => Some(requiredCallSignature(["k", "default"], false))
446 case "isEmpty" | "clear" => Some(requiredCallSignature([], false))
447 case "update" => Some(requiredCallSignature(
448 ["k", "initial", "f"],
449 false,
450 ))
451 case "mapValues" | "filter" =>
452 Some(requiredCallSignature(["f"], false))
453 case _ => None
454 }
455 }
456 if receiver.name == "Set" {
457 return match name {
458 case "add" | "remove" | "contains" =>
459 Some(requiredCallSignature(["x"], false))
460 case "union" | "intersection" | "difference" =>
461 Some(requiredCallSignature(["other"], false))
462 case "isEmpty" | "toArray" | "clear" =>
463 Some(requiredCallSignature([], false))
464 case _ => None
465 }
466 }
467 None
470function scalarReceiverCallSignature(
471 receiver: TypeValue,
472 name: string,
473) -> Option<CallSignature> {
474 if (receiver.kind == "primitive" || receiver.kind == "literal") &&
475 receiver.name == "int" {
476 return match name {
477 case "atLeast" => Some(requiredCallSignature(["min"], false))
478 case "atMost" => Some(requiredCallSignature(["max"], false))
479 case _ => None
480 }
481 }
482 if (receiver.kind == "primitive" || receiver.kind == "literal") &&
483 receiver.name == "string" {
484 return match name {
485 case "startsWith" => Some(requiredCallSignature(["prefix"], false))
486 case "endsWith" => Some(requiredCallSignature(["suffix"], false))
487 case "contains" | "indexOf" | "lastIndexOf" =>
488 Some(requiredCallSignature(["sub"], false))
489 case "codePointAt" => Some(requiredCallSignature(["i"], false))
490 case "split" => Some(requiredCallSignature(["sep"], false))
491 case "slice" => Some(requiredCallSignature(["start", "end"], false))
492 case "replace" => Some(requiredCallSignature(["old", "new"], false))
493 case "scalars" | "trim" | "trimStart" | "trimEnd" | "byteLength" =>
494 Some(requiredCallSignature([], false))
495 case _ => None
496 }
497 }
498 None
501function runtimeReceiverCallSignature(
502 receiver: TypeValue,
503 name: string,
504) -> Option<CallSignature> {
505 if receiver.kind == "file" {
506 return match name {
507 case "write" => Some(requiredCallSignature(["s"], false))
508 case "read" | "close" => Some(requiredCallSignature([], false))
509 case _ => None
510 }
511 }
512 if receiver.kind == "json-value" {
513 return match name {
514 case "get" => Some(requiredCallSignature(["key"], false))
515 case "at" => Some(requiredCallSignature(["index"], false))
516 case "kind" | "isNull" | "asString" | "asBool" | "asInt" |
517 "numberText" | "length" | "asArray" | "keys" | "values" =>
518 Some(requiredCallSignature([], false))
519 case _ => None
520 }
521 }
522 if receiver.kind == "bytes" {
523 return match name {
524 case "get" => Some(requiredCallSignature(["index"], false))
525 case "slice" => Some(requiredCallSignature(["start", "end"], false))
526 case "decodeUtf8" | "toHex" | "toBase64" | "length" |
527 "isEmpty" | "toArray" => Some(requiredCallSignature([], false))
528 case _ => None
529 }
530 }
531 if receiver.kind == "byte-buffer" {
532 return match name {
533 case "get" => Some(requiredCallSignature(["index"], false))
534 case "set" => Some(requiredCallSignature(["index", "value"], false))
535 case "fill" => Some(requiredCallSignature(
536 ["start", "length", "value"],
537 false,
538 ))
539 case "copy" => Some(requiredCallSignature(
540 ["source", "sourceStart", "targetStart", "length"],
541 false,
542 ))
543 case "length" | "toBytes" => Some(requiredCallSignature([], false))
544 case _ => None
545 }
546 }
547 if receiver.kind == "path" {
548 return match name {
549 case "join" => Some(requiredCallSignature(["child"], false))
550 case "withExtension" => Some(requiredCallSignature(["ext"], false))
551 case "parent" | "fileName" | "extension" | "normalize" | "toString" =>
552 Some(requiredCallSignature([], false))
553 case _ => None
554 }
555 }
556 if receiver.kind == "regex" {
557 return match name {
558 case "isMatch" | "find" | "findAll" | "split" =>
559 Some(requiredCallSignature(["text"], false))
560 case "replaceAll" => Some(requiredCallSignature(
561 ["text", "replacement"],
562 false,
563 ))
564 case _ => None
565 }
566 }
567 if receiver.kind == "url" {
568 return match name {
569 case "scheme" | "path" | "toString" | "host" | "fragment" | "query" =>
570 Some(requiredCallSignature([], false))
571 case _ => None
572 }
573 }
574 if receiver.kind == "date" {
575 return match name {
576 case "addDays" => Some(requiredCallSignature(["days"], false))
577 case "toIso" | "year" | "month" | "day" =>
578 Some(requiredCallSignature([], false))
579 case _ => None
580 }
581 }
582 if receiver.kind == "big-int" {
583 return match name {
584 case "toString" => Some(requiredCallSignature(["radix"], false))
585 case "div" | "mod" => Some(requiredCallSignature(["other"], false))
586 case "toInt" => Some(requiredCallSignature([], false))
587 case _ => None
588 }
589 }
590 if receiver.kind == "decimal" {
591 return match name {
592 case "round" => Some(defaultedCallSignature(
593 ["scale", "mode"],
594 [false, true],
595 ))
596 case "div" => Some(defaultedCallSignature(
597 ["other", "scale", "mode"],
598 [false, false, true],
599 ))
600 case "toString" | "scale" | "toInt" =>
601 Some(requiredCallSignature([], false))
602 case _ => None
603 }
604 }
605 None
608export function builtinReceiverCallSignature(
609 receiver: TypeValue,
610 name: string,
611) -> Option<CallSignature> {
612 if receiver.kind == "union" {
613 for member in receiver.members {
614 match builtinReceiverCallSignature(member, name) {
615 case Some(found) => return Some(found)
616 case None => ()
617 }
618 }
619 return None
620 }
621 match containerReceiverCallSignature(receiver, name) {
622 case Some(found) => return Some(found)
623 case None => ()
624 }
625 match scalarReceiverCallSignature(receiver, name) {
626 case Some(found) => return Some(found)
627 case None => ()
628 }
629 runtimeReceiverCallSignature(receiver, name)
632export function builtinMemberCallSignature(
633 service: CheckerService,
634 state: CheckerState,
635 moduleIndex: int,
636 source: Bytes,
637 call: SyntaxNode,
638 callee: SyntaxNode,
639) -> Option<CallSignature> {
640 if callee.kind != "expression/member" &&
641 callee.kind != "expression/optional-access" {
642 return None
643 }
644 let object = match child(callee, "object") {
645 case Some(found) => found
646 case None => return None
647 }
648 let field = match child(callee, "field") {
649 case Some(found) => found
650 case None => return None
651 }
652 match userProtocolObject(state, moduleIndex, source, object) {
653 case Some(_) => return None
654 case None => ()
655 }
656 if builtinNamespaceObject(service, state, moduleIndex, source, object) {
657 return staticBuiltinCallSignature(
658 text(source, object),
659 text(source, field),
660 )
661 }
662 let objectType = serviceInferExpression(service, state, moduleIndex, source, object)
663 let receiverType = if callee.kind == "expression/optional-access" {
664 match optionalReceiver(contextualMemberObjectType(
665 state,
666 call,
667 objectType,
668 )) {
669 case Some(receiver) => receiver.innerType
670 case None => emptyType("unknown")
671 }
672 } else {
673 objectType
674 }
675 match receiverMethodCallSignature(
676 state,
677 moduleIndex,
678 receiverType,
679 text(source, field),
680 ) {
681 case Some(signature) => Some(signature)
682 case None => builtinReceiverCallSignature(receiverType, text(source, field))
683 }
686function callParameterIndex(
687 signature: CallSignature,
688 name: string,
689) -> int {
690 let mut index = 0
691 while index < signature.parameterCount {
692 match signature.parameterNames.get(index) {
693 case Some(parameterName) => if parameterName == name {
694 return index
695 }
696 case None => ()
697 }
698 index += 1
699 }
700 -1
703function reportNamedCallArity(
704 state: CheckerState,
705 moduleIndex: int,
706 node: SyntaxNode,
707 signature: CallSignature,
708 filled: Array<bool>,
709 leadingSlot: int,
710 supplied: int,
711 overflow: int,
712) -> () {
713 let mut required = 0
714 let mut missing = false
715 let mut index = 0
716 while index < signature.parameterCount {
717 let defaulted = match signature.parameterDefaults.get(index) {
718 case Some(value) => value
719 case None => false
720 }
721 let present = match filled.get(index) {
722 case Some(value) => value
723 case None => false
724 }
725 if !defaulted {
726 required += 1
727 if !present {
728 missing = true
729 }
730 }
731 index += 1
732 }
733 if overflow > 0 || missing {
734 if signature.positionalOnlyIdentity != "" {
735 let noun = if required == 1 { "argument" } else { "arguments" }
736 addDiagnostic(
737 state,
738 moduleIndex,
739 "TPZ5004",
740 "`{signature.positionalOnlyIdentity}` takes {required} {noun}, found {supplied}",
741 "",
742 node,
743 )
744 } else if leadingSlot == -2 {
745 addDiagnostic(
746 state,
747 moduleIndex,
748 "TPZ5004",
749 "the pipeline inserts the piped value, but this function takes no parameters",
750 "",
751 node,
752 )
753 } else {
754 let optional = signature.variadic || signature.parameterCount > required
755 let suffix = if optional { "+" } else { "" }
756 let noun = if required == 1 { "argument" } else { "arguments" }
757 addDiagnostic(
758 state,
759 moduleIndex,
760 "TPZ5004",
761 "this call needs {required}{suffix} {noun}, found {supplied}",
762 "",
763 node,
764 )
765 }
766 }
767 ()
770record NamedCallPlan {
771 slots: Array<int>,
772 filled: Array<bool>,
773 positional: int,
774 sawNamed: bool,
775 sawSpread: bool,
776 overflow: int,
777 leadingSlot: int,
778 invalid: bool,
781function emptyNamedCallPlan(
782 signature: CallSignature,
783 hasLeading: bool,
784) -> NamedCallPlan {
785 let mut filled: Array<bool> = []
786 let mut index = 0
787 while index < signature.parameterCount {
788 filled.push(false)
789 index += 1
790 }
791 let mut positional = 0
792 let mut overflow = 0
793 let mut leadingSlot = -3
794 if hasLeading {
795 if signature.parameterCount > 0 {
796 filled[0] = true
797 positional = 1
798 leadingSlot = 0
799 } else if signature.variadic {
800 leadingSlot = -1
801 } else {
802 overflow = 1
803 leadingSlot = -2
804 }
805 }
806 NamedCallPlan {
807 slots: [],
808 filled: filled,
809 positional: positional,
810 sawNamed: false,
811 sawSpread: false,
812 overflow: overflow,
813 leadingSlot: leadingSlot,
814 invalid: overflow > 0,
815 }
818function planNamedSignatureArgument(
819 state: CheckerState,
820 moduleIndex: int,
821 source: Bytes,
822 signature: CallSignature,
823 plan: NamedCallPlan,
824 argument: SyntaxNode,
825) -> NamedCallPlan {
826 let nameNode = match child(argument, "name") {
827 case Some(found) => found
828 case None => argument
829 }
830 let parameterIndex = callParameterIndex(signature, text(source, nameNode))
831 let available = parameterIndex >= 0 && match plan.filled.get(parameterIndex) {
832 case Some(value) => !value
833 case None => false
834 }
835 let mut slots = plan.slots
836 if available {
837 let mut filled = plan.filled
838 filled[parameterIndex] = true
839 slots.push(parameterIndex)
840 } else {
841 let message = if parameterIndex == 0 && plan.leadingSlot == 0 {
842 "`{text(source, nameNode)}` is already supplied by the pipeline (§11)"
843 } else {
844 "no parameter named `{text(source, nameNode)}`"
845 }
846 addDiagnostic(
847 state,
848 moduleIndex,
849 "TPZ5004",
850 message,
851 "",
852 nameNode,
853 )
854 slots.push(-2)
855 return NamedCallPlan {
856 ...plan,
857 sawNamed: true,
858 invalid: true,
859 }
860 }
861 NamedCallPlan { ...plan, sawNamed: true }
864function hasUnsatisfiedFixedParameter(
865 signature: CallSignature,
866 plan: NamedCallPlan,
867) -> bool {
868 let mut index = plan.positional
869 while index < signature.parameterCount {
870 let filled = match plan.filled.get(index) {
871 case Some(value) => value
872 case None => false
873 }
874 let defaulted = match signature.parameterDefaults.get(index) {
875 case Some(value) => value
876 case None => false
877 }
878 if !filled && !defaulted {
879 return true
880 }
881 index += 1
882 }
883 false
886function planSpreadSignatureArgument(
887 state: CheckerState,
888 moduleIndex: int,
889 signature: CallSignature,
890 plan: NamedCallPlan,
891 argument: SyntaxNode,
892) -> NamedCallPlan {
893 let mut slots = plan.slots
894 let mut invalid = plan.invalid
895 if plan.sawNamed {
896 addDiagnostic(
897 state,
898 moduleIndex,
899 "TPZ5004",
900 "named arguments must follow spread arguments",
901 "",
902 argument,
903 )
904 invalid = true
905 }
906 if !plan.sawSpread && hasUnsatisfiedFixedParameter(signature, plan) {
907 addDiagnostic(
908 state,
909 moduleIndex,
910 "TPZ5004",
911 "a spread argument cannot skip an unsatisfied fixed parameter",
912 "",
913 argument,
914 )
915 invalid = true
916 }
917 slots.push(-1)
918 NamedCallPlan {
919 ...plan,
920 sawSpread: true,
921 invalid: invalid,
922 }
925function stageEarlierCallArgumentForSlot(
926 slots: Array<int>,
927 target: int,
928) -> Array<int> {
929 let mut staged = slots
930 let mut index = 0
931 while index < staged.length {
932 match staged.get(index) {
933 case Some(slot) => if slot == target {
934 staged[index] = -2
935 return staged
936 }
937 case None => ()
938 }
939 index += 1
940 }
941 staged
944function planPositionalSignatureArgument(
945 state: CheckerState,
946 moduleIndex: int,
947 signature: CallSignature,
948 plan: NamedCallPlan,
949 argument: SyntaxNode,
950) -> NamedCallPlan {
951 let mut invalid = plan.invalid
952 if plan.sawNamed {
953 addDiagnostic(
954 state,
955 moduleIndex,
956 "TPZ5004",
957 "positional arguments may not follow named arguments",
958 "",
959 argument,
960 )
961 invalid = true
962 }
963 if plan.sawSpread {
964 let mut slots = plan.slots
965 slots.push(-1)
966 return NamedCallPlan {
967 ...plan,
968 slots: slots,
969 invalid: invalid,
970 }
971 }
972 let mut positional = plan.positional
973 if !plan.sawNamed {
974 while positional < signature.parameterCount {
975 let alreadyFilled = match plan.filled.get(positional) {
976 case Some(value) => value
977 case None => false
978 }
979 if !alreadyFilled { break }
980 positional += 1
981 }
982 }
983 let mut slots = plan.slots
984 if positional < signature.parameterCount {
985 if plan.sawNamed {
986 slots = stageEarlierCallArgumentForSlot(slots, positional)
987 }
988 let mut filled = plan.filled
989 filled[positional] = true
990 slots.push(positional)
991 positional += 1
992 NamedCallPlan {
993 ...plan,
994 positional: positional,
995 invalid: invalid,
996 }
997 } else if signature.variadic {
998 slots.push(-1)
999 NamedCallPlan {
1000 ...plan,

SHA256 e103291bb5fb898b02d082d21fddefdb59abfc52ad3a37c9205f8a821fcd4b66