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_complete.tpz

Topaz · 44856 B · Original file

1// Argument inference and completion of checker call preparations.
2// call_plan selects signatures; this leaf binds actuals and resolves remaining
3// type variables through the shared expression callbacks.
5import types { SyntaxNode }
6import checker_types { TypeValue }
7import checker.model {
8 CallArgumentPlan,
9 CallPreparation,
10 CallResultCandidates,
11 CallSignature,
12 CheckerService,
13 CheckerState,
14 CompletedCall,
15 InferredCallArguments,
16 LeadingCallArgument,
17 TypeBinding,
18 constructor,
19 emptyType,
20 functionType,
21 literal,
22 nominal,
23 rigid,
24 serviceInferExpression,
26import checker.type_algebra {
27 hasHole,
28 transformTypeComponents,
29 typeEqual,
30 typeText,
31 unionType,
32 widen,
34import checker.syntax_index {
35 addDiagnostic,
36 addNode,
37 child,
38 children,
39 containsString,
40 functionDefinitionForReference,
41 replaceNodeType,
42 text,
44import checker.nominals { substitute }
45import checker.protocols_methods {
46 newtypeDefinitionForType,
47 receiverMethodCallSignature,
48 receiverMethodType,
50import checker.members_capabilities {
51 applyBindings,
52 bindingIndex,
53 checkFunctionBounds,
54 collectTypeBindings,
55 comparableType,
56 gateCollectionKey,
57 iterableElement,
58 jsonCapability,
59 memberType,
60 orderCapability,
61 protocolCallResult,
62 rigidOrUnionRigid,
63 rigidProtocolBound,
64 typeConforms,
66import checker.call_facts {
67 addCall,
68 argumentValue,
69 builtinCallTypeParameters,
70 builtinNamespaceObject,
71 contextualBuiltinArgumentType,
72 contextualBuiltinCall,
73 contextualGeneratedLispexCall,
74 contextualStandardWrapperCall,
75 currentExpectedExpression,
76 inferExpressionWithExpected,
77 isBareBindingExpression,
78 calleeReference,
79 refineExpressionToExpected,
80 reportExpectedType,
81 unshadowedIdentifier,
83import checker.expression_core {
84 checkNominalConstructionContext,
85 inferLambda,
86 wrapOptionalResult,
88import checker.call_plan {
89 builtinMemberCallSignature,
90 builtinReceiverCallSignature,
91 callArgumentExpectedType,
92 callSlotExpectedType,
93 declaredCallSignature,
94 explicitCallBindings,
95 inferCallCallee,
96 nominalConstructorCallResult,
97 planSignatureArguments,
98 planTypedCallArguments,
99 unknownCallArgumentPlan,
100 userProtocolCallSignature,
101 userProtocolCallTarget,
104function unsolvedCallExpectation(
105 value: TypeValue,
106 parameterOrigins: Array<string>,
107) -> Option<TypeValue> {
108 if value.kind == "rigid" && (value.origin.startsWith("builtin::") ||
109 containsString(parameterOrigins, value.origin)) {
110 // A generic parameter is a pass-through inference context. An unannotated
111 // callable parameter is an actual unknown expectation and cannot solve a
112 // polymorphic constructor's missing type argument.
113 return Some(TypeValue { ...emptyType("unknown"), origin: "generic-call-expectation" })
114 }
115 None
118export function callArgumentInferenceContext(
119 expected: TypeValue,
120 parameterOrigins: Array<string>,
121) -> TypeValue {
122 transformTypeComponents(
123 expected,
124 (value: TypeValue) => unsolvedCallExpectation(value, parameterOrigins),
125 )
128export function inferContextualCallArgument(
129 service: CheckerService,
130 state: CheckerState,
131 moduleIndex: int,
132 source: Bytes,
133 node: SyntaxNode,
134 expected: Option<TypeValue>,
135) -> TypeValue {
136 if node.kind == "expression/parenthesized" {
137 match child(node, "operand") {
138 case Some(operand) => {
139 let inferred = inferContextualCallArgument(service, state, moduleIndex, source, operand, expected)
140 addNode(state, moduleIndex, "expression", node, inferred)
141 return inferred
142 }
143 case None => ()
144 }
145 }
146 match expected {
147 case Some(context) => {
148 if node.kind == "expression/lambda" && context.kind == "function" {
149 let inferred = inferLambda(service, state, moduleIndex, source, node, Some(context))
150 addNode(state, moduleIndex, "expression", node, inferred)
151 return inferred
152 }
153 if context.kind == "function" {
154 match calleeReference(state, moduleIndex, node) {
155 case Some(reference) => match functionDefinitionForReference(state, reference) {
156 case Some(definition) => if definition.typeParameters.length > 0 {
157 let inferred = serviceInferExpression(service, state, moduleIndex, source, node)
158 let mut bindings: Array<TypeBinding> = []
159 collectTypeBindings(inferred, context, bindings, true)
160 checkFunctionBounds(state, moduleIndex, node, definition, bindings)
161 let specialized = applyBindings(inferred, bindings)
162 replaceNodeType(state, moduleIndex, "expression", node, specialized)
163 return specialized
164 }
165 case None => ()
166 }
167 case None => ()
168 }
169 }
170 inferExpressionWithExpected(service, state, moduleIndex, source, node, context, true)
171 }
172 case None => serviceInferExpression(service, state, moduleIndex, source, node)
173 }
176export function inferCallArguments(
177 service: CheckerService,
178 state: CheckerState,
179 moduleIndex: int,
180 source: Bytes,
181 node: SyntaxNode,
182 callee: SyntaxNode,
183 calleeType: TypeValue,
184 argumentSlots: Array<int>,
185 initialActuals: Array<TypeValue>,
186) -> InferredCallArguments {
187 let mut inferredTypes: Array<TypeValue> = []
188 let mut inferredNodes: Array<SyntaxNode> = []
189 let mut contextualActuals: Array<TypeValue> = []
190 let mut argumentBindings: Array<TypeBinding> = []
191 let definition = match calleeReference(state, moduleIndex, callee) {
192 case Some(reference) => functionDefinitionForReference(state, reference)
193 case None => None
194 }
195 let parameterOrigins = match definition {
196 case Some(found) => found.typeParameterOrigins
197 case None => []
198 }
199 for actual in initialActuals {
200 contextualActuals.push(actual)
201 }
202 while contextualActuals.length < calleeType.parameters.length {
203 contextualActuals.push(emptyType("unknown"))
204 }
205 let arguments = children(node, "arguments")
206 let mut order: Array<int> = []
207 let mut index = 0
208 for argument in arguments {
209 order.push(index)
210 inferredTypes.push(emptyType("unknown"))
211 inferredNodes.push(argumentValue(argument))
212 index += 1
213 }
214 // Type dependencies follow parameter slots. The call's evaluation plan
215 // continues to preserve the source order of argument effects.
216 order.sortBy((position: int) => match argumentSlots.get(position) {
217 case Some(slot) => if slot >= 0 { slot } else { calleeType.parameters.length }
218 case None => calleeType.parameters.length
219 })
220 for position in order {
221 let argument = arguments[position]
222 let valueNode = argumentValue(argument)
223 let slot = match argumentSlots.get(position) {
224 case Some(found) => found
225 case None => -2
226 }
227 let declaredExpected = contextualBuiltinArgumentType(
228 state,
229 moduleIndex,
230 source,
231 callee,
232 slot,
233 contextualActuals,
234 callArgumentExpectedType(calleeType, argument, slot),
235 )
236 // Earlier actuals specialize a later callback before its body is checked.
237 // A still-unsolved builtin scheme variable is an inference hole, not a
238 // rigid requirement imposed on the callback's result.
239 let expected = match declaredExpected {
240 case Some(found) => Some(callArgumentInferenceContext(
241 applyBindings(found, argumentBindings),
242 parameterOrigins))
243 case None => None
244 }
245 let inferred = inferContextualCallArgument(
246 service, state, moduleIndex, source, valueNode, expected)
247 inferredTypes[position] = inferred
248 match declaredExpected {
249 case Some(found) => collectTypeBindings(found, inferred, argumentBindings, true)
250 case None => ()
251 }
252 if slot >= 0 && slot < contextualActuals.length {
253 contextualActuals[slot] = inferred
254 }
255 }
256 InferredCallArguments {
257 types: inferredTypes,
258 nodes: inferredNodes,
259 }
262export function leadingCallActuals(
263 leading: Option<LeadingCallArgument>,
264) -> Array<TypeValue> {
265 match leading {
266 case Some(argument) => [argument.typeValue]
267 case None => []
268 }
271function completeCallActuals(
272 leading: Option<LeadingCallArgument>,
273 explicit: Array<TypeValue>,
274) -> Array<TypeValue> {
275 let mut actuals = leadingCallActuals(leading)
276 for actual in explicit {
277 actuals.push(actual)
278 }
279 actuals
282function collectCallTypeBindings(
283 state: CheckerState,
284 node: SyntaxNode,
285 calleeType: TypeValue,
286 argumentPlan: CallArgumentPlan,
287 actuals: Array<TypeValue>,
288 leading: Option<LeadingCallArgument>,
289 bindings: Array<TypeBinding>,
290) -> () {
291 let mut inferredBindings = bindings
292 match currentExpectedExpression(state, node) {
293 case Some(expectedResult) => match calleeType.result.get(0) {
294 case Some(declaredResult) => collectTypeBindings(
295 declaredResult,
296 expectedResult,
297 inferredBindings,
298 false,
299 )
300 case None => ()
301 }
302 case None => ()
303 }
304 collectCallInputTypeBindings(
305 node,
306 calleeType,
307 argumentPlan,
308 actuals,
309 leading,
310 inferredBindings,
311 )
314function collectCallInputTypeBindings(
315 node: SyntaxNode,
316 calleeType: TypeValue,
317 argumentPlan: CallArgumentPlan,
318 actuals: Array<TypeValue>,
319 leading: Option<LeadingCallArgument>,
320 bindings: Array<TypeBinding>,
321) -> () {
322 let mut inferredBindings = bindings
323 match leading {
324 case Some(argument) => match callSlotExpectedType(
325 calleeType,
326 argumentPlan.leadingSlot,
327 false,
328 ) {
329 case Some(expected) => collectTypeBindings(
330 expected,
331 argument.typeValue,
332 inferredBindings,
333 true,
334 )
335 case None => ()
336 }
337 case None => ()
338 }
339 let mut index = 0
340 for argument in children(node, "arguments") {
341 let slot = match argumentPlan.slots.get(index) {
342 case Some(found) => found
343 case None => -2
344 }
345 match callArgumentExpectedType(calleeType, argument, slot) {
346 case Some(expected) => match actuals.get(index) {
347 case Some(actual) => collectTypeBindings(
348 expected,
349 actual,
350 inferredBindings,
351 true,
352 )
353 case None => ()
354 }
355 case None => ()
356 }
357 index += 1
358 }
359 ()
362function projectedCallMember(
363 source: Bytes,
364 callee: SyntaxNode,
365 receiverType: TypeValue,
366) -> string {
367 if !rigidOrUnionRigid(receiverType) ||
368 (callee.kind != "expression/member" &&
369 callee.kind != "expression/optional-access") {
370 return ""
371 }
372 match child(callee, "field") {
373 case Some(field) => text(source, field)
374 case None => ""
375 }
378function specializeCallCallee(
379 state: CheckerState,
380 moduleIndex: int,
381 source: Bytes,
382 node: SyntaxNode,
383 callee: SyntaxNode,
384 calleeType: TypeValue,
385 actuals: Array<TypeValue>,
386 bindings: Array<TypeBinding>,
387) -> TypeValue {
388 match contextualStandardWrapperCall(
389 state,
390 moduleIndex,
391 node,
392 callee,
393 calleeType,
394 actuals,
395 ) {
396 case Some(found) => found
397 case None => match contextualGeneratedLispexCall(
398 state,
399 moduleIndex,
400 source,
401 node,
402 callee,
403 actuals,
404 ) {
405 case Some(found) => found
406 case None => match contextualBuiltinCall(
407 state,
408 moduleIndex,
409 source,
410 node,
411 callee,
412 calleeType,
413 ) {
414 case Some(found) => found
415 case None => applyBindings(calleeType, bindings)
416 }
417 }
418 }
421function eraseUnboundBuiltinCallTypeParameter(
422 value: TypeValue,
423 parameters: Array<string>,
424 bindings: Array<TypeBinding>,
425) -> Option<TypeValue> {
426 if value.kind == "rigid" && containsString(parameters, value.name) &&
427 bindingIndex(bindings, value.name) < 0 {
428 return Some(emptyType("unknown"))
429 }
430 None
433function eraseUnboundBuiltinCallTypeParameters(
434 service: CheckerService,
435 state: CheckerState,
436 moduleIndex: int,
437 source: Bytes,
438 callee: SyntaxNode,
439 receiverType: TypeValue,
440 bindings: Array<TypeBinding>,
441 specialized: TypeValue,
442) -> TypeValue {
443 let parameters = builtinCallTypeParameters(
444 service,
445 state,
446 moduleIndex,
447 source,
448 callee,
449 receiverType,
450 )
451 if parameters.length == 0 {
452 return specialized
453 }
454 transformTypeComponents(
455 specialized,
456 (component: TypeValue) => eraseUnboundBuiltinCallTypeParameter(
457 component,
458 parameters,
459 bindings,
460 ),
461 )
464function refineCallArguments(
465 state: CheckerState,
466 moduleIndex: int,
467 node: SyntaxNode,
468 calleeType: TypeValue,
469 argumentSlots: Array<int>,
470 skipFirstSlot: bool,
471 actuals: Array<TypeValue>,
472 actualNodes: Array<SyntaxNode>,
473 bindings: Array<TypeBinding>,
474) -> () {
475 let arguments = children(node, "arguments")
476 let mut index = 0
477 while index < actuals.length {
478 let expected = match arguments.get(index) {
479 case Some(argument) => match argumentSlots.get(index) {
480 case Some(slot) => if skipFirstSlot && slot == 0 {
481 None
482 } else {
483 callArgumentExpectedType(calleeType, argument, slot)
484 }
485 case None => None
486 }
487 case None => None
488 }
489 match expected {
490 case Some(expectedType) => match actualNodes.get(index) {
491 case Some(actualNode) => match actuals.get(index) {
492 case Some(actualType) => {
493 let specializedActual = applyBindings(actualType, bindings)
494 if !typeEqual(specializedActual, actualType) {
495 replaceNodeType(
496 state,
497 moduleIndex,
498 "expression",
499 actualNode,
500 specializedActual,
501 )
502 }
503 let refined = refineExpressionToExpected(
504 state,
505 moduleIndex,
506 actualNode,
507 specializedActual,
508 expectedType,
509 )
510 reportExpectedType(
511 state,
512 moduleIndex,
513 actualNode,
514 expectedType,
515 refined,
516 )
517 }
518 case None => ()
519 }
520 case None => ()
521 }
522 case None => ()
523 }
524 index += 1
525 }
526 ()
529function refineLeadingCallArgument(
530 state: CheckerState,
531 moduleIndex: int,
532 calleeType: TypeValue,
533 argumentPlan: CallArgumentPlan,
534 leading: Option<LeadingCallArgument>,
535 diagnosticNode: SyntaxNode,
536) -> () {
537 match leading {
538 case Some(argument) => match callSlotExpectedType(
539 calleeType,
540 argumentPlan.leadingSlot,
541 false,
542 ) {
543 case Some(expected) => {
544 let refined = refineExpressionToExpected(
545 state,
546 moduleIndex,
547 argument.node,
548 argument.typeValue,
549 expected,
550 )
551 reportExpectedType(
552 state,
553 moduleIndex,
554 diagnosticNode,
555 expected,
556 refined,
557 )
558 }
559 case None => ()
560 }
561 case None => ()
562 }
565function callResultType(
566 specializedCallee: TypeValue,
567 calleeType: TypeValue,
568 constructorResult: Option<TypeValue>,
569 protocolResult: Option<TypeValue>,
570 bindings: Array<TypeBinding>,
571) -> TypeValue {
572 match specializedCallee.result.get(0) {
573 case Some(found) => found
574 case None => match constructorResult {
575 case Some(found) => found
576 case None => match protocolResult {
577 case Some(found) => found
578 case None => match calleeType.result.get(0) {
579 case Some(found) => applyBindings(found, bindings)
580 case None => if calleeType.kind == "nominal-record" {
581 calleeType
582 } else {
583 emptyType("unknown")
584 }
585 }
586 }
587 }
588 }
591record BuiltinCallTarget {
592 namespace: string,
593 member: string,
596function builtinCallTarget(
597 service: CheckerService,
598 state: CheckerState,
599 moduleIndex: int,
600 source: Bytes,
601 callee: SyntaxNode,
602) -> BuiltinCallTarget {
603 if callee.kind != "expression/member" &&
604 callee.kind != "expression/optional-access" {
605 return BuiltinCallTarget { namespace: "", member: "" }
606 }
607 let object = match child(callee, "object") {
608 case Some(found) => found
609 case None => return BuiltinCallTarget { namespace: "", member: "" }
610 }
611 BuiltinCallTarget {
612 namespace: if builtinNamespaceObject(
613 service,
614 state,
615 moduleIndex,
616 source,
617 object,
618 ) {
619 text(source, object)
620 } else {
621 ""
622 },
623 member: match child(callee, "field") {
624 case Some(found) => text(source, found)
625 case None => ""
626 },
627 }
630function preparedCallActual(
631 preparation: CallPreparation,
632 inferred: InferredCallArguments,
633 slot: int,
634) -> Option<TypeValue> {
635 match preparation.leading {
636 case Some(argument) => if preparation.argumentPlan.leadingSlot == slot {
637 return Some(argument.typeValue)
638 }
639 case None => ()
640 }
641 let mut index = 0
642 for actual in inferred.types {
643 match preparation.argumentPlan.slots.get(index) {
644 case Some(actualSlot) => if actualSlot == slot {
645 return Some(actual)
646 }
647 case None => ()
648 }
649 index += 1
650 }
651 None
654function preparedCallActualNode(
655 preparation: CallPreparation,
656 inferred: InferredCallArguments,
657 slot: int,
658) -> Option<SyntaxNode> {
659 match preparation.leading {
660 case Some(argument) => if preparation.argumentPlan.leadingSlot == slot {
661 return Some(preparation.diagnosticNode)
662 }
663 case None => ()
664 }
665 let mut index = 0
666 for actualNode in inferred.nodes {
667 match preparation.argumentPlan.slots.get(index) {
668 case Some(actualSlot) => if actualSlot == slot {
669 return Some(actualNode)
670 }
671 case None => ()
672 }
673 index += 1
674 }
675 None
678function userProtocolCallConformance(
679 state: CheckerState,
680 moduleIndex: int,
681 preparation: CallPreparation,
682 inferred: InferredCallArguments,
683) -> string {
684 let target = match preparation.userProtocolTarget {
685 case Some(found) => found
686 case None => return "not-protocol"
687 }
688 match preparedCallActual(preparation, inferred, 0) {
689 case Some(actual) => {
690 let value = widen(actual)
691 if hasHole(value) {
692 "defer"
693 } else if typeConforms(
694 state,
695 moduleIndex,
696 value,
697 target.protocolName,
698 ) {
699 "accept"
700 } else {
701 "reject"
702 }
703 }
704 case None => "defer"
705 }
708function gateUserProtocolCall(
709 state: CheckerState,
710 moduleIndex: int,
711 preparation: CallPreparation,
712 inferred: InferredCallArguments,
713 conformance: string,
714) -> () {
715 let target = match preparation.userProtocolTarget {
716 case Some(found) => found
717 case None => return ()
718 }
719 let actualCount = inferred.types.length + match preparation.leading {
720 case Some(_) => 1
721 case None => 0
722 }
723 if conformance == "accept" ||
724 (conformance == "defer" && actualCount == 0) {
725 let expectedCount = target.method.typeValue.parameters.length
726 if actualCount != expectedCount {
727 addDiagnostic(
728 state,
729 moduleIndex,
730 "TPZ5004",
731 "`{target.protocolName}.{target.method.name}` takes {expectedCount} argument" +
732 if expectedCount == 1 { "" } else { "s" } +
733 ", found {actualCount}",
734 "",
735 preparation.diagnosticNode,
736 )
737 }
738 }
739 if conformance != "reject" {
740 return ()
741 }
742 match preparedCallActual(preparation, inferred, 0) {
743 case Some(actual) => {
744 let value = widen(actual)
745 let nominal = value.kind == "nominal-record" ||
746 value.kind == "enum" || value.kind == "newtype" ||
747 value.kind == "foreign" || value.kind == "rigid"
748 addDiagnostic(
749 state,
750 moduleIndex,
751 "TPZ5522",
752 if nominal {
753 "`{typeText(value)}` does not conform to `{target.protocolName}`"
754 } else {
755 "`{typeText(value)}` does not conform to `{target.protocolName}`: a protocol receiver must be a record/enum/newtype"
756 },
757 "",
758 match preparedCallActualNode(preparation, inferred, 0) {
759 case Some(found) => found
760 case None => preparation.diagnosticNode
761 },
762 )
763 }
764 case None => ()
765 }
766 ()
769function gatePreparedCallCapabilities(
770 service: CheckerService,
771 state: CheckerState,
772 moduleIndex: int,
773 source: Bytes,
774 node: SyntaxNode,
775 preparation: CallPreparation,
776 inferred: InferredCallArguments,
777 bindings: Array<TypeBinding>,
778 rawResult: TypeValue,
779) -> () {
780 let target = builtinCallTarget(
781 service,
782 state,
783 moduleIndex,
784 source,
785 preparation.callee,
786 )
787 let bareCallContext = match currentExpectedExpression(state, node) {
788 case Some(_) => false
789 case None => true
790 }
791 let bareBindingCall = isBareBindingExpression(state, node)
792 let unknownExpectation = match currentExpectedExpression(state, node) {
793 case Some(expected) => expected.kind == "unknown" && expected.origin == "" ||
794 expected.kind == "inference-variable"
795 case None => false
796 }
797 let constructorName = if unshadowedIdentifier(state, moduleIndex, preparation.callee) {
798 text(source, preparation.callee)
799 } else { "" }
800 if (constructorName == "Ok" || constructorName == "Err" || constructorName == "Some") &&
801 (bareBindingCall || unknownExpectation) && hasHole(rawResult) {
802 let mut unknownInput = false
803 for actual in inferred.types { if hasHole(actual) { unknownInput = true } }
804 if !unknownInput {
805 addDiagnostic(state, moduleIndex, "TPZ5020",
806 "this expression needs a contextual type (§22.1): annotate the binding or surrounding expectation",
807 "", preparation.diagnosticNode)
808 }
809 }
810 if bareCallContext && bareBindingCall &&
811 target.namespace == "Map" && target.member == "new" &&
812 hasHole(rawResult) {
813 addDiagnostic(
814 state,
815 moduleIndex,
816 "TPZ5020",
817 "this expression needs a contextual type (§22.1): annotate the binding or surrounding expectation",
818 "",
819 preparation.diagnosticNode,
820 )
821 }
822 if preparation.callee.kind == "expression/identifier" &&
823 unshadowedIdentifier(state, moduleIndex, preparation.callee) &&
824 (text(source, preparation.callee) == "map" ||
825 text(source, preparation.callee) == "filter" ||
826 text(source, preparation.callee) == "reduce") {
827 match preparedCallActual(preparation, inferred, 0) {
828 case Some(iterable) => if iterableElement(iterable).kind == "unknown" &&
829 !hasHole(iterable) {
830 addDiagnostic(
831 state,
832 moduleIndex,
833 "TPZ5001",
834 "`{typeText(iterable)}` is not iterable (§10)",
835 "",
836 match preparedCallActualNode(preparation, inferred, 0) {
837 case Some(actualNode) => actualNode
838 case None => preparation.diagnosticNode
839 },
840 )
841 }
842 case None => ()
843 }
844 if bareCallContext && hasHole(rawResult) {
845 addDiagnostic(
846 state,
847 moduleIndex,
848 "TPZ5020",
849 "this expression needs a contextual type (§22.1): annotate the binding or surrounding expectation",
850 "",
851 preparation.diagnosticNode,
852 )
853 }
854 }
855 if bareCallContext &&
856 ((target.namespace == "Set" && target.member == "of") ||
857 (target.namespace == "Map" &&
858 (target.member == "new" || target.member == "ofEntries"))) &&
859 rawResult.kind == "constructor" {
860 match rawResult.arguments.get(0) {
861 case Some(key) => if !hasHole(key) { gateCollectionKey(
862 service,
863 state,
864 moduleIndex,
865 preparation.diagnosticNode,
866 key,
867 ) }
868 case None => ()
869 }
870 }
871 if target.namespace == "" &&
872 (target.member == "sorted" || target.member == "sort") &&
873 preparation.receiverType.kind == "constructor" &&
874 preparation.receiverType.name == "Array" {
875 match preparation.receiverType.arguments.get(0) {
876 case Some(element) => {
877 let specialized = applyBindings(element, bindings)
878 if orderCapability(
879 state,
880 moduleIndex,
881 specialized,
882 [],
883 ) == "reject" {
884 addDiagnostic(
885 state,
886 moduleIndex,
887 "TPZ5007",
888 "`{typeText(specialized)}` is not ordered comparable, so `.{target.member}()` cannot sort it",
889 "",
890 preparation.diagnosticNode,
891 )
892 }
893 }
894 case None => ()
895 }
896 }
897 if target.namespace == "" &&
898 (target.member == "sortedBy" || target.member == "sortBy") {
899 match preparedCallActual(preparation, inferred, 0) {
900 case Some(callback) => if callback.kind == "function" {
901 match callback.result.get(0) {
902 case Some(result) => {
903 let key = applyBindings(result, bindings)
904 if orderCapability(state, moduleIndex, key, []) == "reject" {
905 addDiagnostic(
906 state,
907 moduleIndex,
908 "TPZ5007",
909 "the `{target.member}` key type `{typeText(key)}` is not ordered comparable, so `.{target.member}()` cannot sort it",
910 "",
911 preparation.diagnosticNode,
912 )
913 }
914 }
915 case None => ()
916 }
917 }
918 case None => ()
919 }
920 }
921 if target.namespace == "JSON" && target.member == "stringify" {
922 match preparedCallActual(preparation, inferred, 0) {
923 case Some(actual) => {
924 let value = applyBindings(actual, bindings)
925 if jsonCapability(state, moduleIndex, value, [], false) == "reject" &&
926 !rigidProtocolBound(state, value, "JSON") {
927 addDiagnostic(
928 state,
929 moduleIndex,
930 "TPZ5533",
931 "`{typeText(value)}` is not JSON-encodable, so `JSON.stringify` cannot encode it",
932 "",
933 preparation.diagnosticNode,
934 )
935 }
936 }
937 case None => ()
938 }
939 }
940 if target.namespace == "JSON" &&
941 (target.member == "parseAs" || target.member == "decode") {
942 let typeArguments = children(node, "typeArguments")
943 if typeArguments.length == 0 {
944 if bareCallContext {
945 addDiagnostic(
946 state,
947 moduleIndex,
948 "TPZ5020",
949 "this expression needs a contextual type (§22.1): annotate the binding or surrounding expectation",
950 "",
951 preparation.diagnosticNode,
952 )
953 }
954 addDiagnostic(
955 state,
956 moduleIndex,
957 "TPZ5534",
958 "`JSON.{target.member}` requires an explicit type argument, e.g. `JSON.{target.member}<User>(...)`",
959 "",
960 preparation.diagnosticNode,
961 )
962 } else if rawResult.kind == "constructor" && rawResult.name == "Result" {
963 match rawResult.arguments.get(0) {
964 case Some(value) => if jsonCapability(
965 state,
966 moduleIndex,
967 value,
968 [],
969 true,
970 ) == "reject" {
971 addDiagnostic(
972 state,
973 moduleIndex,
974 "TPZ5534",
975 "`{typeText(value)}` is not JSON-decodable, so `JSON.{target.member}` cannot decode into it",
976 "",
977 preparation.diagnosticNode,
978 )
979 }
980 case None => ()
981 }
982 }
983 }
984 if target.namespace == "Test" &&
985 (target.member == "assertEq" || target.member == "assertNe") {
986 match preparedCallActual(preparation, inferred, 0) {
987 case Some(value) => {
988 let actual = applyBindings(value, bindings)
989 if !comparableType(state, moduleIndex, actual, []) {
990 addDiagnostic(
991 state,
992 moduleIndex,
993 "TPZ5007",
994 "`{typeText(actual)}` is not comparable, so `Test.{target.member}` cannot compare it",
995 "",
996 preparation.diagnosticNode,
997 )
998 }
999 }
1000 case None => ()

SHA256 0529d01100994e8d987ab7d2021667635be4fba825a8dc8224a6544d6092e052