Cache propagating variance flags in the relationship cache (#32225)

* Cache propagating variance flags in the relationship cache

* Convert base fields in relation comparison result to flags
This commit is contained in:
Wesley Wigham 2019-09-23 16:55:19 -07:00 committed by GitHub
parent 367b82055c
commit 00a43d7b49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 206 additions and 32 deletions

View File

@ -12561,12 +12561,12 @@ namespace ts {
return true;
}
const id = getSymbolId(sourceSymbol) + "," + getSymbolId(targetSymbol);
const relation = enumRelation.get(id);
if (relation !== undefined && !(relation === RelationComparisonResult.Failed && errorReporter)) {
return relation === RelationComparisonResult.Succeeded;
const entry = enumRelation.get(id);
if (entry !== undefined && !(!(entry & RelationComparisonResult.Reported) && entry & RelationComparisonResult.Failed && errorReporter)) {
return !!(entry & RelationComparisonResult.Succeeded);
}
if (sourceSymbol.escapedName !== targetSymbol.escapedName || !(sourceSymbol.flags & SymbolFlags.RegularEnum) || !(targetSymbol.flags & SymbolFlags.RegularEnum)) {
enumRelation.set(id, RelationComparisonResult.FailedAndReported);
enumRelation.set(id, RelationComparisonResult.Failed | RelationComparisonResult.Reported);
return false;
}
const targetEnumType = getTypeOfSymbol(targetSymbol);
@ -12577,7 +12577,7 @@ namespace ts {
if (errorReporter) {
errorReporter(Diagnostics.Property_0_is_missing_in_type_1, symbolName(property),
typeToString(getDeclaredTypeOfSymbol(targetSymbol), /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType));
enumRelation.set(id, RelationComparisonResult.FailedAndReported);
enumRelation.set(id, RelationComparisonResult.Failed | RelationComparisonResult.Reported);
}
else {
enumRelation.set(id, RelationComparisonResult.Failed);
@ -12642,7 +12642,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.Succeeded);
}
}
if (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable) {
@ -13463,18 +13463,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
@ -13485,24 +13473,24 @@ namespace ts {
return Ternary.False;
}
const id = getRelationKey(source, target, relation);
const related = relation.get(id);
if (related !== undefined) {
if (reportErrors && related === RelationComparisonResult.Failed) {
const entry = relation.get(id);
if (entry !== undefined) {
if (reportErrors && entry & RelationComparisonResult.Failed && !(entry & RelationComparisonResult.Reported)) {
// 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.
}
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 && (<TypeReference>source).target === (<TypeReference>target).target && length((<TypeReference>source).typeArguments)) {
propagateSidebandVarianceFlags((<TypeReference>source).typeArguments!, getVariances((<TypeReference>source).target));
if (saved & RelationComparisonResult.ReportsUnreliable) {
instantiateType(source, reportUnreliableMarkers);
}
}
return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False;
return entry & RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False;
}
}
if (!maybeKeys) {
@ -13531,14 +13519,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;
}
@ -13546,7 +13546,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.Reported : 0) | RelationComparisonResult.Failed | propagatingVarianceFlags);
maybeCount = maybeStart;
}
return result;

View File

@ -614,9 +614,13 @@ namespace ts {
/* @internal */
export const enum RelationComparisonResult {
Succeeded = 1, // Should be truthy
Failed = 2,
FailedAndReported = 3
Succeeded = 1 << 0, // Should be truthy
Failed = 1 << 1,
Reported = 1 << 2,
ReportsUnmeasurable = 1 << 3,
ReportsUnreliable = 1 << 4,
ReportsMask = ReportsUnmeasurable | ReportsUnreliable
}
export interface Node extends TextRange {

View File

@ -0,0 +1,25 @@
//// [varianceRepeatedlyPropegatesWithUnreliableFlag.ts]
type A = { a: number };
type B = { b: number };
type X<T> = ({ [K in keyof T]: T[K] } & Record<string, void>)[keyof T];
type P1<T> = { data: X<T> };
type P2<T> = { data: X<T> };
interface I<T> {
fn<K extends keyof T>(p1: P1<Pick<T, K>>, p2: P2<Pick<T, K>>): void;
}
const i: I<A & B> = null as any;
const p2: P2<A> = null as any;
// Commenting out the below line will remove the error on the `const _i: I<A> = i;`
i.fn(null as any, p2);
const _i: I<A> = i;
//// [varianceRepeatedlyPropegatesWithUnreliableFlag.js]
var i = null;
var p2 = null;
// Commenting out the below line will remove the error on the `const _i: I<A> = i;`
i.fn(null, p2);
var _i = i;

View File

@ -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<T> = ({ [K in keyof T]: T[K] } & Record<string, void>)[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<T> = { data: X<T> };
>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<T> = { data: X<T> };
>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<T> {
>I : Symbol(I, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 4, 28))
>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 12))
fn<K extends keyof T>(p1: P1<Pick<T, K>>, p2: P2<Pick<T, K>>): 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<A & B> = 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<A> = 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<A> = 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<A> = 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))

View File

@ -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<T> = ({ [K in keyof T]: T[K] } & Record<string, void>)[keyof T];
>X : ({ [K in keyof T]: T[K]; } & Record<string, void>)[keyof T]
type P1<T> = { data: X<T> };
>P1 : P1<T>
>data : ({ [K in keyof T]: T[K]; } & Record<string, void>)[keyof T]
type P2<T> = { data: X<T> };
>P2 : P2<T>
>data : ({ [K in keyof T]: T[K]; } & Record<string, void>)[keyof T]
interface I<T> {
fn<K extends keyof T>(p1: P1<Pick<T, K>>, p2: P2<Pick<T, K>>): void;
>fn : <K extends keyof T>(p1: P1<Pick<T, K>>, p2: P2<Pick<T, K>>) => void
>p1 : P1<Pick<T, K>>
>p2 : P2<Pick<T, K>>
}
const i: I<A & B> = null as any;
>i : I<A & B>
>null as any : any
>null : null
const p2: P2<A> = null as any;
>p2 : P2<A>
>null as any : any
>null : null
// Commenting out the below line will remove the error on the `const _i: I<A> = i;`
i.fn(null as any, p2);
>i.fn(null as any, p2) : void
>i.fn : <K extends "a" | "b">(p1: P1<Pick<A & B, K>>, p2: P2<Pick<A & B, K>>) => void
>i : I<A & B>
>fn : <K extends "a" | "b">(p1: P1<Pick<A & B, K>>, p2: P2<Pick<A & B, K>>) => void
>null as any : any
>null : null
>p2 : P2<A>
const _i: I<A> = i;
>_i : I<A>
>i : I<A & B>

View File

@ -0,0 +1,17 @@
type A = { a: number };
type B = { b: number };
type X<T> = ({ [K in keyof T]: T[K] } & Record<string, void>)[keyof T];
type P1<T> = { data: X<T> };
type P2<T> = { data: X<T> };
interface I<T> {
fn<K extends keyof T>(p1: P1<Pick<T, K>>, p2: P2<Pick<T, K>>): void;
}
const i: I<A & B> = null as any;
const p2: P2<A> = null as any;
// Commenting out the below line will remove the error on the `const _i: I<A> = i;`
i.fn(null as any, p2);
const _i: I<A> = i;