Skip to content

Commit e3812ff

Browse files
committed
Merge pull request #3157 from Microsoft/fixUnionReduction
Fix infinite recursion in union type reduction
2 parents 8d6dd18 + 194680f commit e3812ff

11 files changed

+76
-45
lines changed

src/compiler/checker.ts

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ module ts {
8888
let undefinedType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined");
8989
let nullType = createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsUndefinedOrNull, "null");
9090
let unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
91+
let circularType = createIntrinsicType(TypeFlags.Any, "__circular__");
9192

9293
let emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
9394
let anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
@@ -3575,28 +3576,14 @@ module ts {
35753576
return false;
35763577
}
35773578

3578-
// Since removeSubtypes checks the subtype relation, and the subtype relation on a union
3579-
// may attempt to reduce a union, it is possible that removeSubtypes could be called
3580-
// recursively on the same set of types. The removeSubtypesStack is used to track which
3581-
// sets of types are currently undergoing subtype reduction.
3582-
let removeSubtypesStack: string[] = [];
35833579
function removeSubtypes(types: Type[]) {
3584-
let typeListId = getTypeListId(types);
3585-
if (removeSubtypesStack.lastIndexOf(typeListId) >= 0) {
3586-
return;
3587-
}
3588-
3589-
removeSubtypesStack.push(typeListId);
3590-
35913580
let i = types.length;
35923581
while (i > 0) {
35933582
i--;
35943583
if (isSubtypeOfAny(types[i], types)) {
35953584
types.splice(i, 1);
35963585
}
35973586
}
3598-
3599-
removeSubtypesStack.pop();
36003587
}
36013588

36023589
function containsAnyType(types: Type[]) {
@@ -3651,10 +3638,20 @@ module ts {
36513638
return type;
36523639
}
36533640

3641+
// Subtype reduction is basically an optimization we do to avoid excessively large union types, which take longer
3642+
// to process and look strange in quick info and error messages. Semantically there is no difference between the
3643+
// reduced type and the type itself. So, when we detect a circularity we simply say that the reduced type is the
3644+
// type itself.
36543645
function getReducedTypeOfUnionType(type: UnionType): Type {
3655-
// If union type was created without subtype reduction, perform the deferred reduction now
36563646
if (!type.reducedType) {
3657-
type.reducedType = getUnionType(type.types, /*noSubtypeReduction*/ false);
3647+
type.reducedType = circularType;
3648+
let reducedType = getUnionType(type.types, /*noSubtypeReduction*/ false);
3649+
if (type.reducedType === circularType) {
3650+
type.reducedType = reducedType;
3651+
}
3652+
}
3653+
else if (type.reducedType === circularType) {
3654+
type.reducedType = type;
36583655
}
36593656
return type.reducedType;
36603657
}

tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.js renamed to tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction1.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//// [unionTypeWithRecursiveSubtypeReduction.ts]
1+
//// [unionTypeWithRecursiveSubtypeReduction1.ts]
22
class Module {
33
public members: Class[];
44
}
@@ -16,9 +16,10 @@ class Property {
1616
}
1717

1818
var t: Class | Property;
19-
t.parent;
19+
t.parent;
20+
2021

21-
//// [unionTypeWithRecursiveSubtypeReduction.js]
22+
//// [unionTypeWithRecursiveSubtypeReduction1.js]
2223
var Module = (function () {
2324
function Module() {
2425
}
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,45 @@
1-
=== tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction.ts ===
1+
=== tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction1.ts ===
22
class Module {
3-
>Module : Symbol(Module, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 0, 0))
3+
>Module : Symbol(Module, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 0, 0))
44

55
public members: Class[];
6-
>members : Symbol(members, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 0, 14))
7-
>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1))
6+
>members : Symbol(members, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 0, 14))
7+
>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 6, 1))
88
}
99

1010
class Namespace {
11-
>Namespace : Symbol(Namespace, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 2, 1))
11+
>Namespace : Symbol(Namespace, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 2, 1))
1212

1313
public members: (Class | Property)[];
14-
>members : Symbol(members, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 4, 17))
15-
>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1))
16-
>Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 10, 1))
14+
>members : Symbol(members, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 4, 17))
15+
>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 6, 1))
16+
>Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 10, 1))
1717
}
1818

