diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b3f88661a8cc5..fdd3cac69b4f0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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 && (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; + 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; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index eb568ed5cff35..76bc461d8786b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -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 { 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