Skip to content

Commit 8918c8a

Browse files
committed
Merge pull request #3239 from Microsoft/cyclicTypeInstantiation
Detect cycles during type instantiation
2 parents 2859ccb + ebcdd85 commit 8918c8a

File tree

6 files changed

+181
-0
lines changed

6 files changed

+181
-0
lines changed

src/compiler/checker.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3866,6 +3866,18 @@ module ts {
38663866
}
38673867

38683868
function instantiateAnonymousType(type: ObjectType, mapper: TypeMapper): ObjectType {
3869+
// If this type has already been instantiated using this mapper, returned the cached result. This guards against
3870+
// infinite instantiations of cyclic types, e.g. "var x: { a: T, b: typeof x };"
3871+
if (mapper.mappings) {
3872+
let cached = <ObjectType>mapper.mappings[type.id];
3873+
if (cached) {
3874+
return cached;
3875+
}
3876+
}
3877+
else {
3878+
mapper.mappings = {};
3879+
}
3880+
// Instantiate the given type using the given mapper and cache the result
38693881
let result = <ResolvedType>createObjectType(TypeFlags.Anonymous, type.symbol);
38703882
result.properties = instantiateList(getPropertiesOfObjectType(type), mapper, instantiateSymbol);
38713883
result.members = createSymbolTable(result.properties);
@@ -3875,6 +3887,7 @@ module ts {
38753887
let numberIndexType = getIndexTypeOfType(type, IndexKind.Number);
38763888
if (stringIndexType) result.stringIndexType = instantiateType(stringIndexType, mapper);
38773889
if (numberIndexType) result.numberIndexType = instantiateType(numberIndexType, mapper);
3890+
mapper.mappings[type.id] = result;
38783891
return result;
38793892
}
38803893

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,6 +1589,7 @@ module ts {
15891589
/* @internal */
15901590
export interface TypeMapper {
15911591
(t: TypeParameter): Type;
1592+
mappings?: Map<Type>; // Type mapping cache
15921593
}
15931594

15941595
/* @internal */
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//// [cyclicTypeInstantiation.ts]
2+
function foo<T>() {
3+
var x: {
4+
a: T;
5+
b: typeof x;
6+
};
7+
return x;
8+
}
9+
10+
function bar<T>() {
11+
var x: {
12+
a: T;
13+
b: typeof x;
14+
};
15+
return x;
16+
}
17+
18+
var a = foo<string>();
19+
var b = bar<string>();
20+
// Relating types of a and b produces instantiations of the cyclic anonymous types in foo and bar
21+
a = b;
22+
23+
24+
//// [cyclicTypeInstantiation.js]
25+
function foo() {
26+
var x;
27+
return x;
28+
}
29+
function bar() {
30+
var x;
31+
return x;
32+
}
33+
var a = foo();
34+
var b = bar();
35+
// Relating types of a and b produces instantiations of the cyclic anonymous types in foo and bar
36+
a = b;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
=== tests/cases/compiler/cyclicTypeInstantiation.ts ===
2+
function foo<T>() {
3+
>foo : Symbol(foo, Decl(cyclicTypeInstantiation.ts, 0, 0))
4+
>T : Symbol(T, Decl(cyclicTypeInstantiation.ts, 0, 13))
5+
6+
var x: {
7+
>x : Symbol(x, Decl(cyclicTypeInstantiation.ts, 1, 7))
8+
9+
a: T;
10+
>a : Symbol(a, Decl(cyclicTypeInstantiation.ts, 1, 12))
11+
>T : Symbol(T, Decl(cyclicTypeInstantiation.ts, 0, 13))
12+
13+
b: typeof x;
14+
>b : Symbol(b, Decl(cyclicTypeInstantiation.ts, 2, 13))
15+
>x : Symbol(x, Decl(cyclicTypeInstantiation.ts, 1, 7))
16+
17+
};
18+
return x;
19+
>x : Symbol(x, Decl(cyclicTypeInstantiation.ts, 1, 7))
20+
}
21+
22+
function bar<T>() {
23+
>bar : Symbol(bar, Decl(cyclicTypeInstantiation.ts, 6, 1))
24+
>T : Symbol(T, Decl(cyclicTypeInstantiation.ts, 8, 13))
25+
26+
var x: {
27+
>x : Symbol(x, Decl(cyclicTypeInstantiation.ts, 9, 7))
28+
29+
a: T;
30+
>a : Symbol(a, Decl(cyclicTypeInstantiation.ts, 9, 12))
31+
>T : Symbol(T, Decl(cyclicTypeInstantiation.ts, 8, 13))
32+
33+
b: typeof x;
34+
>b : Symbol(b, Decl(cyclicTypeInstantiation.ts, 10, 13))
35+
>x : Symbol(x, Decl(cyclicTypeInstantiation.ts, 9, 7))
36+
37+
};
38+
return x;
39+
>x : Symbol(x, Decl(cyclicTypeInstantiation.ts, 9, 7))
40+
}
41+
42+
var a = foo<string>();
43+
>a : Symbol(a, Decl(cyclicTypeInstantiation.ts, 16, 3))
44+
>foo : Symbol(foo, Decl(cyclicTypeInstantiation.ts, 0, 0))
45+
46+
var b = bar<string>();
47+
>b : Symbol(b, Decl(cyclicTypeInstantiation.ts, 17, 3))
48+
>bar : Symbol(bar, Decl(cyclicTypeInstantiation.ts, 6, 1))
49+
50+
// Relating types of a and b produces instantiations of the cyclic anonymous types in foo and bar
51+
a = b;
52+
>a : Symbol(a, Decl(cyclicTypeInstantiation.ts, 16, 3))
53+
>b : Symbol(b, Decl(cyclicTypeInstantiation.ts, 17, 3))
54+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
=== tests/cases/compiler/cyclicTypeInstantiation.ts ===
2+
function foo<T>() {
3+
>foo : <T>() => { a: T; b: any; }
4+
>T : T
5+
6+
var x: {
7+
>x : { a: T; b: any; }
8+
9+
a: T;
10+
>a : T
11+
>T : T
12+
13+
b: typeof x;
14+
>b : { a: T; b: any; }
15+
>x : { a: T; b: any; }
16+
17+
};
18+
return x;
19+
>x : { a: T; b: any; }
20+
}
21+
22+
function bar<T>() {
23+
>bar : <T>() => { a: T; b: any; }
24+
>T : T
25+
26+
var x: {
27+
>x : { a: T; b: any; }
28+
29+
a: T;
30+
>a : T
31+
>T : T
32+
33+
b: typeof x;
34+
>b : { a: T; b: any; }
35+
>x : { a: T; b: any; }
36+
37+
};
38+
return x;
39+
>x : { a: T; b: any; }
40+
}
41+
42+
var a = foo<string>();
43+
>a : { a: string; b: any; }
44+
>foo<string>() : { a: string; b: any; }
45+
>foo : <T>() => { a: T; b: any; }
46+
47+
var b = bar<string>();
48+
>b : { a: string; b: any; }
49+
>bar<string>() : { a: string; b: any; }
50+
>bar : <T>() => { a: T; b: any; }
51+
52+
// Relating types of a and b produces instantiations of the cyclic anonymous types in foo and bar
53+
a = b;
54+
>a = b : { a: string; b: any; }
55+
>a : { a: string; b: any; }
56+
>b : { a: string; b: any; }
57+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
function foo<T>() {
2+
var x: {
3+
a: T;
4+
b: typeof x;
5+
};
6+
return x;
7+
}
8+
9+
function bar<T>() {
10+
var x: {
11+
a: T;
12+
b: typeof x;
13+
};
14+
return x;
15+
}
16+
17+
var a = foo<string>();
18+
var b = bar<string>();
19+
// Relating types of a and b produces instantiations of the cyclic anonymous types in foo and bar
20+
a = b;

0 commit comments

Comments
 (0)