Skip to content

Commit 72783bc

Browse files
committed
Merge pull request microsoft#3452 from Microsoft/deeplyNestedTypeArgumentInference
Type argument inference fix for infinitely recursive anonymous types
2 parents b920291 + aeda847 commit 72783bc

File tree

5 files changed

+231
-53
lines changed

5 files changed

+231
-53
lines changed

src/compiler/checker.ts

Lines changed: 43 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4439,8 +4439,8 @@ namespace ts {
44394439
maybeStack[depth][id] = RelationComparisonResult.Succeeded;
44404440
depth++;
44414441
let saveExpandingFlags = expandingFlags;
4442-
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack)) expandingFlags |= 1;
4443-
if (!(expandingFlags & 2) && isDeeplyNestedGeneric(target, targetStack)) expandingFlags |= 2;
4442+
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack, depth)) expandingFlags |= 1;
4443+
if (!(expandingFlags & 2) && isDeeplyNestedGeneric(target, targetStack, depth)) expandingFlags |= 2;
44444444
let result: Ternary;
44454445
if (expandingFlags === 3) {
44464446
result = Ternary.Maybe;
@@ -4476,27 +4476,6 @@ namespace ts {
44764476
return result;
44774477
}
44784478

4479-
// Return true if the given type is part of a deeply nested chain of generic instantiations. We consider this to be the case
4480-
// when structural type comparisons have been started for 10 or more instantiations of the same generic type. It is possible,
4481-
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely expanding.
4482-
// Effectively, we will generate a false positive when two types are structurally equal to at least 10 levels, but unequal at
4483-
// some level beyond that.
4484-
function isDeeplyNestedGeneric(type: ObjectType, stack: ObjectType[]): boolean {
4485-
// We track type references (created by createTypeReference) and instantiated types (created by instantiateType)
4486-
if (type.flags & (TypeFlags.Reference | TypeFlags.Instantiated) && depth >= 10) {
4487-
let symbol = type.symbol;
4488-
let count = 0;
4489-
for (let i = 0; i < depth; i++) {
4490-
let t = stack[i];
4491-
if (t.flags & (TypeFlags.Reference | TypeFlags.Instantiated) && t.symbol === symbol) {
4492-
count++;
4493-
if (count >= 10) return true;
4494-
}
4495-
}
4496-
}
4497-
return false;
4498-
}
4499-
45004479
function propertiesRelatedTo(source: ObjectType, target: ObjectType, reportErrors: boolean): Ternary {
45014480
if (relation === identityRelation) {
45024481
return propertiesIdenticalTo(source, target);
@@ -4815,6 +4794,27 @@ namespace ts {
48154794
}
48164795
}
48174796

4797+
// Return true if the given type is part of a deeply nested chain of generic instantiations. We consider this to be the case
4798+
// when structural type comparisons have been started for 10 or more instantiations of the same generic type. It is possible,
4799+
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely expanding.
4800+
// Effectively, we will generate a false positive when two types are structurally equal to at least 10 levels, but unequal at
4801+
// some level beyond that.
4802+
function isDeeplyNestedGeneric(type: Type, stack: Type[], depth: number): boolean {
4803+
// We track type references (created by createTypeReference) and instantiated types (created by instantiateType)
4804+
if (type.flags & (TypeFlags.Reference | TypeFlags.Instantiated) && depth >= 5) {
4805+
let symbol = type.symbol;
4806+
let count = 0;
4807+
for (let i = 0; i < depth; i++) {
4808+
let t = stack[i];
4809+
if (t.flags & (TypeFlags.Reference | TypeFlags.Instantiated) && t.symbol === symbol) {
4810+
count++;
4811+
if (count >= 5) return true;
4812+
}
4813+
}
4814+
}
4815+
return false;
4816+
}
4817+
48184818
function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
48194819
return compareProperties(sourceProp, targetProp, compareTypes) !== Ternary.False;
48204820
}
@@ -5129,21 +5129,6 @@ namespace ts {
51295129
return false;
51305130
}
51315131

5132-
function isWithinDepthLimit(type: Type, stack: Type[]) {
5133-
if (depth >= 5) {
5134-
let target = (<TypeReference>type).target;
5135-
let count = 0;
5136-
for (let i = 0; i < depth; i++) {
5137-
let t = stack[i];
5138-
if (t.flags & TypeFlags.Reference && (<TypeReference>t).target === target) {
5139-
count++;
5140-
}
5141-
}
5142-
return count < 5;
5143-
}
5144-
return true;
5145-
}
5146-
51475132
function inferFromTypes(source: Type, target: Type) {
51485133
if (source === anyFunctionType) {
51495134
return;
@@ -5211,22 +5196,27 @@ namespace ts {
52115196
else if (source.flags & TypeFlags.ObjectType && (target.flags & (TypeFlags.Reference | TypeFlags.Tuple) ||
52125197
(target.flags & TypeFlags.Anonymous) && target.symbol && target.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral))) {
52135198
// If source is an object type, and target is a type reference, a tuple type, the type of a method, or a type literal, infer from members
5214-
if (!isInProcess(source, target) && isWithinDepthLimit(source, sourceStack) && isWithinDepthLimit(target, targetStack)) {
5215-
if (depth === 0) {
5216-
sourceStack = [];
5217-
targetStack = [];
5218-
}
5219-
sourceStack[depth] = source;
5220-
targetStack[depth] = target;
5221-
depth++;
5222-
inferFromProperties(source, target);
5223-
inferFromSignatures(source, target, SignatureKind.Call);
5224-
inferFromSignatures(source, target, SignatureKind.Construct);
5225-
inferFromIndexTypes(source, target, IndexKind.String, IndexKind.String);
5226-
inferFromIndexTypes(source, target, IndexKind.Number, IndexKind.Number);
5227-
inferFromIndexTypes(source, target, IndexKind.String, IndexKind.Number);
5228-
depth--;
5199+
if (isInProcess(source, target)) {
5200+
return;
52295201
}
5202+
if (isDeeplyNestedGeneric(source, sourceStack, depth) && isDeeplyNestedGeneric(target, targetStack, depth)) {
5203+
return;
5204+
}
5205+
5206+
if (depth === 0) {
5207+
sourceStack = [];
5208+
targetStack = [];
5209+
}
5210+
sourceStack[depth] = source;
5211+
targetStack[depth] = target;
5212+
depth++;
5213+
inferFromProperties(source, target);
5214+
inferFromSignatures(source, target, SignatureKind.Call);
5215+
inferFromSignatures(source, target, SignatureKind.Construct);
5216+
inferFromIndexTypes(source, target, IndexKind.String, IndexKind.String);
5217+
inferFromIndexTypes(source, target, IndexKind.Number, IndexKind.Number);
5218+
inferFromIndexTypes(source, target, IndexKind.String, IndexKind.Number);
5219+
depth--;
52305220
}
52315221
}
52325222

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//// [cyclicGenericTypeInstantiationInference.ts]
2+
function foo<T>() {
3+
var z = foo<typeof y>();
4+
var y: {
5+
y2: typeof z
6+
};
7+
return y;
8+
}
9+
10+
11+
function bar<T>() {
12+
var z = bar<typeof y>();
13+
var y: {
14+
y2: typeof z;
15+
}
16+
return y;
17+
}
18+
19+
var a = foo<number>();
20+
var b = bar<number>();
21+
22+
function test<T>(x: typeof a): void { }
23+
test(b);
24+
25+
//// [cyclicGenericTypeInstantiationInference.js]
26+
function foo() {
27+
var z = foo();
28+
var y;
29+
return y;
30+
}
31+
function bar() {
32+
var z = bar();
33+
var y;
34+
return y;
35+
}
36+
var a = foo();
37+
var b = bar();
38+
function test(x) { }
39+
test(b);
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
=== tests/cases/compiler/cyclicGenericTypeInstantiationInference.ts ===
2+
function foo<T>() {
3+
>foo : Symbol(foo, Decl(cyclicGenericTypeInstantiationInference.ts, 0, 0))
4+
>T : Symbol(T, Decl(cyclicGenericTypeInstantiationInference.ts, 0, 13))
5+
6+
var z = foo<typeof y>();
7+
>z : Symbol(z, Decl(cyclicGenericTypeInstantiationInference.ts, 1, 7))
8+
>foo : Symbol(foo, Decl(cyclicGenericTypeInstantiationInference.ts, 0, 0))
9+
>y : Symbol(y, Decl(cyclicGenericTypeInstantiationInference.ts, 2, 7))
10+
11+
var y: {
12+
>y : Symbol(y, Decl(cyclicGenericTypeInstantiationInference.ts, 2, 7))
13+
14+
y2: typeof z
15+
>y2 : Symbol(y2, Decl(cyclicGenericTypeInstantiationInference.ts, 2, 12))
16+
>z : Symbol(z, Decl(cyclicGenericTypeInstantiationInference.ts, 1, 7))
17+
18+
};
19+
return y;
20+
>y : Symbol(y, Decl(cyclicGenericTypeInstantiationInference.ts, 2, 7))
21+
}
22+
23+
24+
function bar<T>() {
25+
>bar : Symbol(bar, Decl(cyclicGenericTypeInstantiationInference.ts, 6, 1))
26+
>T : Symbol(T, Decl(cyclicGenericTypeInstantiationInference.ts, 9, 13))
27+
28+
var z = bar<typeof y>();
29+
>z : Symbol(z, Decl(cyclicGenericTypeInstantiationInference.ts, 10, 7))
30+
>bar : Symbol(bar, Decl(cyclicGenericTypeInstantiationInference.ts, 6, 1))
31+
>y : Symbol(y, Decl(cyclicGenericTypeInstantiationInference.ts, 11, 7))
32+
33+
var y: {
34+
>y : Symbol(y, Decl(cyclicGenericTypeInstantiationInference.ts, 11, 7))
35+
36+
y2: typeof z;
37+
>y2 : Symbol(y2, Decl(cyclicGenericTypeInstantiationInference.ts, 11, 12))
38+
>z : Symbol(z, Decl(cyclicGenericTypeInstantiationInference.ts, 10, 7))
39+
}
40+
return y;
41+
>y : Symbol(y, Decl(cyclicGenericTypeInstantiationInference.ts, 11, 7))
42+
}
43+
44+
var a = foo<number>();
45+
>a : Symbol(a, Decl(cyclicGenericTypeInstantiationInference.ts, 17, 3))
46+
>foo : Symbol(foo, Decl(cyclicGenericTypeInstantiationInference.ts, 0, 0))
47+
48+
var b = bar<number>();
49+
>b : Symbol(b, Decl(cyclicGenericTypeInstantiationInference.ts, 18, 3))
50+
>bar : Symbol(bar, Decl(cyclicGenericTypeInstantiationInference.ts, 6, 1))
51+
52+
function test<T>(x: typeof a): void { }
53+
>test : Symbol(test, Decl(cyclicGenericTypeInstantiationInference.ts, 18, 22))
54+
>T : Symbol(T, Decl(cyclicGenericTypeInstantiationInference.ts, 20, 14))
55+
>x : Symbol(x, Decl(cyclicGenericTypeInstantiationInference.ts, 20, 17))
56+
>a : Symbol(a, Decl(cyclicGenericTypeInstantiationInference.ts, 17, 3))
57+
58+
test(b);
59+
>test : Symbol(test, Decl(cyclicGenericTypeInstantiationInference.ts, 18, 22))
60+
>b : Symbol(b, Decl(cyclicGenericTypeInstantiationInference.ts, 18, 3))
61+
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
=== tests/cases/compiler/cyclicGenericTypeInstantiationInference.ts ===
2+
function foo<T>() {
3+
>foo : <T>() => { y2: any; }
4+
>T : T
5+
6+
var z = foo<typeof y>();
7+
>z : { y2: any; }
8+
>foo<typeof y>() : { y2: any; }
9+
>foo : <T>() => { y2: any; }
10+
>y : { y2: any; }
11+
12+
var y: {
13+
>y : { y2: any; }
14+
15+
y2: typeof z
16+
>y2 : { y2: any; }
17+
>z : { y2: any; }
18+
19+
};
20+
return y;
21+
>y : { y2: any; }
22+
}
23+
24+
25+
function bar<T>() {
26+
>bar : <T>() => { y2: any; }
27+
>T : T
28+
29+
var z = bar<typeof y>();
30+
>z : { y2: any; }
31+
>bar<typeof y>() : { y2: any; }
32+
>bar : <T>() => { y2: any; }
33+
>y : { y2: any; }
34+
35+
var y: {
36+
>y : { y2: any; }
37+
38+
y2: typeof z;
39+
>y2 : { y2: any; }
40+
>z : { y2: any; }
41+
}
42+
return y;
43+
>y : { y2: any; }
44+
}
45+
46+
var a = foo<number>();
47+
>a : { y2: any; }
48+
>foo<number>() : { y2: any; }
49+
>foo : <T>() => { y2: any; }
50+
51+
var b = bar<number>();
52+
>b : { y2: any; }
53+
>bar<number>() : { y2: any; }
54+
>bar : <T>() => { y2: any; }
55+
56+
function test<T>(x: typeof a): void { }
57+
>test : <T>(x: { y2: any; }) => void
58+
>T : T
59+
>x : { y2: any; }
60+
>a : { y2: any; }
61+
62+
test(b);
63+
>test(b) : void
64+
>test : <T>(x: { y2: any; }) => void
65+
>b : { y2: any; }
66+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
function foo<T>() {
2+
var z = foo<typeof y>();
3+
var y: {
4+
y2: typeof z
5+
};
6+
return y;
7+
}
8+
9+
10+
function bar<T>() {
11+
var z = bar<typeof y>();
12+
var y: {
13+
y2: typeof z;
14+
}
15+
return y;
16+
}
17+
18+
var a = foo<number>();
19+
var b = bar<number>();
20+
21+
function test<T>(x: typeof a): void { }
22+
test(b);

0 commit comments

Comments
 (0)