From 5e551180764bdab714b55633d7a6ab0fd4da92e6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 25 Sep 2018 18:07:51 -0700 Subject: [PATCH 1/4] Only make contravariant inferences from pure contravariant positions --- src/compiler/checker.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d7c86a5905501..f3201ecf8af69 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1,3 +1,6 @@ + + + /* @internal */ namespace ts { const ambientModuleSymbolRegex = /^".+"$/; @@ -13460,6 +13463,7 @@ namespace ts { let symbolStack: Symbol[]; let visited: Map; let contravariant = false; + let bivariant = false; let propagationType: Type; let allowComplexConstraintInference = true; inferFromTypes(originalSource, originalTarget); @@ -13546,7 +13550,9 @@ namespace ts { } if (priority === inference.priority) { const candidate = propagationType || source; - if (contravariant) { + // We make contravariant inferences only if we are in a pure contravariant position, + // i.e. only if we have not descended into a bivariant position. + if (contravariant && !bivariant) { inference.contraCandidates = append(inference.contraCandidates, candidate); } else { @@ -13798,7 +13804,12 @@ namespace ts { function inferFromSignature(source: Signature, target: Signature, skipParameters: boolean) { if (!skipParameters) { + const saveBivariant = bivariant; + const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown; + // Once we descend into a bivariant signature we remain bivariant for all nested inferences + bivariant = bivariant || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.Constructor; forEachMatchingParameterType(source, target, inferFromContravariantTypes); + bivariant = saveBivariant; } const sourceTypePredicate = getTypePredicateOfSignature(source); const targetTypePredicate = getTypePredicateOfSignature(target); From 4bb5cfb9bb1b371c09b08b151bb50522537726b9 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 25 Sep 2018 18:17:21 -0700 Subject: [PATCH 2/4] Add regression test --- .../typeInference/bivariantInferences.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/cases/conformance/types/typeRelationships/typeInference/bivariantInferences.ts diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/bivariantInferences.ts b/tests/cases/conformance/types/typeRelationships/typeInference/bivariantInferences.ts new file mode 100644 index 0000000000000..c0e2ee497d0da --- /dev/null +++ b/tests/cases/conformance/types/typeRelationships/typeInference/bivariantInferences.ts @@ -0,0 +1,12 @@ +// @strict: true + +// Repro from #27337 + +interface Array { + equalsShallow(this: ReadonlyArray, other: ReadonlyArray): boolean; +} + +declare const a: (string | number)[] | null[] | undefined[] | {}[]; +declare const b: (string | number)[] | null[] | undefined[] | {}[]; + +let x = a.equalsShallow(b); From 272157185f29e8ec94973cdd4f2e38155c175181 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 25 Sep 2018 18:17:30 -0700 Subject: [PATCH 3/4] Accept new baselines --- .../reference/bivariantInferences.js | 17 ++++++++++ .../reference/bivariantInferences.symbols | 31 +++++++++++++++++++ .../reference/bivariantInferences.types | 26 ++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 tests/baselines/reference/bivariantInferences.js create mode 100644 tests/baselines/reference/bivariantInferences.symbols create mode 100644 tests/baselines/reference/bivariantInferences.types diff --git a/tests/baselines/reference/bivariantInferences.js b/tests/baselines/reference/bivariantInferences.js new file mode 100644 index 0000000000000..a910ca15e93bb --- /dev/null +++ b/tests/baselines/reference/bivariantInferences.js @@ -0,0 +1,17 @@ +//// [bivariantInferences.ts] +// Repro from #27337 + +interface Array { + equalsShallow(this: ReadonlyArray, other: ReadonlyArray): boolean; +} + +declare const a: (string | number)[] | null[] | undefined[] | {}[]; +declare const b: (string | number)[] | null[] | undefined[] | {}[]; + +let x = a.equalsShallow(b); + + +//// [bivariantInferences.js] +"use strict"; +// Repro from #27337 +var x = a.equalsShallow(b); diff --git a/tests/baselines/reference/bivariantInferences.symbols b/tests/baselines/reference/bivariantInferences.symbols new file mode 100644 index 0000000000000..63cc239f040ff --- /dev/null +++ b/tests/baselines/reference/bivariantInferences.symbols @@ -0,0 +1,31 @@ +=== tests/cases/conformance/types/typeRelationships/typeInference/bivariantInferences.ts === +// Repro from #27337 + +interface Array { +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(bivariantInferences.ts, 0, 0)) +>T : Symbol(T, Decl(lib.es5.d.ts, --, --), Decl(bivariantInferences.ts, 2, 16)) + + equalsShallow(this: ReadonlyArray, other: ReadonlyArray): boolean; +>equalsShallow : Symbol(Array.equalsShallow, Decl(bivariantInferences.ts, 2, 20)) +>T : Symbol(T, Decl(bivariantInferences.ts, 3, 18)) +>this : Symbol(this, Decl(bivariantInferences.ts, 3, 21)) +>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(bivariantInferences.ts, 3, 18)) +>other : Symbol(other, Decl(bivariantInferences.ts, 3, 44)) +>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(bivariantInferences.ts, 3, 18)) +} + +declare const a: (string | number)[] | null[] | undefined[] | {}[]; +>a : Symbol(a, Decl(bivariantInferences.ts, 6, 13)) + +declare const b: (string | number)[] | null[] | undefined[] | {}[]; +>b : Symbol(b, Decl(bivariantInferences.ts, 7, 13)) + +let x = a.equalsShallow(b); +>x : Symbol(x, Decl(bivariantInferences.ts, 9, 3)) +>a.equalsShallow : Symbol(equalsShallow, Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20)) +>a : Symbol(a, Decl(bivariantInferences.ts, 6, 13)) +>equalsShallow : Symbol(equalsShallow, Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20)) +>b : Symbol(b, Decl(bivariantInferences.ts, 7, 13)) + diff --git a/tests/baselines/reference/bivariantInferences.types b/tests/baselines/reference/bivariantInferences.types new file mode 100644 index 0000000000000..e9bf31dad8ea1 --- /dev/null +++ b/tests/baselines/reference/bivariantInferences.types @@ -0,0 +1,26 @@ +=== tests/cases/conformance/types/typeRelationships/typeInference/bivariantInferences.ts === +// Repro from #27337 + +interface Array { + equalsShallow(this: ReadonlyArray, other: ReadonlyArray): boolean; +>equalsShallow : (this: ReadonlyArray, other: ReadonlyArray) => boolean +>this : ReadonlyArray +>other : ReadonlyArray +} + +declare const a: (string | number)[] | null[] | undefined[] | {}[]; +>a : (string | number)[] | null[] | undefined[] | {}[] +>null : null + +declare const b: (string | number)[] | null[] | undefined[] | {}[]; +>b : (string | number)[] | null[] | undefined[] | {}[] +>null : null + +let x = a.equalsShallow(b); +>x : boolean +>a.equalsShallow(b) : boolean +>a.equalsShallow : ((this: ReadonlyArray, other: ReadonlyArray) => boolean) | ((this: ReadonlyArray, other: ReadonlyArray) => boolean) | ((this: ReadonlyArray, other: ReadonlyArray) => boolean) | ((this: ReadonlyArray, other: ReadonlyArray) => boolean) +>a : (string | number)[] | null[] | undefined[] | {}[] +>equalsShallow : ((this: ReadonlyArray, other: ReadonlyArray) => boolean) | ((this: ReadonlyArray, other: ReadonlyArray) => boolean) | ((this: ReadonlyArray, other: ReadonlyArray) => boolean) | ((this: ReadonlyArray, other: ReadonlyArray) => boolean) +>b : (string | number)[] | null[] | undefined[] | {}[] + From f59229bf22098462cae5bb94778f0a47b4aaedff Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 26 Sep 2018 06:54:37 -0700 Subject: [PATCH 4/4] Only add unique inferences to candidate arrays --- src/compiler/checker.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f3201ecf8af69..ef367e8640534 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1,6 +1,3 @@ - - - /* @internal */ namespace ts { const ambientModuleSymbolRegex = /^".+"$/; @@ -13553,10 +13550,10 @@ namespace ts { // We make contravariant inferences only if we are in a pure contravariant position, // i.e. only if we have not descended into a bivariant position. if (contravariant && !bivariant) { - inference.contraCandidates = append(inference.contraCandidates, candidate); + inference.contraCandidates = appendIfUnique(inference.contraCandidates, candidate); } else { - inference.candidates = append(inference.candidates, candidate); + inference.candidates = appendIfUnique(inference.candidates, candidate); } } if (!(priority & InferencePriority.ReturnType) && target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, target)) {