Merge pull request #1180 from Microsoft/recursiveTypeComparison
Improve caching in recursive type comparisons (fixes #1170)
This commit is contained in:
commit
9f284184be
7 changed files with 205 additions and 12 deletions
|
@ -3234,9 +3234,9 @@ module ts {
|
||||||
|
|
||||||
// TYPE CHECKING
|
// TYPE CHECKING
|
||||||
|
|
||||||
var subtypeRelation: Map<Ternary> = {};
|
var subtypeRelation: Map<boolean> = {};
|
||||||
var assignableRelation: Map<Ternary> = {};
|
var assignableRelation: Map<boolean> = {};
|
||||||
var identityRelation: Map<Ternary> = {};
|
var identityRelation: Map<boolean> = {};
|
||||||
|
|
||||||
function isTypeIdenticalTo(source: Type, target: Type): boolean {
|
function isTypeIdenticalTo(source: Type, target: Type): boolean {
|
||||||
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined);
|
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined);
|
||||||
|
@ -3271,7 +3271,7 @@ module ts {
|
||||||
function checkTypeRelatedTo(
|
function checkTypeRelatedTo(
|
||||||
source: Type,
|
source: Type,
|
||||||
target: Type,
|
target: Type,
|
||||||
relation: Map<Ternary>,
|
relation: Map<boolean>,
|
||||||
errorNode: Node,
|
errorNode: Node,
|
||||||
headMessage?: DiagnosticMessage,
|
headMessage?: DiagnosticMessage,
|
||||||
containingMessageChain?: DiagnosticMessageChain): boolean {
|
containingMessageChain?: DiagnosticMessageChain): boolean {
|
||||||
|
@ -3279,6 +3279,7 @@ module ts {
|
||||||
var errorInfo: DiagnosticMessageChain;
|
var errorInfo: DiagnosticMessageChain;
|
||||||
var sourceStack: ObjectType[];
|
var sourceStack: ObjectType[];
|
||||||
var targetStack: ObjectType[];
|
var targetStack: ObjectType[];
|
||||||
|
var maybeStack: Map<boolean>[];
|
||||||
var expandingFlags: number;
|
var expandingFlags: number;
|
||||||
var depth = 0;
|
var depth = 0;
|
||||||
var overflow = false;
|
var overflow = false;
|
||||||
|
@ -3437,12 +3438,12 @@ module ts {
|
||||||
var id = source.id + "," + target.id;
|
var id = source.id + "," + target.id;
|
||||||
var related = relation[id];
|
var related = relation[id];
|
||||||
if (related !== undefined) {
|
if (related !== undefined) {
|
||||||
return related;
|
return related ? Ternary.True : Ternary.False;
|
||||||
}
|
}
|
||||||
if (depth > 0) {
|
if (depth > 0) {
|
||||||
for (var i = 0; i < depth; i++) {
|
for (var i = 0; i < depth; i++) {
|
||||||
// If source and target are already being compared, consider them related with assumptions
|
// 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;
|
return Ternary.Maybe;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3454,16 +3455,19 @@ module ts {
|
||||||
else {
|
else {
|
||||||
sourceStack = [];
|
sourceStack = [];
|
||||||
targetStack = [];
|
targetStack = [];
|
||||||
|
maybeStack = [];
|
||||||
expandingFlags = 0;
|
expandingFlags = 0;
|
||||||
}
|
}
|
||||||
sourceStack[depth] = source;
|
sourceStack[depth] = source;
|
||||||
targetStack[depth] = target;
|
targetStack[depth] = target;
|
||||||
|
maybeStack[depth] = {};
|
||||||
|
maybeStack[depth][id] = true;
|
||||||
depth++;
|
depth++;
|
||||||
var saveExpandingFlags = expandingFlags;
|
var saveExpandingFlags = expandingFlags;
|
||||||
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack)) expandingFlags |= 1;
|
if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack)) expandingFlags |= 1;
|
||||||
if (!(expandingFlags & 2) && isDeeplyNestedGeneric(target, targetStack)) expandingFlags |= 2;
|
if (!(expandingFlags & 2) && isDeeplyNestedGeneric(target, targetStack)) expandingFlags |= 2;
|
||||||
if (expandingFlags === 3) {
|
if (expandingFlags === 3) {
|
||||||
var result = Ternary.True;
|
var result = Ternary.Maybe;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var result = propertiesRelatedTo(source, target, reportErrors);
|
var result = propertiesRelatedTo(source, target, reportErrors);
|
||||||
|
@ -3482,9 +3486,18 @@ module ts {
|
||||||
}
|
}
|
||||||
expandingFlags = saveExpandingFlags;
|
expandingFlags = saveExpandingFlags;
|
||||||
depth--;
|
depth--;
|
||||||
// Only cache results that are free of assumptions
|
if (result) {
|
||||||
if (result !== Ternary.Maybe) {
|
var maybeCache = maybeStack[depth];
|
||||||
relation[id] = result;
|
// If result is definitely true, copy assumptions to global cache, else copy to next level up
|
||||||
|
var destinationCache = result === Ternary.True || depth === 0 ? relation : maybeStack[depth - 1];
|
||||||
|
for (var p in maybeCache) {
|
||||||
|
destinationCache[p] = maybeCache[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;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -5420,7 +5433,7 @@ module ts {
|
||||||
return typeArgumentsAreAssignable;
|
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++) {
|
for (var i = 0; i < args.length; i++) {
|
||||||
var arg = args[i];
|
var arg = args[i];
|
||||||
var argType: Type;
|
var argType: Type;
|
||||||
|
@ -5614,7 +5627,7 @@ module ts {
|
||||||
|
|
||||||
return resolveErrorCall(node);
|
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++) {
|
for (var i = 0; i < candidates.length; i++) {
|
||||||
if (!hasCorrectArity(node, args, candidates[i])) {
|
if (!hasCorrectArity(node, args, candidates[i])) {
|
||||||
continue;
|
continue;
|
||||||
|
|
21
tests/baselines/reference/recursiveTypeComparison.js
Normal file
21
tests/baselines/reference/recursiveTypeComparison.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//// [recursiveTypeComparison.ts]
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
|
||||||
|
//// [recursiveTypeComparison.js]
|
||||||
|
// Before fix this would take an exceeding long time to complete (#1170)
|
||||||
|
var p;
|
||||||
|
var stuck = p;
|
44
tests/baselines/reference/recursiveTypeComparison.types
Normal file
44
tests/baselines/reference/recursiveTypeComparison.types
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
=== tests/cases/compiler/recursiveTypeComparison.ts ===
|
||||||
|
// Before fix this would take an exceeding long time to complete (#1170)
|
||||||
|
|
||||||
|
interface Observable<T> {
|
||||||
|
>Observable : Observable<T>
|
||||||
|
>T : T
|
||||||
|
|
||||||
|
// This member can't be of type T, Property<T>, or Observable<anything but T>
|
||||||
|
needThisOne: Observable<T>;
|
||||||
|
>needThisOne : Observable<T>
|
||||||
|
>Observable : Observable<T>
|
||||||
|
>T : T
|
||||||
|
|
||||||
|
// Add more to make it slower
|
||||||
|
expo1: Property<T[]>; // 0.31 seconds in check
|
||||||
|
>expo1 : Property<T[]>
|
||||||
|
>Property : Property<T>
|
||||||
|
>T : T
|
||||||
|
|
||||||
|
expo2: Property<T[]>; // 3.11 seconds
|
||||||
|
>expo2 : Property<T[]>
|
||||||
|
>Property : Property<T>
|
||||||
|
>T : T
|
||||||
|
|
||||||
|
expo3: Property<T[]>; // 82.28 seconds
|
||||||
|
>expo3 : Property<T[]>
|
||||||
|
>Property : Property<T>
|
||||||
|
>T : T
|
||||||
|
}
|
||||||
|
interface Property<T> extends Observable<T> { }
|
||||||
|
>Property : Property<T>
|
||||||
|
>T : T
|
||||||
|
>Observable : Observable<T>
|
||||||
|
>T : T
|
||||||
|
|
||||||
|
var p: Observable<{}>;
|
||||||
|
>p : Observable<{}>
|
||||||
|
>Observable : Observable<T>
|
||||||
|
|
||||||
|
var stuck: Property<number> = p;
|
||||||
|
>stuck : Property<number>
|
||||||
|
>Property : Property<T>
|
||||||
|
>p : Observable<{}>
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
tests/cases/compiler/recursiveTypeComparison2.ts(13,80): error TS2304: Cannot find name 'StateValue'.
|
||||||
|
|
||||||
|
|
||||||
|
==== tests/cases/compiler/recursiveTypeComparison2.ts (1 errors) ====
|
||||||
|
// 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>;
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
!!! error TS2304: Cannot find name 'StateValue'.
|
||||||
|
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();
|
35
tests/baselines/reference/recursiveTypeComparison2.js
Normal file
35
tests/baselines/reference/recursiveTypeComparison2.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
//// [recursiveTypeComparison2.ts]
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
//// [recursiveTypeComparison2.js]
|
||||||
|
// Before fix this would cause compiler to hang (#1170)
|
||||||
|
var stuck = new Bacon.Bus();
|
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