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 the3
// checker service so imports remain one-way.5
import types {6
PreviewAstAttribute,7
SyntaxNode,8
}9
import checker_types { TypeValue }10
import 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,23
}24
import checker.type_algebra {25
assignable,26
hasHole,27
typeText,28
widen,29
}30
import 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,44
}45
import checker.nominals {46
formType,47
substitute,48
targetType,49
}50
import checker.patterns {51
bindNamedPatternType,52
patternBindingType,53
patternIsIrrefutable,54
}55
import checker.protocols_methods { predeclareBlockFunctions }56
import checker.call_facts {57
checkExpressionExpected,58
checkReturn,59
inferExpressionContext,60
inferExpressionWithExpected,61
refineExpressionToExpected,62
reportExpectedType,63
}64
import checker.expression_core {65
hasBranchJoinContext,66
checkAssignmentRootMutable,67
expressionTerminates,68
hasLiteralJoinContext,69
inferLambda,70
optionalReceiver,71
}72
import checker.expression_control {73
checkLoopExitStatement,74
inferBinaryExpression,75
inferExpression,76
}78
function 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
None115
}117
function 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
inferred139
} 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 the152
// initializer itself. Parentheses and block tails preserve it;153
// receivers, arguments, elements and loop iterables do not.154
let mut contexts = state.bareBindingExpressions155
contexts.push(bareBindingExpression(value))156
let inferred = inferExpression(service, state, moduleIndex, source, value)157
contexts.pop()158
inferred159
}160
}161
case None => emptyType("unknown")162
}163
}165
function 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
value182
}184
function 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) => found234
case None => item235
}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)246
}248
function 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 => None256
}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 => None264
}265
} else {266
None267
}268
case None => None269
}270
}271
None272
}274
function 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
None284
}285
case None => None286
}287
}289
function recordFunctionAlias(290
state: CheckerState,291
moduleIndex: int,292
item: SyntaxNode,293
) -> () {294
let declaration = match bindingDeclarationNode(item) {295
case Some(found) => found296
case None => return ()297
}298
let value = match child(item, "value") {299
case Some(found) => found300
case None => return ()301
}302
let referenceNode = match aliasReferenceNode(value) {303
case Some(found) => found304
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.functionAliases316
aliases.insert(317
spanKey(318
moduleIndex,319
declaration.lo,320
declaration.hi,321
),322
functionIndex,323
)324
}325
}326
case None => ()327
}328
()329
}331
function functionAliasSemanticType(332
state: CheckerState,333
moduleIndex: int,334
item: SyntaxNode,335
) -> Option<TypeValue> {336
let declaration = match bindingDeclarationNode(item) {337
case Some(found) => found338
case None => return None339
}340
let functionIndex = match state.functionAliases.get(spanKey(341
moduleIndex,342
declaration.lo,343
declaration.hi,344
)) {345
case Some(found) => found346
case None => return None347
}348
let definition = match state.functions.get(functionIndex) {349
case Some(found) => found350
case None => return None351
}352
if definition.typeParameters.length == 0 {353
return None354
}355
let mut arguments: Array<TypeValue> = []356
let mut index = 0357
for _ in definition.typeParameters {358
arguments.push(inference(index))359
index += 1360
}361
Some(substitute(362
functionType(363
definition.parameterTypes,364
definition.variadic,365
definition.result,366
),367
definition.typeParameters,368
arguments,369
))370
}372
function 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) => found390
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
()463
}465
function 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
()507
}509
function 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.statementExpressionContexts524
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
()533
}535
function assignmentTargetHasOptional(node: SyntaxNode) -> bool {536
if node.kind == "expression/optional-access" {537
return true538
}539
match child(node, "object") {540
case Some(object) => assignmentTargetHasOptional(object)541
case None => false542
}543
}545
function 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 => None550
}551
}552
if node.kind == "expression/index" {553
return Some(node)554
}555
None556
}558
function 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
()585
}587
function 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
}608
}610
function 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) => found619
case None => return ()620
}621
let value = match child(item, "value") {622
case Some(found) => found623
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
()695
}697
function 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.loopFrames718
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
()734
}736
function 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
()754
}756
export 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
}808
}810
export 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.literalJoinContexts821
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.branchJoinContexts830
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
inferred838
}839
case None => primitive("unit")840
}841
if inheritedContext {842
let mut contexts = state.literalJoinContexts843
contexts.push(block.lo)844
}845
result846
}848
export 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
refined888
}889
case None => primitive("unit")890
}891
}893
export 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.typeCapabilitySuppressions910
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
()960
}962
export 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 true970
}971
if node.kind == "expression/string" {972
for part in node.children {973
if part.kind != "string-part/text" {974
return false975
}976
}977
return true978
}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 => false984
}985
}986
if node.kind != "expression/binary" {987
return false988
}989
let operator = attribute(node, "operator")990
if operator == "and" || operator == "or" || operator == "coalesce" {991
return false992
}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 => false998
}999
case None => false1000
}1001
}1003
function minimumInt() -> int {1004
match toInt(MINIMUM_INT_DECIMAL) {1005
case Some(found) => found1006
case None => 01007
}1008
}1010
function 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,1023
)1024
}1026
function 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
None1038
}1039
}1040
}1042
function constantIntegerPower(base: BigInt, exponent: int) -> BigInt {1043
let mut result = BigInt.fromInt(1)1044
let mut factor = base1045
let mut remaining = exponent1046
while remaining > 0 {1047
if remaining % 2 == 1 {1048
result = result * factor1049
}1050
remaining = remaining / 21051
if remaining > 0 {1052
factor = factor * factor1053
}1054
}1055
result1056
}1058
export 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 => None1067
}1068
}1069
if node.kind == "expression/parenthesized" {1070
return match child(node, "operand") {1071
case Some(found) => foldConstantInteger(state, moduleIndex, found)1072
case None => None1073
}1074
}1075
if node.kind == "expression/unary" {1076
let operand = match child(node, "operand") {1077
case Some(found) => found1078
case None => return None1079
}1080
let value = match foldConstantInteger(state, moduleIndex, operand) {1081
case Some(found) => found1082
case None => return None1083
}1084
let operator = attribute(node, "operator")1085
if operator == "plus" {1086
return Some(value)1087
}1088
if operator != "minus" {1089
return None1090
}1091
return constantIntegerResult(1092
state,1093
moduleIndex,1094
node,1095
-value,1096
"integer negation overflows",1097
)1098
}1099
if node.kind != "expression/binary" {1100
return None1101
}1102
let operator = attribute(node, "operator")1103
if operator == "and" || operator == "or" || operator == "coalesce" {1104
return None1105
}1106
let left = match child(node, "left") {1107
case Some(found) => match foldConstantInteger(state, moduleIndex, found) {1108
case Some(value) => value1109
case None => return None1110
}1111
case None => return None1112
}1113
let right = match child(node, "right") {1114
case Some(found) => match foldConstantInteger(state, moduleIndex, found) {1115
case Some(value) => value1116
case None => return None1117
}1118
case None => return None1119
}1120
let leftInt = match left.toInt() {1121
case Some(found) => found1122
case None => return None1123
}1124
let rightInt = match right.toInt() {1125
case Some(found) => found1126
case None => return None1127
}1128
match operator {1129
case "add" => constantIntegerResult(1130
state,1131
moduleIndex,1132
node,1133
left + right,1134
"integer addition overflows",1135
)1136
case "subtract" => constantIntegerResult(1137
state,1138
moduleIndex,1139
node,1140
left - right,1141
"integer subtraction overflows",1142
)1143
case "multiply" => constantIntegerResult(1144
state,1145
moduleIndex,1146
node,1147
left * right,1148
"integer multiplication overflows",1149
)1150
case "divide" => if rightInt == 0 {1151
constantExpressionFault(1152
state,1153
moduleIndex,1154
node,1155
"integer division by zero",1156
)1157
None1158
} else {1159
match left.div(right) {1160
case Ok(found) => constantIntegerResult(1161
state,1162
moduleIndex,1163
node,1164
found,1165
"integer division overflows",1166
)1167
case Err(_) => None1168
}1169
}1170
case "remainder" => if rightInt == 0 {1171
constantExpressionFault(1172
state,1173
moduleIndex,1174
node,1175
"integer remainder by zero",1176
)1177
None1178
} else if leftInt == minimumInt() && rightInt == -1 {1179
constantExpressionFault(1180
state,1181
moduleIndex,1182
node,1183
"integer remainder overflows",1184
)1185
None1186
} else {1187
match left.mod(right) {1188
case Ok(found) => Some(found)1189
case Err(_) => None1190
}1191
}1192
case "power" => if rightInt < 0 {1193
constantExpressionFault(1194
state,1195
moduleIndex,1196
node,1197
"integer exponent must be non-negative; use float operands",1198
)1199
None1200
} else if rightInt > MAX_CONSTANT_EXPONENT {1201
constantExpressionFault(1202
state,1203
moduleIndex,1204
node,1205
"integer exponentiation overflows",1206
)1207
None1208
} else {1209
constantIntegerResult(1210
state,1211
moduleIndex,1212
node,1213
constantIntegerPower(left, rightInt),1214
"integer exponentiation overflows",1215
)1216
}1217
case _ => return None1218
}1219
}SHA256 ad20a09d5320f85a25e451c823e064b52b05d17e4021f6be5a2f8bf5057f0283