diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 10ddb3c0c88b4..f6cffb8246a61 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -765,6 +765,7 @@ type Checker struct { uniqueLiteralType *Type uniqueLiteralMapper *TypeMapper reliabilityFlags RelationComparisonResult + sawCircularVariance bool reportUnreliableMapper *TypeMapper reportUnmeasurableMapper *TypeMapper restrictiveMapper *TypeMapper diff --git a/tsc/internal/checker/relater.go b/tsc/internal/checker/relater.go index d3b6c86014498..9ad8d2bb880f1 100644 --- a/tsc/internal/checker/relater.go +++ b/tsc/internal/checker/relater.go @@ -1369,7 +1369,9 @@ func (c *Checker) getVariancesWorker(symbol *ast.Symbol, typeParameters []*Type) variance = VarianceFlagsContravariant default: saveReliabilityFlags := c.reliabilityFlags + saveSawCircularVariance := c.sawCircularVariance c.reliabilityFlags = 0 + c.sawCircularVariance = false // We first compare instantiations where the type parameter is replaced with // marker types that have a known subtype relationship. From this we can infer // invariance, covariance, contravariance or bivariance. @@ -1380,9 +1382,15 @@ func (c *Checker) getVariancesWorker(symbol *ast.Symbol, typeParameters []*Type) // If the instantiations appear to be related bivariantly it may be because the // type parameter is independent (i.e. it isn't witnessed anywhere in the generic // type). To determine this we compare instantiations where the type parameter is - // replaced with marker types that are known to be unrelated. + // replaced with marker types that are known to be unrelated. However, if the comparisons + // relied on variances of circular types still being computed, the type parameter may be + // witnessed through those types, so we can only conclude that the variance is unmeasurable. if variance == VarianceFlagsBivariant && c.isTypeAssignableTo(c.createMarkerType(symbol, tp, c.markerOtherType), typeWithSuper) { - variance = VarianceFlagsIndependent + if c.sawCircularVariance { + variance |= VarianceFlagsUnmeasurable + } else { + variance = VarianceFlagsIndependent + } } if c.reliabilityFlags&RelationComparisonResultReportsUnmeasurable != 0 { variance |= VarianceFlagsUnmeasurable @@ -1391,6 +1399,7 @@ func (c *Checker) getVariancesWorker(symbol *ast.Symbol, typeParameters []*Type) variance |= VarianceFlagsUnreliable } c.reliabilityFlags = saveReliabilityFlags + c.sawCircularVariance = saveSawCircularVariance } // If variance computation was restarted due to a circularity we may have already // computed variances for this generic type. If so, we exit early. @@ -3426,6 +3435,7 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo target.alias != nil && source.alias.symbol == target.alias.symbol && !(r.c.isMarkerType(source) || r.c.isMarkerType(target)) { variances := r.c.getAliasVariances(source.alias.symbol) if len(variances) == 0 { + r.c.sawCircularVariance = true return TernaryUnknown } params := r.c.typeAliasLinks.Get(source.alias.symbol).typeParameters @@ -3865,6 +3875,7 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo // effectively means we measure variance only from type parameter occurrences that aren't nested in // recursive instantiations of the generic type. if len(variances) == 0 { + r.c.sawCircularVariance = true return TernaryUnknown } varianceResult, ok := relateVariances(r.c.getTypeArguments(source), r.c.getTypeArguments(target), variances, intersectionState) diff --git a/tsc/testdata/baselines/reference/compiler/circularVariance3.errors.txt b/tsc/testdata/baselines/reference/compiler/circularVariance3.errors.txt new file mode 100644 index 0000000000000..647460190de4a --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/circularVariance3.errors.txt @@ -0,0 +1,30 @@ +circularVariance3.ts(6,14): error TS2322: Type 'Handler' is not assignable to type 'Handler'. + Types of parameters 'params' and 'params' are incompatible. + Type 'Params' is not assignable to type 'Params'. + Type 'string' is not assignable to type 'number'. +circularVariance3.ts(7,14): error TS2322: Type 'Handler' is not assignable to type 'Handler'. + Types of parameters 'params' and 'params' are incompatible. + Type 'Params' is not assignable to type 'Params'. + Type 'string' is not assignable to type 'boolean'. + + +==== circularVariance3.ts (2 errors) ==== + type Params = { config: Config }; + type Handler = (params: Params) => void; + type Config = { read: (item: T) => void; handler: Handler }; + + declare const a: Handler; + export const b: Handler = a; // Error + ~ +!!! error TS2322: Type 'Handler' is not assignable to type 'Handler'. +!!! error TS2322: Types of parameters 'params' and 'params' are incompatible. +!!! error TS2322: Type 'Params' is not assignable to type 'Params'. +!!! error TS2322: Type 'string' is not assignable to type 'number'. + export const c: Handler = a; // Error + ~ +!!! error TS2322: Type 'Handler' is not assignable to type 'Handler'. +!!! error TS2322: Types of parameters 'params' and 'params' are incompatible. +!!! error TS2322: Type 'Params' is not assignable to type 'Params'. +!!! error TS2322: Type 'string' is not assignable to type 'boolean'. + export const d: Handler = a; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/circularVariance3.symbols b/tsc/testdata/baselines/reference/compiler/circularVariance3.symbols new file mode 100644 index 0000000000000..13eb331ffaf23 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/circularVariance3.symbols @@ -0,0 +1,53 @@ +//// [tests/cases/compiler/circularVariance3.ts] //// + +=== circularVariance3.ts === +type Params = { config: Config }; +>Params : Symbol(Params, Decl(circularVariance3.ts, 0, 0)) +>T : Symbol(T, Decl(circularVariance3.ts, 0, 12)) +>K : Symbol(K, Decl(circularVariance3.ts, 0, 14)) +>config : Symbol(config, Decl(circularVariance3.ts, 0, 21)) +>Config : Symbol(Config, Decl(circularVariance3.ts, 1, 52)) +>T : Symbol(T, Decl(circularVariance3.ts, 0, 12)) +>K : Symbol(K, Decl(circularVariance3.ts, 0, 14)) + +type Handler = (params: Params) => void; +>Handler : Symbol(Handler, Decl(circularVariance3.ts, 0, 45)) +>T : Symbol(T, Decl(circularVariance3.ts, 1, 13)) +>K : Symbol(K, Decl(circularVariance3.ts, 1, 15)) +>params : Symbol(params, Decl(circularVariance3.ts, 1, 22)) +>Params : Symbol(Params, Decl(circularVariance3.ts, 0, 0)) +>T : Symbol(T, Decl(circularVariance3.ts, 1, 13)) +>K : Symbol(K, Decl(circularVariance3.ts, 1, 15)) + +type Config = { read: (item: T) => void; handler: Handler }; +>Config : Symbol(Config, Decl(circularVariance3.ts, 1, 52)) +>T : Symbol(T, Decl(circularVariance3.ts, 2, 12)) +>K : Symbol(K, Decl(circularVariance3.ts, 2, 14)) +>read : Symbol(read, Decl(circularVariance3.ts, 2, 21)) +>item : Symbol(item, Decl(circularVariance3.ts, 2, 29)) +>T : Symbol(T, Decl(circularVariance3.ts, 2, 12)) +>handler : Symbol(handler, Decl(circularVariance3.ts, 2, 46)) +>Handler : Symbol(Handler, Decl(circularVariance3.ts, 0, 45)) +>T : Symbol(T, Decl(circularVariance3.ts, 2, 12)) +>K : Symbol(K, Decl(circularVariance3.ts, 2, 14)) + +declare const a: Handler; +>a : Symbol(a, Decl(circularVariance3.ts, 4, 13)) +>Handler : Symbol(Handler, Decl(circularVariance3.ts, 0, 45)) + +export const b: Handler = a; // Error +>b : Symbol(b, Decl(circularVariance3.ts, 5, 12)) +>Handler : Symbol(Handler, Decl(circularVariance3.ts, 0, 45)) +>a : Symbol(a, Decl(circularVariance3.ts, 4, 13)) + +export const c: Handler = a; // Error +>c : Symbol(c, Decl(circularVariance3.ts, 6, 12)) +>Handler : Symbol(Handler, Decl(circularVariance3.ts, 0, 45)) +>x : Symbol(x, Decl(circularVariance3.ts, 6, 34)) +>a : Symbol(a, Decl(circularVariance3.ts, 4, 13)) + +export const d: Handler = a; +>d : Symbol(d, Decl(circularVariance3.ts, 7, 12)) +>Handler : Symbol(Handler, Decl(circularVariance3.ts, 0, 45)) +>a : Symbol(a, Decl(circularVariance3.ts, 4, 13)) + diff --git a/tsc/testdata/baselines/reference/compiler/circularVariance3.types b/tsc/testdata/baselines/reference/compiler/circularVariance3.types new file mode 100644 index 0000000000000..beb5a3135dcd4 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/circularVariance3.types @@ -0,0 +1,33 @@ +//// [tests/cases/compiler/circularVariance3.ts] //// + +=== circularVariance3.ts === +type Params = { config: Config }; +>Params : Params +>config : Config + +type Handler = (params: Params) => void; +>Handler : Handler +>params : Params + +type Config = { read: (item: T) => void; handler: Handler }; +>Config : Config +>read : (item: T) => void +>item : T +>handler : Handler + +declare const a: Handler; +>a : Handler + +export const b: Handler = a; // Error +>b : Handler +>a : Handler + +export const c: Handler = a; // Error +>c : Handler +>x : 1 +>a : Handler + +export const d: Handler = a; +>d : Handler +>a : Handler + diff --git a/tsc/testdata/baselines/reference/compiler/circularVariance4.errors.txt b/tsc/testdata/baselines/reference/compiler/circularVariance4.errors.txt new file mode 100644 index 0000000000000..a6957be9f4b42 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/circularVariance4.errors.txt @@ -0,0 +1,38 @@ +circularVariance4.ts(6,14): error TS2322: Type 'Params' is not assignable to type 'Params'. + Type 'number' is not assignable to type 'string'. +circularVariance4.ts(9,14): error TS2322: Type 'Handler' is not assignable to type 'Handler'. + Types of parameters 'params' and 'params' are incompatible. + Type 'Params' is not assignable to type 'Params'. + Type 'string' is not assignable to type 'number'. +circularVariance4.ts(10,14): error TS2322: Type 'Handler' is not assignable to type 'Handler'. + Types of parameters 'params' and 'params' are incompatible. + Type 'Params' is not assignable to type 'Params'. + Type 'string' is not assignable to type 'boolean'. + + +==== circularVariance4.ts (3 errors) ==== + type Params = { config: Config }; + type Handler = (params: Params) => void; + type Config = { read: (item: T) => void; handler: Handler }; + + declare const p: Params; + export const q: Params = p; // Error + ~ +!!! error TS2322: Type 'Params' is not assignable to type 'Params'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + + declare const a: Handler; + export const b: Handler = a; // Error + ~ +!!! error TS2322: Type 'Handler' is not assignable to type 'Handler'. +!!! error TS2322: Types of parameters 'params' and 'params' are incompatible. +!!! error TS2322: Type 'Params' is not assignable to type 'Params'. +!!! error TS2322: Type 'string' is not assignable to type 'number'. + export const c: Handler = a; // Error + ~ +!!! error TS2322: Type 'Handler' is not assignable to type 'Handler'. +!!! error TS2322: Types of parameters 'params' and 'params' are incompatible. +!!! error TS2322: Type 'Params' is not assignable to type 'Params'. +!!! error TS2322: Type 'string' is not assignable to type 'boolean'. + export const d: Handler = a; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/circularVariance4.symbols b/tsc/testdata/baselines/reference/compiler/circularVariance4.symbols new file mode 100644 index 0000000000000..9ee4d6119f4c1 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/circularVariance4.symbols @@ -0,0 +1,62 @@ +//// [tests/cases/compiler/circularVariance4.ts] //// + +=== circularVariance4.ts === +type Params = { config: Config }; +>Params : Symbol(Params, Decl(circularVariance4.ts, 0, 0)) +>T : Symbol(T, Decl(circularVariance4.ts, 0, 12)) +>K : Symbol(K, Decl(circularVariance4.ts, 0, 14)) +>config : Symbol(config, Decl(circularVariance4.ts, 0, 21)) +>Config : Symbol(Config, Decl(circularVariance4.ts, 1, 52)) +>T : Symbol(T, Decl(circularVariance4.ts, 0, 12)) +>K : Symbol(K, Decl(circularVariance4.ts, 0, 14)) + +type Handler = (params: Params) => void; +>Handler : Symbol(Handler, Decl(circularVariance4.ts, 0, 45)) +>T : Symbol(T, Decl(circularVariance4.ts, 1, 13)) +>K : Symbol(K, Decl(circularVariance4.ts, 1, 15)) +>params : Symbol(params, Decl(circularVariance4.ts, 1, 22)) +>Params : Symbol(Params, Decl(circularVariance4.ts, 0, 0)) +>T : Symbol(T, Decl(circularVariance4.ts, 1, 13)) +>K : Symbol(K, Decl(circularVariance4.ts, 1, 15)) + +type Config = { read: (item: T) => void; handler: Handler }; +>Config : Symbol(Config, Decl(circularVariance4.ts, 1, 52)) +>T : Symbol(T, Decl(circularVariance4.ts, 2, 12)) +>K : Symbol(K, Decl(circularVariance4.ts, 2, 14)) +>read : Symbol(read, Decl(circularVariance4.ts, 2, 21)) +>item : Symbol(item, Decl(circularVariance4.ts, 2, 29)) +>T : Symbol(T, Decl(circularVariance4.ts, 2, 12)) +>handler : Symbol(handler, Decl(circularVariance4.ts, 2, 46)) +>Handler : Symbol(Handler, Decl(circularVariance4.ts, 0, 45)) +>T : Symbol(T, Decl(circularVariance4.ts, 2, 12)) +>K : Symbol(K, Decl(circularVariance4.ts, 2, 14)) + +declare const p: Params; +>p : Symbol(p, Decl(circularVariance4.ts, 4, 13)) +>Params : Symbol(Params, Decl(circularVariance4.ts, 0, 0)) + +export const q: Params = p; // Error +>q : Symbol(q, Decl(circularVariance4.ts, 5, 12)) +>Params : Symbol(Params, Decl(circularVariance4.ts, 0, 0)) +>p : Symbol(p, Decl(circularVariance4.ts, 4, 13)) + +declare const a: Handler; +>a : Symbol(a, Decl(circularVariance4.ts, 7, 13)) +>Handler : Symbol(Handler, Decl(circularVariance4.ts, 0, 45)) + +export const b: Handler = a; // Error +>b : Symbol(b, Decl(circularVariance4.ts, 8, 12)) +>Handler : Symbol(Handler, Decl(circularVariance4.ts, 0, 45)) +>a : Symbol(a, Decl(circularVariance4.ts, 7, 13)) + +export const c: Handler = a; // Error +>c : Symbol(c, Decl(circularVariance4.ts, 9, 12)) +>Handler : Symbol(Handler, Decl(circularVariance4.ts, 0, 45)) +>x : Symbol(x, Decl(circularVariance4.ts, 9, 34)) +>a : Symbol(a, Decl(circularVariance4.ts, 7, 13)) + +export const d: Handler = a; +>d : Symbol(d, Decl(circularVariance4.ts, 10, 12)) +>Handler : Symbol(Handler, Decl(circularVariance4.ts, 0, 45)) +>a : Symbol(a, Decl(circularVariance4.ts, 7, 13)) + diff --git a/tsc/testdata/baselines/reference/compiler/circularVariance4.types b/tsc/testdata/baselines/reference/compiler/circularVariance4.types new file mode 100644 index 0000000000000..99770a22368a1 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/circularVariance4.types @@ -0,0 +1,40 @@ +//// [tests/cases/compiler/circularVariance4.ts] //// + +=== circularVariance4.ts === +type Params = { config: Config }; +>Params : Params +>config : Config + +type Handler = (params: Params) => void; +>Handler : Handler +>params : Params + +type Config = { read: (item: T) => void; handler: Handler }; +>Config : Config +>read : (item: T) => void +>item : T +>handler : Handler + +declare const p: Params; +>p : Params + +export const q: Params = p; // Error +>q : Params +>p : Params + +declare const a: Handler; +>a : Handler + +export const b: Handler = a; // Error +>b : Handler +>a : Handler + +export const c: Handler = a; // Error +>c : Handler +>x : 1 +>a : Handler + +export const d: Handler = a; +>d : Handler +>a : Handler + diff --git a/tsc/testdata/baselines/reference/compiler/varianceMeasurement.errors.txt b/tsc/testdata/baselines/reference/compiler/varianceMeasurement.errors.txt index 7a361e5b94ac6..3ba500b0151f1 100644 --- a/tsc/testdata/baselines/reference/compiler/varianceMeasurement.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/varianceMeasurement.errors.txt @@ -23,7 +23,9 @@ varianceMeasurement.ts(57,7): error TS2322: Type 'Fn' is not ass varianceMeasurement.ts(62,7): error TS2322: Type 'Fn' is not assignable to type 'Fn'. Type 'number' is not assignable to type '0'. varianceMeasurement.ts(75,7): error TS2322: Type 'C' is not assignable to type 'C'. - Type 'number' is not assignable to type 'string'. + Types of property 'sub' are incompatible. + Type 'I' is not assignable to type 'I'. + Type 'number' is not assignable to type 'string'. ==== varianceMeasurement.ts (9 errors) ==== @@ -136,5 +138,7 @@ varianceMeasurement.ts(75,7): error TS2322: Type 'C' is not ass const c1: C = new C(); // Error ~~ !!! error TS2322: Type 'C' is not assignable to type 'C'. -!!! error TS2322: Type 'number' is not assignable to type 'string'. +!!! error TS2322: Types of property 'sub' are incompatible. +!!! error TS2322: Type 'I' is not assignable to type 'I'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. \ No newline at end of file diff --git a/tsc/testdata/tests/cases/compiler/circularVariance3.ts b/tsc/testdata/tests/cases/compiler/circularVariance3.ts new file mode 100644 index 0000000000000..e83bd5e78e18e --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/circularVariance3.ts @@ -0,0 +1,11 @@ +// @strict: true +// @noEmit: true + +type Params = { config: Config }; +type Handler = (params: Params) => void; +type Config = { read: (item: T) => void; handler: Handler }; + +declare const a: Handler; +export const b: Handler = a; // Error +export const c: Handler = a; // Error +export const d: Handler = a; diff --git a/tsc/testdata/tests/cases/compiler/circularVariance4.ts b/tsc/testdata/tests/cases/compiler/circularVariance4.ts new file mode 100644 index 0000000000000..2245c3cb34899 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/circularVariance4.ts @@ -0,0 +1,14 @@ +// @strict: true +// @noEmit: true + +type Params = { config: Config }; +type Handler = (params: Params) => void; +type Config = { read: (item: T) => void; handler: Handler }; + +declare const p: Params; +export const q: Params = p; // Error + +declare const a: Handler; +export const b: Handler = a; // Error +export const c: Handler = a; // Error +export const d: Handler = a;