Skip to content

Commit 8a3372c

Browse files
authored
fix(tolk): better handling for recursive types in type inference (#233)
1 parent bcf3ca5 commit 8a3372c

2 files changed

Lines changed: 33 additions & 20 deletions

File tree

server/src/cache/cache.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,21 @@ export class Cache<TKey, TValue> {
1616
return value
1717
}
1818

19+
public cachedIf(key: TKey, reevaluateIfValue: TValue, cb: () => TValue): TValue {
20+
const cached = this.data.get(key)
21+
if (cached !== undefined && cached !== reevaluateIfValue) {
22+
return cached
23+
}
24+
25+
const value = cb()
26+
this.data.set(key, value)
27+
return value
28+
}
29+
30+
public setValue(key: TKey, value: TValue): void {
31+
this.data.set(key, value)
32+
}
33+
1934
public clear(): void {
2035
this.data.clear()
2136
}

server/src/languages/tolk/type-inference.ts

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -963,15 +963,6 @@ class InferenceWalker {
963963
return ExprFlow.create(nextFlow, false)
964964
}
965965

966-
public lambdaParameters(lambda: SyntaxNode): SyntaxNode[] {
967-
const parametersNode = lambda.childForFieldName("parameters")
968-
if (!parametersNode) return []
969-
970-
return parametersNode.children
971-
.filter(value => value?.type === "parameter_declaration")
972-
.filter(value => value !== null)
973-
}
974-
975966
private inferGenericInstantiation(
976967
node: SyntaxNode,
977968
flow: FlowContext,
@@ -1271,7 +1262,7 @@ class InferenceWalker {
12711262
this.processMethods(searchName, method => {
12721263
const receiverTypeString = method.receiverTypeString()
12731264
if (qualifierTypeString === receiverTypeString) {
1274-
// fast path, if types are equal it is exact match, no need to search more methods
1265+
// fast path, if types are equal, it is an exact match, no need to search for more methods
12751266
result.push(method)
12761267
return false
12771268
}
@@ -1287,7 +1278,7 @@ class InferenceWalker {
12871278
this.processMethods(searchName, method => {
12881279
const receiverTypeNode = method.receiverTypeNode()
12891280
if (!receiverTypeNode) return true
1290-
const receiverType = typeOf(receiverTypeNode, method.file)
1281+
const receiverType = InferenceWalker.convertType(receiverTypeNode, this.ctx.file)
12911282

12921283
if (!receiverType?.hasGenerics() && receiverType?.equals(qualifierType)) {
12931284
result.push(method)
@@ -1304,7 +1295,7 @@ class InferenceWalker {
13041295
this.processMethods(searchName, method => {
13051296
const receiverTypeNode = method.receiverTypeNode()
13061297
if (!receiverTypeNode) return true
1307-
const receiverType = typeOf(receiverTypeNode, method.file)
1298+
const receiverType = InferenceWalker.convertType(receiverTypeNode, this.ctx.file)
13081299

13091300
if (!receiverType?.hasGenerics() && receiverType?.canRhsBeAssigned(qualifierType)) {
13101301
result.push(method)
@@ -1322,7 +1313,7 @@ class InferenceWalker {
13221313
this.processMethods(searchName, method => {
13231314
const receiverTypeNode = method.receiverTypeNode()
13241315
if (!receiverTypeNode) return true
1325-
const receiverType = typeOf(receiverTypeNode, method.file)
1316+
const receiverType = InferenceWalker.convertType(receiverTypeNode, this.ctx.file)
13261317

13271318
// Foo<T>, but not T
13281319
if (receiverType?.hasGenerics() && !(receiverType instanceof TypeParameterTy)) {
@@ -1354,7 +1345,7 @@ class InferenceWalker {
13541345
this.processMethods(searchName, method => {
13551346
const receiverTypeNode = method.receiverTypeNode()
13561347
if (!receiverTypeNode) return true
1357-
const receiverType = typeOf(receiverTypeNode, method.file)
1348+
const receiverType = InferenceWalker.convertType(receiverTypeNode, this.ctx.file)
13581349

13591350
if (receiverType instanceof TypeParameterTy) {
13601351
result.push(method)
@@ -2080,9 +2071,11 @@ class InferenceWalker {
20802071
file: TolkFile | null | undefined,
20812072
): Ty | null {
20822073
if (!typeNode || !file) return null
2083-
return TOLK_CACHE.typeCache.cached(typeNode.id, () =>
2084-
InferenceWalker.convertTypeImpl(typeNode, file),
2085-
)
2074+
return TOLK_CACHE.typeCache.cached(typeNode.id, () => {
2075+
// add unknown type to avoid recursion
2076+
TOLK_CACHE.typeCache.setValue(typeNode.id, UnknownTy.UNKNOWN)
2077+
return InferenceWalker.convertTypeImpl(typeNode, file)
2078+
})
20862079
}
20872080

20882081
private static convertTypeImpl(node: SyntaxNode | null | undefined, file: TolkFile): Ty | null {
@@ -2232,6 +2225,12 @@ class InferenceWalker {
22322225
}
22332226

22342227
public static namedNodeType(node: NamedNode): Ty | null {
2228+
return TOLK_CACHE.typeCache.cachedIf(node.node.id, UnknownTy.UNKNOWN, () =>
2229+
InferenceWalker.namedNodeTypeImpl(node),
2230+
)
2231+
}
2232+
2233+
public static namedNodeTypeImpl(node: NamedNode): Ty | null {
22352234
if (node instanceof Struct) {
22362235
const fieldTypes = node.fields().map(it => {
22372236
try {
@@ -2271,10 +2270,9 @@ class InferenceWalker {
22712270
const underlyingType = node.underlyingType()
22722271
if (underlyingType === null) return null
22732272

2274-
const underlyingTypeName = underlyingType.text
2275-
if (underlyingTypeName === "builtin") {
2273+
if (underlyingType.type === "builtin_specifier") {
22762274
const name = node.name()
2277-
const type = InferenceWalker.asPrimitiveType(node.name())
2275+
const type = InferenceWalker.asPrimitiveType(name)
22782276
if (type) {
22792277
return type
22802278
}

0 commit comments

Comments
 (0)