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

Topaz · 42378 B · Original file

1// Collection and validation of protocols, conformances, and receiver methods.
2// Nominal formation precedes this leaf, and member capability lookup consumes
3// the resulting catalogs afterward.
5import types { SyntaxNode }
6import checker_types { TypeValue }
7import checker.model {
8 CallSignature,
9 CheckerService,
10 CheckerState,
11 EnumDefinition,
12 FunctionDefinition,
13 ModuleSyntax,
14 NewtypeDefinition,
15 ProtocolConformance,
16 ProtocolDefinition,
17 ProtocolMethodDefinition,
18 ReceiverMethodDefinition,
19 RecordDefinition,
20 emptyType,
21 functionType,
22 nominal,
23 nominalInModule,
24 primitive,
25 rigid,
27import checker.type_algebra {
28 typeEqual,
29 typeListText,
30 typeText,
32import checker.syntax_index {
33 addDiagnostic,
34 bindSymbol,
35 boolAttribute,
36 child,
37 children,
38 containsString,
39 declarationFor,
40 definitionNameKey,
41 enumDefinitionIndex,
42 functionBounds,
43 moduleIdentityAt,
44 moduleIndexByIdentity,
45 newtypeDefinitionIndex,
46 protocolDefinition,
47 protocolMethodDefinition,
48 recordDefinitionIndex,
49 referenceFor,
50 registerTypeParameters,
51 resolvedTypeOfReference,
52 spanKey,
53 symbolTypeByName,
54 text,
55 typeParameterNames,
56 unwrapExport,
58import checker.nominals {
59 formType,
60 substitute,
63export function protocolConformanceIndex(
64 state: CheckerState,
65 moduleIndex: int,
66 typeName: string,
67 protocol: string,
68) -> int {
69 match state.conformanceIndexes.get(conformanceKey(
70 moduleIndex,
71 typeName,
72 protocol,
73 )) {
74 case Some(index) => index
75 case None => -1
76 }
79export function appendConformance(
80 state: CheckerState,
81 moduleIndex: int,
82 typeName: string,
83 protocol: string,
84 derived: bool,
85) -> () {
86 let mut conformances = state.conformances
87 let index = conformances.length
88 let mut indexes = state.conformanceIndexes
89 indexes.insert(conformanceKey(moduleIndex, typeName, protocol), index)
90 let mut typeIndexes = state.conformanceTypeIndexes
91 typeIndexes.insert(conformanceTypeKey(typeName, protocol), index)
92 conformances.push(ProtocolConformance {
93 moduleIndex: moduleIndex,
94 typeName: typeName,
95 protocol: protocol,
96 derived: derived,
97 })
98 ()
101function builtinProtocolDefinition(name: string) -> Option<ProtocolDefinition> {
102 let conforming = rigid("Self", "builtin::{name}::protocol")
103 if name == "Show" {
104 return Some(ProtocolDefinition {
105 name: name,
106 methods: [ProtocolMethodDefinition {
107 name: "show",
108 parameterNames: ["value"],
109 typeValue: functionType([conforming], [], primitive("string")),
110 }],
111 })
112 }
113 if name == "Eq" {
114 return Some(ProtocolDefinition {
115 name: name,
116 methods: [ProtocolMethodDefinition {
117 name: "equals",
118 parameterNames: ["a", "b"],
119 typeValue: functionType(
120 [conforming, conforming],
121 [],
122 primitive("bool"),
123 ),
124 }],
125 })
126 }
127 if name == "Order" {
128 return Some(ProtocolDefinition {
129 name: name,
130 methods: [ProtocolMethodDefinition {
131 name: "compare",
132 parameterNames: ["a", "b"],
133 typeValue: functionType(
134 [conforming, conforming],
135 [],
136 primitive("int"),
137 ),
138 }],
139 })
140 }
141 if name == "JSON" {
142 return Some(ProtocolDefinition { name: name, methods: [] })
143 }
144 None
147function protocolDefinitionForImpl(
148 state: CheckerState,
149 moduleIndex: int,
150 name: string,
151) -> Option<ProtocolDefinition> {
152 match builtinProtocolDefinition(name) {
153 case Some(found) => Some(found)
154 case None => protocolDefinition(state, moduleIndex, name)
155 }
158record OwnNominalImplTarget {
159 typeValue: TypeValue,
160 typeParameterCount: int,
163function ownNominalImplTarget(
164 state: CheckerState,
165 moduleIndex: int,
166 name: string,
167) -> Option<OwnNominalImplTarget> {
168 let recordIndex = recordDefinitionIndex(state, moduleIndex, name)
169 if recordIndex >= 0 {
170 return match state.records.get(recordIndex) {
171 case Some(definition) => Some(OwnNominalImplTarget {
172 typeValue: nominalInModule(
173 "nominal-record",
174 name,
175 [],
176 moduleIdentityAt(state, moduleIndex),
177 ),
178 typeParameterCount: definition.typeParameters.length,
179 })
180 case None => None
181 }
182 }
183 let enumIndex = enumDefinitionIndex(state, moduleIndex, name)
184 if enumIndex >= 0 {
185 return match state.enums.get(enumIndex) {
186 case Some(definition) => Some(OwnNominalImplTarget {
187 typeValue: nominalInModule(
188 "enum",
189 name,
190 [],
191 moduleIdentityAt(state, moduleIndex),
192 ),
193 typeParameterCount: definition.typeParameters.length,
194 })
195 case None => None
196 }
197 }
198 let newtypeIndex = newtypeDefinitionIndex(state, moduleIndex, name)
199 if newtypeIndex >= 0 {
200 return match state.newtypes.get(newtypeIndex) {
201 case Some(definition) => Some(OwnNominalImplTarget {
202 typeValue: nominalInModule(
203 "newtype",
204 name,
205 [],
206 moduleIdentityAt(state, moduleIndex),
207 ),
208 typeParameterCount: definition.typeParameters.length,
209 })
210 case None => None
211 }
212 }
213 None
216function predeclareProtocolImplMethod(
217 service: CheckerService,
218 state: CheckerState,
219 module: ModuleSyntax,
220 declaration: SyntaxNode,
221) -> () {
222 let prepared = prepareFunctionDeclaration(
223 service,
224 state,
225 module,
226 declaration,
227 None,
228 )
229 let mut functions = state.functions
230 let index = functions.length
231 functions.push(prepared.definition)
232 let mut pending = state.pendingFunctions
233 pending.push(prepared.definition.result.kind == "unknown")
234 let mut indexes = state.functionIndexes
235 indexes.insert(
236 spanKey(
237 module.moduleIndex,
238 prepared.nameNode.lo,
239 prepared.nameNode.hi,
240 ),
241 index,
242 )
243 ()
246function protocolImplMethodDefinition(
247 state: CheckerState,
248 moduleIndex: int,
249 declaration: SyntaxNode,
250) -> Option<FunctionDefinition> {
251 let nameNode = match child(declaration, "name") {
252 case Some(found) => found
253 case None => return None
254 }
255 match state.functionIndexes.get(spanKey(
256 moduleIndex,
257 nameNode.lo,
258 nameNode.hi,
259 )) {
260 case Some(index) => state.functions.get(index)
261 case None => None
262 }
265function protocolImplExpectedType(
266 method: ProtocolMethodDefinition,
267 receiver: TypeValue,
268) -> TypeValue {
269 substitute(method.typeValue, ["Self"], [receiver])
272function protocolImplSignatureText(value: TypeValue) -> string {
273 "(" + typeListText(value.parameters) + ") -> `" +
274 typeText(match value.result.get(0) {
275 case Some(found) => found
276 case None => emptyType("unknown")
277 }) + "`"
280function registerManualConformance(
281 state: CheckerState,
282 moduleIndex: int,
283 typeName: string,
284 protocol: string,
285) -> () {
286 appendConformance(state, moduleIndex, typeName, protocol, false)
289record ProtocolImplCheckContext {
290 state: CheckerState,
291 module: ModuleSyntax,
292 protocol: string,
293 typeName: string,
294 definition: ProtocolDefinition,
295 receiver: TypeValue,
298function checkProtocolImplMethod(
299 context: ProtocolImplCheckContext,
300 method: SyntaxNode,
301 seen: Set<string>,
302) -> bool {
303 let declaration = match child(method, "declaration") {
304 case Some(found) => found
305 case None => return false
306 }
307 let methodNameNode = match child(declaration, "name") {
308 case Some(found) => found
309 case None => return false
310 }
311 let methodName = text(context.module.source, methodNameNode)
312 match protocolMethodDefinition(
313 context.definition,
314 methodName,
315 ) {
316 case Some(expectedMethod) => checkProtocolImplMethodAgainstDefinition(
317 context,
318 method,
319 seen,
320 declaration,
321 methodNameNode,
322 methodName,
323 expectedMethod,
324 )
325 case None => {
326 addDiagnostic(
327 context.state,
328 context.module.moduleIndex,
329 "TPZ5022",
330 "protocol `{context.protocol}` has no method `{methodName}`",
331 "",
332 methodNameNode,
333 )
334 false
335 }
336 }
339function checkProtocolImplMethodAgainstDefinition(
340 context: ProtocolImplCheckContext,
341 method: SyntaxNode,
342 seen: Set<string>,
343 declaration: SyntaxNode,
344 methodNameNode: SyntaxNode,
345 methodName: string,
346 expectedMethod: ProtocolMethodDefinition,
347) -> bool {
348 if seen.contains(methodName) {
349 addDiagnostic(
350 context.state,
351 context.module.moduleIndex,
352 "TPZ5008",
353 "method `{methodName}` is implemented twice in `impl {context.protocol}<{context.typeName}>`",
354 "",
355 methodNameNode,
356 )
357 return false
358 }
359 let mut acceptedNames = seen
360 acceptedNames.add(methodName)
361 if boolAttribute(method, "exported") {
362 addDiagnostic(
363 context.state,
364 context.module.moduleIndex,
365 "TPZ5022",
366 "protocol implementation method `{context.protocol}.{methodName}` is module-local and cannot be exported",
367 "",
368 method,
369 )
370 return false
371 }
372 if children(declaration, "typeParameters").length > 0 {
373 addDiagnostic(
374 context.state,
375 context.module.moduleIndex,
376 "TPZ5022",
377 "protocol implementation method `{context.protocol}.{methodName}` cannot be generic",
378 "",
379 methodNameNode,
380 )
381 return false
382 }
383 match child(declaration, "returnType") {
384 case Some(_) => ()
385 case None => {
386 addDiagnostic(
387 context.state,
388 context.module.moduleIndex,
389 "TPZ5022",
390 "protocol implementation method `{context.protocol}.{methodName}` requires an explicit return type (use `-> ()` for unit)",
391 "",
392 methodNameNode,
393 )
394 return false
395 }
396 }
397 for parameter in children(declaration, "parameters") {
398 if boolAttribute(parameter, "variadic") {
399 addDiagnostic(
400 context.state,
401 context.module.moduleIndex,
402 "TPZ5022",
403 "protocol implementation method `{context.protocol}.{methodName}` cannot be variadic",
404 "",
405 parameter,
406 )
407 return false
408 }
409 }
410 for parameter in children(declaration, "parameters") {
411 match child(parameter, "default") {
412 case Some(_) => {
413 addDiagnostic(
414 context.state,
415 context.module.moduleIndex,
416 "TPZ5022",
417 "protocol implementation method `{context.protocol}.{methodName}` cannot declare parameter defaults",
418 "",
419 parameter,
420 )
421 return false
422 }
423 case None => ()
424 }
425 }
426 let actualDefinition = match protocolImplMethodDefinition(
427 context.state,
428 context.module.moduleIndex,
429 declaration,
430 ) {
431 case Some(found) => found
432 case None => return false
433 }
434 let actualType = functionType(
435 actualDefinition.parameterTypes,
436 actualDefinition.variadic,
437 actualDefinition.result,
438 )
439 let expectedType = protocolImplExpectedType(
440 expectedMethod,
441 context.receiver,
442 )
443 if typeEqual(actualType, expectedType) {
444 return true
445 }
446 addDiagnostic(
447 context.state,
448 context.module.moduleIndex,
449 "TPZ5022",
450 "protocol implementation method `{context.protocol}.{methodName}` must match the declared signature exactly; expected {protocolImplSignatureText(expectedType)}, found {protocolImplSignatureText(actualType)}",
451 "",
452 methodNameNode,
453 )
454 false
457function collectProtocolImpl(
458 service: CheckerService,
459 state: CheckerState,
460 module: ModuleSyntax,
461 statement: SyntaxNode,
462) -> () {
463 let item = unwrapExport(statement)
464 if item.kind != "statement/impl" {
465 return ()
466 }
467 if statement.kind == "statement/export" {
468 return ()
469 }
470 let targetNode = match child(item, "target") {
471 case Some(found) => found
472 case None => return ()
473 }
474 let protocolNode = match child(item, "name") {
475 case Some(found) => found
476 case None => return ()
477 }
478 for method in children(item, "methods") {
479 match child(method, "declaration") {
480 case Some(declaration) => predeclareProtocolImplMethod(
481 service,
482 state,
483 module,
484 declaration,
485 )
486 case None => ()
487 }
488 }
489 let protocol = text(module.source, protocolNode)
490 let typeName = text(module.source, targetNode)
491 match protocolDefinitionForImpl(
492 state,
493 module.moduleIndex,
494 protocol,
495 ) {
496 case Some(protocolDefinitionValue) => collectProtocolImplForDefinition(
497 state,
498 module,
499 item,
500 targetNode,
501 protocolNode,
502 protocol,
503 typeName,
504 protocolDefinitionValue,
505 )
506 case None => {
507 addDiagnostic(
508 state,
509 module.moduleIndex,
510 "TPZ5022",
511 "unknown protocol `{protocol}` in `impl {protocol}<{typeName}>`",
512 "",
513 protocolNode,
514 )
515 ()
516 }
517 }
520function collectProtocolImplForDefinition(
521 state: CheckerState,
522 module: ModuleSyntax,
523 item: SyntaxNode,
524 targetNode: SyntaxNode,
525 protocolNode: SyntaxNode,
526 protocol: string,
527 typeName: string,
528 protocolDefinitionValue: ProtocolDefinition,
529) -> () {
530 if protocol == "JSON" {
531 addDiagnostic(
532 state,
533 module.moduleIndex,
534 "TPZ5022",
535 "`JSON` conformance is derive-only; use `derives JSON`",
536 "",
537 protocolNode,
538 )
539 return ()
540 }
541 match ownNominalImplTarget(
542 state,
543 module.moduleIndex,
544 typeName,
545 ) {
546 case Some(target) => collectProtocolImplForTarget(
547 state,
548 module,
549 item,
550 targetNode,
551 protocolNode,
552 protocol,
553 typeName,
554 protocolDefinitionValue,
555 target,
556 )
557 case None => {
558 let ownProtocol = match builtinProtocolDefinition(protocol) {
559 case Some(_) => false
560 case None => true
561 }
562 addDiagnostic(
563 state,
564 module.moduleIndex,
565 "TPZ5520",
566 if ownProtocol {
567 "cannot implement `{protocol}` for `{typeName}`: a protocol impl's conforming type must be an own-module record/enum/newtype"
568 } else {
569 "cannot implement foreign protocol `{protocol}` for foreign type `{typeName}` (orphan rule §4.2): the protocol or the type must be defined in this module"
570 },
571 "",
572 targetNode,
573 )
574 ()
575 }
576 }
579function collectProtocolImplForTarget(
580 state: CheckerState,
581 module: ModuleSyntax,
582 item: SyntaxNode,
583 targetNode: SyntaxNode,
584 protocolNode: SyntaxNode,
585 protocol: string,
586 typeName: string,
587 protocolDefinitionValue: ProtocolDefinition,
588 target: OwnNominalImplTarget,
589) -> () {
590 if target.typeParameterCount > 0 {
591 addDiagnostic(
592 state,
593 module.moduleIndex,
594 "TPZ5022",
595 "cannot implement protocol `{protocol}` for generic nominal `{typeName}` yet; generic conformance binders are not defined",
596 "",
597 targetNode,
598 )
599 return ()
600 }
601 let existingIndex = protocolConformanceIndex(
602 state,
603 module.moduleIndex,
604 typeName,
605 protocol,
606 )
607 if existingIndex >= 0 {
608 let how = match state.conformances.get(existingIndex) {
609 case Some(existing) => if existing.derived {
610 "via `derives`"
611 } else {
612 "by a previous `impl`"
613 }
614 case None => "by a previous `impl`"
615 }
616 addDiagnostic(
617 state,
618 module.moduleIndex,
619 "TPZ5521",
620 "`{typeName}` already conforms to `{protocol}` {how}; a conformance must be unique",
621 "",
622 targetNode,
623 )
624 return ()
625 }
626 let mut seen: Set<string> = set {}
627 let mut valid = true
628 let context = ProtocolImplCheckContext {
629 state: state,
630 module: module,
631 protocol: protocol,
632 typeName: typeName,
633 definition: protocolDefinitionValue,
634 receiver: target.typeValue,
635 }
636 for method in children(item, "methods") {
637 if !checkProtocolImplMethod(context, method, seen) {
638 valid = false
639 }
640 }
641 for required in protocolDefinitionValue.methods {
642 if !seen.contains(required.name) {
643 addDiagnostic(
644 state,
645 module.moduleIndex,
646 "TPZ5022",
647 "`impl {protocol}<{typeName}>` is missing method `{required.name}`",
648 "",
649 protocolNode,
650 )
651 valid = false
652 }
653 }
654 if valid {
655 registerManualConformance(
656 state,
657 module.moduleIndex,
658 typeName,
659 protocol,
660 )
661 }
662 ()
665export function collectProtocolImpls(service: CheckerService, state: CheckerState) -> () {
666 for module in state.modules {
667 for statement in module.root.children {
668 collectProtocolImpl(service, state, module, statement)
669 }
670 }
671 ()
674function receiverMethodKey(
675 moduleIndex: int,
676 typeName: string,
677 methodName: string,
678) -> string {
679 definitionNameKey(moduleIndex, "{typeName}.{methodName}")
682export function conformanceTypeKey(typeName: string, protocol: string) -> string {
683 "{protocol.byteLength()}:{protocol}{typeName}"
686function conformanceKey(
687 moduleIndex: int,
688 typeName: string,
689 protocol: string,
690) -> string {
691 definitionNameKey(moduleIndex, conformanceTypeKey(typeName, protocol))
694function builtinReceiverTypeName(name: string) -> bool {
695 match name {
696 case "int" | "float" | "string" | "bool" | "template" | "Array" | "Map" |
697 "Set" | "Option" | "Result" | "JSONValue" | "File" | "Bytes" |
698 "ByteBuffer" | "Path" | "Regex" | "Match" | "TOMLValue" | "URL" |
699 "Date" | "BigInt" | "Decimal" | "RoundingMode" => true
700 case _ => false
701 }
704function reservedReceiverMethodName(name: string) -> bool {
705 match name {
706 case "atLeast" | "atMost" | "scalars" | "startsWith" | "endsWith" |
707 "contains" | "indexOf" | "lastIndexOf" | "trim" | "trimStart" |
708 "trimEnd" | "byteLength" | "codePointAt" | "split" | "slice" |
709 "replace" | "okOr" | "okOrElse" | "map" | "flatMap" | "filter" |
710 "reduce" | "push" | "get" | "length" | "join" | "sorted" |
711 "sortedBy" | "pop" | "clear" | "reverse" | "insert" | "removeAt" |
712 "sort" | "sortBy" | "retain" | "getOr" | "remove" | "keys" |
713 "values" | "entries" | "isEmpty" | "containsKey" | "update" |
714 "mapValues" | "add" | "toArray" | "union" | "intersection" |
715 "difference" | "read" | "write" | "close" | "tag" | "parts" |
716 "kind" | "isNull" | "asString" | "asBool" | "asInt" |
717 "numberText" | "at" | "asArray" | "decodeUtf8" | "toHex" |
718 "toBase64" | "set" | "fill" | "copy" | "toBytes" | "parent" |
719 "fileName" | "extension" | "withExtension" | "normalize" |
720 "toString" | "isMatch" | "find" | "findAll" | "replaceAll" |
721 "start" | "end" | "text" | "groups" | "named" | "scheme" |
722 "host" | "path" | "query" | "fragment" | "toIso" | "addDays" |
723 "year" | "month" | "day" | "toInt" | "div" | "mod" | "scale" |
724 "round" | "value" => true
725 case _ => false
726 }
729function bareSelfParameter(source: Bytes, parameter: SyntaxNode) -> bool {
730 let nameNode = match child(parameter, "name") {
731 case Some(found) => found
732 case None => return false
733 }
734 let implicitType = match child(parameter, "type") {
735 case Some(found) => found.kind == "type/named" &&
736 found.lo == nameNode.lo && found.hi == nameNode.hi
737 case None => false
738 }
739 let noDefault = match child(parameter, "default") {
740 case Some(_) => false
741 case None => true
742 }
743 text(source, nameNode) == "self" && !boolAttribute(parameter, "variadic") && implicitType &&
744 noDefault
747function predeclareReceiverImplMethod(
748 service: CheckerService,
749 state: CheckerState,
750 module: ModuleSyntax,
751 declaration: SyntaxNode,
752 typeName: string,
753 receiver: TypeValue,
754 exported: bool,
755) -> () {
756 let prepared = prepareFunctionDeclaration(
757 service,
758 state,
759 module,
760 declaration,
761 Some(receiver),
762 )
763 let definition = prepared.definition
764 let mut functions = state.functions
765 let functionIndex = functions.length
766 functions.push(definition)
767 let mut pending = state.pendingFunctions
768 pending.push(definition.result.kind == "unknown")
769 let mut functionIndexes = state.functionIndexes
770 functionIndexes.insert(
771 spanKey(module.moduleIndex, prepared.nameNode.lo, prepared.nameNode.hi),
772 functionIndex,
773 )
774 let mut receiverMethods = state.receiverMethods
775 let methodIndex = receiverMethods.length
776 receiverMethods.push(ReceiverMethodDefinition {
777 moduleIndex: module.moduleIndex,
778 typeName: typeName,
779 name: definition.name,
780 functionIndex: functionIndex,
781 exported: exported,
782 })
783 let mut indexes = state.receiverMethodIndexes
784 indexes.insert(
785 receiverMethodKey(module.moduleIndex, typeName, definition.name),
786 methodIndex,
787 )
788 ()
791function collectReceiverImplMethod(
792 service: CheckerService,
793 state: CheckerState,
794 module: ModuleSyntax,
795 method: SyntaxNode,
796 declaration: SyntaxNode,
797 typeName: string,
798 receiver: TypeValue,
799) -> () {
800 let methodNameNode = match child(declaration, "name") {
801 case Some(found) => found
802 case None => declaration
803 }
804 let methodName = text(module.source, methodNameNode)
805 let parameters = children(declaration, "parameters")
806 if parameters.length == 0 {
807 addDiagnostic(
808 state,
809 module.moduleIndex,
810 "TPZ5022",
811 "method `{methodName}` on `{typeName}` must take `self` as its first parameter",
812 "",
813 methodNameNode,
814 )
815 return ()
816 }
817 let first = parameters[0]
818 if !bareSelfParameter(module.source, first) {
819 addDiagnostic(
820 state,
821 module.moduleIndex,
822 "TPZ5022",
823 "method `{methodName}` on `{typeName}` must take bare `self` as its first parameter (no type annotation, default, or variadic marker)",
824 "",
825 first,
826 )
827 return ()
828 }
829 if children(declaration, "typeParameters").length > 0 {
830 addDiagnostic(
831 state,
832 module.moduleIndex,
833 "TPZ5022",
834 "generic methods are not supported yet (method `{methodName}`)",
835 "",
836 methodNameNode,
837 )
838 return ()
839 }
840 if reservedReceiverMethodName(methodName) {
841 addDiagnostic(
842 state,
843 module.moduleIndex,
844 "TPZ5022",
845 "method name `{methodName}` on `{typeName}` collides with a builtin member; choose another name",
846 "",
847 methodNameNode,
848 )
849 return ()
850 }
851 let key = receiverMethodKey(module.moduleIndex, typeName, methodName)
852 match state.receiverMethodIndexes.get(key) {
853 case Some(_) => {
854 addDiagnostic(
855 state,
856 module.moduleIndex,
857 "TPZ5008",
858 "method `{methodName}` is already defined for `{typeName}`",
859 "",
860 methodNameNode,
861 )
862 return ()
863 }
864 case None => ()
865 }
866 let recordIndex = recordDefinitionIndex(
867 state,
868 module.moduleIndex,
869 typeName,
870 )
871 let mut fieldCollision = false
872 match state.records.get(recordIndex) {
873 case Some(definition) => for field in definition.fields {
874 if field.name == methodName {
875 fieldCollision = true
876 }
877 }
878 case None => ()
879 }
880 if fieldCollision {
881 addDiagnostic(
882 state,
883 module.moduleIndex,
884 "TPZ5022",
885 "method `{methodName}` on `{typeName}` collides with a field of the same name; rename the method (a field shadows a method)",
886 "",
887 methodNameNode,
888 )
889 return ()
890 }
891 predeclareReceiverImplMethod(
892 service,
893 state,
894 module,
895 declaration,
896 typeName,
897 receiver,
898 boolAttribute(method, "exported"),
899 )
900 ()
903function collectReceiverImpl(
904 service: CheckerService,
905 state: CheckerState,
906 module: ModuleSyntax,
907 statement: SyntaxNode,
908) -> () {
909 let item = unwrapExport(statement)
910 if item.kind != "statement/impl" {
911 return ()
912 }
913 if statement.kind == "statement/export" {
914 let nameNode = match child(item, "name") {
915 case Some(found) => found
916 case None => item
917 }
918 addDiagnostic(
919 state,
920 module.moduleIndex,
921 "TPZ5022",
922 "an `impl` block cannot be exported; mark individual methods with `export`",
923 "",
924 nameNode,
925 )
926 return ()
927 }
928 match child(item, "target") {
929 case Some(_) => return ()
930 case None => ()
931 }
932 let nameNode = match child(item, "name") {
933 case Some(found) => found
934 case None => return ()
935 }
936 let typeName = text(module.source, nameNode)
937 match ownNominalImplTarget(
938 state,
939 module.moduleIndex,
940 typeName,
941 ) {
942 case Some(target) => collectReceiverImplForTarget(
943 service,
944 state,
945 module,
946 item,
947 nameNode,
948 typeName,
949 target,
950 )
951 case None => {
952 let what = if builtinReceiverTypeName(typeName) {
953 "a builtin type"
954 } else {
955 "not a declared record/enum/newtype in this module"
956 }
957 addDiagnostic(
958 state,
959 module.moduleIndex,
960 "TPZ5022",
961 "cannot define methods on `{typeName}` ({what}); `impl` is only allowed on an own-module record, enum, or newtype",
962 "",
963 nameNode,
964 )
965 ()
966 }
967 }
970function collectReceiverImplForTarget(
971 service: CheckerService,
972 state: CheckerState,
973 module: ModuleSyntax,
974 item: SyntaxNode,
975 nameNode: SyntaxNode,
976 typeName: string,
977 target: OwnNominalImplTarget,
978) -> () {
979 if target.typeParameterCount > 0 {
980 addDiagnostic(
981 state,
982 module.moduleIndex,
983 "TPZ5022",
984 "cannot define receiver methods on generic nominal `{typeName}` yet; generic `impl` binders are not defined",
985 "",
986 nameNode,
987 )
988 return ()
989 }
990 for method in children(item, "methods") {
991 match child(method, "declaration") {
992 case Some(declaration) => collectReceiverImplMethod(
993 service,
994 state,
995 module,
996 method,
997 declaration,
998 typeName,
999 target.typeValue,

SHA256 87ce2af7c9930041fd20c6358070aa5ed15a72e74cfc3c8a66ebdf28392d7329