Compare commits

...

3 commits

Author SHA1 Message Date
Anders Hejlsberg f528318a82 Call isDeeplyNestedType only when something changed 2021-10-25 07:36:17 -07:00
Anders Hejlsberg 4f5361ab60 Add regression test 2021-10-24 11:48:12 -07:00
Anders Hejlsberg 64d989e4c1 Still check for deep nesting when bypassing relation caching 2021-10-24 11:48:04 -07:00
6 changed files with 192 additions and 49 deletions

View file

@ -18213,7 +18213,7 @@ namespace ts {
if (source.flags & TypeFlags.UnionOrIntersection || target.flags & TypeFlags.UnionOrIntersection) { if (source.flags & TypeFlags.UnionOrIntersection || target.flags & TypeFlags.UnionOrIntersection) {
result = getConstituentCount(source) * getConstituentCount(target) >= 4 ? result = getConstituentCount(source) * getConstituentCount(target) >= 4 ?
recursiveTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck, recursionFlags) : recursiveTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck, recursionFlags) :
structuredTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck); recursiveTypeRelatedToUncached(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck, recursionFlags);
} }
if (!result && !(source.flags & TypeFlags.Union) && (source.flags & (TypeFlags.StructuredOrInstantiable) || target.flags & TypeFlags.StructuredOrInstantiable)) { if (!result && !(source.flags & TypeFlags.Union) && (source.flags & (TypeFlags.StructuredOrInstantiable) || target.flags & TypeFlags.StructuredOrInstantiable)) {
if (result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState, recursionFlags)) { if (result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState, recursionFlags)) {
@ -18626,9 +18626,6 @@ namespace ts {
// equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion // equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion
// and issue an error. Otherwise, actually compare the structure of the two types. // and issue an error. Otherwise, actually compare the structure of the two types.
function recursiveTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState, recursionFlags: RecursionFlags): Ternary { function recursiveTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState, recursionFlags: RecursionFlags): Ternary {
if (overflow) {
return Ternary.False;
}
const id = getRelationKey(source, target, intersectionState | (inPropertyCheck ? IntersectionState.InPropertyCheck : 0), relation); const id = getRelationKey(source, target, intersectionState | (inPropertyCheck ? IntersectionState.InPropertyCheck : 0), relation);
const entry = relation.get(id); const entry = relation.get(id);
if (entry !== undefined) { if (entry !== undefined) {
@ -18652,8 +18649,6 @@ namespace ts {
} }
if (!maybeKeys) { if (!maybeKeys) {
maybeKeys = []; maybeKeys = [];
sourceStack = [];
targetStack = [];
} }
else { else {
// generate a key where all type parameter id positions are replaced with unconstrained type parameter ids // generate a key where all type parameter id positions are replaced with unconstrained type parameter ids
@ -18669,25 +18664,10 @@ namespace ts {
return Ternary.Maybe; return Ternary.Maybe;
} }
} }
if (sourceDepth === 100 || targetDepth === 100) {
overflow = true;
return Ternary.False;
}
} }
const maybeStart = maybeCount; const maybeStart = maybeCount;
maybeKeys[maybeCount] = id; maybeKeys[maybeCount] = id;
maybeCount++; maybeCount++;
if (recursionFlags & RecursionFlags.Source) {
sourceStack[sourceDepth] = source;
sourceDepth++;
}
if (recursionFlags & RecursionFlags.Target) {
targetStack[targetDepth] = target;
targetDepth++;
}
const saveExpandingFlags = expandingFlags;
if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, sourceDepth)) expandingFlags |= ExpandingFlags.Source;
if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, targetDepth)) expandingFlags |= ExpandingFlags.Target;
let originalHandler: typeof outofbandVarianceMarkerHandler; let originalHandler: typeof outofbandVarianceMarkerHandler;
let propagatingVarianceFlags: RelationComparisonResult = 0; let propagatingVarianceFlags: RelationComparisonResult = 0;
if (outofbandVarianceMarkerHandler) { if (outofbandVarianceMarkerHandler) {
@ -18697,29 +18677,10 @@ namespace ts {
return originalHandler!(onlyUnreliable); return originalHandler!(onlyUnreliable);
}; };
} }
const result = recursiveTypeRelatedToUncached(source, target, reportErrors, intersectionState, recursionFlags);
if (expandingFlags === ExpandingFlags.Both) {
tracing?.instant(tracing.Phase.CheckTypes, "recursiveTypeRelatedTo_DepthLimit", {
sourceId: source.id,
sourceIdStack: sourceStack.map(t => t.id),
targetId: target.id,
targetIdStack: targetStack.map(t => t.id),
depth: sourceDepth,
targetDepth
});
}
const result = expandingFlags !== ExpandingFlags.Both ? structuredTypeRelatedTo(source, target, reportErrors, intersectionState) : Ternary.Maybe;
if (outofbandVarianceMarkerHandler) { if (outofbandVarianceMarkerHandler) {
outofbandVarianceMarkerHandler = originalHandler; outofbandVarianceMarkerHandler = originalHandler;
} }
expandingFlags = saveExpandingFlags;
if (recursionFlags & RecursionFlags.Source) {
sourceDepth--;
}
if (recursionFlags & RecursionFlags.Target) {
targetDepth--;
}
if (result) { if (result) {
if (result === Ternary.True || (sourceDepth === 0 && targetDepth === 0)) { if (result === Ternary.True || (sourceDepth === 0 && targetDepth === 0)) {
if (result === Ternary.True || result === Ternary.Maybe) { if (result === Ternary.True || result === Ternary.Maybe) {
@ -18741,14 +18702,52 @@ namespace ts {
return result; return result;
} }
function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary { function recursiveTypeRelatedToUncached(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState, recursionFlags: RecursionFlags): Ternary {
tracing?.push(tracing.Phase.CheckTypes, "structuredTypeRelatedTo", { sourceId: source.id, targetId: target.id }); if (sourceDepth >= 100 || targetDepth >= 100) {
const result = structuredTypeRelatedToWorker(source, target, reportErrors, intersectionState); overflow = true;
tracing?.pop(); }
if (overflow) {
return Ternary.False;
}
let result;
const saveExpandingFlags = expandingFlags;
if (recursionFlags & RecursionFlags.Source) {
(sourceStack || (sourceStack = []))[sourceDepth] = source;
sourceDepth++;
if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, sourceDepth)) expandingFlags |= ExpandingFlags.Source;
}
if (recursionFlags & RecursionFlags.Target) {
(targetStack || (targetStack = []))[targetDepth] = target;
targetDepth++;
if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, targetDepth)) expandingFlags |= ExpandingFlags.Target;
}
if (expandingFlags !== ExpandingFlags.Both) {
tracing?.push(tracing.Phase.CheckTypes, "structuredTypeRelatedTo", { sourceId: source.id, targetId: target.id });
result = structuredTypeRelatedTo(source, target, reportErrors, intersectionState);
tracing?.pop();
}
else {
tracing?.instant(tracing.Phase.CheckTypes, "recursiveTypeRelatedTo_DepthLimit", {
sourceId: source.id,
sourceIdStack: sourceStack.map(t => t.id),
targetId: target.id,
targetIdStack: targetStack.map(t => t.id),
depth: sourceDepth,
targetDepth
});
result = Ternary.Maybe;
}
if (recursionFlags & RecursionFlags.Source) {
sourceDepth--;
}
if (recursionFlags & RecursionFlags.Target) {
targetDepth--;
}
expandingFlags = saveExpandingFlags;
return result; return result;
} }
function structuredTypeRelatedToWorker(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary { function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
if (intersectionState & IntersectionState.PropertyCheck) { if (intersectionState & IntersectionState.PropertyCheck) {
return propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None); return propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None);
} }

View file

@ -46,4 +46,23 @@ tests/cases/compiler/deepComparisons.ts(4,9): error TS2322: Type 'T[K1][K2]' is
function f3<U>() { function f3<U>() {
let x: Foo1<U> = 0 as any as Bar<U>; // No error! let x: Foo1<U> = 0 as any as Bar<U>; // No error!
} }
// Repro from #46500
type F<T> = {} & (
T extends [any, ...any[]]
? { [K in keyof T]?: F<T[K]> }
: T extends any[]
? F<T[number]>[]
: T extends { [K: string]: any }
? { [K in keyof T]?: F<T[K]> }
: { x: string }
);
declare function f<T = any>(): F<T>;
function g() {
return f() as F<any>;
}

View file

@ -17,7 +17,26 @@ type Foo2<T> = { x: Foo1<T> };
function f3<U>() { function f3<U>() {
let x: Foo1<U> = 0 as any as Bar<U>; // No error! let x: Foo1<U> = 0 as any as Bar<U>; // No error!
} }
// Repro from #46500
type F<T> = {} & (
T extends [any, ...any[]]
? { [K in keyof T]?: F<T[K]> }
: T extends any[]
? F<T[number]>[]
: T extends { [K: string]: any }
? { [K in keyof T]?: F<T[K]> }
: { x: string }
);
declare function f<T = any>(): F<T>;
function g() {
return f() as F<any>;
}
//// [deepComparisons.js] //// [deepComparisons.js]
function f1() { function f1() {
@ -31,3 +50,6 @@ function f2() {
function f3() { function f3() {
var x = 0; // No error! var x = 0; // No error!
} }
function g() {
return f();
}

View file

@ -84,3 +84,57 @@ function f3<U>() {
>Bar : Symbol(Bar, Decl(deepComparisons.ts, 6, 28)) >Bar : Symbol(Bar, Decl(deepComparisons.ts, 6, 28))
>U : Symbol(U, Decl(deepComparisons.ts, 16, 12)) >U : Symbol(U, Decl(deepComparisons.ts, 16, 12))
} }
// Repro from #46500
type F<T> = {} & (
>F : Symbol(F, Decl(deepComparisons.ts, 18, 1))
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
T extends [any, ...any[]]
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
? { [K in keyof T]?: F<T[K]> }
>K : Symbol(K, Decl(deepComparisons.ts, 24, 13))
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
>F : Symbol(F, Decl(deepComparisons.ts, 18, 1))
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
>K : Symbol(K, Decl(deepComparisons.ts, 24, 13))
: T extends any[]
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
? F<T[number]>[]
>F : Symbol(F, Decl(deepComparisons.ts, 18, 1))
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
: T extends { [K: string]: any }
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
>K : Symbol(K, Decl(deepComparisons.ts, 27, 27))
? { [K in keyof T]?: F<T[K]> }
>K : Symbol(K, Decl(deepComparisons.ts, 28, 21))
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
>F : Symbol(F, Decl(deepComparisons.ts, 18, 1))
>T : Symbol(T, Decl(deepComparisons.ts, 22, 7))
>K : Symbol(K, Decl(deepComparisons.ts, 28, 21))
: { x: string }
>x : Symbol(x, Decl(deepComparisons.ts, 29, 19))
);
declare function f<T = any>(): F<T>;
>f : Symbol(f, Decl(deepComparisons.ts, 30, 2))
>T : Symbol(T, Decl(deepComparisons.ts, 32, 19))
>F : Symbol(F, Decl(deepComparisons.ts, 18, 1))
>T : Symbol(T, Decl(deepComparisons.ts, 32, 19))
function g() {
>g : Symbol(g, Decl(deepComparisons.ts, 32, 36))
return f() as F<any>;
>f : Symbol(f, Decl(deepComparisons.ts, 30, 2))
>F : Symbol(F, Decl(deepComparisons.ts, 18, 1))
}

View file

@ -56,3 +56,34 @@ function f3<U>() {
>0 as any : any >0 as any : any
>0 : 0 >0 : 0
} }
// Repro from #46500
type F<T> = {} & (
>F : F<T>
T extends [any, ...any[]]
? { [K in keyof T]?: F<T[K]> }
: T extends any[]
? F<T[number]>[]
: T extends { [K: string]: any }
>K : string
? { [K in keyof T]?: F<T[K]> }
: { x: string }
>x : string
);
declare function f<T = any>(): F<T>;
>f : <T = any>() => F<T>
function g() {
>g : () => F<any>
return f() as F<any>;
>f() as F<any> : F<any>
>f() : F<any>
>f : <T = any>() => F<T>
}

View file

@ -16,4 +16,22 @@ type Foo2<T> = { x: Foo1<T> };
function f3<U>() { function f3<U>() {
let x: Foo1<U> = 0 as any as Bar<U>; // No error! let x: Foo1<U> = 0 as any as Bar<U>; // No error!
} }
// Repro from #46500
type F<T> = {} & (
T extends [any, ...any[]]
? { [K in keyof T]?: F<T[K]> }
: T extends any[]
? F<T[number]>[]
: T extends { [K: string]: any }
? { [K in keyof T]?: F<T[K]> }
: { x: string }
);
declare function f<T = any>(): F<T>;
function g() {
return f() as F<any>;
}