Improve caching in recursive type comparisons (fixes #1170)
This commit is contained in:
parent
bdfb5697af
commit
100e1c8a77
3 changed files with 68 additions and 11 deletions
|
@ -3221,9 +3221,9 @@ module ts {
|
|||
|
||||
// TYPE CHECKING
|
||||
|
||||
var subtypeRelation: Map<Ternary> = {};
|
||||
var assignableRelation: Map<Ternary> = {};
|
||||
var identityRelation: Map<Ternary> = {};
|
||||
var subtypeRelation: Map<boolean> = {};
|
||||
var assignableRelation: Map<boolean> = {};
|
||||
var identityRelation: Map<boolean> = {};
|
||||
|
||||
function isTypeIdenticalTo(source: Type, target: Type): boolean {
|
||||
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined);
|
||||
|
@ -3258,7 +3258,7 @@ module ts {
|
|||
function checkTypeRelatedTo(
|
||||
source: Type,
|
||||
target: Type,
|
||||
relation: Map<Ternary>,
|
||||
relation: Map<boolean>,
|
||||
errorNode: Node,
|
||||
headMessage?: DiagnosticMessage,
|
||||
containingMessageChain?: DiagnosticMessageChain): boolean {
|
||||
|
@ -3266,6 +3266,7 @@ module ts {
|
|||
var errorInfo: DiagnosticMessageChain;
|
||||
var sourceStack: ObjectType[];
|
||||
var targetStack: ObjectType[];
|
||||
var maybeStack: Map<boolean>[];
|
||||
var expandingFlags: number;
|
||||
var depth = 0;
|
||||
var overflow = false;
|
||||
|
@ -3424,12 +3425,12 @@ module ts {
|
|||
var id = source.id + "," + target.id;
|
||||
var related = relation[id];
|
||||
if (related !== undefined) {
|
||||
return related;
|
||||
return related ? Ternary.True : Ternary.False;
|
||||
}
|
||||
if (depth > 0) {
|
||||
for (var i = 0; i < depth; i++) {
|
||||
// If source and target are already being compared, consider them related with assumptions
|
||||
if (source === sourceStack[i] && target === targetStack[i]) {
|
||||
if (maybeStack[i][id]) {
|
||||
return Ternary.Maybe;
|
||||
}
|
||||
}
|
||||
|
@ -3441,10 +3442,13 @@ module ts {
|
|||
else {
|
||||
sourceStack = [];
|
||||
targetStack = [];
|
||||
maybeStack = [];
|
||||
expandingFlags = 0;
|
||||
}
|
||||
sourceStack[depth] = source;
|
||||
targetStack[depth] = target;
|
||||
maybeStack[depth] = {};
|
||||
maybeStack[depth][id] = true;
|
||||
depth++;
|
||||
var saveExpandingFlags = expandingFlags;
|
||||
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack)) expandingFlags |= 1;
|
||||
|
@ -3469,9 +3473,18 @@ module ts {
|
|||
}
|
||||
expandingFlags = saveExpandingFlags;
|
||||
depth--;
|
||||
// Only cache results that are free of assumptions
|
||||
if (result !== Ternary.Maybe) {
|
||||
relation[id] = result;
|
||||
if (result) {
|
||||
var sourceCache = maybeStack[depth];
|
||||
// If result is definitely true, copy assumptions to global cache, else copy to next level up
|
||||
var targetCache = result === Ternary.True || depth === 0 ? relation : maybeStack[depth - 1];
|
||||
for (var p in sourceCache) {
|
||||
targetCache[p] = sourceCache[p];
|
||||
}
|
||||
}
|
||||
else {
|
||||
// A false result goes straight into global cache (when something is false under assumptions it
|
||||
// will also be false without assumptions)
|
||||
relation[id] = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -5399,7 +5412,7 @@ module ts {
|
|||
return typeArgumentsAreAssignable;
|
||||
}
|
||||
|
||||
function checkApplicableSignature(node: CallLikeExpression, args: Node[], signature: Signature, relation: Map<Ternary>, excludeArgument: boolean[], reportErrors: boolean) {
|
||||
function checkApplicableSignature(node: CallLikeExpression, args: Node[], signature: Signature, relation: Map<boolean>, excludeArgument: boolean[], reportErrors: boolean) {
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
var arg = args[i];
|
||||
var argType: Type;
|
||||
|
@ -5593,7 +5606,7 @@ module ts {
|
|||
|
||||
return resolveErrorCall(node);
|
||||
|
||||
function chooseOverload(candidates: Signature[], relation: Map<Ternary>) {
|
||||
function chooseOverload(candidates: Signature[], relation: Map<boolean>) {
|
||||
for (var i = 0; i < candidates.length; i++) {
|
||||
if (!hasCorrectArity(node, args, candidates[i])) {
|
||||
continue;
|
||||
|
|
14
tests/cases/compiler/recursiveTypeComparison.ts
Normal file
14
tests/cases/compiler/recursiveTypeComparison.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Before fix this would take an exceeding long time to complete (#1170)
|
||||
|
||||
interface Observable<T> {
|
||||
// This member can't be of type T, Property<T>, or Observable<anything but T>
|
||||
needThisOne: Observable<T>;
|
||||
// Add more to make it slower
|
||||
expo1: Property<T[]>; // 0.31 seconds in check
|
||||
expo2: Property<T[]>; // 3.11 seconds
|
||||
expo3: Property<T[]>; // 82.28 seconds
|
||||
}
|
||||
interface Property<T> extends Observable<T> { }
|
||||
|
||||
var p: Observable<{}>;
|
||||
var stuck: Property<number> = p;
|
30
tests/cases/compiler/recursiveTypeComparison2.ts
Normal file
30
tests/cases/compiler/recursiveTypeComparison2.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Before fix this would cause compiler to hang (#1170)
|
||||
|
||||
declare module Bacon {
|
||||
interface Event<T> {
|
||||
}
|
||||
interface Error<T> extends Event<T> {
|
||||
}
|
||||
interface Observable<T> {
|
||||
zip<U, V>(other: EventStream<U>, f: (a: T, b: U) => V): EventStream<V>;
|
||||
slidingWindow(max: number, min?: number): Property<T[]>;
|
||||
log(): Observable<T>;
|
||||
combine<U, V>(other: Observable<U>, f: (a: T, b: U) => V): Property<V>;
|
||||
withStateMachine<U, V>(initState: U, f: (state: U, event: Event<T>) => StateValue<U, V>): EventStream<V>;
|
||||
decode(mapping: Object): Property<any>;
|
||||
awaiting<U>(other: Observable<U>): Property<boolean>;
|
||||
endOnError(f?: (value: T) => boolean): Observable<T>;
|
||||
withHandler(f: (event: Event<T>) => any): Observable<T>;
|
||||
name(name: string): Observable<T>;
|
||||
withDescription(...args: any[]): Observable<T>;
|
||||
}
|
||||
interface Property<T> extends Observable<T> {
|
||||
}
|
||||
interface EventStream<T> extends Observable<T> {
|
||||
}
|
||||
interface Bus<T> extends EventStream<T> {
|
||||
}
|
||||
var Bus: new <T>() => Bus<T>;
|
||||
}
|
||||
|
||||
var stuck: Bacon.Bus<number> = new Bacon.Bus();
|
Loading…
Reference in a new issue