Skip to content

Commit 00a43d7

Browse files
authored
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
1 parent 367b820 commit 00a43d7

6 files changed

+206
-32
lines changed

src/compiler/checker.ts

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12561,12 +12561,12 @@ namespace ts {
1256112561
return true;
1256212562
}
1256312563
const id = getSymbolId(sourceSymbol) + "," + getSymbolId(targetSymbol);
12564-
const relation = enumRelation.get(id);
12565-
if (relation !== undefined && !(relation === RelationComparisonResult.Failed && errorReporter)) {
12566-
return relation === RelationComparisonResult.Succeeded;
12564+
const entry = enumRelation.get(id);
12565+
if (entry !== undefined && !(!(entry & RelationComparisonResult.Reported) && entry & RelationComparisonResult.Failed && errorReporter)) {
12566+
return !!(entry & RelationComparisonResult.Succeeded);
1256712567
}
1256812568
if (sourceSymbol.escapedName !== targetSymbol.escapedName || !(sourceSymbol.flags & SymbolFlags.RegularEnum) || !(targetSymbol.flags & SymbolFlags.RegularEnum)) {
12569-
enumRelation.set(id, RelationComparisonResult.FailedAndReported);
12569+
enumRelation.set(id, RelationComparisonResult.Failed | RelationComparisonResult.Reported);
1257012570
return false;
1257112571
}
1257212572
const targetEnumType = getTypeOfSymbol(targetSymbol);
@@ -12577,7 +12577,7 @@ namespace ts {
1257712577
if (errorReporter) {
1257812578
errorReporter(Diagnostics.Property_0_is_missing_in_type_1, symbolName(property),
1257912579
typeToString(getDeclaredTypeOfSymbol(targetSymbol), /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType));
12580-
enumRelation.set(id, RelationComparisonResult.FailedAndReported);
12580+
enumRelation.set(id, RelationComparisonResult.Failed | RelationComparisonResult.Reported);
1258112581
}
1258212582
else {
1258312583
enumRelation.set(id, RelationComparisonResult.Failed);
@@ -12642,7 +12642,7 @@ namespace ts {
1264212642
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
1264312643
const related = relation.get(getRelationKey(source, target, relation));
1264412644
if (related !== undefined) {
12645-
return related === RelationComparisonResult.Succeeded;
12645+
return !!(related & RelationComparisonResult.Succeeded);
1264612646
}
1264712647
}
1264812648
if (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable) {
@@ -13463,18 +13463,6 @@ namespace ts {
1346313463
return result;
1346413464
}
1346513465

13466-
function propagateSidebandVarianceFlags(typeArguments: readonly Type[], variances: VarianceFlags[]) {
13467-
for (let i = 0; i < variances.length; i++) {
13468-
const v = variances[i];
13469-
if (v & VarianceFlags.Unmeasurable) {
13470-
instantiateType(typeArguments[i], reportUnmeasurableMarkers);
13471-
}
13472-
if (v & VarianceFlags.Unreliable) {
13473-
instantiateType(typeArguments[i], reportUnreliableMarkers);
13474-
}
13475-
}
13476-
}
13477-
1347813466
// Determine if possibly recursive types are related. First, check if the result is already available in the global cache.
1347913467
// Second, check if we have already started a comparison of the given two types in which case we assume the result to be true.
1348013468
// 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 {
1348513473
return Ternary.False;
1348613474
}
1348713475
const id = getRelationKey(source, target, relation);
13488-
const related = relation.get(id);
13489-
if (related !== undefined) {
13490-
if (reportErrors && related === RelationComparisonResult.Failed) {
13476+
const entry = relation.get(id);
13477+
if (entry !== undefined) {
13478+
if (reportErrors && entry & RelationComparisonResult.Failed && !(entry & RelationComparisonResult.Reported)) {
1349113479
// We are elaborating errors and the cached result is an unreported failure. The result will be reported
1349213480
// as a failure, and should be updated as a reported failure by the bottom of this function.
1349313481
}
1349413482
else {
1349513483
if (outofbandVarianceMarkerHandler) {
1349613484
// We're in the middle of variance checking - integrate any unmeasurable/unreliable flags from this cached component
13497-
if (source.flags & (TypeFlags.Object | TypeFlags.Conditional) && source.aliasSymbol &&
13498-
source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) {
13499-
propagateSidebandVarianceFlags(source.aliasTypeArguments, getAliasVariances(source.aliasSymbol));
13485+
const saved = entry & RelationComparisonResult.ReportsMask;
13486+
if (saved & RelationComparisonResult.ReportsUnmeasurable) {
13487+
instantiateType(source, reportUnmeasurableMarkers);
1350013488
}
13501-
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target && length((<TypeReference>source).typeArguments)) {
13502-
propagateSidebandVarianceFlags((<TypeReference>source).typeArguments!, getVariances((<TypeReference>source).target));
13489+
if (saved & RelationComparisonResult.ReportsUnreliable) {
13490+
instantiateType(source, reportUnreliableMarkers);
1350313491
}
1350413492
}
13505-
return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False;
13493+
return entry & RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False;
1350613494
}
1350713495
}
1350813496
if (!maybeKeys) {
@@ -13531,22 +13519,34 @@ namespace ts {
1353113519
const saveExpandingFlags = expandingFlags;
1353213520
if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, depth)) expandingFlags |= ExpandingFlags.Source;
1353313521
if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, depth)) expandingFlags |= ExpandingFlags.Target;
13522+
let originalHandler: typeof outofbandVarianceMarkerHandler;
13523+
let propagatingVarianceFlags: RelationComparisonResult = 0;
13524+
if (outofbandVarianceMarkerHandler) {
13525+
originalHandler = outofbandVarianceMarkerHandler;
13526+
outofbandVarianceMarkerHandler = onlyUnreliable => {
13527+
propagatingVarianceFlags |= onlyUnreliable ? RelationComparisonResult.ReportsUnreliable : RelationComparisonResult.ReportsUnmeasurable;
13528+
return originalHandler!(onlyUnreliable);
13529+
};
13530+
}
1353413531
const result = expandingFlags !== ExpandingFlags.Both ? structuredTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent) : Ternary.Maybe;
13532+
if (outofbandVarianceMarkerHandler) {
13533+
outofbandVarianceMarkerHandler = originalHandler;
13534+
}
1353513535
expandingFlags = saveExpandingFlags;
1353613536
depth--;
1353713537
if (result) {
1353813538
if (result === Ternary.True || depth === 0) {
1353913539
// If result is definitely true, record all maybe keys as having succeeded
1354013540
for (let i = maybeStart; i < maybeCount; i++) {
13541-
relation.set(maybeKeys[i], RelationComparisonResult.Succeeded);
13541+
relation.set(maybeKeys[i], RelationComparisonResult.Succeeded | propagatingVarianceFlags);
1354213542
}
1354313543
maybeCount = maybeStart;
1354413544
}
1354513545
}
1354613546
else {
1354713547
// A false result goes straight into global cache (when something is false under
1354813548
// assumptions it will also be false without assumptions)
13549-
relation.set(id, reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed);
13549+
relation.set(id, (reportErrors ? RelationComparisonResult.Reported : 0) | RelationComparisonResult.Failed | propagatingVarianceFlags);
1355013550
maybeCount = maybeStart;
1355113551
}
1355213552
return result;

src/compiler/types.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -614,9 +614,13 @@ namespace ts {
614614

615615
/* @internal */
616616
export const enum RelationComparisonResult {
617-
Succeeded = 1, // Should be truthy
618-
Failed = 2,
619-
FailedAndReported = 3
617+
Succeeded = 1 << 0, // Should be truthy
618+
Failed = 1 << 1,
619+
Reported = 1 << 2,
620+
621+
ReportsUnmeasurable = 1 << 3,
622+
ReportsUnreliable = 1 << 4,
623+
ReportsMask = ReportsUnmeasurable | ReportsUnreliable
620624
}
621625

622626
export interface Node extends TextRange {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [varianceRepeatedlyPropegatesWithUnreliableFlag.ts]
2+
type A = { a: number };
3+
type B = { b: number };
4+
type X<T> = ({ [K in keyof T]: T[K] } & Record<string, void>)[keyof T];
5+
type P1<T> = { data: X<T> };
6+
type P2<T> = { data: X<T> };
7+
8+
interface I<T> {
9+
fn<K extends keyof T>(p1: P1<Pick<T, K>>, p2: P2<Pick<T, K>>): void;
10+
}
11+
12+
const i: I<A & B> = null as any;
13+
const p2: P2<A> = null as any;
14+
15+
// Commenting out the below line will remove the error on the `const _i: I<A> = i;`
16+
i.fn(null as any, p2);
17+
18+
const _i: I<A> = i;
19+
20+
//// [varianceRepeatedlyPropegatesWithUnreliableFlag.js]
21+
var i = null;
22+
var p2 = null;
23+
// Commenting out the below line will remove the error on the `const _i: I<A> = i;`
24+
i.fn(null, p2);
25+
var _i = i;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
=== tests/cases/compiler/varianceRepeatedlyPropegatesWithUnreliableFlag.ts ===
2+
type A = { a: number };
3+
>A : Symbol(A, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 0))
4+
>a : Symbol(a, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 10))
5+
6+
type B = { b: number };
7+
>B : Symbol(B, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 23))
8+
>b : Symbol(b, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 1, 10))
9+
10+
type X<T> = ({ [K in keyof T]: T[K] } & Record<string, void>)[keyof T];
11+
>X : Symbol(X, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 1, 23))
12+
>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 7))
13+
>K : Symbol(K, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 16))
14+
>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 7))
15+
>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 7))
16+
>K : Symbol(K, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 16))
17+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
18+
>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 7))
19+
20+
type P1<T> = { data: X<T> };
21+
>P1 : Symbol(P1, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 71))
22+
>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 3, 8))
23+
>data : Symbol(data, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 3, 14))
24+
>X : Symbol(X, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 1, 23))
25+
>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 3, 8))
26+
27+
type P2<T> = { data: X<T> };
28+
>P2 : Symbol(P2, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 3, 28))
29+
>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 4, 8))
30+
>data : Symbol(data, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 4, 14))
31+
>X : Symbol(X, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 1, 23))
32+
>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 4, 8))
33+
34+
interface I<T> {
35+
>I : Symbol(I, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 4, 28))
36+
>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 12))
37+
38+
fn<K extends keyof T>(p1: P1<Pick<T, K>>, p2: P2<Pick<T, K>>): void;
39+
>fn : Symbol(I.fn, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 16))
40+
>K : Symbol(K, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 7, 7))
41+
>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 12))
42+
>p1 : Symbol(p1, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 7, 26))
43+
>P1 : Symbol(P1, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 2, 71))
44+
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
45+
>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 12))
46+
>K : Symbol(K, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 7, 7))
47+
>p2 : Symbol(p2, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 7, 45))
48+
>P2 : Symbol(P2, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 3, 28))
49+
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
50+
>T : Symbol(T, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 12))
51+
>K : Symbol(K, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 7, 7))
52+
}
53+
54+
const i: I<A & B> = null as any;
55+
>i : Symbol(i, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 10, 5))
56+
>I : Symbol(I, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 4, 28))
57+
>A : Symbol(A, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 0))
58+
>B : Symbol(B, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 23))
59+
60+
const p2: P2<A> = null as any;
61+
>p2 : Symbol(p2, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 11, 5))
62+
>P2 : Symbol(P2, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 3, 28))
63+
>A : Symbol(A, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 0))
64+
65+
// Commenting out the below line will remove the error on the `const _i: I<A> = i;`
66+
i.fn(null as any, p2);
67+
>i.fn : Symbol(I.fn, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 16))
68+
>i : Symbol(i, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 10, 5))
69+
>fn : Symbol(I.fn, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 6, 16))
70+
>p2 : Symbol(p2, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 11, 5))
71+
72+
const _i: I<A> = i;
73+
>_i : Symbol(_i, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 16, 5))
74+
>I : Symbol(I, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 4, 28))
75+
>A : Symbol(A, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 0, 0))
76+
>i : Symbol(i, Decl(varianceRepeatedlyPropegatesWithUnreliableFlag.ts, 10, 5))
77+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
=== tests/cases/compiler/varianceRepeatedlyPropegatesWithUnreliableFlag.ts ===
2+
type A = { a: number };
3+
>A : A
4+
>a : number
5+
6+
type B = { b: number };
7+
>B : B
8+
>b : number
9+
10+
type X<T> = ({ [K in keyof T]: T[K] } & Record<string, void>)[keyof T];
11+
>X : ({ [K in keyof T]: T[K]; } & Record<string, void>)[keyof T]
12+
13+
type P1<T> = { data: X<T> };
14+
>P1 : P1<T>
15+
>data : ({ [K in keyof T]: T[K]; } & Record<string, void>)[keyof T]
16+
17+
type P2<T> = { data: X<T> };
18+
>P2 : P2<T>
19+
>data : ({ [K in keyof T]: T[K]; } & Record<string, void>)[keyof T]
20+
21+
interface I<T> {
22+
fn<K extends keyof T>(p1: P1<Pick<T, K>>, p2: P2<Pick<T, K>>): void;
23+
>fn : <K extends keyof T>(p1: P1<Pick<T, K>>, p2: P2<Pick<T, K>>) => void
24+
>p1 : P1<Pick<T, K>>
25+
>p2 : P2<Pick<T, K>>
26+
}
27+
28+
const i: I<A & B> = null as any;
29+
>i : I<A & B>
30+
>null as any : any
31+
>null : null
32+
33+
const p2: P2<A> = null as any;
34+
>p2 : P2<A>
35+
>null as any : any
36+
>null : null
37+
38+
// Commenting out the below line will remove the error on the `const _i: I<A> = i;`
39+
i.fn(null as any, p2);
40+
>i.fn(null as any, p2) : void
41+
>i.fn : <K extends "a" | "b">(p1: P1<Pick<A & B, K>>, p2: P2<Pick<A & B, K>>) => void
42+
>i : I<A & B>
43+
>fn : <K extends "a" | "b">(p1: P1<Pick<A & B, K>>, p2: P2<Pick<A & B, K>>) => void
44+
>null as any : any
45+
>null : null
46+
>p2 : P2<A>
47+
48+
const _i: I<A> = i;
49+
>_i : I<A>
50+
>i : I<A & B>
51+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
type A = { a: number };
2+
type B = { b: number };
3+
type X<T> = ({ [K in keyof T]: T[K] } & Record<string, void>)[keyof T];
4+
type P1<T> = { data: X<T> };
5+
type P2<T> = { data: X<T> };
6+
7+
interface I<T> {
8+
fn<K extends keyof T>(p1: P1<Pick<T, K>>, p2: P2<Pick<T, K>>): void;
9+
}
10+
11+
const i: I<A & B> = null as any;
12+
const p2: P2<A> = null as any;
13+
14+
// Commenting out the below line will remove the error on the `const _i: I<A> = i;`
15+
i.fn(null as any, p2);
16+
17+
const _i: I<A> = i;

0 commit comments

Comments
 (0)