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

Topaz · 29606 B · Original file

1// Statement checking for bindings, assignment, effects, and control transfer.
2// Block sequencing is owned here; expression inference returns through the
3// checker service so imports remain one-way.
5import types {
6 PreviewAstAttribute,
7 SyntaxNode,
8}
9import checker_types { TypeValue }
10import checker.model {
11 CheckerService,
12 CheckerRequest,
13 CheckerState,
14 LoopFrame,
15 MAX_CONSTANT_EXPONENT,
16 MINIMUM_INT_DECIMAL,
17 ModuleSyntax,
18 constructor,
19 emptyType,
20 functionType,
21 inference,
22 primitive,
24import checker.type_algebra {
25 assignable,
26 hasHole,
27 typeText,
28 widen,
30import checker.syntax_index {
31 addDiagnostic,
32 addNode,
33 attribute,
34 bindSymbol,
35 boolAttribute,
36 child,
37 children,
38 functionIndexForReference,
39 referenceFor,
40 replaceNodeType,
41 spanKey,
42 text,
43 unwrapExport,
45import checker.nominals {
46 formType,
47 substitute,
48 targetType,
50import checker.patterns {
51 bindNamedPatternType,
52 patternBindingType,
53 patternIsIrrefutable,
55import checker.protocols_methods { predeclareBlockFunctions }
56import checker.call_facts {
57 checkExpressionExpected,
58 checkReturn,
59 inferExpressionContext,
60 inferExpressionWithExpected,
61 refineExpressionToExpected,
62 reportExpectedType,
64import checker.expression_core {
65 hasBranchJoinContext,
66 checkAssignmentRootMutable,
67 expressionTerminates,
68 hasLiteralJoinContext,
69 inferLambda,
70 optionalReceiver,
72import checker.expression_control {
73 checkLoopExitStatement,
74 inferBinaryExpression,
75 inferExpression,
78function bindingAnnotationType(
79 service: CheckerService,
80 state: CheckerState,
81 moduleIndex: int,
82 source: Bytes,
83 item: SyntaxNode,
84) -> Option<TypeValue> {
85 match child(item, "type") {
86 case Some(found) => return Some(formType(
87 service,
88 state,
89 moduleIndex,
90 source,
91 found,
92 [],
93 "binding",
94 ))
95 case None => ()
96 }
97 match child(item, "pattern") {
98 case Some(pattern) => if pattern.kind == "pattern/typed" {
99 match child(pattern, "type") {
100 case Some(found) => return Some(formType(
101 service,
102 state,
103 moduleIndex,
104 source,
105 found,
106 [],
107 "binding",
108 ))
109 case None => ()
110 }
111 }
112 case None => ()
113 }
114 None
117function inferBindingValue(
118 service: CheckerService,
119 state: CheckerState,
120 moduleIndex: int,
121 source: Bytes,
122 item: SyntaxNode,
123 annotation: Option<TypeValue>,
124) -> TypeValue {
125 match child(item, "value") {
126 case Some(value) => match annotation {
127 case Some(expected) => if value.kind == "expression/lambda" &&
128 expected.kind == "function" {
129 let inferred = inferLambda(
130 service,
131 state,
132 moduleIndex,
133 source,
134 value,
135 Some(expected),
136 )
137 addNode(state, moduleIndex, "expression", value, inferred)
138 inferred
139 } else {
140 inferExpressionWithExpected(
141 service,
142 state,
143 moduleIndex,
144 source,
145 value,
146 expected,
147 true,
148 )
149 }
150 case None => {
151 // Like Stage 0's at_bare_binding, the reporting site is the
152 // initializer itself. Parentheses and block tails preserve it;
153 // receivers, arguments, elements and loop iterables do not.
154 let mut contexts = state.bareBindingExpressions
155 contexts.push(bareBindingExpression(value))
156 let inferred = inferExpression(service, state, moduleIndex, source, value)
157 contexts.pop()
158 inferred
159 }
160 }
161 case None => emptyType("unknown")
162 }
165function bareBindingExpression(value: SyntaxNode) -> SyntaxNode {
166 if value.kind == "expression/parenthesized" {
167 match child(value, "operand") {
168 case Some(inner) => return bareBindingExpression(inner)
169 case None => ()
170 }
171 }
172 if value.kind == "expression/block" {
173 match child(value, "block") {
174 case Some(block) => match child(block, "tail") {
175 case Some(tail) => return bareBindingExpression(tail)
176 case None => ()
177 }
178 case None => ()
179 }
180 }
181 value
184function bindCheckedDeclaration(
185 service: CheckerService,
186 state: CheckerState,
187 moduleIndex: int,
188 source: Bytes,
189 item: SyntaxNode,
190 expected: TypeValue,
191) -> () {
192 if item.kind == "statement/let" {
193 match child(item, "pattern") {
194 case Some(pattern) => {
195 if pattern.kind == "pattern/typed" {
196 bindNamedPatternType(
197 state,
198 moduleIndex,
199 source,
200 pattern,
201 expected,
202 expected,
203 boolAttribute(item, "mutable"),
204 )
205 } else {
206 let coverage = patternBindingType(
207 service,
208 state,
209 moduleIndex,
210 source,
211 pattern,
212 expected,
213 boolAttribute(item, "mutable"),
214 )
215 if pattern.kind != "pattern/binding" &&
216 !patternIsIrrefutable(state, moduleIndex, expected, coverage) {
217 addDiagnostic(
218 state,
219 moduleIndex,
220 "TPZ5026",
221 "refutable pattern in `let`: it does not match every value of the type — use `if let` to handle the non-matching case",
222 "",
223 pattern,
224 )
225 }
226 }
227 }
228 case None => ()
229 }
230 return ()
231 }
232 let nameNode = match child(item, "name") {
233 case Some(found) => found
234 case None => item
235 }
236 bindSymbol(
237 state,
238 moduleIndex,
239 nameNode.lo,
240 nameNode.hi,
241 text(source, nameNode),
242 expected,
243 false,
244 )
245 addNode(state, moduleIndex, "binding", nameNode, expected)
248function aliasReferenceNode(node: SyntaxNode) -> Option<SyntaxNode> {
249 if node.kind == "expression/identifier" {
250 return Some(node)
251 }
252 if node.kind == "expression/parenthesized" {
253 return match child(node, "operand") {
254 case Some(found) => aliasReferenceNode(found)
255 case None => None
256 }
257 }
258 if node.kind == "expression/block" {
259 return match child(node, "block") {
260 case Some(block) => if children(block, "statements").length == 0 {
261 match child(block, "tail") {
262 case Some(found) => aliasReferenceNode(found)
263 case None => None
264 }
265 } else {
266 None
267 }
268 case None => None
269 }
270 }
271 None
274function bindingDeclarationNode(item: SyntaxNode) -> Option<SyntaxNode> {
275 if item.kind == "statement/const" {
276 return child(item, "name")
277 }
278 match child(item, "pattern") {
279 case Some(pattern) => if pattern.kind == "pattern/binding" ||
280 pattern.kind == "pattern/typed" {
281 child(pattern, "name")
282 } else {
283 None
284 }
285 case None => None
286 }
289function recordFunctionAlias(
290 state: CheckerState,
291 moduleIndex: int,
292 item: SyntaxNode,
293) -> () {
294 let declaration = match bindingDeclarationNode(item) {
295 case Some(found) => found
296 case None => return ()
297 }
298 let value = match child(item, "value") {
299 case Some(found) => found
300 case None => return ()
301 }
302 let referenceNode = match aliasReferenceNode(value) {
303 case Some(found) => found
304 case None => return ()
305 }
306 match referenceFor(
307 state,
308 moduleIndex,
309 referenceNode.lo,
310 referenceNode.hi,
311 ) {
312 case Some(reference) => {
313 let functionIndex = functionIndexForReference(state, reference)
314 if functionIndex >= 0 {
315 let mut aliases = state.functionAliases
316 aliases.insert(
317 spanKey(
318 moduleIndex,
319 declaration.lo,
320 declaration.hi,
321 ),
322 functionIndex,
323 )
324 }
325 }
326 case None => ()
327 }
328 ()
331function functionAliasSemanticType(
332 state: CheckerState,
333 moduleIndex: int,
334 item: SyntaxNode,
335) -> Option<TypeValue> {
336 let declaration = match bindingDeclarationNode(item) {
337 case Some(found) => found
338 case None => return None
339 }
340 let functionIndex = match state.functionAliases.get(spanKey(
341 moduleIndex,
342 declaration.lo,
343 declaration.hi,
344 )) {
345 case Some(found) => found
346 case None => return None
347 }
348 let definition = match state.functions.get(functionIndex) {
349 case Some(found) => found
350 case None => return None
351 }
352 if definition.typeParameters.length == 0 {
353 return None
354 }
355 let mut arguments: Array<TypeValue> = []
356 let mut index = 0
357 for _ in definition.typeParameters {
358 arguments.push(inference(index))
359 index += 1
360 }
361 Some(substitute(
362 functionType(
363 definition.parameterTypes,
364 definition.variadic,
365 definition.result,
366 ),
367 definition.typeParameters,
368 arguments,
369 ))
372function checkBindingStatement(
373 service: CheckerService,
374 state: CheckerState,
375 moduleIndex: int,
376 source: Bytes,
377 item: SyntaxNode,
378) -> () {
379 let annotation = bindingAnnotationType(service, state, moduleIndex, source, item)
380 let mut value = inferBindingValue(
381 service,
382 state,
383 moduleIndex,
384 source,
385 item,
386 annotation,
387 )
388 let expected = match annotation {
389 case Some(found) => found
390 case None => if item.kind == "statement/const" { value } else { widen(value) }
391 }
392 match annotation {
393 case Some(_) => ()
394 case None => match child(item, "value") {
395 case Some(found) => if found.kind == "expression/comprehension" &&
396 hasHole(value) {
397 addDiagnostic(
398 state,
399 moduleIndex,
400 "TPZ5612",
401 "this comprehension's element type cannot be inferred (§6.4): annotate the binding with its collection type",
402 "",
403 found,
404 )
405 }
406 case None => ()
407 }
408 }
409 match child(item, "value") {
410 case Some(found) => {
411 value = refineExpressionToExpected(
412 state,
413 moduleIndex,
414 found,
415 value,
416 expected,
417 )
418 }
419 case None => ()
420 }
421 if !assignable(expected, value) {
422 match child(item, "value") {
423 case Some(found) => addDiagnostic(
424 state,
425 moduleIndex,
426 "TPZ5001",
427 "expected `{typeText(expected)}`, found `{typeText(widen(value))}`",
428 "",
429 found,
430 )
431 case None => ()
432 }
433 }
434 if item.kind == "statement/const" {
435 match child(item, "value") {
436 case Some(found) => { let _ = foldConstantInteger(
437 state,
438 moduleIndex,
439 found,
440 ) }
441 case None => ()
442 }
443 }
444 bindCheckedDeclaration(service, state, moduleIndex, source, item, expected)
445 recordFunctionAlias(state, moduleIndex, item)
446 match annotation {
447 case None => match functionAliasSemanticType(state, moduleIndex, item) {
448 case Some(projected) => match bindingDeclarationNode(item) {
449 case Some(declaration) => replaceNodeType(
450 state,
451 moduleIndex,
452 "binding",
453 declaration,
454 projected,
455 )
456 case None => ()
457 }
458 case None => ()
459 }
460 case Some(_) => ()
461 }
462 ()
465function checkUsingStatement(
466 service: CheckerService,
467 state: CheckerState,
468 moduleIndex: int,
469 source: Bytes,
470 item: SyntaxNode,
471) -> () {
472 match child(item, "value") {
473 case Some(found) => {
474 let _ = checkExpressionExpected(
475 service,
476 state,
477 moduleIndex,
478 source,
479 found,
480 emptyType("file"),
481 )
482 }
483 case None => ()
484 }
485 match child(item, "name") {
486 case Some(name) => {
487 bindSymbol(
488 state,
489 moduleIndex,
490 name.lo,
491 name.hi,
492 text(source, name),
493 emptyType("file"),
494 false,
495 )
496 addNode(state, moduleIndex, "binding", name, emptyType("file"))
497 }
498 case None => ()
499 }
500 match child(item, "body") {
501 case Some(body) => {
502 let _ = checkBlock(service, state, moduleIndex, source, body)
503 }
504 case None => ()
505 }
506 ()
509function checkEffectStatement(
510 service: CheckerService,
511 state: CheckerState,
512 moduleIndex: int,
513 source: Bytes,
514 item: SyntaxNode,
515) -> () {
516 let expression = if item.kind == "statement/defer" {
517 child(item, "value")
518 } else {
519 item.children.get(0)
520 }
521 match expression {
522 case Some(found) => if found.kind == "expression/for" {
523 let mut contexts = state.statementExpressionContexts
524 contexts.push(found.lo)
525 let _ = inferExpression(service, state, moduleIndex, source, found)
526 contexts.pop()
527 } else {
528 let _ = inferExpression(service, state, moduleIndex, source, found)
529 }
530 case None => ()
531 }
532 ()
535function assignmentTargetHasOptional(node: SyntaxNode) -> bool {
536 if node.kind == "expression/optional-access" {
537 return true
538 }
539 match child(node, "object") {
540 case Some(object) => assignmentTargetHasOptional(object)
541 case None => false
542 }
545function lastAssignmentIndex(node: SyntaxNode) -> Option<SyntaxNode> {
546 if node.kind == "expression/member" {
547 return match child(node, "object") {
548 case Some(object) => lastAssignmentIndex(object)
549 case None => None
550 }
551 }
552 if node.kind == "expression/index" {
553 return Some(node)
554 }
555 None
558function reportMapAssignmentTarget(
559 service: CheckerService,
560 state: CheckerState,
561 moduleIndex: int,
562 source: Bytes,
563 target: SyntaxNode,
564) -> () {
565 match lastAssignmentIndex(target) {
566 case Some(index) => match child(index, "object") {
567 case Some(object) => {
568 let objectType = inferExpression(service, state, moduleIndex, source, object)
569 if objectType.kind == "constructor" && objectType.name == "Map" {
570 addDiagnostic(
571 state,
572 moduleIndex,
573 "TPZ5001",
574 "a Map is not index-assignable; use `m.insert` (§22)",
575 "",
576 target,
577 )
578 }
579 }
580 case None => ()
581 }
582 case None => ()
583 }
584 ()
587function assignmentBinaryNode(
588 item: SyntaxNode,
589 target: SyntaxNode,
590 value: SyntaxNode,
591) -> SyntaxNode {
592 SyntaxNode {
593 kind: "expression/binary",
594 lo: item.lo,
595 hi: item.hi,
596 field: "",
597 index: 0,
598 attributes: [PreviewAstAttribute {
599 name: "operator",
600 kind: "string",
601 value: attribute(item, "operator"),
602 }],
603 children: [
604 SyntaxNode { ...target, field: "left", index: 0 },
605 SyntaxNode { ...value, field: "right", index: 1 },
606 ],
607 }
610function checkAssignmentStatement(
611 service: CheckerService,
612 state: CheckerState,
613 moduleIndex: int,
614 source: Bytes,
615 item: SyntaxNode,
616) -> () {
617 let target = match child(item, "target") {
618 case Some(found) => found
619 case None => return ()
620 }
621 let value = match child(item, "value") {
622 case Some(found) => found
623 case None => return ()
624 }
625 checkAssignmentRootMutable(state, moduleIndex, source, target, false)
626 if assignmentTargetHasOptional(target) {
627 addDiagnostic(
628 state,
629 moduleIndex,
630 "TPZ5001",
631 "cannot assign through optional access `?.` (§4)",
632 "",
633 target,
634 )
635 }
636 reportMapAssignmentTarget(service, state, moduleIndex, source, target)
637 let operator = attribute(item, "operator")
638 if operator == "assign" {
639 let targetType = inferExpression(service, state, moduleIndex, source, target)
640 let _ = checkExpressionExpected(
641 service,
642 state,
643 moduleIndex,
644 source,
645 value,
646 targetType,
647 )
648 return ()
649 }
650 if operator == "coalesce" {
651 let targetType = inferExpression(service, state, moduleIndex, source, target)
652 match optionalReceiver(targetType) {
653 case Some(_) => {
654 let _ = checkExpressionExpected(
655 service,
656 state,
657 moduleIndex,
658 source,
659 value,
660 targetType,
661 )
662 }
663 case None => {
664 if !hasHole(targetType) {
665 addDiagnostic(
666 state,
667 moduleIndex,
668 "TPZ5001",
669 "`??=` needs an Option or nullable target, found `{typeText(targetType)}`",
670 "",
671 target,
672 )
673 }
674 let _ = inferExpression(service, state, moduleIndex, source, value)
675 }
676 }
677 return ()
678 }
679 let targetType = inferExpression(service, state, moduleIndex, source, target)
680 let result = inferBinaryExpression(
681 service,
682 state,
683 moduleIndex,
684 source,
685 assignmentBinaryNode(item, target, value),
686 )
687 reportExpectedType(
688 state,
689 moduleIndex,
690 item,
691 widen(targetType),
692 result,
693 )
694 ()
697function checkWhileStatement(
698 service: CheckerService,
699 state: CheckerState,
700 moduleIndex: int,
701 source: Bytes,
702 item: SyntaxNode,
703) -> () {
704 match child(item, "condition") {
705 case Some(found) => {
706 let _ = checkExpressionExpected(
707 service,
708 state,
709 moduleIndex,
710 source,
711 found,
712 primitive("bool"),
713 )
714 }
715 case None => ()
716 }
717 let mut frames = state.loopFrames
718 frames.push(LoopFrame {
719 label: "",
720 valueLoop: false,
721 bareTarget: true,
722 bareError: "",
723 expected: None,
724 breaks: [],
725 })
726 match child(item, "body") {
727 case Some(found) => {
728 let _ = checkBlock(service, state, moduleIndex, source, found)
729 }
730 case None => ()
731 }
732 frames.pop()
733 ()
736function checkModuleOnlyDeclaration(
737 state: CheckerState,
738 moduleIndex: int,
739 statement: SyntaxNode,
740 kind: string,
741) -> () {
742 match child(statement, "name") {
743 case Some(name) => addDiagnostic(
744 state,
745 moduleIndex,
746 "TPZ5022",
747 "{kind} declarations are module-top-level only",
748 "",
749 name,
750 )
751 case None => ()
752 }
753 ()
756export function checkStatement(
757 service: CheckerService,
758 state: CheckerState,
759 moduleIndex: int,
760 source: Bytes,
761 statement: SyntaxNode,
762) -> () {
763 let item = unwrapExport(statement)
764 match item.kind {
765 case "statement/function" => match child(item, "declaration") {
766 case Some(declaration) => {
767 let _ = service(state, CheckerRequest {
768 kind: "check-function",
769 moduleIndex: moduleIndex,
770 source: source,
771 node: declaration,
772 typeValue: emptyType("unknown"),
773 })
774 }
775 case None => ()
776 }
777 case "statement/let" | "statement/const" =>
778 checkBindingStatement(service, state, moduleIndex, source, item)
779 case "statement/using" => checkUsingStatement(
780 service,
781 state,
782 moduleIndex,
783 source,
784 item,
785 )
786 case "statement/return" =>
787 checkReturn(service, state, moduleIndex, source, item)
788 case "statement/break" | "statement/continue" =>
789 checkLoopExitStatement(service, state, moduleIndex, source, item)
790 case "statement/expression" | "statement/defer" =>
791 checkEffectStatement(service, state, moduleIndex, source, item)
792 case "statement/assign" =>
793 checkAssignmentStatement(service, state, moduleIndex, source, item)
794 case "statement/while" => checkWhileStatement(
795 service,
796 state,
797 moduleIndex,
798 source,
799 item,
800 )
801 case "statement/protocol" => checkModuleOnlyDeclaration(state, moduleIndex, item, "protocol")
802 case "statement/impl" => checkModuleOnlyDeclaration(state, moduleIndex, item, "impl")
803 case "statement/record" => checkModuleOnlyDeclaration(state, moduleIndex, item, "record")
804 case "statement/enum" => checkModuleOnlyDeclaration(state, moduleIndex, item, "enum")
805 case "statement/newtype" => checkModuleOnlyDeclaration(state, moduleIndex, item, "newtype")
806 case _ => ()
807 }
810export function checkBlock(
811 service: CheckerService,
812 state: CheckerState,
813 moduleIndex: int,
814 source: Bytes,
815 block: SyntaxNode,
816) -> TypeValue {
817 let inheritedContext = hasLiteralJoinContext(state, block)
818 let inheritedJoin = hasBranchJoinContext(state, block)
819 if inheritedContext {
820 let mut contexts = state.literalJoinContexts
821 contexts.pop()
822 }
823 predeclareBlockFunctions(service, state, moduleIndex, block)
824 for statement in children(block, "statements") {
825 checkStatement(service, state, moduleIndex, source, statement)
826 }
827 let result = match child(block, "tail") {
828 case Some(found) => {
829 let mut joins = state.branchJoinContexts
830 if inheritedJoin { joins.push(found.lo) }
831 let inferred = if inheritedContext {
832 inferExpressionContext(service, state, moduleIndex, source, found)
833 } else {
834 inferExpression(service, state, moduleIndex, source, found)
835 }
836 if inheritedJoin { joins.pop() }
837 inferred
838 }
839 case None => primitive("unit")
840 }
841 if inheritedContext {
842 let mut contexts = state.literalJoinContexts
843 contexts.push(block.lo)
844 }
845 result
848export function checkBlockExpected(
849 service: CheckerService,
850 state: CheckerState,
851 moduleIndex: int,
852 source: Bytes,
853 block: SyntaxNode,
854 expectedReturn: TypeValue,
855) -> TypeValue {
856 predeclareBlockFunctions(service, state, moduleIndex, block)
857 for statement in children(block, "statements") {
858 checkStatement(service, state, moduleIndex, source, statement)
859 }
860 match child(block, "tail") {
861 case Some(found) => {
862 let inferred = inferExpressionWithExpected(
863 service,
864 state,
865 moduleIndex,
866 source,
867 found,
868 expectedReturn,
869 true,
870 )
871 let refined = refineExpressionToExpected(
872 state,
873 moduleIndex,
874 found,
875 inferred,
876 expectedReturn,
877 )
878 if !expressionTerminates(found) {
879 reportExpectedType(
880 state,
881 moduleIndex,
882 found,
883 expectedReturn,
884 refined,
885 )
886 }
887 refined
888 }
889 case None => primitive("unit")
890 }
893export function precheckTopLevelBindings(
894 service: CheckerService,
895 state: CheckerState,
896 module: ModuleSyntax,
897) -> () {
898 for statement in module.root.children {
899 let item = unwrapExport(statement)
900 if item.kind == "statement/const" {
901 checkStatement(
902 service,
903 state,
904 module.moduleIndex,
905 module.source,
906 statement,
907 )
908 } else if item.kind == "statement/let" {
909 let mut suppressions = state.typeCapabilitySuppressions
910 suppressions.push(true)
911 match child(item, "pattern") {
912 case Some(pattern) => {
913 let mut expectedValues: Array<TypeValue> = []
914 match child(item, "type") {
915 case Some(typeNode) => expectedValues.push(formType(
916 service,
917 state,
918 module.moduleIndex,
919 module.source,
920 typeNode,
921 [],
922 "binding",
923 ))
924 case None => if pattern.kind == "pattern/typed" {
925 match child(pattern, "type") {
926 case Some(typeNode) => expectedValues.push(formType(
927 service,
928 state,
929 module.moduleIndex,
930 module.source,
931 typeNode,
932 [],
933 "binding",
934 ))
935 case None => ()
936 }
937 } else {
938 ()
939 }
940 }
941 match expectedValues.get(0) {
942 case Some(expected) => patternBindingType(
943 service,
944 state,
945 module.moduleIndex,
946 module.source,
947 pattern,
948 expected,
949 boolAttribute(item, "mutable"),
950 )
951 case None => ()
952 }
953 }
954 case None => ()
955 }
956 suppressions.pop()
957 }
958 }
959 ()
962export function constantExpressionShape(node: SyntaxNode) -> bool {
963 if node.kind == "expression/integer" ||
964 node.kind == "expression/float" ||
965 node.kind == "expression/boolean" ||
966 node.kind == "expression/null" ||
967 node.kind == "expression/unit" ||
968 node.kind == "expression/identifier" {
969 return true
970 }
971 if node.kind == "expression/string" {
972 for part in node.children {
973 if part.kind != "string-part/text" {
974 return false
975 }
976 }
977 return true
978 }
979 if node.kind == "expression/parenthesized" ||
980 node.kind == "expression/unary" {
981 return match child(node, "operand") {
982 case Some(found) => constantExpressionShape(found)
983 case None => false
984 }
985 }
986 if node.kind != "expression/binary" {
987 return false
988 }
989 let operator = attribute(node, "operator")
990 if operator == "and" || operator == "or" || operator == "coalesce" {
991 return false
992 }
993 match child(node, "left") {
994 case Some(left) => match child(node, "right") {
995 case Some(right) => constantExpressionShape(left) &&
996 constantExpressionShape(right)
997 case None => false
998 }
999 case None => false
1003function minimumInt() -> int {
1004 match toInt(MINIMUM_INT_DECIMAL) {
1005 case Some(found) => found
1006 case None => 0
1010function constantExpressionFault(
1011 state: CheckerState,
1012 moduleIndex: int,
1013 node: SyntaxNode,
1014 message: string,
1015) -> () {
1016 addDiagnostic(
1017 state,
1018 moduleIndex,
1019 "TPZ5001",
1020 "constant expression error: {message}",
1021 "",
1022 node,
1026function constantIntegerResult(
1027 state: CheckerState,
1028 moduleIndex: int,
1029 node: SyntaxNode,
1030 value: BigInt,
1031 overflow: string,
1032) -> Option<BigInt> {
1033 match value.toInt() {
1034 case Some(_) => Some(value)
1035 case None => {
1036 constantExpressionFault(state, moduleIndex, node, overflow)
1037 None
1042function constantIntegerPower(base: BigInt, exponent: int) -> BigInt {
1043 let mut result = BigInt.fromInt(1)
1044 let mut factor = base
1045 let mut remaining = exponent
1046 while remaining > 0 {
1047 if remaining % 2 == 1 {
1048 result = result * factor
1050 remaining = remaining / 2
1051 if remaining > 0 {
1052 factor = factor * factor
1055 result
1058export function foldConstantInteger(
1059 state: CheckerState,
1060 moduleIndex: int,
1061 node: SyntaxNode,
1062) -> Option<BigInt> {
1063 if node.kind == "expression/integer" {
1064 return match toInt(attribute(node, "valueDecimal")) {
1065 case Some(found) => Some(BigInt.fromInt(found))
1066 case None => None
1069 if node.kind == "expression/parenthesized" {
1070 return match child(node, "operand") {
1071 case Some(found) => foldConstantInteger(state, moduleIndex, found)
1072 case None => None
1075 if node.kind == "expression/unary" {
1076 let operand = match child(node, "operand") {
1077 case Some(found) => found
1078 case None => return None
1080 let value = match foldConstantInteger(state, moduleIndex, operand) {
1081 case Some(found) => found
1082 case None => return None
1084 let operator = attribute(node, "operator")
1085 if operator == "plus" {
1086 return Some(value)
1088 if operator != "minus" {
1089 return None
1091 return constantIntegerResult(
1092 state,
1093 moduleIndex,
1094 node,
1095 -value,
1096 "integer negation overflows",
1099 if node.kind != "expression/binary" {
1100 return None
1102 let operator = attribute(node, "operator")
1103 if operator == "and" || operator == "or" || operator == "coalesce" {
1104 return None
1106 let left = match child(node, "left") {
1107 case Some(found) => match foldConstantInteger(state, moduleIndex, found) {
1108 case Some(value) => value
1109 case None => return None
1111 case None => return None
1113 let right = match child(node, "right") {
1114 case Some(found) => match foldConstantInteger(state, moduleIndex, found) {
1115 case Some(value) => value
1116 case None => return None
1118 case None => return None
1120 let leftInt = match left.toInt() {
1121 case Some(found) => found
1122 case None => return None
1124 let rightInt = match right.toInt() {
1125 case Some(found) => found
1126 case None => return None
1128 match operator {
1129 case "add" => constantIntegerResult(
1130 state,
1131 moduleIndex,
1132 node,
1133 left + right,
1134 "integer addition overflows",
1136 case "subtract" => constantIntegerResult(
1137 state,
1138 moduleIndex,
1139 node,
1140 left - right,
1141 "integer subtraction overflows",
1143 case "multiply" => constantIntegerResult(
1144 state,
1145 moduleIndex,
1146 node,
1147 left * right,
1148 "integer multiplication overflows",
1150 case "divide" => if rightInt == 0 {
1151 constantExpressionFault(
1152 state,
1153 moduleIndex,
1154 node,
1155 "integer division by zero",
1157 None
1158 } else {
1159 match left.div(right) {
1160 case Ok(found) => constantIntegerResult(
1161 state,
1162 moduleIndex,
1163 node,
1164 found,
1165 "integer division overflows",
1167 case Err(_) => None
1170 case "remainder" => if rightInt == 0 {
1171 constantExpressionFault(
1172 state,
1173 moduleIndex,
1174 node,
1175 "integer remainder by zero",
1177 None
1178 } else if leftInt == minimumInt() && rightInt == -1 {
1179 constantExpressionFault(
1180 state,
1181 moduleIndex,
1182 node,
1183 "integer remainder overflows",
1185 None
1186 } else {
1187 match left.mod(right) {
1188 case Ok(found) => Some(found)
1189 case Err(_) => None
1192 case "power" => if rightInt < 0 {
1193 constantExpressionFault(
1194 state,
1195 moduleIndex,
1196 node,
1197 "integer exponent must be non-negative; use float operands",
1199 None
1200 } else if rightInt > MAX_CONSTANT_EXPONENT {
1201 constantExpressionFault(
1202 state,
1203 moduleIndex,
1204 node,
1205 "integer exponentiation overflows",
1207 None
1208 } else {
1209 constantIntegerResult(
1210 state,
1211 moduleIndex,
1212 node,
1213 constantIntegerPower(left, rightInt),
1214 "integer exponentiation overflows",
1217 case _ => return None

SHA256 ad20a09d5320f85a25e451c823e064b52b05d17e4021f6be5a2f8bf5057f0283