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/profile.tpz
Topaz · 23553 B · Original file
1
import types {2
PreviewAstNode,3
PreviewCheckDiagnostic,4
PreviewModuleResult,5
PreviewResolvedReference,6
PreviewToken,7
PreviewTypeAtom,8
PreviewTypedCall,9
PreviewTypedCapture,10
PreviewTypedNode,11
}13
record ProfileSpan {14
lo: int,15
hi: int,16
}18
record ProfileDenial {19
rule: string,20
message: string,21
}23
function moduleText(source: Bytes, lo: int, hi: int) -> string {24
match source.slice(lo, hi).decodeUtf8() {25
case Ok(value) => value26
case Err(_) => ""27
}28
}30
function sameFinding(31
left: PreviewCheckDiagnostic,32
right: PreviewCheckDiagnostic,33
) -> bool {34
left.moduleIndex == right.moduleIndex &&35
left.lo == right.lo &&36
left.hi == right.hi &&37
left.code == right.code &&38
left.message == right.message &&39
left.profileRule == right.profileRule40
}42
function before(43
left: PreviewCheckDiagnostic,44
right: PreviewCheckDiagnostic,45
) -> bool {46
left.moduleIndex < right.moduleIndex ||47
(left.moduleIndex == right.moduleIndex && left.lo < right.lo) ||48
(left.moduleIndex == right.moduleIndex && left.lo == right.lo &&49
left.hi < right.hi) ||50
(left.moduleIndex == right.moduleIndex && left.lo == right.lo &&51
left.hi == right.hi && left.profileRule < right.profileRule) ||52
(left.moduleIndex == right.moduleIndex && left.lo == right.lo &&53
left.hi == right.hi && left.profileRule == right.profileRule &&54
left.message < right.message)55
}57
function insertFinding(58
output: Array<PreviewCheckDiagnostic>,59
finding: PreviewCheckDiagnostic,60
) -> () {61
let mut target = output62
let mut index = 063
while index < target.length {64
let existing = target[index]65
if sameFinding(existing, finding) {66
return ()67
}68
if before(finding, existing) {69
target.insert(index, finding)70
return ()71
}72
index += 173
}74
target.push(finding)75
()76
}78
function insertTypedFinding(79
output: Array<PreviewCheckDiagnostic>,80
finding: PreviewCheckDiagnostic,81
) -> () {82
let mut target = output83
let mut index = 084
while index < target.length {85
let existing = target[index]86
if existing.moduleIndex == finding.moduleIndex &&87
existing.lo == finding.lo &&88
existing.hi == finding.hi {89
return ()90
}91
if before(finding, existing) {92
target.insert(index, finding)93
return ()94
}95
index += 196
}97
target.push(finding)98
()99
}101
function finding(102
moduleIndex: int,103
lo: int,104
hi: int,105
rule: string,106
message: string,107
primaryMessage: string,108
notes: Array<string>,109
) -> PreviewCheckDiagnostic {110
PreviewCheckDiagnostic {111
moduleIndex: moduleIndex,112
code: "TPZ5801",113
message: message,114
primaryMessage: primaryMessage,115
lo: lo,116
hi: hi,117
secondary: [],118
notes: notes,119
profileRule: rule,120
}121
}123
function sourceFinding(124
moduleIndex: int,125
lo: int,126
hi: int,127
rule: string,128
message: string,129
) -> PreviewCheckDiagnostic {130
finding(131
moduleIndex,132
lo,133
hi,134
rule,135
message,136
"not in the deterministic compiler-kernel inventory",137
[138
"profile rule: {rule}",139
"move this effect to the Rust host shell or use a deterministic admitted value/library leaf",140
],141
)142
}144
function typedFinding(145
moduleIndex: int,146
lo: int,147
hi: int,148
rule: string,149
message: string,150
) -> PreviewCheckDiagnostic {151
finding(152
moduleIndex,153
lo,154
hi,155
rule,156
message,157
"Bootstrap Profile package boundary",158
["profile rule: {rule}"],159
)160
}162
record DirectChildIndexes {163
byParent: Map<int, Map<string, int>>,164
}166
record ProfileSpanIndexes {167
byModule: Map<int, Map<int, Map<int, int>>>,168
}170
function directChildIndexes(171
nodes: Array<PreviewAstNode>,172
) -> DirectChildIndexes {173
let mut byParent: Map<int, Map<string, int>> = Map.new()174
let mut index = 0175
for node in nodes {176
match byParent.get(node.parent) {177
case Some(found) => {178
let mut byField = found179
match byField.get(node.field) {180
case Some(_) => ()181
case None => byField.insert(node.field, index)182
}183
}184
case None => {185
let mut byField: Map<string, int> = Map.new()186
byField.insert(node.field, index)187
byParent.insert(node.parent, byField)188
}189
}190
index += 1191
}192
DirectChildIndexes { byParent: byParent }193
}195
function directChildIndex(196
indexes: DirectChildIndexes,197
parent: int,198
field: string,199
) -> Option<int> {200
match indexes.byParent.get(parent) {201
case Some(byField) => byField.get(field)202
case None => None203
}204
}206
function directChild(207
nodes: Array<PreviewAstNode>,208
indexes: DirectChildIndexes,209
parent: int,210
field: string,211
) -> Option<PreviewAstNode> {212
match directChildIndex(indexes, parent, field) {213
case Some(index) => Some(nodes[index])214
case None => None215
}216
}218
function insertProfileSpanIndex(219
indexes: ProfileSpanIndexes,220
moduleIndex: int,221
lo: int,222
hi: int,223
index: int,224
) -> () {225
let mut byModule = indexes.byModule226
let mut byLo = match byModule.get(moduleIndex) {227
case Some(found) => found228
case None => {229
let value: Map<int, Map<int, int>> = Map.new()230
byModule.insert(moduleIndex, value)231
value232
}233
}234
let mut byHi = match byLo.get(lo) {235
case Some(found) => found236
case None => {237
let value: Map<int, int> = Map.new()238
byLo.insert(lo, value)239
value240
}241
}242
match byHi.get(hi) {243
case Some(_) => ()244
case None => byHi.insert(hi, index)245
}246
}248
function profileSpanIndex(249
indexes: ProfileSpanIndexes,250
moduleIndex: int,251
lo: int,252
hi: int,253
) -> Option<int> {254
match indexes.byModule.get(moduleIndex) {255
case Some(byLo) => match byLo.get(lo) {256
case Some(byHi) => byHi.get(hi)257
case None => None258
}259
case None => None260
}261
}263
function memberReference(264
catalog: ProfileReferenceCatalog,265
moduleIndex: int,266
lo: int,267
hi: int,268
) -> Option<PreviewResolvedReference> {269
match profileSpanIndex(catalog.memberReferences, moduleIndex, lo, hi) {270
case Some(index) => Some(catalog.references[index])271
case None => None272
}273
}275
function memberIdentity(276
reference: Option<PreviewResolvedReference>,277
objectName: string,278
fieldName: string,279
) -> string {280
match reference {281
case Some(found) => {282
let reference = found283
let targetName = if reference.targetName == "" {284
fieldName285
} else {286
reference.targetName287
}288
"{reference.targetModule}::{targetName}"289
}290
case None => "{objectName}::{fieldName}"291
}292
}294
function composeSpan(295
module: PreviewModuleResult,296
childIndexes: DirectChildIndexes,297
node: PreviewAstNode,298
ordinal: int,299
) -> ProfileSpan {300
match [301
directChild(module.ast, childIndexes, ordinal, "left"),302
directChild(module.ast, childIndexes, ordinal, "right"),303
] {304
case [Some(left), Some(right)] => for token in module.raw {305
if token.kind == "gt-gt" &&306
token.lo >= left.hi &&307
token.hi <= right.lo {308
return ProfileSpan { lo: token.lo, hi: token.hi }309
}310
}311
case _ => ()312
}313
ProfileSpan { lo: node.lo, hi: node.hi }314
}316
function identityHasHead(identity: string, head: string) -> bool {317
identity == head ||318
identity.startsWith("{head}.") ||319
identity.startsWith("{head}::")320
}322
function deniedModule(identity: string) -> bool {323
identityHasHead(identity, "std.fs") ||324
identityHasHead(identity, "std.io") ||325
identityHasHead(identity, "std.path") ||326
identityHasHead(identity, "std.cli") ||327
identityHasHead(identity, "std.test") ||328
identityHasHead(identity, "std.regex") ||329
identityHasHead(identity, "std.codec") ||330
identityHasHead(identity, "std.crypto") ||331
identityHasHead(identity, "std.time") ||332
identityHasHead(identity, "std.random") ||333
identityHasHead(identity, "std.process") ||334
identityHasHead(identity, "std.database")335
}337
function deniedLeaf(identity: string) -> bool {338
deniedModule(identity) ||339
identityHasHead(identity, "FS") ||340
identityHasHead(identity, "Cli") ||341
identityHasHead(identity, "Path") ||342
identityHasHead(identity, "HTTP") ||343
identityHasHead(identity, "DOM") ||344
identityHasHead(identity, "Test") ||345
identityHasHead(identity, "Regex") ||346
identityHasHead(identity, "Date") ||347
identityHasHead(identity, "Math") ||348
identityHasHead(identity, "HMAC") ||349
identityHasHead(identity, "Base64") ||350
identityHasHead(identity, "Deflate") ||351
identityHasHead(identity, "Zlib") ||352
identityHasHead(identity, "ReedSolomon")353
}355
function testFrameworkIdentity(identity: string) -> bool {356
identityHasHead(identity, "std.test") || identityHasHead(identity, "Test")357
}359
function testMember(name: string) -> bool {360
name == "assert" ||361
name == "assertEq" ||362
name == "assertNe" ||363
name == "assertContains" ||364
name == "assertOk" ||365
name == "assertErr" ||366
name == "assertSome" ||367
name == "assertNone" ||368
name == "assertGolden"369
}371
function typeDenial(atoms: Array<PreviewTypeAtom>) -> Option<ProfileDenial> {372
for atom in atoms {373
if (atom.kind == "primitive" || atom.kind == "literal") &&374
atom.name == "float" {375
return Some(ProfileDenial {376
rule: "bootstrap/no-float",377
message: "`float` is not available to the deterministic Bootstrap Profile",378
})379
}380
if atom.kind == "file" || atom.kind == "path" {381
return Some(ProfileDenial {382
rule: "bootstrap/no-resource",383
message: "host file and path values are not available to the Bootstrap Profile",384
})385
}386
if atom.kind == "regex" ||387
atom.kind == "match" ||388
atom.kind == "toml-value" ||389
atom.kind == "url" ||390
atom.kind == "date" ||391
atom.kind == "big-int" ||392
atom.kind == "decimal" ||393
atom.kind == "rounding-mode" {394
return Some(ProfileDenial {395
rule: "bootstrap/no-host-leaf",396
message: "this runtime value family is outside the Bootstrap Profile inventory",397
})398
}399
}400
None401
}403
function appendTypeFinding(404
output: Array<PreviewCheckDiagnostic>,405
moduleIndex: int,406
lo: int,407
hi: int,408
atoms: Array<PreviewTypeAtom>,409
) -> () {410
match typeDenial(atoms) {411
case Some(value) => insertTypedFinding(412
output,413
typedFinding(moduleIndex, lo, hi, value.rule, value.message),414
)415
case None => ()416
}417
}419
function appendCallTypeFinding(420
output: Array<PreviewCheckDiagnostic>,421
call: PreviewTypedCall,422
) -> () {423
match typeDenial(call.calleeType) {424
case Some(value) => insertTypedFinding(425
output,426
typedFinding(427
call.moduleIndex,428
call.lo,429
call.hi,430
value.rule,431
value.message,432
),433
)434
case None => match typeDenial(call.resultType) {435
case Some(value) => insertTypedFinding(436
output,437
typedFinding(438
call.moduleIndex,439
call.lo,440
call.hi,441
value.rule,442
value.message,443
),444
)445
case None => ()446
}447
}448
}450
function appendTestFramework(451
output: Array<PreviewCheckDiagnostic>,452
profile: string,453
moduleIndex: int,454
lo: int,455
hi: int,456
label: string,457
) -> () {458
let rule = if profile == "agent-pack" {459
"agent-pack/no-test-framework"460
} else if profile == "test-profile" {461
"test-profile/no-test-framework"462
} else {463
"bootstrap/no-test-framework"464
}465
insertFinding(466
output,467
finding(468
moduleIndex,469
lo,470
hi,471
rule,472
"test-framework APIs are not allowed by profile `{profile}`",473
label,474
[475
"profile rule: {rule}",476
"the executable test-profile grants only the canonical free `assert(...)` function",477
],478
),479
)480
}482
record ProfileReferenceCatalog {483
references: Array<PreviewResolvedReference>,484
resolvedTargets: ProfileSpanIndexes,485
memberReferences: ProfileSpanIndexes,486
}488
record ProfileModuleContext {489
profile: string,490
moduleIndex: int,491
module: PreviewModuleResult,492
source: Bytes,493
referenceCatalog: ProfileReferenceCatalog,494
childIndexes: DirectChildIndexes,495
}497
function profileReferenceCatalog(498
references: Array<PreviewResolvedReference>,499
) -> ProfileReferenceCatalog {500
let resolvedTargets = ProfileSpanIndexes { byModule: Map.new() }501
let memberReferences = ProfileSpanIndexes { byModule: Map.new() }502
let mut referenceIndex = 0503
for reference in references {504
if reference.targetModuleIndex >= 0 {505
insertProfileSpanIndex(506
resolvedTargets,507
reference.moduleIndex,508
reference.lo,509
reference.hi,510
referenceIndex,511
)512
}513
if reference.targetModule != "" {514
insertProfileSpanIndex(515
memberReferences,516
reference.moduleIndex,517
reference.lo,518
reference.hi,519
referenceIndex,520
)521
}522
referenceIndex += 1523
}524
ProfileReferenceCatalog {525
references: references,526
resolvedTargets: resolvedTargets,527
memberReferences: memberReferences,528
}529
}531
function appendCompositionFinding(532
output: Array<PreviewCheckDiagnostic>,533
context: ProfileModuleContext,534
node: PreviewAstNode,535
ordinal: int,536
) -> () {537
if node.kind != "expression/compose" ||538
(context.profile != "agent-pack" && context.profile != "test-profile") {539
return ()540
}541
let span = composeSpan(context.module, context.childIndexes, node, ordinal)542
let rule = "{context.profile}/no-composition"543
insertFinding(544
output,545
finding(546
context.moduleIndex,547
span.lo,548
span.hi,549
rule,550
"function composition is not allowed by profile `{context.profile}`",551
"profile-disallowed `>>`",552
[553
"profile rule: {rule}",554
"use an explicit lambda, or a pipeline only when it preserves the intended semantics",555
],556
),557
)558
}560
function appendIdentifierFinding(561
output: Array<PreviewCheckDiagnostic>,562
context: ProfileModuleContext,563
node: PreviewAstNode,564
) -> () {565
if node.kind != "expression/identifier" {566
return ()567
}568
let name = moduleText(context.source, node.lo, node.hi)569
let unresolved = profileSpanIndex(570
context.referenceCatalog.resolvedTargets,571
context.moduleIndex,572
node.lo,573
node.hi,574
) == None575
if context.profile == "bootstrap" && unresolved &&576
(name == "print" || name == "input" || name == "open" || name == "assert") {577
insertFinding(578
output,579
sourceFinding(580
context.moduleIndex,581
node.lo,582
node.hi,583
"bootstrap/no-host-leaf",584
"runtime leaf `{name}` is not available to the Bootstrap Profile",585
),586
)587
} else if context.profile == "agent-pack" && unresolved && name == "assert" {588
insertFinding(589
output,590
finding(591
context.moduleIndex,592
node.lo,593
node.hi,594
"agent-pack/no-assert",595
"`assert` is not allowed by profile `agent-pack`",596
"test-only function reference",597
[598
"profile rule: agent-pack/no-assert",599
"use `--profile test-profile` only for test code, or express the failure with canonical Result/control flow",600
],601
),602
)603
}604
}606
function bootstrapSyntaxDenial(kind: string) -> Option<ProfileDenial> {607
if kind == "expression/float" {608
Some(ProfileDenial {609
rule: "bootstrap/no-float",610
message: "`float` is not available to the deterministic Bootstrap Profile",611
})612
} else if kind == "expression/duration" {613
Some(ProfileDenial {614
rule: "bootstrap/no-duration",615
message: "duration values are not available to the deterministic Bootstrap Profile",616
})617
} else if kind == "expression/concurrent" {618
Some(ProfileDenial {619
rule: "bootstrap/no-concurrency",620
message: "`concurrent` is not available to the deterministic Bootstrap Profile",621
})622
} else if kind == "statement/using" {623
Some(ProfileDenial {624
rule: "bootstrap/no-resource",625
message: "`using` and host resource values are not available to the Bootstrap Profile",626
})627
} else {628
None629
}630
}632
function appendBootstrapSyntaxFinding(633
output: Array<PreviewCheckDiagnostic>,634
context: ProfileModuleContext,635
node: PreviewAstNode,636
) -> () {637
if context.profile != "bootstrap" {638
return ()639
}640
match bootstrapSyntaxDenial(node.kind) {641
case Some(denial) => insertFinding(642
output,643
sourceFinding(644
context.moduleIndex,645
node.lo,646
node.hi,647
denial.rule,648
denial.message,649
),650
)651
case None => ()652
}653
}655
function appendImportFinding(656
output: Array<PreviewCheckDiagnostic>,657
context: ProfileModuleContext,658
node: PreviewAstNode,659
ordinal: int,660
) -> () {661
if node.kind != "statement/import" {662
return ()663
}664
// The parser nests the module path under the statement's `import-item`.665
let item = match directChildIndex(context.childIndexes, ordinal, "import") {666
case Some(index) => index667
case None => return ()668
}669
let pathIndex = match directChildIndex(context.childIndexes, item, "path") {670
case Some(index) => index671
case None => return ()672
}673
let path = context.module.ast[pathIndex]674
let identity = modulePathIdentity(context, pathIndex)675
if context.profile == "bootstrap" && deniedModule(identity) {676
insertFinding(677
output,678
sourceFinding(679
context.moduleIndex,680
path.lo,681
path.hi,682
"bootstrap/no-host-module",683
"module `{identity}` is not available to the Bootstrap Profile",684
),685
)686
}687
if identity == "std.test" {688
appendTestFramework(689
output,690
context.profile,691
context.moduleIndex,692
path.lo,693
path.hi,694
"test-only `std.test` import",695
)696
}697
}699
// Joins the path segments, which directly follow their module path in the700
// flattened tree, so spacing or comments between segments keep one identity.701
function modulePathIdentity(context: ProfileModuleContext, path: int) -> string {702
let mut segments: Array<string> = []703
let mut index = path + 1704
while index < context.module.ast.length {705
let segment = context.module.ast[index]706
if segment.parent != path {707
break708
}709
segments.push(moduleText(context.source, segment.lo, segment.hi))710
index += 1711
}712
segments.join(".")713
}715
function appendMemberFinding(716
output: Array<PreviewCheckDiagnostic>,717
context: ProfileModuleContext,718
node: PreviewAstNode,719
ordinal: int,720
) -> () {721
if node.kind != "expression/member" && node.kind != "expression/optional-access" {722
return ()723
}724
match [725
directChild(context.module.ast, context.childIndexes, ordinal, "object"),726
directChild(context.module.ast, context.childIndexes, ordinal, "field"),727
] {728
case [Some(object), Some(field)] => if object.kind == "expression/identifier" {729
let objectName = moduleText(context.source, object.lo, object.hi)730
let fieldName = moduleText(context.source, field.lo, field.hi)731
let objectResolved = profileSpanIndex(732
context.referenceCatalog.resolvedTargets,733
context.moduleIndex,734
object.lo,735
object.hi,736
) != None737
let resolvedMember = memberReference(738
context.referenceCatalog,739
context.moduleIndex,740
field.lo,741
field.hi,742
)743
let memberResolved = match resolvedMember {744
case Some(_) => true745
case None => false746
}747
let identity = memberIdentity(748
resolvedMember,749
objectName,750
fieldName,751
)752
if context.profile == "bootstrap" &&753
(memberResolved || !objectResolved) && deniedLeaf(identity) {754
insertFinding(755
output,756
sourceFinding(757
context.moduleIndex,758
node.lo,759
node.hi,760
"bootstrap/no-host-leaf",761
"runtime leaf `{identity}` is not available to the Bootstrap Profile",762
),763
)764
}765
let testNamespace = if memberResolved {766
testFrameworkIdentity(identity)767
} else {768
!objectResolved && objectName == "Test"769
}770
if testNamespace && testMember(fieldName) {771
appendTestFramework(772
output,773
context.profile,774
context.moduleIndex,775
node.lo,776
node.hi,777
"test-only `Test.*` namespace member",778
)779
}780
}781
case _ => ()782
}783
}785
function appendSourceNodeFindings(786
output: Array<PreviewCheckDiagnostic>,787
context: ProfileModuleContext,788
node: PreviewAstNode,789
ordinal: int,790
) -> () {791
appendCompositionFinding(output, context, node, ordinal)792
appendIdentifierFinding(output, context, node)793
appendBootstrapSyntaxFinding(output, context, node)794
appendImportFinding(output, context, node, ordinal)795
appendMemberFinding(output, context, node, ordinal)796
}798
function appendSourceFindings(799
output: Array<PreviewCheckDiagnostic>,800
profile: string,801
modules: Array<PreviewModuleResult>,802
referenceCatalog: ProfileReferenceCatalog,803
) -> () {804
let mut moduleIndex = 0805
for module in modules {806
if module.identity != "std" && !module.identity.startsWith("std.") {807
let context = ProfileModuleContext {808
profile: profile,809
moduleIndex: moduleIndex,810
module: module,811
source: Bytes.encodeUtf8(module.source),812
referenceCatalog: referenceCatalog,813
childIndexes: directChildIndexes(module.ast),814
}815
let mut ordinal = 0816
for node in module.ast {817
appendSourceNodeFindings(output, context, node, ordinal)818
ordinal += 1819
}820
}821
moduleIndex += 1822
}823
()824
}826
function appendBootstrapTypedFindings(827
output: Array<PreviewCheckDiagnostic>,828
typedNodes: Array<PreviewTypedNode>,829
typedCalls: Array<PreviewTypedCall>,830
typedCaptures: Array<PreviewTypedCapture>,831
) -> () {832
let mut typedOutput: Array<PreviewCheckDiagnostic> = []833
for node in typedNodes {834
if node.ambient {835
insertTypedFinding(836
typedOutput,837
typedFinding(838
node.moduleIndex,839
node.lo,840
node.hi,841
"bootstrap/no-ambient-type",842
"the compiler kernel may not retain an ambient or unexplained type",843
),844
)845
}846
}847
for call in typedCalls {848
if call.ambient {849
insertTypedFinding(850
typedOutput,851
typedFinding(852
call.moduleIndex,853
call.lo,854
call.hi,855
"bootstrap/no-ambient-type",856
"the compiler kernel may not retain an unexplained callable or result type",857
),858
)859
}860
}861
for node in typedNodes {862
appendTypeFinding(typedOutput, node.moduleIndex, node.lo, node.hi, node.typeValue)863
}864
for call in typedCalls {865
appendCallTypeFinding(typedOutput, call)866
}867
for capture in typedCaptures {868
appendTypeFinding(869
typedOutput,870
capture.moduleIndex,871
capture.referenceLo,872
capture.referenceHi,873
capture.typeValue,874
)875
}876
for found in typedOutput {877
insertFinding(output, found)878
}879
()880
}882
export function checkProfile(883
profile: string,884
modules: Array<PreviewModuleResult>,885
references: Array<PreviewResolvedReference>,886
typedNodes: Array<PreviewTypedNode>,887
typedCalls: Array<PreviewTypedCall>,888
typedCaptures: Array<PreviewTypedCapture>,889
) -> Result<Array<PreviewCheckDiagnostic>, string> {890
if profile == "" {891
return Ok([])892
}893
if profile != "agent-pack" &&894
profile != "test-profile" &&895
profile != "bootstrap" {896
return Err("unsupported compiler profile `{profile}`")897
}898
let mut output: Array<PreviewCheckDiagnostic> = []899
let referenceCatalog = profileReferenceCatalog(references)900
appendSourceFindings(output, profile, modules, referenceCatalog)901
if profile == "bootstrap" {902
appendBootstrapTypedFindings(output, typedNodes, typedCalls, typedCaptures)903
}904
Ok(output)905
}SHA256 4f5bc0cdb1e60b03e838db64fde4e0758d185e88638353984c00cfbfec698bd9