From c4f96b67089ce3ce975dacb5e53028393d10311b Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 2 Jul 2019 16:24:02 -0700 Subject: [PATCH] Cache propagating variance flags in the relationship cache --- src/compiler/checker.ts | 50 ++++++------ src/compiler/types.ts | 7 +- ...eRepeatedlyPropegatesWithUnreliableFlag.js | 25 ++++++ ...atedlyPropegatesWithUnreliableFlag.symbols | 77 +++++++++++++++++++ ...peatedlyPropegatesWithUnreliableFlag.types | 51 ++++++++++++ ...eRepeatedlyPropegatesWithUnreliableFlag.ts | 17 ++++ 6 files changed, 202 insertions(+), 25 deletions(-) create mode 100644 tests/baselines/reference/varianceRepeatedlyPropegatesWithUnreliableFlag.js create mode 100644 tests/baselines/reference/varianceRepeatedlyPropegatesWithUnreliableFlag.symbols create mode 100644 tests/baselines/reference/varianceRepeatedlyPropegatesWithUnreliableFlag.types create mode 100644 tests/cases/compiler/varianceRepeatedlyPropegatesWithUnreliableFlag.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 893e7901d64cd..07aed33f6223e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12264,8 +12264,9 @@ namespace ts { return true; } const id = getSymbolId(sourceSymbol) + "," + getSymbolId(targetSymbol); - const relation = enumRelation.get(id); - if (relation !== undefined && !(relation === RelationComparisonResult.Failed && errorReporter)) { + const entry = enumRelation.get(id); + const relation = entry! & RelationComparisonResult.ResultMask; + if (entry !== undefined && !(relation === RelationComparisonResult.Failed && errorReporter)) { return relation === RelationComparisonResult.Succeeded; } if (sourceSymbol.escapedName !== targetSymbol.escapedName || !(sourceSymbol.flags & SymbolFlags.RegularEnum) || !(targetSymbol.flags & SymbolFlags.RegularEnum)) { @@ -12345,7 +12346,7 @@ namespace ts { if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) { const related = relation.get(getRelationKey(source, target, relation)); if (related !== undefined) { - return related === RelationComparisonResult.Succeeded; + return (related & RelationComparisonResult.ResultMask) === RelationComparisonResult.Succeeded; } } if (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable) { @@ -13011,18 +13012,6 @@ namespace ts { return result; } - function propagateSidebandVarianceFlags(typeArguments: readonly Type[], variances: VarianceFlags[]) { - for (let i = 0; i < variances.length; i++) { - const v = variances[i]; - if (v & VarianceFlags.Unmeasurable) { - instantiateType(typeArguments[i], reportUnmeasurableMarkers); - } - if (v & VarianceFlags.Unreliable) { - instantiateType(typeArguments[i], reportUnreliableMarkers); - } - } - } - // Determine if possibly recursive types are related. First, check if the result is already available in the global cache. // Second, check if we have already started a comparison of the given two types in which case we assume the result to be true. // Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are @@ -13033,8 +13022,9 @@ namespace ts { return Ternary.False; } const id = getRelationKey(source, target, relation); - const related = relation.get(id); - if (related !== undefined) { + const entry = relation.get(id); + const related = entry! & RelationComparisonResult.ResultMask; + if (entry !== undefined) { if (reportErrors && related === RelationComparisonResult.Failed) { // We are elaborating errors and the cached result is an unreported failure. The result will be reported // as a failure, and should be updated as a reported failure by the bottom of this function. @@ -13042,12 +13032,12 @@ namespace ts { else { if (outofbandVarianceMarkerHandler) { // We're in the middle of variance checking - integrate any unmeasurable/unreliable flags from this cached component - if (source.flags & (TypeFlags.Object | TypeFlags.Conditional) && source.aliasSymbol && - source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) { - propagateSidebandVarianceFlags(source.aliasTypeArguments, getAliasVariances(source.aliasSymbol)); + const saved = entry & RelationComparisonResult.ReportsMask; + if (saved & RelationComparisonResult.ReportsUnmeasurable) { + instantiateType(source, reportUnmeasurableMarkers); } - if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target && length((source).typeArguments)) { - propagateSidebandVarianceFlags((source).typeArguments!, getVariances((source).target)); + if (saved & RelationComparisonResult.ReportsUnreliable) { + instantiateType(source, reportUnreliableMarkers); } } return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False; @@ -13079,14 +13069,26 @@ namespace ts { const saveExpandingFlags = expandingFlags; if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, depth)) expandingFlags |= ExpandingFlags.Source; if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, depth)) expandingFlags |= ExpandingFlags.Target; + let originalHandler: typeof outofbandVarianceMarkerHandler; + let propagatingVarianceFlags: RelationComparisonResult = 0; + if (outofbandVarianceMarkerHandler) { + originalHandler = outofbandVarianceMarkerHandler; + outofbandVarianceMarkerHandler = onlyUnreliable => { + propagatingVarianceFlags |= onlyUnreliable ? RelationComparisonResult.ReportsUnreliable : RelationComparisonResult.ReportsUnmeasurable; + return originalHandler!(onlyUnreliable); + }; + } const result = expandingFlags !== ExpandingFlags.Both ? structuredTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent) : Ternary.Maybe; + if (outofbandVarianceMarkerHandler) { + outofbandVarianceMarkerHandler = originalHandler; + } expandingFlags = saveExpandingFlags; depth--; if (result) { if (result === Ternary.True || depth === 0) { // If result is definitely true, record all maybe keys as having succeeded for (let i = maybeStart; i < maybeCount; i++) { - relation.set(maybeKeys[i], RelationComparisonResult.Succeeded); + relation.set(maybeKeys[i], RelationComparisonResult.Succeeded | propagatingVarianceFlags); } maybeCount = maybeStart; } @@ -13094,7 +13096,7 @@ namespace ts { else { // A false result goes straight into global cache (when something is false under // assumptions it will also be false without assumptions) - relation.set(id, reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed); + relation.set(id, (reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed) | propagatingVarianceFlags); maybeCount = maybeStart; } return result; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7e3483f843422..36fbc1db04426 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -612,7 +612,12 @@ namespace ts { export const enum RelationComparisonResult { Succeeded = 1, // Should be truthy Failed = 2, - FailedAndReported = 3 + FailedAndReported = 3, + ResultMask = 0x3, + + ReportsUnmeasurable = 1 << 2, + ReportsUnreliable = 1 << 3, + ReportsMask = ReportsUnmeasurable | ReportsUnreliable } export interface Node extends TextRange { diff --git a/tests/baselines/reference/varianceRepeatedlyPropegatesWithUnreliableFlag.js b/tests/baselines/reference/varianceRepeatedlyPropegatesWithUnreliableFlag.js new file mode 100644 index 0000000000000..73062e82014f6 --- /dev/null +++ b/tests/baselines/reference/varianceRepeatedlyPropegatesWithUnreliableFlag.js @@ -0,0 +1,25 @@ +//// [varianceRepeatedlyPropegatesWithUnreliableFlag.ts] +type A = { a: number }; +type B = { b: number }; +type X = ({ [K in keyof T]: T[K] } & Record)[keyof T]; +type P1 = { data: X }; +type P2 = { data: X }; + +interface I { + fn(p1: P1>, p2: P2>): void; +} + +const i: I = null as any; +const p2: P2 = null as any; + +// Commenting out the below line will remove the error on the `const _i: I = i;` +i.fn(null as any, p2); + +const _i: I = i; + +//// [varianceRepeatedlyPropegatesWithUnreliableFlag.js] +var i = null; +var p2 = null; +// Commenting out the below line will remove the error on the `const _i: I = i;` +i.fn(null, p2); +var _i = i; diff --git a/tests/baselines/reference/varianceRepeatedlyPropegatesWithUnreliableFlag.symbols b/tests/baselines/reference/varianceRepeatedlyPropegatesWithUnreliableFlag.symbols new file mode 100644 index 0000000000000..b2e1f46f1e87b --- /dev/null +++ b/tests/baselines/reference/varianceRepeatedlyPropegatesWithUnreliableFlag.symbols @@ -0,0 +1,77 @@ +=== tests/cases/compiler/varianceRepeatedlyPropegatesWithUnreliableFlag.ts === +type A = { a: number }; +>A : Symbol(A, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 0)) +>a : Symbol(a, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 10)) + +type B = { b: number }; +>B : Symbol(B, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 23)) +>b : Symbol(b, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 1, 10)) + +type X = ({ [K in keyof T]: T[K] } & Record)[keyof T]; +>X : Symbol(X, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 1, 23)) +>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 7)) +>K : Symbol(K, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 16)) +>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 7)) +>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 7)) +>K : Symbol(K, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 16)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 7)) + +type P1 = { data: X }; +>P1 : Symbol(P1, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 71)) +>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 3, 8)) +>data : Symbol(data, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 3, 14)) +>X : Symbol(X, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 1, 23)) +>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 3, 8)) + +type P2 = { data: X }; +>P2 : Symbol(P2, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 3, 28)) +>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 4, 8)) +>data : Symbol(data, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 4, 14)) +>X : Symbol(X, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 1, 23)) +>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 4, 8)) + +interface I { +>I : Symbol(I, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 4, 28)) +>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 12)) + + fn(p1: P1>, p2: P2>): void; +>fn : Symbol(I.fn, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 16)) +>K : Symbol(K, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 7, 7)) +>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 12)) +>p1 : Symbol(p1, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 7, 26)) +>P1 : Symbol(P1, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 71)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 12)) +>K : Symbol(K, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 7, 7)) +>p2 : Symbol(p2, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 7, 45)) +>P2 : Symbol(P2, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 3, 28)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 12)) +>K : Symbol(K, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 7, 7)) +} + +const i: I = null as any; +>i : Symbol(i, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 10, 5)) +>I : Symbol(I, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 4, 28)) +>A : Symbol(A, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 0)) +>B : Symbol(B, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 23)) + +const p2: P2 = null as any; +>p2 : Symbol(p2, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 11, 5)) +>P2 : Symbol(P2, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 3, 28)) +>A : Symbol(A, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 0)) + +// Commenting out the below line will remove the error on the `const _i: I = i;` +i.fn(null as any, p2); +>i.fn : Symbol(I.fn, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 16)) +>i : Symbol(i, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 10, 5)) +>fn : Symbol(I.fn, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 16)) +>p2 : Symbol(p2, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 11, 5)) + +const _i: I = i; +>_i : Symbol(_i, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 16, 5)) +>I : Symbol(I, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 4, 28)) +>A : Symbol(A, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 0)) +>i : Symbol(i, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 10, 5)) + diff --git a/tests/baselines/reference/varianceRepeatedlyPropegatesWithUnreliableFlag.types b/tests/baselines/reference/varianceRepeatedlyPropegatesWithUnreliableFlag.types new file mode 100644 index 0000000000000..82bf01d05d922 --- /dev/null +++ b/tests/baselines/reference/varianceRepeatedlyPropegatesWithUnreliableFlag.types @@ -0,0 +1,51 @@ +=== tests/cases/compiler/varianceRepeatedlyPropegatesWithUnreliableFlag.ts === +type A = { a: number }; +>A : A +>a : number + +type B = { b: number }; +>B : B +>b : number + +type X = ({ [K in keyof T]: T[K] } & Record)[keyof T]; +>X : ({ [K in keyof T]: T[K]; } & Record)[keyof T] + +type P1 = { data: X }; +>P1 : P1 +>data : ({ [K in keyof T]: T[K]; } & Record)[keyof T] + +type P2 = { data: X }; +>P2 : P2 +>data : ({ [K in keyof T]: T[K]; } & Record)[keyof T] + +interface I { + fn(p1: P1>, p2: P2>): void; +>fn : (p1: P1>, p2: P2>) => void +>p1 : P1> +>p2 : P2> +} + +const i: I = null as any; +>i : I +>null as any : any +>null : null + +const p2: P2 = null as any; +>p2 : P2 +>null as any : any +>null : null + +// Commenting out the below line will remove the error on the `const _i: I = i;` +i.fn(null as any, p2); +>i.fn(null as any, p2) : void +>i.fn : (p1: P1>, p2: P2>) => void +>i : I +>fn : (p1: P1>, p2: P2>) => void +>null as any : any +>null : null +>p2 : P2 + +const _i: I = i; +>_i : I +>i : I + diff --git a/tests/cases/compiler/varianceRepeatedlyPropegatesWithUnreliableFlag.ts b/tests/cases/compiler/varianceRepeatedlyPropegatesWithUnreliableFlag.ts new file mode 100644 index 0000000000000..03cdaee0e9d83 --- /dev/null +++ b/tests/cases/compiler/varianceRepeatedlyPropegatesWithUnreliableFlag.ts @@ -0,0 +1,17 @@ +type A = { a: number }; +type B = { b: number }; +type X = ({ [K in keyof T]: T[K] } & Record)[keyof T]; +type P1 = { data: X }; +type P2 = { data: X }; + +interface I { + fn(p1: P1>, p2: P2>): void; +} + +const i: I = null as any; +const p2: P2 = null as any; + +// Commenting out the below line will remove the error on the `const _i: I = i;` +i.fn(null as any, p2); + +const _i: I = i; \ No newline at end of file