From de8597ca8c31eec0e7c5e08bff149db8832fa978 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 20 May 2015 16:30:50 -0700 Subject: [PATCH 1/3] Add cache to instantiateType function to break cycles --- src/compiler/checker.ts | 14 ++++++++++++++ src/compiler/types.ts | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9bd0d6475fe..e49deba6032 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3866,6 +3866,19 @@ module ts { } function instantiateAnonymousType(type: ObjectType, mapper: TypeMapper): ObjectType { + // If this type has already been instantiated using this mapper, returned the cached result. This guards against + // infinite instantiations of cyclic types, e.g. "var x: { a: T, b: typeof x };" + if (mapper.mappings) { + for (let mapping of mapper.mappings) { + if (mapping.type === type) { + return mapping.result; + } + } + } + else { + mapper.mappings = []; + } + // Instantiate the given type using the given mapper and cache the result let result = createObjectType(TypeFlags.Anonymous, type.symbol); result.properties = instantiateList(getPropertiesOfObjectType(type), mapper, instantiateSymbol); result.members = createSymbolTable(result.properties); @@ -3875,6 +3888,7 @@ module ts { let numberIndexType = getIndexTypeOfType(type, IndexKind.Number); if (stringIndexType) result.stringIndexType = instantiateType(stringIndexType, mapper); if (numberIndexType) result.numberIndexType = instantiateType(numberIndexType, mapper); + mapper.mappings.push({ type, result }); return result; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 86e680ca8b0..0f810c9fe61 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1586,9 +1586,15 @@ module ts { Number, } + export interface TypeMapping { + type: Type; + result: Type; + } + /* @internal */ export interface TypeMapper { (t: TypeParameter): Type; + mappings?: TypeMapping[]; // Type mapping cache } /* @internal */ From c303e14b2854dc7b289f864324a46dd52eee11e3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 20 May 2015 16:31:03 -0700 Subject: [PATCH 2/3] Adding regression test --- .../reference/cyclicTypeInstantiation.js | 36 ++++++++++++ .../reference/cyclicTypeInstantiation.symbols | 54 ++++++++++++++++++ .../reference/cyclicTypeInstantiation.types | 57 +++++++++++++++++++ .../cases/compiler/cyclicTypeInstantiation.ts | 20 +++++++ 4 files changed, 167 insertions(+) create mode 100644 tests/baselines/reference/cyclicTypeInstantiation.js create mode 100644 tests/baselines/reference/cyclicTypeInstantiation.symbols create mode 100644 tests/baselines/reference/cyclicTypeInstantiation.types create mode 100644 tests/cases/compiler/cyclicTypeInstantiation.ts diff --git a/tests/baselines/reference/cyclicTypeInstantiation.js b/tests/baselines/reference/cyclicTypeInstantiation.js new file mode 100644 index 00000000000..087eb002418 --- /dev/null +++ b/tests/baselines/reference/cyclicTypeInstantiation.js @@ -0,0 +1,36 @@ +//// [cyclicTypeInstantiation.ts] +function foo() { + var x: { + a: T; + b: typeof x; + }; + return x; +} + +function bar() { + var x: { + a: T; + b: typeof x; + }; + return x; +} + +var a = foo(); +var b = bar(); +// Relating types of a and b produces instantiations of the cyclic anonymous types in foo and bar +a = b; + + +//// [cyclicTypeInstantiation.js] +function foo() { + var x; + return x; +} +function bar() { + var x; + return x; +} +var a = foo(); +var b = bar(); +// Relating types of a and b produces instantiations of the cyclic anonymous types in foo and bar +a = b; diff --git a/tests/baselines/reference/cyclicTypeInstantiation.symbols b/tests/baselines/reference/cyclicTypeInstantiation.symbols new file mode 100644 index 00000000000..74e59da188e --- /dev/null +++ b/tests/baselines/reference/cyclicTypeInstantiation.symbols @@ -0,0 +1,54 @@ +=== tests/cases/compiler/cyclicTypeInstantiation.ts === +function foo() { +>foo : Symbol(foo, Decl(cyclicTypeInstantiation.ts, 0, 0)) +>T : Symbol(T, Decl(cyclicTypeInstantiation.ts, 0, 13)) + + var x: { +>x : Symbol(x, Decl(cyclicTypeInstantiation.ts, 1, 7)) + + a: T; +>a : Symbol(a, Decl(cyclicTypeInstantiation.ts, 1, 12)) +>T : Symbol(T, Decl(cyclicTypeInstantiation.ts, 0, 13)) + + b: typeof x; +>b : Symbol(b, Decl(cyclicTypeInstantiation.ts, 2, 13)) +>x : Symbol(x, Decl(cyclicTypeInstantiation.ts, 1, 7)) + + }; + return x; +>x : Symbol(x, Decl(cyclicTypeInstantiation.ts, 1, 7)) +} + +function bar() { +>bar : Symbol(bar, Decl(cyclicTypeInstantiation.ts, 6, 1)) +>T : Symbol(T, Decl(cyclicTypeInstantiation.ts, 8, 13)) + + var x: { +>x : Symbol(x, Decl(cyclicTypeInstantiation.ts, 9, 7)) + + a: T; +>a : Symbol(a, Decl(cyclicTypeInstantiation.ts, 9, 12)) +>T : Symbol(T, Decl(cyclicTypeInstantiation.ts, 8, 13)) + + b: typeof x; +>b : Symbol(b, Decl(cyclicTypeInstantiation.ts, 10, 13)) +>x : Symbol(x, Decl(cyclicTypeInstantiation.ts, 9, 7)) + + }; + return x; +>x : Symbol(x, Decl(cyclicTypeInstantiation.ts, 9, 7)) +} + +var a = foo(); +>a : Symbol(a, Decl(cyclicTypeInstantiation.ts, 16, 3)) +>foo : Symbol(foo, Decl(cyclicTypeInstantiation.ts, 0, 0)) + +var b = bar(); +>b : Symbol(b, Decl(cyclicTypeInstantiation.ts, 17, 3)) +>bar : Symbol(bar, Decl(cyclicTypeInstantiation.ts, 6, 1)) + +// Relating types of a and b produces instantiations of the cyclic anonymous types in foo and bar +a = b; +>a : Symbol(a, Decl(cyclicTypeInstantiation.ts, 16, 3)) +>b : Symbol(b, Decl(cyclicTypeInstantiation.ts, 17, 3)) + diff --git a/tests/baselines/reference/cyclicTypeInstantiation.types b/tests/baselines/reference/cyclicTypeInstantiation.types new file mode 100644 index 00000000000..c3a39fc90a3 --- /dev/null +++ b/tests/baselines/reference/cyclicTypeInstantiation.types @@ -0,0 +1,57 @@ +=== tests/cases/compiler/cyclicTypeInstantiation.ts === +function foo() { +>foo : () => { a: T; b: any; } +>T : T + + var x: { +>x : { a: T; b: any; } + + a: T; +>a : T +>T : T + + b: typeof x; +>b : { a: T; b: any; } +>x : { a: T; b: any; } + + }; + return x; +>x : { a: T; b: any; } +} + +function bar() { +>bar : () => { a: T; b: any; } +>T : T + + var x: { +>x : { a: T; b: any; } + + a: T; +>a : T +>T : T + + b: typeof x; +>b : { a: T; b: any; } +>x : { a: T; b: any; } + + }; + return x; +>x : { a: T; b: any; } +} + +var a = foo(); +>a : { a: string; b: any; } +>foo() : { a: string; b: any; } +>foo : () => { a: T; b: any; } + +var b = bar(); +>b : { a: string; b: any; } +>bar() : { a: string; b: any; } +>bar : () => { a: T; b: any; } + +// Relating types of a and b produces instantiations of the cyclic anonymous types in foo and bar +a = b; +>a = b : { a: string; b: any; } +>a : { a: string; b: any; } +>b : { a: string; b: any; } + diff --git a/tests/cases/compiler/cyclicTypeInstantiation.ts b/tests/cases/compiler/cyclicTypeInstantiation.ts new file mode 100644 index 00000000000..79a222df089 --- /dev/null +++ b/tests/cases/compiler/cyclicTypeInstantiation.ts @@ -0,0 +1,20 @@ +function foo() { + var x: { + a: T; + b: typeof x; + }; + return x; +} + +function bar() { + var x: { + a: T; + b: typeof x; + }; + return x; +} + +var a = foo(); +var b = bar(); +// Relating types of a and b produces instantiations of the cyclic anonymous types in foo and bar +a = b; From ebcdd85ad06503b3776135f6c50e56c7d0f8a27d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 May 2015 09:18:55 -0700 Subject: [PATCH 3/3] Changing type mapping cache to be a dictionary --- src/compiler/checker.ts | 11 +++++------ src/compiler/types.ts | 7 +------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e49deba6032..528ee97602b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3869,14 +3869,13 @@ module ts { // If this type has already been instantiated using this mapper, returned the cached result. This guards against // infinite instantiations of cyclic types, e.g. "var x: { a: T, b: typeof x };" if (mapper.mappings) { - for (let mapping of mapper.mappings) { - if (mapping.type === type) { - return mapping.result; - } + let cached = mapper.mappings[type.id]; + if (cached) { + return cached; } } else { - mapper.mappings = []; + mapper.mappings = {}; } // Instantiate the given type using the given mapper and cache the result let result = createObjectType(TypeFlags.Anonymous, type.symbol); @@ -3888,7 +3887,7 @@ module ts { let numberIndexType = getIndexTypeOfType(type, IndexKind.Number); if (stringIndexType) result.stringIndexType = instantiateType(stringIndexType, mapper); if (numberIndexType) result.numberIndexType = instantiateType(numberIndexType, mapper); - mapper.mappings.push({ type, result }); + mapper.mappings[type.id] = result; return result; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0f810c9fe61..c8d22b26728 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1586,15 +1586,10 @@ module ts { Number, } - export interface TypeMapping { - type: Type; - result: Type; - } - /* @internal */ export interface TypeMapper { (t: TypeParameter): Type; - mappings?: TypeMapping[]; // Type mapping cache + mappings?: Map; // Type mapping cache } /* @internal */