diff --git a/internal/checker/checker.go b/internal/checker/checker.go index a8223d2bac..f7f80dba72 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -287,7 +287,8 @@ type InferenceContext struct { type InferenceInfo struct { typeParameter *Type // Type parameter for which inferences are being made - candidates []*Type // Candidates in covariant positions + candidates []*Type // Candidates in covariant positions in decreasing depth order + candidateDepths []int // Type argument depths of covariant inferences contraCandidates []*Type // Candidates in contravariant positions inferredType *Type // Cache for resolved inferred type priority InferencePriority // Priority of current inference set diff --git a/internal/checker/inference.go b/internal/checker/inference.go index fb5da8b822..21af42387d 100644 --- a/internal/checker/inference.go +++ b/internal/checker/inference.go @@ -27,6 +27,7 @@ type InferenceState struct { sourceStack []*Type targetStack []*Type next *InferenceState + depth int } func (c *Checker) getInferenceState() *InferenceState { @@ -187,6 +188,7 @@ func (c *Checker) inferFromTypes(n *InferenceState, source *Type, target *Type) } if n.priority < inference.priority { inference.candidates = nil + inference.candidateDepths = nil inference.contraCandidates = nil inference.topLevel = true inference.priority = n.priority @@ -199,9 +201,28 @@ func (c *Checker) inferFromTypes(n *InferenceState, source *Type, target *Type) inference.contraCandidates = append(inference.contraCandidates, candidate) clearCachedInferences(n.inferences) } - } else if !slices.Contains(inference.candidates, candidate) { - inference.candidates = append(inference.candidates, candidate) - clearCachedInferences(n.inferences) + } else { + index := slices.Index(inference.candidates, candidate) + if index < 0 || inference.candidateDepths[index] < n.depth { + // Candidate isn't present or is present with lower depth + if index >= 0 { + // Remove candidate with lower depth + inference.candidates = slices.Delete(inference.candidates, index, index+1) + inference.candidateDepths = slices.Delete(inference.candidateDepths, index, index+1) + } + index = 0 + for index < len(inference.candidateDepths) { + if inference.candidateDepths[index] < n.depth { + break + } + index++ + } + // Insert candidate at end or immediately before first candidate with lower depth. + // This ensures candidates with the highest depth are stored first. + inference.candidates = slices.Insert(inference.candidates, index, candidate) + inference.candidateDepths = slices.Insert(inference.candidateDepths, index, n.depth) + clearCachedInferences(n.inferences) + } } } if n.priority&InferencePriorityReturnType == 0 && target.flags&TypeFlagsTypeParameter != 0 && inference.topLevel && !c.isTypeParameterAtTopLevel(n.originalTarget, target, 0) { @@ -282,6 +303,7 @@ func (c *Checker) inferFromTypes(n *InferenceState, source *Type, target *Type) } func (c *Checker) inferFromTypeArguments(n *InferenceState, sourceTypes []*Type, targetTypes []*Type, variances []VarianceFlags) { + n.depth++ for i := range min(len(sourceTypes), len(targetTypes)) { if i < len(variances) && variances[i]&VarianceFlagsVarianceMask == VarianceFlagsContravariant { c.inferFromContravariantTypes(n, sourceTypes[i], targetTypes[i]) @@ -289,6 +311,7 @@ func (c *Checker) inferFromTypeArguments(n *InferenceState, sourceTypes []*Type, c.inferFromTypes(n, sourceTypes[i], targetTypes[i]) } } + n.depth-- } func (c *Checker) inferWithPriority(n *InferenceState, source *Type, target *Type, newPriority InferencePriority) { @@ -1451,9 +1474,9 @@ func (c *Checker) isTypeParameterAtTopLevelInReturnType(signature *Signature, ty func (c *Checker) getTypeFromInference(inference *InferenceInfo) *Type { switch { - case inference.candidates != nil: + case len(inference.candidates) != 0: return c.getUnionTypeEx(inference.candidates, UnionReductionSubtype, nil, nil) - case inference.contraCandidates != nil: + case len(inference.contraCandidates) != 0: return c.getIntersectionType(inference.contraCandidates) } return nil @@ -1573,6 +1596,7 @@ func cloneInferenceInfo(info *InferenceInfo) *InferenceInfo { return &InferenceInfo{ typeParameter: info.typeParameter, candidates: slices.Clone(info.candidates), + candidateDepths: slices.Clone(info.candidateDepths), contraCandidates: slices.Clone(info.contraCandidates), inferredType: info.inferredType, priority: info.priority, @@ -1595,7 +1619,7 @@ func hasInferenceCandidates(info *InferenceInfo) bool { } func hasInferenceCandidatesOrDefault(info *InferenceInfo) bool { - return info.candidates != nil || info.contraCandidates != nil || hasTypeParameterDefault(info.typeParameter) + return hasInferenceCandidates(info) || hasTypeParameterDefault(info.typeParameter) } func hasTypeParameterDefault(tp *Type) bool { diff --git a/testdata/baselines/reference/compiler/nestedGenericTypeInference.symbols b/testdata/baselines/reference/compiler/nestedGenericTypeInference.symbols new file mode 100644 index 0000000000..6d6f2f521d --- /dev/null +++ b/testdata/baselines/reference/compiler/nestedGenericTypeInference.symbols @@ -0,0 +1,90 @@ +//// [tests/cases/compiler/nestedGenericTypeInference.ts] //// + +=== nestedGenericTypeInference.ts === +// https://github.com/microsoft/typescript-go/issues/1789 + +declare function flat(args: T[] | T[][]): void; +>flat : Symbol(flat, Decl(nestedGenericTypeInference.ts, 0, 0)) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 2, 22)) +>args : Symbol(args, Decl(nestedGenericTypeInference.ts, 2, 25)) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 2, 22)) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 2, 22)) + +type Value = 1 | 2; +>Value : Symbol(Value, Decl(nestedGenericTypeInference.ts, 2, 50)) + +declare const n: Value[] | Value[][]; +>n : Symbol(n, Decl(nestedGenericTypeInference.ts, 4, 13)) +>Value : Symbol(Value, Decl(nestedGenericTypeInference.ts, 2, 50)) +>Value : Symbol(Value, Decl(nestedGenericTypeInference.ts, 2, 50)) + +flat(n); +>flat : Symbol(flat, Decl(nestedGenericTypeInference.ts, 0, 0)) +>n : Symbol(n, Decl(nestedGenericTypeInference.ts, 4, 13)) + +type Box = { value: T }; +>Box : Symbol(Box, Decl(nestedGenericTypeInference.ts, 5, 8)) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 7, 9)) +>value : Symbol(value, Decl(nestedGenericTypeInference.ts, 7, 15)) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 7, 9)) + +declare function flat0(args: Box | Box>): void; +>flat0 : Symbol(flat0, Decl(nestedGenericTypeInference.ts, 7, 27)) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 9, 23)) +>args : Symbol(args, Decl(nestedGenericTypeInference.ts, 9, 26)) +>Box : Symbol(Box, Decl(nestedGenericTypeInference.ts, 5, 8)) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 9, 23)) +>Box : Symbol(Box, Decl(nestedGenericTypeInference.ts, 5, 8)) +>Box : Symbol(Box, Decl(nestedGenericTypeInference.ts, 5, 8)) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 9, 23)) + +declare const arg0: Box | Box>; +>arg0 : Symbol(arg0, Decl(nestedGenericTypeInference.ts, 10, 13)) +>Box : Symbol(Box, Decl(nestedGenericTypeInference.ts, 5, 8)) +>Box : Symbol(Box, Decl(nestedGenericTypeInference.ts, 5, 8)) +>Box : Symbol(Box, Decl(nestedGenericTypeInference.ts, 5, 8)) + +flat0(arg0); +>flat0 : Symbol(flat0, Decl(nestedGenericTypeInference.ts, 7, 27)) +>arg0 : Symbol(arg0, Decl(nestedGenericTypeInference.ts, 10, 13)) + +declare function flat1(args: Array | Array>): void; +>flat1 : Symbol(flat1, Decl(nestedGenericTypeInference.ts, 11, 12)) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 13, 23)) +>args : Symbol(args, Decl(nestedGenericTypeInference.ts, 13, 26)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 13, 23)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>Box : Symbol(Box, Decl(nestedGenericTypeInference.ts, 5, 8)) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 13, 23)) + +declare const arg1: Array | Array>; +>arg1 : Symbol(arg1, Decl(nestedGenericTypeInference.ts, 14, 13)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>Box : Symbol(Box, Decl(nestedGenericTypeInference.ts, 5, 8)) + +flat1(arg1); +>flat1 : Symbol(flat1, Decl(nestedGenericTypeInference.ts, 11, 12)) +>arg1 : Symbol(arg1, Decl(nestedGenericTypeInference.ts, 14, 13)) + +declare function flat2(args: Box | Box>): void; +>flat2 : Symbol(flat2, Decl(nestedGenericTypeInference.ts, 15, 12)) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 17, 23)) +>args : Symbol(args, Decl(nestedGenericTypeInference.ts, 17, 26)) +>Box : Symbol(Box, Decl(nestedGenericTypeInference.ts, 5, 8)) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 17, 23)) +>Box : Symbol(Box, Decl(nestedGenericTypeInference.ts, 5, 8)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>T : Symbol(T, Decl(nestedGenericTypeInference.ts, 17, 23)) + +declare const arg2: Box | Box>; +>arg2 : Symbol(arg2, Decl(nestedGenericTypeInference.ts, 18, 13)) +>Box : Symbol(Box, Decl(nestedGenericTypeInference.ts, 5, 8)) +>Box : Symbol(Box, Decl(nestedGenericTypeInference.ts, 5, 8)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) + +flat2(arg2); +>flat2 : Symbol(flat2, Decl(nestedGenericTypeInference.ts, 15, 12)) +>arg2 : Symbol(arg2, Decl(nestedGenericTypeInference.ts, 18, 13)) + diff --git a/testdata/baselines/reference/compiler/nestedGenericTypeInference.types b/testdata/baselines/reference/compiler/nestedGenericTypeInference.types new file mode 100644 index 0000000000..2f9c651216 --- /dev/null +++ b/testdata/baselines/reference/compiler/nestedGenericTypeInference.types @@ -0,0 +1,60 @@ +//// [tests/cases/compiler/nestedGenericTypeInference.ts] //// + +=== nestedGenericTypeInference.ts === +// https://github.com/microsoft/typescript-go/issues/1789 + +declare function flat(args: T[] | T[][]): void; +>flat : (args: T[] | T[][]) => void +>args : T[] | T[][] + +type Value = 1 | 2; +>Value : Value + +declare const n: Value[] | Value[][]; +>n : Value[][] | Value[] + +flat(n); +>flat(n) : void +>flat : (args: T[] | T[][]) => void +>n : Value[][] | Value[] + +type Box = { value: T }; +>Box : Box +>value : T + +declare function flat0(args: Box | Box>): void; +>flat0 : (args: Box | Box>) => void +>args : Box | Box> + +declare const arg0: Box | Box>; +>arg0 : Box | Box> + +flat0(arg0); +>flat0(arg0) : void +>flat0 : (args: Box | Box>) => void +>arg0 : Box | Box> + +declare function flat1(args: Array | Array>): void; +>flat1 : (args: Array | Array>) => void +>args : T[] | Box[] + +declare const arg1: Array | Array>; +>arg1 : string[] | Box[] + +flat1(arg1); +>flat1(arg1) : void +>flat1 : (args: Array | Array>) => void +>arg1 : string[] | Box[] + +declare function flat2(args: Box | Box>): void; +>flat2 : (args: Box | Box>) => void +>args : Box | Box + +declare const arg2: Box | Box>; +>arg2 : Box | Box + +flat2(arg2); +>flat2(arg2) : void +>flat2 : (args: Box | Box>) => void +>arg2 : Box | Box + diff --git a/testdata/baselines/reference/submodule/conformance/assignmentCompatWithGenericCallSignatures2.errors.txt b/testdata/baselines/reference/submodule/conformance/assignmentCompatWithGenericCallSignatures2.errors.txt index 3ae018672d..af48acb76b 100644 --- a/testdata/baselines/reference/submodule/conformance/assignmentCompatWithGenericCallSignatures2.errors.txt +++ b/testdata/baselines/reference/submodule/conformance/assignmentCompatWithGenericCallSignatures2.errors.txt @@ -1,7 +1,6 @@ assignmentCompatWithGenericCallSignatures2.ts(15,1): error TS2322: Type 'B' is not assignable to type 'A'. - Types of parameters 'y' and 'y' are incompatible. - Type 'T[]' is not assignable to type 'T'. - 'T' could be instantiated with an arbitrary type which could be unrelated to 'T[]'. + Types of parameters 'x' and 'x' are incompatible. + Type 'T' is not assignable to type 'T[]'. assignmentCompatWithGenericCallSignatures2.ts(16,1): error TS2322: Type 'A' is not assignable to type 'B'. Types of parameters 'y' and 'y' are incompatible. Type 'S' is not assignable to type 'S[]'. @@ -25,9 +24,9 @@ assignmentCompatWithGenericCallSignatures2.ts(16,1): error TS2322: Type 'A' is n a = b; ~ !!! error TS2322: Type 'B' is not assignable to type 'A'. -!!! error TS2322: Types of parameters 'y' and 'y' are incompatible. -!!! error TS2322: Type 'T[]' is not assignable to type 'T'. -!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'T[]'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'T' is not assignable to type 'T[]'. +!!! related TS2208 assignmentCompatWithGenericCallSignatures2.ts:4:6: This type parameter might need an `extends T[]` constraint. b = a; ~ !!! error TS2322: Type 'A' is not assignable to type 'B'. diff --git a/testdata/baselines/reference/submodule/conformance/assignmentCompatWithGenericCallSignatures2.errors.txt.diff b/testdata/baselines/reference/submodule/conformance/assignmentCompatWithGenericCallSignatures2.errors.txt.diff new file mode 100644 index 0000000000..934faa7d72 --- /dev/null +++ b/testdata/baselines/reference/submodule/conformance/assignmentCompatWithGenericCallSignatures2.errors.txt.diff @@ -0,0 +1,25 @@ +--- old.assignmentCompatWithGenericCallSignatures2.errors.txt ++++ new.assignmentCompatWithGenericCallSignatures2.errors.txt +@@= skipped -0, +0 lines =@@ + assignmentCompatWithGenericCallSignatures2.ts(15,1): error TS2322: Type 'B' is not assignable to type 'A'. +- Types of parameters 'y' and 'y' are incompatible. +- Type 'T[]' is not assignable to type 'T'. +- 'T' could be instantiated with an arbitrary type which could be unrelated to 'T[]'. ++ Types of parameters 'x' and 'x' are incompatible. ++ Type 'T' is not assignable to type 'T[]'. + assignmentCompatWithGenericCallSignatures2.ts(16,1): error TS2322: Type 'A' is not assignable to type 'B'. + Types of parameters 'y' and 'y' are incompatible. + Type 'S' is not assignable to type 'S[]'. +@@= skipped -24, +23 lines =@@ + a = b; + ~ + !!! error TS2322: Type 'B' is not assignable to type 'A'. +-!!! error TS2322: Types of parameters 'y' and 'y' are incompatible. +-!!! error TS2322: Type 'T[]' is not assignable to type 'T'. +-!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'T[]'. ++!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. ++!!! error TS2322: Type 'T' is not assignable to type 'T[]'. ++!!! related TS2208 assignmentCompatWithGenericCallSignatures2.ts:4:6: This type parameter might need an `extends T[]` constraint. + b = a; + ~ + !!! error TS2322: Type 'A' is not assignable to type 'B'. \ No newline at end of file diff --git a/testdata/tests/cases/compiler/nestedGenericTypeInference.ts b/testdata/tests/cases/compiler/nestedGenericTypeInference.ts new file mode 100644 index 0000000000..f20b557bda --- /dev/null +++ b/testdata/tests/cases/compiler/nestedGenericTypeInference.ts @@ -0,0 +1,22 @@ +// @noEmit: true + +// https://github.com/microsoft/typescript-go/issues/1789 + +declare function flat(args: T[] | T[][]): void; +type Value = 1 | 2; +declare const n: Value[] | Value[][]; +flat(n); + +type Box = { value: T }; + +declare function flat0(args: Box | Box>): void; +declare const arg0: Box | Box>; +flat0(arg0); + +declare function flat1(args: Array | Array>): void; +declare const arg1: Array | Array>; +flat1(arg1); + +declare function flat2(args: Box | Box>): void; +declare const arg2: Box | Box>; +flat2(arg2);