Improve caching in recursive type comparisons (fixes #1170)

This commit is contained in:
Anders Hejlsberg 2014-11-17 10:42:53 -08:00
parent bdfb5697af
commit 100e1c8a77
3 changed files with 68 additions and 11 deletions

View file

@ -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;

View 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;

View 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();