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/lowering.tpz
Topaz · 86763 B · Original file
1
import stage1_types {2
LoweringOutput,3
STAGE1_EXCHANGE_SCHEMA,4
STAGE1_IR_SCHEMA,5
Stage1Module,6
Stage1Operation,7
Stage1Response,8
compilerProvenance,9
}10
import types {11
LowerBridgeRequest,12
decodeLowerBridgeRequest,13
PreviewAstNode,14
PreviewImportEdge,15
PreviewCallArgument,16
PreviewCallEvaluation,17
PreviewModuleResult,18
PreviewResolvedDeclaration,19
PreviewResolvedReference,20
PreviewTypeAtom,21
PreviewTypedCall,22
PreviewTypedCapture,23
PreviewTypedNode,24
}26
// Prepared Lispex rules are runtime handles, so lowering preserves their resolved identity.27
const LISPEX_RULE_HANDLE_PREFIX = "topaz.lispex-rule-handle/v1:"29
record LowerKindPlan {30
name: string,31
tag: string,32
roleName: string,33
runtimeLeaf: string,34
}36
function lowerKind(37
name: string,38
tagName: string,39
roleName: string,40
runtimeLeaf: string,41
) -> LowerKindPlan {42
LowerKindPlan {43
name: name,44
tag: tagName,45
roleName: roleName,46
runtimeLeaf: runtimeLeaf,47
}48
}50
function statementKind(name: string, tagName: string) -> LowerKindPlan {51
lowerKind(name, tagName, "statement", "")52
}54
function expressionKind(name: string, tagName: string) -> LowerKindPlan {55
lowerKind(name, tagName, "expression", "")56
}58
function runtimeExpressionKind(59
name: string,60
tagName: string,61
runtimeLeaf: string,62
) -> LowerKindPlan {63
lowerKind(name, tagName, "expression", runtimeLeaf)64
}66
function patternKind(name: string, tagName: string) -> LowerKindPlan {67
lowerKind(name, tagName, "pattern", "pattern")68
}70
function lowerKindPlan(kind: string) -> LowerKindPlan {71
match kind {72
case "program" => lowerKind("module", "module", "module-initialization", "")73
case "block" => expressionKind("expression/block", "block")74
case "string-part/text" => runtimeExpressionKind("expression/string-text", "string-text", "string")75
case "parameter" => lowerKind("binding/parameter", "parameter", "binding", "")76
case "lambda-parameter" => lowerKind("binding/parameter", "parameter", "binding", "")77
case "function-declaration" => lowerKind("function", "function", "declaration", "")78
case "statement/import" => statementKind("import", "import")79
case "statement/export" => statementKind("export", "export")80
case "statement/function" => statementKind("function", "function")81
case "statement/type-alias" => statementKind("type-alias", "type-alias")82
case "statement/enum" => statementKind("enum", "enum")83
case "statement/record" => statementKind("record", "record")84
case "statement/newtype" => statementKind("newtype", "newtype")85
case "statement/impl" => statementKind("implementation", "implementation")86
case "statement/protocol" => statementKind("protocol", "protocol")87
case "statement/let" => statementKind("let", "let")88
case "statement/const" => statementKind("constant", "constant")89
case "statement/assign" => statementKind("assignment", "assignment")90
case "statement/return" => statementKind("return", "return")91
case "statement/defer" => lowerKind("defer", "defer", "cleanup", "")92
case "statement/using" => statementKind("using", "using")93
case "statement/while" => statementKind("while", "while")94
case "statement/break" => statementKind("break", "break")95
case "statement/continue" => statementKind("continue", "continue")96
case "statement/expression" => statementKind("expression/block", "expression-statement")97
case "expression/integer" => expressionKind("expression/integer", "integer")98
case "expression/float" => expressionKind("expression/float", "float")99
case "expression/duration" => expressionKind("expression/duration", "duration")100
case "expression/boolean" => expressionKind("expression/boolean", "boolean")101
case "expression/null" => expressionKind("expression/null", "null")102
case "expression/unit" => expressionKind("expression/unit", "unit")103
case "expression/string" => runtimeExpressionKind("expression/string", "string", "string")104
case "expression/template" => runtimeExpressionKind("expression/string", "string", "template")105
case "expression/identifier" => expressionKind("expression/identifier", "identifier")106
case "expression/placeholder" => expressionKind("expression/placeholder", "placeholder")107
case "expression/parenthesized" => expressionKind("expression/parenthesized", "parenthesized")108
case "expression/block" => expressionKind("expression/block", "block-expression")109
case "expression/if" => expressionKind("expression/if", "if")110
case "expression/match" => expressionKind("expression/match", "match")111
case "expression/for" => expressionKind("expression/for", "for")112
case "expression/loop" => expressionKind("expression/loop", "loop")113
case "expression/concurrent" => runtimeExpressionKind("expression/concurrent", "concurrent", "concurrent")114
case "expression/call" => runtimeExpressionKind("expression/call", "call", "call")115
case "expression/member" => runtimeExpressionKind("expression/member", "member", "member")116
case "expression/index" => runtimeExpressionKind("expression/index", "index", "collection")117
case "expression/optional-access" => runtimeExpressionKind("expression/optional-member", "optional-member", "member")118
case "expression/try" => runtimeExpressionKind("expression/result-propagation", "result-propagation", "result")119
case "expression/unary" => runtimeExpressionKind("expression/unary", "unary", "unary")120
case "expression/binary" => runtimeExpressionKind("expression/binary", "binary", "binary")121
case "expression/range" => runtimeExpressionKind("expression/range", "range", "range")122
case "expression/compose" => runtimeExpressionKind("expression/compose", "compose", "callable")123
case "expression/pipe" => runtimeExpressionKind("expression/pipeline", "pipeline", "callable")124
case "expression/lambda" => runtimeExpressionKind("expression/lambda", "lambda", "callable")125
case "expression/record-literal" => runtimeExpressionKind("expression/record-literal", "record-literal", "record")126
case "expression/record-update" => runtimeExpressionKind("expression/record-update", "record-update", "record")127
case "expression/array" => runtimeExpressionKind("expression/array", "array", "array")128
case "expression/set" => runtimeExpressionKind("expression/set", "set", "set")129
case "expression/map" => runtimeExpressionKind("expression/map", "map", "map")130
case "expression/comprehension" => runtimeExpressionKind("expression/comprehension", "comprehension", "collection")131
case "pattern/or" => patternKind("pattern/alternatives", "alternatives")132
case "pattern/binding" => patternKind("pattern/binding", "binding")133
case "pattern/wildcard" => patternKind("pattern/wildcard", "wildcard")134
case "pattern/literal" => patternKind("pattern/literal", "literal")135
case "pattern/range" => patternKind("pattern/range", "range")136
case "pattern/typed" => patternKind("pattern/typed-binding", "typed-binding")137
case "pattern/constructor" => patternKind("pattern/constructor", "constructor")138
case "pattern/list" => patternKind("pattern/list", "list")139
case "pattern/record" => patternKind("pattern/record", "record")140
case "pattern/nominal-record" => patternKind("pattern/nominal-record", "nominal-record")141
case _ => lowerKind("", "", "", "")142
}143
}145
function admittedRole(146
plan: LowerKindPlan,147
sourceKind: string,148
insideProtocol: bool,149
parentKind: string,150
) -> string {151
if insideProtocol && sourceKind != "statement/protocol" {152
return ""153
}154
if sourceKind == "function-declaration" && parentKind != "impl-method" {155
return ""156
}157
plan.roleName158
}160
function roleIdentity(roleName: string) -> string {161
match roleName {162
case "module-initialization" => "ModuleInitialization"163
case "statement" => "Statement"164
case "expression" => "Expression"165
case "pattern" => "Pattern"166
case "binding" => "Binding"167
case "declaration" => "Declaration"168
case "cleanup" => "Cleanup"169
case _ => "Unknown"170
}171
}173
function operationId(174
module: string,175
sourceOrdinal: int,176
roleName: string,177
tagName: string,178
lo: int,179
hi: int,180
) -> string {181
"op:{module}:{sourceOrdinal}:{lo}:{hi}:{roleIdentity(roleName)}:{tagName}"182
}184
function sourceText(source: Bytes, lo: int, hi: int) -> string {185
match source.slice(lo, hi).decodeUtf8() {186
case Ok(value) => value187
case Err(_) => ""188
}189
}191
function attribute(192
names: Array<string>,193
values: Array<string>,194
wanted: string,195
) -> string {196
let mut index = 0197
while index < names.length {198
match names.get(index) {199
case Some(name) => if name == wanted {200
return match values.get(index) {201
case Some(value) => value202
case None => ""203
}204
}205
case None => ()206
}207
index += 1208
}209
""210
}212
function childText(213
nodes: Array<PreviewAstNode>,214
childIndexes: Map<string, int>,215
parent: int,216
field: string,217
source: Bytes,218
) -> string {219
match childIndexes.get("{parent}:{field}") {220
case Some(index) => match nodes.get(index) {221
case Some(node) => return sourceText(source, node.lo, node.hi)222
case None => ()223
}224
case None => ()225
}226
""227
}229
function detail(230
nodes: Array<PreviewAstNode>,231
childIndexes: Map<string, int>,232
nodeIndex: int,233
node: PreviewAstNode,234
source: Bytes,235
) -> string {236
match node.kind {237
case "expression/integer" => attribute(238
node.attributeNames,239
node.attributeValues,240
"valueDecimal",241
)242
case "expression/float" => sourceText(source, node.lo, node.hi)243
case "expression/duration" => sourceText(source, node.lo, node.hi)244
case "expression/boolean" => attribute(245
node.attributeNames,246
node.attributeValues,247
"value",248
)249
case "expression/identifier" => sourceText(source, node.lo, node.hi)250
case "string-part/text" => sourceText(source, node.lo, node.hi)251
case "statement/assign" => attribute(252
node.attributeNames,253
node.attributeValues,254
"operator",255
)256
case "expression/string" => if sourceText(257
source,258
node.lo,259
node.hi,260
).startsWith("\"\"\"") {261
"multiline"262
} else {263
"single-line"264
}265
case "expression/template" => {266
let tag = childText(nodes, childIndexes, nodeIndex, "tag", source)267
if sourceText(source, node.lo, node.hi).startsWith("{tag}\"\"\"") {268
"{tag}|multiline"269
} else {270
"{tag}|single-line"271
}272
}273
case "parameter" => childText(nodes, childIndexes, nodeIndex, "name", source)274
case "lambda-parameter" => childText(nodes, childIndexes, nodeIndex, "name", source)275
case "function-declaration" => childText(nodes, childIndexes, nodeIndex, "name", source)276
case "statement/impl" => {277
let name = childText(nodes, childIndexes, nodeIndex, "name", source)278
let target = childText(nodes, childIndexes, nodeIndex, "target", source)279
if target == "" { name } else { "{name}<{target}>" }280
}281
case "statement/protocol" => childText(282
nodes,283
childIndexes,284
nodeIndex,285
"name",286
source,287
)288
case "expression/member" => childText(nodes, childIndexes, nodeIndex, "field", source)289
case "expression/pipe" => childText(nodes, childIndexes, nodeIndex, "field", source)290
case "expression/optional-access" => childText(291
nodes,292
childIndexes,293
nodeIndex,294
"field",295
source,296
)297
case "pattern/binding" => sourceText(source, node.lo, node.hi)298
case "pattern/typed" => childText(nodes, childIndexes, nodeIndex, "name", source)299
case "pattern/constructor" => childText(nodes, childIndexes, nodeIndex, "name", source)300
case "pattern/nominal-record" => childText(nodes, childIndexes, nodeIndex, "name", source)301
case "expression/range" => attribute(302
node.attributeNames,303
node.attributeValues,304
"inclusive",305
)306
case "pattern/range" => attribute(307
node.attributeNames,308
node.attributeValues,309
"inclusive",310
)311
case "expression/comprehension" => attribute(312
node.attributeNames,313
node.attributeValues,314
"collection",315
)316
case "expression/unary" => attribute(317
node.attributeNames,318
node.attributeValues,319
"operator",320
)321
case "expression/binary" => attribute(322
node.attributeNames,323
node.attributeValues,324
"operator",325
)326
case _ => ""327
}328
}330
function representationForType(value: string) -> string {331
match value {332
case "int" => "i64"333
case "float" => "f64"334
case "bool" => "bool"335
case "unit" => "unit"336
case "Bytes" => "bytes-handle"337
case "ByteBuffer" => "byte-buffer-handle"338
case _ => if value == "" { "" } else { "boxed" }339
}340
}342
function spanKey(moduleIndex: int, lo: int, hi: int) -> string {343
"{moduleIndex}:{lo}:{hi}"344
}346
function projectReferenceIdentity(reference: PreviewResolvedReference) -> string {347
if reference.targetModule != "" && reference.targetName != "" {348
return "{reference.targetModule}::{reference.targetName}"349
}350
if reference.targetModuleIndex >= 0 &&351
reference.targetLo >= 0 &&352
reference.targetHi >= reference.targetLo {353
return "source:{reference.targetModuleIndex}:{reference.targetLo}:{reference.targetHi}"354
}355
"builtin::{reference.name}"356
}358
// References by span. Operations project their reference identity from it,359
// and the type fills and defer captures follow a reference to its declaration.360
function indexReferences(361
values: Array<PreviewResolvedReference>,362
) -> Map<string, PreviewResolvedReference> {363
let mut references: Map<string, PreviewResolvedReference> = Map.new()364
for value in values {365
references.insert(spanKey(value.moduleIndex, value.lo, value.hi), value)366
}367
references368
}370
// The reference at a span when it names a declaration in the program.371
function referenceTargetAt(372
references: Map<string, PreviewResolvedReference>,373
moduleIndex: int,374
lo: int,375
hi: int,376
) -> Option<PreviewResolvedReference> {377
match references.get(spanKey(moduleIndex, lo, hi)) {378
case Some(found) => if found.targetModuleIndex >= 0 { Some(found) } else { None }379
case None => None380
}381
}383
// Resolver import edges grouped by importing module, each group in edge order.384
function indexImportEdges(values: Array<PreviewImportEdge>) -> Map<string, Array<string>> {385
let mut edges: Map<string, Array<string>> = Map.new()386
for value in values {387
match edges.get(value.from) {388
case Some(group) => {389
let mut target = group390
target.push(value.to)391
}392
case None => edges.insert(value.from, [value.to])393
}394
}395
edges396
}398
function indexModuleIdentities(values: Array<PreviewModuleResult>) -> Map<string, int> {399
let mut indexes: Map<string, int> = Map.new()400
let mut index = 0401
for value in values {402
indexes.insert(value.identity, index)403
index += 1404
}405
indexes406
}408
function indexDeclarationsBySpan(409
values: Array<PreviewResolvedDeclaration>,410
) -> Map<string, PreviewResolvedDeclaration> {411
let mut declarations: Map<string, PreviewResolvedDeclaration> = Map.new()412
for value in values {413
declarations.insert(spanKey(value.moduleIndex, value.lo, value.hi), value)414
}415
declarations416
}418
function declarationNameKey(moduleIndex: int, namespace: string, name: string) -> string {419
"{moduleIndex}:{namespace}:{name}"420
}422
function indexDeclarationsByName(423
values: Array<PreviewResolvedDeclaration>,424
) -> Map<string, PreviewResolvedDeclaration> {425
let mut declarations: Map<string, PreviewResolvedDeclaration> = Map.new()426
for value in values {427
declarations.insert(declarationNameKey(value.moduleIndex, value.namespace, value.name), value)428
}429
declarations430
}432
function importEdgesOf(edges: Map<string, Array<string>>, identity: string) -> Array<string> {433
match edges.get(identity) {434
case Some(found) => found435
case None => []436
}437
}439
function referenceIdentity(440
references: Map<string, PreviewResolvedReference>,441
moduleIndex: int,442
lo: int,443
hi: int,444
) -> string {445
match references.get(spanKey(moduleIndex, lo, hi)) {446
case Some(found) => projectReferenceIdentity(found)447
case None => ""448
}449
}451
function mutableBinding(452
nodes: Array<PreviewAstNode>,453
node: PreviewAstNode,454
) -> bool {455
if node.kind != "pattern/binding" || node.parent < 0 {456
return false457
}458
match nodes.get(node.parent) {459
case Some(parent) => parent.kind == "statement/let" && attribute(460
parent.attributeNames,461
parent.attributeValues,462
"mutable",463
) == "true"464
case None => false465
}466
}468
function bindingStorage(kind: string) -> string {469
if kind == "parameter" || kind == "lambda-parameter" {470
return "parameter"471
}472
if kind == "statement/const" ||473
kind == "statement/function" ||474
kind == "function-declaration" {475
return "module"476
}477
if kind == "pattern/binding" || kind == "pattern/typed" {478
return "local"479
}480
if kind == "statement/using" {481
return "local"482
}483
""484
}486
record LowerBindingSource {487
name: string,488
lo: int,489
hi: int,490
}492
function emptyBindingSource() -> LowerBindingSource {493
LowerBindingSource {494
name: "",495
lo: 0,496
hi: 0,497
}498
}500
function bindingSourceAt(501
nodes: Array<PreviewAstNode>,502
index: int,503
source: Bytes,504
) -> LowerBindingSource {505
match nodes.get(index) {506
case Some(node) => LowerBindingSource {507
name: sourceText(source, node.lo, node.hi),508
lo: node.lo,509
hi: node.hi,510
}511
case None => emptyBindingSource()512
}513
}515
function bindingSourceFor(516
nodes: Array<PreviewAstNode>,517
childIndexes: Map<string, int>,518
nodeIndex: int,519
node: PreviewAstNode,520
effectiveKind: string,521
source: Bytes,522
) -> LowerBindingSource {523
if effectiveKind == "pattern/binding" && node.kind == "identifier" {524
return bindingSourceAt(nodes, nodeIndex, source)525
}526
if node.kind == "pattern/binding" {527
return LowerBindingSource {528
name: sourceText(source, node.lo, node.hi),529
lo: node.lo,530
hi: node.hi,531
}532
}533
let nameParent = if node.kind == "pattern/typed" ||534
node.kind == "parameter" ||535
node.kind == "lambda-parameter" ||536
node.kind == "function-declaration" ||537
node.kind == "statement/const" ||538
node.kind == "statement/using" {539
nodeIndex540
} else if node.kind == "statement/function" {541
match childIndexes.get("{nodeIndex}:declaration") {542
case Some(index) => index543
case None => return emptyBindingSource()544
}545
} else {546
return emptyBindingSource()547
}548
match childIndexes.get("{nameParent}:name") {549
case Some(index) => bindingSourceAt(nodes, index, source)550
case None => emptyBindingSource()551
}552
}554
record LowerControlPlan {555
kind: string,556
ownsTarget: bool,557
}559
function lowerControl(kind: string, ownsTarget: bool) -> LowerControlPlan {560
LowerControlPlan {561
kind: kind,562
ownsTarget: ownsTarget,563
}564
}566
function controlPlan(kind: string) -> LowerControlPlan {567
match kind {568
case "expression/if" => lowerControl("branch", true)569
case "expression/match" => lowerControl("match", true)570
case "expression/for" => lowerControl("loop", true)571
case "expression/loop" => lowerControl("loop", true)572
case "statement/while" => lowerControl("loop", true)573
case "statement/break" => lowerControl("break", false)574
case "statement/continue" => lowerControl("continue", false)575
case "statement/return" => lowerControl("return", false)576
case "statement/defer" => lowerControl("cleanup", false)577
case "expression/try" => lowerControl("propagate", false)578
case "expression/concurrent" => lowerControl("concurrent", true)579
case _ => lowerControl("", false)580
}581
}583
function loopControlTarget(584
context: ModuleLoweringContext,585
node: PreviewAstNode,586
astIndex: int,587
) -> string {588
if node.kind != "statement/break" && node.kind != "statement/continue" {589
return ""590
}591
let label = childText(592
context.module.ast,593
context.state.childIndexes,594
astIndex,595
"label",596
context.state.source,597
)598
let mut ancestorIndex = node.parent599
while ancestorIndex >= 0 {600
match context.module.ast.get(ancestorIndex) {601
case Some(ancestor) => {602
let loopKind = ancestor.kind == "expression/loop" ||603
ancestor.kind == "expression/for" ||604
ancestor.kind == "statement/while"605
let target = if label == "" {606
loopKind607
} else {608
ancestor.kind == "expression/loop" && childText(609
context.module.ast,610
context.state.childIndexes,611
ancestorIndex,612
"label",613
context.state.source,614
) == label615
}616
if target {617
match context.state.operationIndexByAst.get(ancestorIndex) {618
case Some(operationIndex) => return operationParentId(619
context,620
operationIndex,621
)622
case None => return ""623
}624
}625
ancestorIndex = ancestor.parent626
}627
case None => return ""628
}629
}630
""631
}633
function runtimeLeaf(kindLeaf: string, callTarget: string) -> string {634
if callTarget.startsWith("builtin::") || callTarget.startsWith("std.") ||635
callTarget.startsWith(LISPEX_RULE_HANDLE_PREFIX) {636
return callTarget637
}638
kindLeaf639
}641
record LowerCallPlan {642
targetIdentity: string,643
calleeKind: string,644
method: string,645
methodClass: string,646
optional: bool,647
shadowFirst: bool,648
stageMethod: string,649
arguments: Array<string>,650
evaluations: Array<string>,651
}653
function callArgumentRows(values: Array<PreviewCallArgument>) -> Array<string> {654
let mut rows: Array<string> = []655
for argument in values {656
let sourceIndex = match argument.sourceIndex {657
case Some(index) => index658
case None => -1659
}660
rows.push(661
"{argument.bindingKind}|{argument.bindingName}|{sourceIndex}|{argument.lo}|{argument.hi}",662
)663
}664
rows665
}667
function callEvaluationRows(values: Array<PreviewCallEvaluation>) -> Array<string> {668
let mut rows: Array<string> = []669
for evaluation in values {670
rows.push("{evaluation.kind}|{evaluation.argumentIndex}")671
}672
rows673
}675
function projectCall(value: PreviewTypedCall) -> LowerCallPlan {676
LowerCallPlan {677
targetIdentity: value.targetIdentity,678
calleeKind: value.calleeKind,679
method: value.method,680
methodClass: value.methodClass,681
optional: value.optional,682
shadowFirst: value.shadowFirst,683
stageMethod: value.stageMethod,684
arguments: callArgumentRows(value.arguments),685
evaluations: callEvaluationRows(value.evaluations),686
}687
}689
function emptyCallPlan() -> LowerCallPlan {690
LowerCallPlan {691
targetIdentity: "",692
calleeKind: "",693
method: "",694
methodClass: "",695
optional: false,696
shadowFirst: false,697
stageMethod: "",698
arguments: [],699
evaluations: [],700
}701
}703
function indexCallPlans(values: Array<PreviewTypedCall>) -> Map<string, LowerCallPlan> {704
let mut plans: Map<string, LowerCallPlan> = Map.new()705
for value in values {706
plans.insert(spanKey(value.moduleIndex, value.lo, value.hi), projectCall(value))707
}708
plans709
}711
function callPlanFor(712
plans: Map<string, LowerCallPlan>,713
moduleIndex: int,714
lo: int,715
hi: int,716
) -> LowerCallPlan {717
match plans.get(spanKey(moduleIndex, lo, hi)) {718
case Some(found) => found719
case None => emptyCallPlan()720
}721
}723
record LowerTypePlan {724
semanticType: string,725
representation: string,726
runtimeType: string,727
atoms: Array<PreviewTypeAtom>,728
}730
function projectLowerType(value: Array<PreviewTypeAtom>) -> LowerTypePlan {731
let semanticType = match value.get(0) {732
case Some(found) => if found.name != "" { found.name } else { found.kind }733
case None => ""734
}735
let runtimeType = if value.length == 0 {736
""737
} else {738
match JSON.stringify(value) {739
case Ok(found) => found740
case Err(_) => ""741
}742
}743
LowerTypePlan {744
semanticType: semanticType,745
representation: representationForType(semanticType),746
runtimeType: runtimeType,747
atoms: value,748
}749
}751
record LowerTypedNodePlan {752
nodeKind: string,753
typePlan: LowerTypePlan,754
}756
function projectTypedNode(value: PreviewTypedNode) -> LowerTypedNodePlan {757
LowerTypedNodePlan {758
nodeKind: value.nodeKind,759
typePlan: projectLowerType(value.typeValue),760
}761
}763
function emptyTypedNodePlan() -> LowerTypedNodePlan {764
LowerTypedNodePlan {765
nodeKind: "",766
typePlan: projectLowerType([]),767
}768
}770
function unitTypeAtom() -> PreviewTypeAtom {771
PreviewTypeAtom {772
parent: -1,773
field: "root",774
index: 0,775
edgeName: "",776
kind: "primitive",777
name: "unit",778
value: "",779
identity: "",780
origin: "",781
}782
}784
function contextualLowerTypePlan(785
kind: string,786
parentKind: string,787
observed: LowerTypePlan,788
) -> LowerTypePlan {789
if kind == "expression/for" && parentKind == "statement/expression" {790
return LowerTypePlan {791
semanticType: "unit",792
representation: representationForType("unit"),793
runtimeType: observed.runtimeType,794
atoms: [unitTypeAtom()],795
}796
}797
observed798
}800
function typedPatternLowerTypePlan(801
context: ModuleLoweringContext,802
node: PreviewAstNode,803
astIndex: int,804
observed: LowerTypePlan,805
) -> LowerTypePlan {806
if node.kind != "pattern/typed" {807
return observed808
}809
let nameIndex = match context.state.childIndexes.get("{astIndex}:name") {810
case Some(found) => found811
case None => return observed812
}813
let nameNode = match context.module.ast.get(nameIndex) {814
case Some(found) => found815
case None => return observed816
}817
let binding = typedNodePlanFor(818
context.input.typedNodePlans,819
context.moduleIndex,820
nameNode.lo,821
nameNode.hi,822
)823
if binding.nodeKind != "binding" || binding.typePlan.runtimeType == "" {824
return observed825
}826
LowerTypePlan {827
semanticType: binding.typePlan.runtimeType,828
representation: binding.typePlan.representation,829
runtimeType: binding.typePlan.runtimeType,830
atoms: binding.typePlan.atoms,831
}832
}834
function unknownTypeAtom() -> PreviewTypeAtom {835
PreviewTypeAtom {836
parent: -1,837
field: "root",838
index: 0,839
edgeName: "",840
kind: "unknown",841
name: "",842
value: "",843
identity: "",844
origin: "",845
}846
}848
record LowerDeclarationSite {849
moduleIndex: int,850
lo: int,851
hi: int,852
}854
// A reference names its declaration; a selected import is followed to the855
// module-scope declaration it imports, through re-exporting imports too.856
function declarationSite(857
input: LoweringInput,858
reference: PreviewResolvedReference,859
namespace: string,860
) -> LowerDeclarationSite {861
let mut site = LowerDeclarationSite {862
moduleIndex: reference.targetModuleIndex,863
lo: reference.targetLo,864
hi: reference.targetHi,865
}866
let mut hops = 0867
while hops < input.modules.length {868
let declaration = match input.declarationsBySpan.get(spanKey(site.moduleIndex, site.lo, site.hi)) {869
case Some(found) => found870
case None => return site871
}872
if declaration.declarationKind != "selected-import" {873
return site874
}875
let moduleIndex = match input.moduleIndexes.get(declaration.targetModule) {876
case Some(found) => found877
case None => return site878
}879
match input.declarationsByName.get(880
declarationNameKey(moduleIndex, namespace, declaration.targetName),881
) {882
case Some(found) => {883
site = LowerDeclarationSite { moduleIndex: moduleIndex, lo: found.lo, hi: found.hi }884
}885
case None => return site886
}887
hops += 1888
}889
site890
}892
// The checker's binding type of a declaration, else its declaration type.893
function declaredTypeEntry(894
context: ModuleLoweringContext,895
moduleIndex: int,896
lo: int,897
hi: int,898
) -> Option<Array<PreviewTypeAtom>> {899
match context.input.declaredTypes.get(declaredTypeKey("binding", moduleIndex, lo, hi)) {900
case Some(found) => return Some(found)901
case None => ()902
}903
context.input.declaredTypes.get(declaredTypeKey("declaration", moduleIndex, lo, hi))904
}906
// Appends the subtree of `atoms` rooted at `root` to `output`, re-rooted under907
// `parent`. Preorder keeps a subtree contiguous: it ends at the first atom908
// whose parent precedes the root.909
function appendTypeAtomSubtree(910
output: Array<PreviewTypeAtom>,911
atoms: Array<PreviewTypeAtom>,912
root: int,913
parent: int,914
field: string,915
index: int,916
edgeName: string,917
) -> () {918
let base = output.length919
let mut target = output920
let mut position = root921
let mut scanning = true922
while scanning {923
match atoms.get(position) {924
case Some(atom) => if position == root {925
target.push(PreviewTypeAtom {926
...atom,927
parent: parent,928
field: field,929
index: index,930
edgeName: edgeName,931
})932
position += 1933
} else if atom.parent < root {934
scanning = false935
} else {936
target.push(PreviewTypeAtom { ...atom, parent: atom.parent - root + base })937
position += 1938
}939
case None => {940
scanning = false941
}942
}943
}944
}946
// A structural record type's field, as its own atom list.947
function recordFieldAtoms(948
atoms: Array<PreviewTypeAtom>,949
fieldName: string,950
) -> Array<PreviewTypeAtom> {951
let output: Array<PreviewTypeAtom> = []952
match atoms.get(0) {953
case Some(root) => if root.kind != "record" {954
return output955
}956
case None => return output957
}958
let mut position = 0959
for atom in atoms {960
if atom.parent == 0 && atom.field == "fields" && atom.edgeName == fieldName {961
appendTypeAtomSubtree(output, atoms, position, -1, "root", 0, "")962
return output963
}964
position += 1965
}966
output967
}969
function stringIndex(values: Array<string>, wanted: string) -> int {970
let mut index = 0971
for value in values {972
if value == wanted {973
return index974
}975
index += 1976
}977
-1978
}980
// A generic record's declared field type with its type parameters replaced by981
// the arguments of the nominal type the pattern matched.982
function substituteRecordArguments(983
declared: Array<PreviewTypeAtom>,984
typeParameters: Array<string>,985
nominal: Array<PreviewTypeAtom>,986
) -> Array<PreviewTypeAtom> {987
if typeParameters.length == 0 {988
return declared989
}990
let mut argumentRoots: Array<int> = []991
let mut position = 0992
for atom in nominal {993
if atom.parent == 0 && atom.field == "arguments" {994
argumentRoots.push(position)995
}996
position += 1997
}998
let output: Array<PreviewTypeAtom> = []999
let mut mapped: Array<int> = []1000
for atom in declared {SHA256 0bef60580a8fc4374bc6a62ffcc9f8bcb11e66059727761fb4f7e1adceea585a