1919
class Class {
20-
>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1))
20+
>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 6, 1))
2121

2222
public parent: Namespace;
23-
>parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 8, 13))
24-
>Namespace : Symbol(Namespace, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 2, 1))
23+
>parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 8, 13))
24+
>Namespace : Symbol(Namespace, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 2, 1))
2525
}
2626

2727
class Property {
28-
>Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 10, 1))
28+
>Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 10, 1))
2929

3030
public parent: Module | Class;
31-
>parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 12, 16))
32-
>Module : Symbol(Module, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 0, 0))
33-
>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1))
31+
>parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 12, 16))
32+
>Module : Symbol(Module, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 0, 0))
33+
>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 6, 1))
3434
}
3535

3636
var t: Class | Property;
37-
>t : Symbol(t, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 16, 3))
38-
>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 6, 1))
39-
>Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 10, 1))
37+
>t : Symbol(t, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 16, 3))
38+
>Class : Symbol(Class, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 6, 1))
39+
>Property : Symbol(Property, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 10, 1))
4040

4141
t.parent;
42-
>t.parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 8, 13), Decl(unionTypeWithRecursiveSubtypeReduction.ts, 12, 16))
43-
>t : Symbol(t, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 16, 3))
44-
>parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction.ts, 8, 13), Decl(unionTypeWithRecursiveSubtypeReduction.ts, 12, 16))
42+
>t.parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 8, 13), Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 12, 16))
43+
>t : Symbol(t, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 16, 3))
44+
>parent : Symbol(parent, Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 8, 13), Decl(unionTypeWithRecursiveSubtypeReduction1.ts, 12, 16))
4545

tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction.types renamed to tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction1.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
=== tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction.ts ===
1+
=== tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction1.ts ===
22
class Module {
33
>Module : Module
44

tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction2.errors.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,5 @@ tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction2.ts(20,1): error TS2
4242
!!! error TS2322: Types of property 'parent' are incompatible.
4343
!!! error TS2322: Type 'Namespace' is not assignable to type 'Module | Class'.
4444
!!! error TS2322: Type 'Namespace' is not assignable to type 'Class'.
45-
!!! error TS2322: Property 'parent' is missing in type 'Namespace'.
45+
!!! error TS2322: Property 'parent' is missing in type 'Namespace'.
46+

tests/baselines/reference/unionTypeWithRecursiveSubtypeReduction2.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ class Property {
1818
var c: Class;
1919
var p: Property;
2020
c = p;
21-
p = c;
21+
p = c;
22+
2223

2324
//// [unionTypeWithRecursiveSubtypeReduction2.js]
2425
var Module = (function () {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction3.ts(5,5): error TS2322: Type '{ prop: number; } | { prop: { prop: number; } | any; }' is not assignable to type 'string'.
2+
Type '{ prop: number; }' is not assignable to type 'string'.
3+
4+
5+
==== tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction3.ts (1 errors) ====
6+
var a27: { prop: number } | { prop: T27 };
7+
type T27 = typeof a27;
8+
9+
var b: T27;
10+
var s: string = b;
11+
~
12+
!!! error TS2322: Type '{ prop: number; } | { prop: { prop: number; } | any; }' is not assignable to type 'string'.
13+
!!! error TS2322: Type '{ prop: number; }' is not assignable to type 'string'.
14+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//// [unionTypeWithRecursiveSubtypeReduction3.ts]
2+
var a27: { prop: number } | { prop: T27 };
3+
type T27 = typeof a27;
4+
5+
var b: T27;
6+
var s: string = b;
7+
8+
9+
//// [unionTypeWithRecursiveSubtypeReduction3.js]
10+
var a27;
11+
var b;
12+
var s = b;

tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction.ts renamed to tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction1.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ class Property {
1515
}
1616

1717
var t: Class | Property;
18-
t.parent;
18+
t.parent;

tests/cases/compiler/unionTypeWithRecursiveSubtypeReduction2.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ class Property {
1717
var c: Class;
1818
var p: Property;
1919
c = p;
20-
p = c;
20+
p = c;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
var a27: { prop: number } | { prop: T27 };
2+
type T27 = typeof a27;
3+
4+
var b: T27;
5+
var s: string = b;

0 commit comments

Comments
 (0)