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/type_algebra.tpz
Topaz · 28215 B · Original file
1
// Structural type equality, assignability, joins, and partial-type solving.2
// The operations are independent of syntax traversal; checker leaves pass only3
// TypeValue data across this boundary.5
import types { PreviewTypeAtom }6
import checker_types { TypeValue }7
import checker.model {8
CheckerState,9
TypeBinding,10
constructor,11
emptyType,12
inference,13
literal,14
nominal,15
primitive,16
rigid,17
}19
export function typeText(value: TypeValue) -> string {20
match value.kind {21
case "primitive" => if value.name == "unit" { "()" } else { value.name }22
case "literal" => match value.name {23
case "string" => "\"{value.value}\""24
case "null" => "null"25
case _ => value.value26
}27
case "constructor" => if value.name == "Range" {28
"range"29
} else {30
"{value.name}<{typeListText(value.arguments)}>"31
}32
case "function" => {33
let mut parameters = value.parameters34
let mut textValue = typeListText(parameters)35
match value.variadic.get(0) {36
case Some(item) => {37
if textValue != "" {38
textValue = "{textValue}, "39
}40
textValue = "{textValue}...{typeText(item)}"41
}42
case None => ()43
}44
let resultValue = match value.result.get(0) {45
case Some(item) => item46
case None => emptyType("unknown")47
}48
"({textValue}) -> {typeText(resultValue)}"49
}50
case "union" => stringJoinTypes(value.members, " | ")51
case "record" => {52
let mut fields: Array<string> = []53
let mut index = 054
for field in value.fields {55
let name = match value.fieldNames.get(index) {56
case Some(found) => found57
case None => ""58
}59
fields.push("{name}: {typeText(field)}")60
index += 161
}62
"\{ " + stringJoin(fields, ", ") + " \}"63
}64
case "enum" | "nominal-record" | "newtype" | "foreign" => if value.arguments.length == 0 {65
value.identity66
} else {67
"{value.identity}<{typeListText(value.arguments)}>"68
}69
case "rigid" => value.name70
case "template" => "template"71
case "file" => "File"72
case "json-value" => "JSONValue"73
case "bytes" => "Bytes"74
case "byte-buffer" => "ByteBuffer"75
case "path" => "Path"76
case "regex" => "Regex"77
case "match" => "Match"78
case "toml-value" => "TOMLValue"79
case "url" => "URL"80
case "date" => "Date"81
case "big-int" => "BigInt"82
case "decimal" => "Decimal"83
case "rounding-mode" => "RoundingMode"84
case "inference-variable" => "?{value.value}"85
case _ => "?"86
}87
}89
export function typeListText(values: Array<TypeValue>) -> string {90
stringJoinTypes(values, ", ")91
}93
function stringJoinTypes(values: Array<TypeValue>, separator: string) -> string {94
let mut output = ""95
let mut index = 096
for value in values {97
if index > 0 {98
output = "{output}{separator}"99
}100
output = "{output}{typeText(value)}"101
index += 1102
}103
output104
}106
export function stringJoin(values: Array<string>, separator: string) -> string {107
let mut output = ""108
let mut index = 0109
for value in values {110
if index > 0 {111
output = "{output}{separator}"112
}113
output = "{output}{value}"114
index += 1115
}116
output117
}119
function typeArraysEqual(left: Array<TypeValue>, right: Array<TypeValue>) -> bool {120
if left.length != right.length {121
return false122
}123
let mut index = 0124
while index < left.length {125
match [left.get(index), right.get(index)] {126
case [Some(a), Some(b)] => if !typeEqual(a, b) { return false }127
case _ => return false128
}129
index += 1130
}131
true132
}134
function stringArraysEqual(left: Array<string>, right: Array<string>) -> bool {135
if left.length != right.length {136
return false137
}138
let mut index = 0139
while index < left.length {140
match [left.get(index), right.get(index)] {141
case [Some(a), Some(b)] => if a != b { return false }142
case _ => return false143
}144
index += 1145
}146
true147
}149
export function typeEqual(left: TypeValue, right: TypeValue) -> bool {150
left.kind == right.kind &&151
left.name == right.name &&152
left.value == right.value &&153
left.identity == right.identity &&154
left.origin == right.origin &&155
typeArraysEqual(left.arguments, right.arguments) &&156
typeArraysEqual(left.members, right.members) &&157
stringArraysEqual(left.fieldNames, right.fieldNames) &&158
typeArraysEqual(left.fields, right.fields) &&159
typeArraysEqual(left.parameters, right.parameters) &&160
typeArraysEqual(left.variadic, right.variadic) &&161
typeArraysEqual(left.result, right.result)162
}164
export function typeContains(165
value: TypeValue,166
predicate: (TypeValue) -> bool,167
) -> bool {168
if predicate(value) {169
return true170
}171
for item in value.arguments {172
if typeContains(item, predicate) { return true }173
}174
for item in value.members {175
if typeContains(item, predicate) { return true }176
}177
for field in value.fields {178
if typeContains(field, predicate) { return true }179
}180
for item in value.parameters {181
if typeContains(item, predicate) { return true }182
}183
for item in value.variadic {184
if typeContains(item, predicate) { return true }185
}186
for item in value.result {187
if typeContains(item, predicate) { return true }188
}189
false190
}192
function holeTypeComponent(value: TypeValue) -> bool {193
value.kind == "unknown" || value.kind == "inference-variable"194
}196
export function hasHole(value: TypeValue) -> bool {197
typeContains(value, holeTypeComponent)198
}200
function covariantArgumentsAssignable(201
expected: Array<TypeValue>,202
actual: Array<TypeValue>,203
) -> bool {204
if expected.length != actual.length {205
return false206
}207
let mut index = 0208
while index < expected.length {209
match [expected.get(index), actual.get(index)] {210
case [Some(expectedValue), Some(actualValue)] => if !assignable(211
expectedValue,212
actualValue,213
) {214
return false215
}216
case _ => return false217
}218
index += 1219
}220
true221
}223
function functionAssignable(expected: TypeValue, actual: TypeValue) -> bool {224
if expected.parameters.length != actual.parameters.length ||225
expected.variadic.length != actual.variadic.length {226
return false227
}228
let mut index = 0229
while index < expected.parameters.length {230
match [expected.parameters.get(index), actual.parameters.get(index)] {231
case [Some(expectedParameter), Some(actualParameter)] => if !assignable(232
actualParameter,233
expectedParameter,234
) {235
return false236
}237
case _ => return false238
}239
index += 1240
}241
if expected.variadic.length == 1 {242
match [expected.variadic.get(0), actual.variadic.get(0)] {243
case [Some(expectedVariadic), Some(actualVariadic)] => if !assignable(244
actualVariadic,245
expectedVariadic,246
) {247
return false248
}249
case _ => return false250
}251
}252
match [expected.result.get(0), actual.result.get(0)] {253
case [Some(expectedResult), Some(actualResult)] => assignable(254
expectedResult,255
actualResult,256
)257
case _ => false258
}259
}261
export function assignable(expected: TypeValue, actual: TypeValue) -> bool {262
if hasHole(expected) || hasHole(actual) {263
return true264
}265
if actual.kind == "union" {266
for member in actual.members {267
if !assignable(expected, member) {268
return false269
}270
}271
return true272
}273
let widened = widen(actual)274
if expected.kind == "primitive" && widened.kind == "primitive" {275
return expected.name == widened.name276
}277
if typeEqual(expected, actual) || typeEqual(expected, widened) {278
return true279
}280
if expected.kind == "constructor" && actual.kind == "constructor" &&281
expected.name == actual.name &&282
expected.arguments.length == actual.arguments.length {283
if expected.name == "Option" || expected.name == "Result" {284
return covariantArgumentsAssignable(expected.arguments, actual.arguments)285
}286
return typeArraysEqual(expected.arguments, actual.arguments)287
}288
if expected.kind == "function" && actual.kind == "function" {289
return functionAssignable(expected, actual)290
}291
if expected.kind == "record" && actual.kind == "record" &&292
expected.fieldNames.length == actual.fieldNames.length {293
let mut index = 0294
while index < expected.fieldNames.length {295
match [expected.fieldNames.get(index), actual.fieldNames.get(index)] {296
case [Some(expectedName), Some(actualName)] => if expectedName !=297
actualName { return false }298
case _ => return false299
}300
match [expected.fields.get(index), actual.fields.get(index)] {301
case [Some(expectedField), Some(actualField)] => if !assignable(302
expectedField,303
actualField,304
) { return false }305
case _ => return false306
}307
index += 1308
}309
return true310
}311
if expected.kind == "union" {312
for member in expected.members {313
if assignable(member, actual) {314
return true315
}316
}317
}318
false319
}321
function flattenTypeInto(322
output: Array<PreviewTypeAtom>,323
value: TypeValue,324
parent: int,325
field: string,326
index: int,327
edgeName: string,328
) -> () {329
let mut target = output330
let ordinal = target.length331
target.push(PreviewTypeAtom {332
parent: parent,333
field: field,334
index: index,335
edgeName: edgeName,336
kind: value.kind,337
name: value.name,338
value: value.value,339
identity: value.identity,340
origin: value.origin,341
})342
let mut childIndex = 0343
for item in value.arguments {344
flattenTypeInto(target, item, ordinal, "arguments", childIndex, "")345
childIndex += 1346
}347
childIndex = 0348
for item in value.members {349
flattenTypeInto(target, item, ordinal, "members", childIndex, "")350
childIndex += 1351
}352
childIndex = 0353
for item in value.fields {354
let fieldName = match value.fieldNames.get(childIndex) {355
case Some(found) => found356
case None => ""357
}358
flattenTypeInto(target, item, ordinal, "fields", childIndex, fieldName)359
childIndex += 1360
}361
childIndex = 0362
for item in value.parameters {363
flattenTypeInto(target, item, ordinal, "parameters", childIndex, "")364
childIndex += 1365
}366
childIndex = 0367
for item in value.variadic {368
flattenTypeInto(target, item, ordinal, "variadic", childIndex, "")369
childIndex += 1370
}371
childIndex = 0372
for item in value.result {373
flattenTypeInto(target, item, ordinal, "result", childIndex, "")374
childIndex += 1375
}376
()377
}379
export function flattenType(value: TypeValue) -> Array<PreviewTypeAtom> {380
let mut output: Array<PreviewTypeAtom> = []381
flattenTypeInto(output, value, -1, "root", 0, "")382
output383
}385
export function flattenedHasHole(value: Array<PreviewTypeAtom>) -> bool {386
for atom in value {387
if atom.kind == "unknown" || atom.kind == "inference-variable" {388
return true389
}390
}391
false392
}394
export function resolvedFlatType(395
value: Array<PreviewTypeAtom>,396
solutions: Map<string, TypeValue>,397
) -> Array<PreviewTypeAtom> {398
let mut output: Array<PreviewTypeAtom> = []399
let mut remappedIndexes: Array<int> = []400
for atom in value {401
let remappedParent = if atom.parent < 0 {402
-1403
} else {404
match remappedIndexes.get(atom.parent) {405
case Some(found) => found406
case None => -1407
}408
}409
let replacement = if atom.kind == "inference-variable" {410
solutions.get("inference:{atom.value}")411
} else {412
None413
}414
match replacement {415
case Some(solution) => {416
let flattened = flattenType(solution)417
let replacementRoot = output.length418
for replacementAtom in flattened {419
if replacementAtom.parent < 0 {420
output.push(PreviewTypeAtom {421
...replacementAtom,422
parent: remappedParent,423
field: atom.field,424
index: atom.index,425
edgeName: atom.edgeName,426
})427
} else {428
output.push(PreviewTypeAtom {429
...replacementAtom,430
parent: replacementRoot + replacementAtom.parent,431
})432
}433
}434
remappedIndexes.push(replacementRoot)435
}436
case None => {437
remappedIndexes.push(output.length)438
output.push(PreviewTypeAtom { ...atom, parent: remappedParent })439
}440
}441
}442
output443
}445
export function widen(value: TypeValue) -> TypeValue {446
if value.kind != "literal" {447
return value448
}449
match value.name {450
case "int" => primitive("int")451
case "float" => primitive("float")452
case "string" => primitive("string")453
case "bool" => primitive("bool")454
case _ => value455
}456
}458
function compareText(left: string, right: string) -> int {459
if left < right {460
-1461
} else if right < left {462
1463
} else {464
0465
}466
}468
function compareInt(left: int, right: int) -> int {469
if left < right {470
-1471
} else if right < left {472
1473
} else {474
0475
}476
}478
function typeKindOrder(kind: string) -> int {479
match kind {480
case "primitive" => 0481
case "literal" => 1482
case "union" => 2483
case "record" => 3484
case "constructor" => 4485
case "function" => 5486
case "foreign" => 6487
case "rigid" => 7488
case "template" => 8489
case "file" => 9490
case "json-value" => 10491
case "bytes" => 11492
case "byte-buffer" => 12493
case "path" => 13494
case "regex" => 14495
case "match" => 15496
case "toml-value" => 16497
case "url" => 17498
case "date" => 18499
case "big-int" => 19500
case "decimal" => 20501
case "rounding-mode" => 21502
case "enum" => 22503
case "nominal-record" => 23504
case "newtype" => 24505
case "unknown" => 25506
case "inference-variable" => 26507
case _ => 27508
}509
}511
function primitiveOrder(name: string) -> int {512
match name {513
case "int" => 0514
case "float" => 1515
case "string" => 2516
case "bool" => 3517
case "unit" => 4518
case _ => 5519
}520
}522
function literalOrder(name: string) -> int {523
match name {524
case "string" => 0525
case "int" => 1526
case "float" => 2527
case "bool" => 3528
case "null" => 4529
case _ => 5530
}531
}533
function constructorOrder(name: string) -> int {534
match name {535
case "Array" => 0536
case "Map" => 1537
case "Set" => 2538
case "Option" => 3539
case "Result" => 4540
case "Range" => 5541
case _ => 6542
}543
}545
function parsedTypeInt(value: string) -> int {546
match toInt(value) {547
case Some(found) => found548
case None => 0549
}550
}552
function compareTypeLists(553
left: Array<TypeValue>,554
right: Array<TypeValue>,555
) -> int {556
let mut index = 0557
while index < left.length && index < right.length {558
let leftValue = match left.get(index) {559
case Some(found) => found560
case None => emptyType("unknown")561
}562
let rightValue = match right.get(index) {563
case Some(found) => found564
case None => emptyType("unknown")565
}566
let compared = compareTypeValues(leftValue, rightValue)567
if compared != 0 {568
return compared569
}570
index += 1571
}572
compareInt(left.length, right.length)573
}575
function compareRecordTypes(left: TypeValue, right: TypeValue) -> int {576
let mut index = 0577
while index < left.fieldNames.length && index < right.fieldNames.length {578
let leftName = match left.fieldNames.get(index) {579
case Some(found) => found580
case None => ""581
}582
let rightName = match right.fieldNames.get(index) {583
case Some(found) => found584
case None => ""585
}586
let nameCompared = compareText(leftName, rightName)587
if nameCompared != 0 {588
return nameCompared589
}590
let leftType = match left.fields.get(index) {591
case Some(found) => found592
case None => emptyType("unknown")593
}594
let rightType = match right.fields.get(index) {595
case Some(found) => found596
case None => emptyType("unknown")597
}598
let typeCompared = compareTypeValues(leftType, rightType)599
if typeCompared != 0 {600
return typeCompared601
}602
index += 1603
}604
compareInt(left.fieldNames.length, right.fieldNames.length)605
}607
function compareTypeValues(left: TypeValue, right: TypeValue) -> int {608
let kindCompared = compareInt(609
typeKindOrder(left.kind),610
typeKindOrder(right.kind),611
)612
if kindCompared != 0 {613
return kindCompared614
}615
match left.kind {616
case "primitive" => compareInt(617
primitiveOrder(left.name),618
primitiveOrder(right.name),619
)620
case "literal" => {621
let literalCompared = compareInt(622
literalOrder(left.name),623
literalOrder(right.name),624
)625
if literalCompared != 0 {626
return literalCompared627
}628
if left.name == "int" {629
compareInt(parsedTypeInt(left.value), parsedTypeInt(right.value))630
} else {631
compareText(left.value, right.value)632
}633
}634
case "union" => compareTypeLists(left.members, right.members)635
case "record" => compareRecordTypes(left, right)636
case "constructor" => {637
let constructorCompared = compareInt(638
constructorOrder(left.name),639
constructorOrder(right.name),640
)641
if constructorCompared != 0 {642
return constructorCompared643
}644
compareTypeLists(left.arguments, right.arguments)645
}646
case "function" => {647
let parametersCompared = compareTypeLists(648
left.parameters,649
right.parameters,650
)651
if parametersCompared != 0 {652
return parametersCompared653
}654
let variadicCompared = compareTypeLists(left.variadic, right.variadic)655
if variadicCompared != 0 {656
return variadicCompared657
}658
compareTypeLists(left.result, right.result)659
}660
case "foreign" | "enum" | "nominal-record" | "newtype" => {661
let identityCompared = compareText(left.identity, right.identity)662
if identityCompared != 0 {663
return identityCompared664
}665
let originCompared = compareText(left.origin, right.origin)666
if originCompared != 0 {667
return originCompared668
}669
compareTypeLists(left.arguments, right.arguments)670
}671
case "rigid" => {672
let nameCompared = compareText(left.name, right.name)673
if nameCompared != 0 {674
return nameCompared675
}676
compareText(left.origin, right.origin)677
}678
case "inference-variable" => compareInt(679
parsedTypeInt(left.value),680
parsedTypeInt(right.value),681
)682
case _ => 0683
}684
}686
export function unionType(values: Array<TypeValue>) -> TypeValue {687
let mut flat: Array<TypeValue> = []688
for value in values {689
if value.kind == "union" {690
for member in value.members {691
flat.push(member)692
}693
} else {694
flat.push(value)695
}696
}697
let mut ordered: Array<TypeValue> = []698
for value in flat {699
let mut duplicate = false700
for existing in ordered {701
if typeEqual(existing, value) {702
duplicate = true703
}704
}705
if !duplicate {706
let mut inserted = false707
let mut index = 0708
while index < ordered.length {709
let existing = match ordered.get(index) {710
case Some(item) => item711
case None => value712
}713
if compareTypeValues(value, existing) < 0 {714
ordered.insert(index, value)715
inserted = true716
break717
}718
index += 1719
}720
if !inserted {721
ordered.push(value)722
}723
}724
}725
match ordered.get(0) {726
case Some(first) => if ordered.length == 1 {727
first728
} else {729
TypeValue {730
kind: "union",731
name: "",732
value: "",733
identity: "",734
origin: "",735
arguments: [],736
members: ordered,737
fieldNames: [],738
fields: [],739
parameters: [],740
variadic: [],741
result: [],742
}743
}744
case None => emptyType("unknown")745
}746
}748
export function widenedUnion(values: Array<TypeValue>) -> TypeValue {749
let mut widened: Array<TypeValue> = []750
for value in values {751
widened.push(widen(value))752
}753
unionType(widened)754
}756
function projectionOrigin(origin: string) -> bool {757
origin.startsWith("projection:") ||758
origin == "builtin::record-pattern" ||759
origin == "builtin::constructor-pattern" ||760
origin == "builtin::list-pattern" ||761
origin == "builtin::iterable" ||762
origin == "builtin::array-spread"763
}765
function projectionTypeComponent(value: TypeValue) -> bool {766
value.kind == "rigid" && projectionOrigin(value.origin)767
}769
export function projectionType(value: TypeValue) -> bool {770
typeContains(value, projectionTypeComponent)771
}773
export function transformTypeComponents(774
value: TypeValue,775
replacement: (TypeValue) -> Option<TypeValue>,776
) -> TypeValue {777
match replacement(value) {778
case Some(transformed) => return transformed779
case None => ()780
}781
let mut transformed: Array<TypeValue> = []782
for item in value.arguments {783
transformed.push(transformTypeComponents(item, replacement))784
}785
let transformedArguments = transformed786
transformed = []787
for item in value.members {788
transformed.push(transformTypeComponents(item, replacement))789
}790
let transformedMembers = transformed791
transformed = []792
for item in value.fields {793
transformed.push(transformTypeComponents(item, replacement))794
}795
let transformedFields = transformed796
transformed = []797
for item in value.parameters {798
transformed.push(transformTypeComponents(item, replacement))799
}800
let transformedParameters = transformed801
transformed = []802
for item in value.variadic {803
transformed.push(transformTypeComponents(item, replacement))804
}805
let transformedVariadic = transformed806
transformed = []807
for item in value.result {808
transformed.push(transformTypeComponents(item, replacement))809
}810
TypeValue {811
kind: value.kind,812
name: value.name,813
value: value.value,814
identity: value.identity,815
origin: value.origin,816
arguments: transformedArguments,817
members: transformedMembers,818
fieldNames: value.fieldNames,819
fields: transformedFields,820
parameters: transformedParameters,821
variadic: transformedVariadic,822
result: transformed,823
}824
}826
function stripProjectionTypeComponent(value: TypeValue) -> Option<TypeValue> {827
if value.kind == "rigid" && projectionOrigin(value.origin) {828
Some(emptyType("unknown"))829
} else {830
None831
}832
}834
export function stripProjectionTypes(value: TypeValue) -> TypeValue {835
transformTypeComponents(value, stripProjectionTypeComponent)836
}838
function partialHoleKey(value: TypeValue) -> string {839
if value.kind == "inference-variable" {840
return "inference:{value.value}"841
}842
if value.kind == "rigid" && value.origin.startsWith("builtin::") &&843
!projectionOrigin(value.origin) {844
return "rigid:{value.origin}:{value.name}"845
}846
""847
}849
function trueUnknownTypeComponent(value: TypeValue) -> bool {850
value.kind == "unknown"851
}853
function hasTrueUnknown(value: TypeValue) -> bool {854
typeContains(value, trueUnknownTypeComponent)855
}857
function partialHoleTypeComponent(value: TypeValue) -> bool {858
partialHoleKey(value) != ""859
}861
export function hasPartialHole(value: TypeValue) -> bool {862
typeContains(value, partialHoleTypeComponent)863
}865
function partialBindingIndex(bindings: Array<TypeBinding>, key: string) -> int {866
let mut index = 0867
for binding in bindings {868
if binding.name == key {869
return index870
}871
index += 1872
}873
-1874
}876
function bindPartialType(877
bindings: Array<TypeBinding>,878
key: string,879
candidate: TypeValue,880
) -> () {881
let candidateKey = partialHoleKey(candidate)882
if candidateKey == key {883
return ()884
}885
let mut target = bindings886
let index = partialBindingIndex(target, key)887
if index >= 0 {888
match target.get(index) {889
case Some(existing) => unifyPartialTypes(890
existing.typeValue,891
candidate,892
target,893
)894
case None => ()895
}896
} else {897
target.push(TypeBinding { name: key, typeValue: candidate })898
}899
()900
}902
function unifyPartialLists(903
left: Array<TypeValue>,904
right: Array<TypeValue>,905
bindings: Array<TypeBinding>,906
) -> () {907
if left.length != right.length {908
return ()909
}910
let mut index = 0911
while index < left.length {912
match [left.get(index), right.get(index)] {913
case [Some(a), Some(b)] => unifyPartialTypes(a, b, bindings)914
case _ => ()915
}916
index += 1917
}918
()919
}921
function unifyPartialTypes(922
left: TypeValue,923
right: TypeValue,924
bindings: Array<TypeBinding>,925
) -> () {926
let leftKey = partialHoleKey(left)927
let rightKey = partialHoleKey(right)928
if leftKey != "" && rightKey != "" {929
if leftKey == rightKey {930
return ()931
}932
if leftKey < rightKey {933
let mut target = bindings934
if partialBindingIndex(target, rightKey) < 0 {935
target.push(TypeBinding { name: rightKey, typeValue: left })936
}937
} else {938
let mut target = bindings939
if partialBindingIndex(target, leftKey) < 0 {940
target.push(TypeBinding { name: leftKey, typeValue: right })941
}942
}943
return ()944
}945
if leftKey != "" {946
bindPartialType(bindings, leftKey, right)947
return ()948
}949
if rightKey != "" {950
bindPartialType(bindings, rightKey, left)951
return ()952
}953
if left.kind != right.kind || left.name != right.name ||954
left.identity != right.identity {955
return ()956
}957
unifyPartialLists(left.arguments, right.arguments, bindings)958
unifyPartialLists(left.members, right.members, bindings)959
unifyPartialLists(left.fields, right.fields, bindings)960
unifyPartialLists(left.parameters, right.parameters, bindings)961
unifyPartialLists(left.variadic, right.variadic, bindings)962
unifyPartialLists(left.result, right.result, bindings)963
()964
}966
function partialTypeComponentReplacement(967
value: TypeValue,968
bindings: Array<TypeBinding>,969
) -> Option<TypeValue> {970
let key = partialHoleKey(value)971
if key == "" {972
return None973
}974
let index = partialBindingIndex(bindings, key)975
if index >= 0 {976
return match bindings.get(index) {977
case Some(found) => Some(substitutePartialTypes(found.typeValue, bindings))978
case None => Some(value)979
}980
}981
Some(value)982
}984
function substitutePartialTypes(985
value: TypeValue,986
bindings: Array<TypeBinding>,987
) -> TypeValue {988
transformTypeComponents(989
value,990
(component: TypeValue) => partialTypeComponentReplacement(991
component,992
bindings,993
),994
)995
}997
function flattenJoinTypes(values: Array<TypeValue>) -> Array<TypeValue> {998
let mut flat: Array<TypeValue> = []999
for value in values {1000
if value.kind == "union" {1001
for member in value.members {1002
flat.push(member)1003
}1004
} else {1005
flat.push(value)1006
}1007
}1008
flat1009
}1011
function retainInferenceSolutions(1012
state: CheckerState,1013
bindings: Array<TypeBinding>,1014
) -> () {1015
let mut solutions = state.inferenceSolutions1016
for binding in bindings {1017
if binding.name.startsWith("inference:") {1018
let completed = substitutePartialTypes(binding.typeValue, bindings)1019
if !hasPartialHole(completed) {1020
solutions.insert(binding.name, completed)1021
}1022
}1023
}1024
()1025
}1027
export function joinedTypes(1028
state: CheckerState,1029
values: Array<TypeValue>,1030
widenResult: bool,1031
) -> TypeValue {1032
let flat = flattenJoinTypes(values)1033
for value in flat {1034
if hasTrueUnknown(value) {1035
return emptyType("unknown")1036
}1037
}1038
let mut bindings: Array<TypeBinding> = []1039
let mut leftIndex = 01040
while leftIndex < flat.length {1041
let mut rightIndex = 01042
while rightIndex < flat.length {1043
if leftIndex != rightIndex {1044
match [flat.get(leftIndex), flat.get(rightIndex)] {1045
case [Some(left), Some(right)] =>1046
unifyPartialTypes(left, right, bindings)1047
case _ => ()1048
}1049
}1050
rightIndex += 11051
}1052
leftIndex += 11053
}1054
let mut solved: Array<TypeValue> = []1055
for value in flat {1056
let completed = substitutePartialTypes(value, bindings)1057
if hasPartialHole(completed) {1058
return emptyType("unknown")1059
}1060
solved.push(completed)1061
}1062
retainInferenceSolutions(state, bindings)1063
if widenResult { widenedUnion(solved) } else { unionType(solved) }1064
}1066
export function solvePartialTypeAgainst(1067
state: CheckerState,1068
partial: TypeValue,1069
concrete: TypeValue,1070
) -> TypeValue {1071
let mut bindings: Array<TypeBinding> = []1072
unifyPartialTypes(partial, concrete, bindings)1073
let completed = substitutePartialTypes(partial, bindings)1074
if hasPartialHole(completed) {1075
partial1076
} else {1077
retainInferenceSolutions(state, bindings)1078
completed1079
}1080
}SHA256 dee4445a70ebd0931f444cb0c13614640f04321a9dd5c93a27a503d31facab03