Skip to content

Commit 100e1c8

Browse files
committed
Improve caching in recursive type comparisons (fixes #1170)
1 parent bdfb569 commit 100e1c8

File tree

3 files changed

+68
-11
lines changed

3 files changed

+68
-11
lines changed

src/compiler/checker.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3221,9 +3221,9 @@ module ts {
32213221

32223222
// TYPE CHECKING
32233223

3224-
var subtypeRelation: Map<Ternary> = {};
3225-
var assignableRelation: Map<Ternary> = {};
3226-
var identityRelation: Map<Ternary> = {};
3224+
var subtypeRelation: Map<boolean> = {};
3225+
var assignableRelation: Map<boolean> = {};
3226+
var identityRelation: Map<boolean> = {};
32273227

32283228
function isTypeIdenticalTo(source: Type, target: Type): boolean {
32293229
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined);
@@ -3258,14 +3258,15 @@ module ts {
32583258
function checkTypeRelatedTo(
32593259
source: Type,
32603260
target: Type,
3261-
relation: Map<Ternary>,
3261+
relation: Map<boolean>,
32623262
errorNode: Node,
32633263
headMessage?: DiagnosticMessage,
32643264
containingMessageChain?: DiagnosticMessageChain): boolean {
32653265

32663266
var errorInfo: DiagnosticMessageChain;
32673267
var sourceStack: ObjectType[];
32683268
var targetStack: ObjectType[];
3269+
var maybeStack: Map<boolean>[];
32693270
var expandingFlags: number;
32703271
var depth = 0;
32713272
var overflow = false;
@@ -3424,12 +3425,12 @@ module ts {
34243425
var id = source.id + "," + target.id;
34253426
var related = relation[id];
34263427
if (related !== undefined) {
3427-
return related;
3428+
return related ? Ternary.True : Ternary.False;
34283429
}
34293430
if (depth > 0) {
34303431
for (var i = 0; i < depth; i++) {
34313432
// If source and target are already being compared, consider them related with assumptions
3432-
if (source === sourceStack[i] && target === targetStack[i]) {
3433+
if (maybeStack[i][id]) {
34333434
return Ternary.Maybe;
34343435
}
34353436
}
@@ -3441,10 +3442,13 @@ module ts {
34413442
else {
34423443
sourceStack = [];
34433444
targetStack = [];
3445+
maybeStack = [];
34443446
expandingFlags = 0;
34453447
}
34463448
sourceStack[depth] = source;
34473449
targetStack[depth] = target;
3450+
maybeStack[depth] = {};
3451+
maybeStack[depth][id] = true;
34483452
depth++;
34493453
var saveExpandingFlags = expandingFlags;
34503454
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack)) expandingFlags |= 1;
@@ -3469,9 +3473,18 @@ module ts {
34693473
}
34703474
expandingFlags = saveExpandingFlags;
34713475
depth--;
3472-
// Only cache results that are free of assumptions
3473-
if (result !== Ternary.Maybe) {
3474-
relation[id] = result;
3476+
if (result) {
3477+
var sourceCache = maybeStack[depth];
3478+
// If result is definitely true, copy assumptions to global cache, else copy to next level up
3479+
var targetCache = result === Ternary.True || depth === 0 ? relation : maybeStack[depth - 1];
3480+
for (var p in sourceCache) {
3481+
targetCache[p] = sourceCache[p];
3482+
}
3483+
}
3484+
else {
3485+
// A false result goes straight into global cache (when something is false under assumptions it
3486+
// will also be false without assumptions)
3487+
relation[id] = false;
34753488
}
34763489
return result;
34773490
}
@@ -5399,7 +5412,7 @@ module ts {
53995412
return typeArgumentsAreAssignable;
54005413
}
54015414

5402-
function checkApplicableSignature(node: CallLikeExpression, args: Node[], signature: Signature, relation: Map<Ternary>, excludeArgument: boolean[], reportErrors: boolean) {
5415+
function checkApplicableSignature(node: CallLikeExpression, args: Node[], signature: Signature, relation: Map<boolean>, excludeArgument: boolean[], reportErrors: boolean) {
54035416
for (var i = 0; i < args.length; i++) {
54045417
var arg = args[i];
54055418
var argType: Type;
@@ -5593,7 +5606,7 @@ module ts {
55935606

55945607
return resolveErrorCall(node);
55955608

5596-
function chooseOverload(candidates: Signature[], relation: Map<Ternary>) {
5609+
function chooseOverload(candidates: Signature[], relation: Map<boolean>) {
55975610
for (var i = 0; i < candidates.length; i++) {
55985611
if (!hasCorrectArity(node, args, candidates[i])) {
55995612
continue;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Before fix this would take an exceeding long time to complete (#1170)
2+
3+
interface Observable<T> {
4+
// This member can't be of type T, Property<T>, or Observable<anything but T>
5+
needThisOne: Observable<T>;
6+
// Add more to make it slower
7+
expo1: Property<T[]>; // 0.31 seconds in check
8+
expo2: Property<T[]>; // 3.11 seconds
9+
expo3: Property<T[]>; // 82.28 seconds
10+
}
11+
interface Property<T> extends Observable<T> { }
12+
13+
var p: Observable<{}>;
14+
var stuck: Property<number> = p;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Before fix this would cause compiler to hang (#1170)
2+
3+
declare module Bacon {
4+
interface Event<T> {
5+
}
6+
interface Error<T> extends Event<T> {
7+
}
8+
interface Observable<T> {
9+
zip<U, V>(other: EventStream<U>, f: (a: T, b: U) => V): EventStream<V>;
10+
slidingWindow(max: number, min?: number): Property<T[]>;
11+
log(): Observable<T>;
12+
combine<U, V>(other: Observable<U>, f: (a: T, b: U) => V): Property<V>;
13+
withStateMachine<U, V>(initState: U, f: (state: U, event: Event<T>) => StateValue<U, V>): EventStream<V>;
14+
decode(mapping: Object): Property<any>;
15+
awaiting<U>(other: Observable<U>): Property<boolean>;
16+
endOnError(f?: (value: T) => boolean): Observable<T>;
17+
withHandler(f: (event: Event<T>) => any): Observable<T>;
18+
name(name: string): Observable<T>;
19+
withDescription(...args: any[]): Observable<T>;
20+
}
21+
interface Property<T> extends Observable<T> {
22+
}
23+
interface EventStream<T> extends Observable<T> {
24+
}
25+
interface Bus<T> extends EventStream<T> {
26+
}
27+
var Bus: new <T>() => Bus<T>;
28+
}
29+
30+
var stuck: Bacon.Bus<number> = new Bacon.Bus();

0 commit comments

Comments
 (0)