diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4c5712299da..b45e281c89f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3572,7 +3572,18 @@ module ts { return false; } + // Since removeSubtypes checks the subtype relation, and the subtype relation on a union + // may attempt to reduce a union, it is possible that removeSubtypes could be called + // recursively on the same set of types. + let removeSubtypesStack: string[] = []; function removeSubtypes(types: Type[]) { + let typeListId = getTypeListId(types); + if (removeSubtypesStack.lastIndexOf(typeListId) >= 0) { + return; + } + + removeSubtypesStack.push(typeListId); + let i = types.length; while (i > 0) { i--; @@ -3580,6 +3591,8 @@ module ts { types.splice(i, 1); } } + + removeSubtypesStack.pop(); } function containsAnyType(types: Type[]) { diff --git a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.js b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.js new file mode 100644 index 00000000000..9442d733aa4 --- /dev/null +++ b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.js @@ -0,0 +1,43 @@ +//// [unionTypeWithRecursiveSubtypeReduction.ts] +class Module { + public members: Class[]; +} + +class Namespace { + public members: (Class | Property)[]; +} + +class Class { + public parent: Namespace; +} + +class Property { + public parent: Module | Class; +} + +var t: Class | Property; +t.parent; + +//// [unionTypeWithRecursiveSubtypeReduction.js] +var Module = (function () { + function Module() { + } + return Module; +})(); +var Namespace = (function () { + function Namespace() { + } + return Namespace; +})(); +var Class = (function () { + function Class() { + } + return Class; +})(); +var Property = (function () { + function Property() { + } + return Property; +})(); +var t; +t.parent; diff --git a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.symbols b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.symbols new file mode 100644 index 00000000000..93351990f6c --- /dev/null +++ b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.symbols @@ -0,0 +1,45 @@ +=== tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction.ts === +class Module { +>Module : Symbol(Module, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 0, 0)) + + public members: Class[]; +>members : Symbol(members, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 0, 14)) +>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1)) +} + +class Namespace { +>Namespace : Symbol(Namespace, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 2, 1)) + + public members: (Class | Property)[]; +>members : Symbol(members, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 4, 17)) +>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1)) +>Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 10, 1)) +} + +class Class { +>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1)) + + public parent: Namespace; +>parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 8, 13)) +>Namespace : Symbol(Namespace, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 2, 1)) +} + +class Property { +>Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 10, 1)) + + public parent: Module | Class; +>parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 12, 16)) +>Module : Symbol(Module, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 0, 0)) +>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1)) +} + +var t: Class | Property; +>t : Symbol(t, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 16, 3)) +>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1)) +>Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 10, 1)) + +t.parent; +>t.parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 8, 13), Decl(unionTypeWithRecursiveSubtypeReduction.ts, 12, 16)) +>t : Symbol(t, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 16, 3)) +>parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 8, 13), Decl(unionTypeWithRecursiveSubtypeReduction.ts, 12, 16)) + diff --git a/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.types b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.types new file mode 100644 index 00000000000..cf8f41d70cc --- /dev/null +++ b/tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.types @@ -0,0 +1,45 @@ +=== tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction.ts === +class Module { +>Module : Module + + public members: Class[]; +>members : Class[] +>Class : Class +} + +class Namespace { +>Namespace : Namespace + + public members: (Class | Property)[]; +>members : (Class | Property)[] +>Class : Class +>Property : Property +} + +class Class { +>Class : Class + + public parent: Namespace; +>parent : Namespace +>Namespace : Namespace +} + +class Property { +>Property : Property + + public parent: Module | Class; +>parent : Module | Class +>Module : Module +>Class : Class +} + +var t: Class | Property; +>t : Class | Property +>Class : Class +>Property : Property + +t.parent; +>t.parent : Class | Namespace +>t : Class | Property +>parent : Class | Namespace + diff --git a/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction.ts b/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction.ts new file mode 100644 index 00000000000..651adf44895 --- /dev/null +++ b/tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction.ts @@ -0,0 +1,18 @@ +class Module { + public members: Class[]; +} + +class Namespace { + public members: (Class | Property)[]; +} + +class Class { + public parent: Namespace; +} + +class Property { + public parent: Module | Class; +} + +var t: Class | Property; +t.parent; \ No newline at end of file