From 7a31706a9ff01cacd58ca7b5a7601940845b0439 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 30 Jan 2019 06:38:13 -0800 Subject: [PATCH 1/6] Only check constraints after type argument inference is complete --- src/compiler/checker.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ed65908d92..44f118b3a5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14788,7 +14788,7 @@ namespace ts { return getWidenedType(unwidenedType); } - function getInferredType(context: InferenceContext, index: number): Type { + function getInferredType(context: InferenceContext, index: number, ignoreConstraints = false): Type { const inference = context.inferences[index]; let inferredType = inference.inferredType; if (!inferredType) { @@ -14836,11 +14836,13 @@ namespace ts { inference.inferredType = inferredType; - const constraint = getConstraintOfTypeParameter(inference.typeParameter); - if (constraint) { - const instantiatedConstraint = instantiateType(constraint, context); - if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) { - inference.inferredType = inferredType = instantiatedConstraint; + if (!ignoreConstraints) { + const constraint = getConstraintOfTypeParameter(inference.typeParameter); + if (constraint) { + const instantiatedConstraint = instantiateType(constraint, context); + if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) { + inference.inferredType = inferredType = instantiatedConstraint; + } } } } @@ -14852,10 +14854,10 @@ namespace ts { return isInJavaScriptFile ? anyType : emptyObjectType; } - function getInferredTypes(context: InferenceContext): Type[] { + function getInferredTypes(context: InferenceContext, ignoreConstraints = false): Type[] { const result: Type[] = []; for (let i = 0; i < context.inferences.length; i++) { - result.push(getInferredType(context, i)); + result.push(getInferredType(context, i, ignoreConstraints)); } return result; } @@ -19925,7 +19927,10 @@ namespace ts { inferTypes(context.inferences, spreadType, restType); } - return getInferredTypes(context); + // If we are excluding context sensitive arguments we know there is another round of type inference + // coming, so we omit constraint checking. Otherwise, when a type parameter's constraint is self- + // referential, checking the constraint would cause inferences to be fixed prematurely. + return getInferredTypes(context, !!excludeArgument); } function getArrayifiedType(type: Type) { From e0082f6bfc44c3a8c340274d8c9fc9658ab7946a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 30 Jan 2019 06:43:28 -0800 Subject: [PATCH 2/6] Accept new baselines --- ...spreadOfParamsFromGeneratorMakesRequiredParams.errors.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/baselines/reference/spreadOfParamsFromGeneratorMakesRequiredParams.errors.txt b/tests/baselines/reference/spreadOfParamsFromGeneratorMakesRequiredParams.errors.txt index 393e21edb6..bca28dbcf5 100644 --- a/tests/baselines/reference/spreadOfParamsFromGeneratorMakesRequiredParams.errors.txt +++ b/tests/baselines/reference/spreadOfParamsFromGeneratorMakesRequiredParams.errors.txt @@ -1,5 +1,5 @@ error TS2318: Cannot find global type 'IterableIterator'. -tests/cases/compiler/spreadOfParamsFromGeneratorMakesRequiredParams.ts(6,1): error TS2554: Expected 2 arguments, but got 1. +tests/cases/compiler/spreadOfParamsFromGeneratorMakesRequiredParams.ts(6,1): error TS2345: Argument of type '[]' is not assignable to parameter of type 'never'. !!! error TS2318: Cannot find global type 'IterableIterator'. @@ -11,5 +11,4 @@ tests/cases/compiler/spreadOfParamsFromGeneratorMakesRequiredParams.ts(6,1): err call(function* (a: 'a') { }); // error, 2nd argument required ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2554: Expected 2 arguments, but got 1. -!!! related TS6210 tests/cases/compiler/spreadOfParamsFromGeneratorMakesRequiredParams.ts:3:5: An argument for 'args' was not provided. \ No newline at end of file +!!! error TS2345: Argument of type '[]' is not assignable to parameter of type 'never'. \ No newline at end of file From caa89cafde7a5d53f14b123630e2b3530caa4f2d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 30 Jan 2019 08:45:43 -0800 Subject: [PATCH 3/6] Add regression test --- .../inferenceAndSelfReferentialConstraint.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/cases/compiler/inferenceAndSelfReferentialConstraint.ts diff --git a/tests/cases/compiler/inferenceAndSelfReferentialConstraint.ts b/tests/cases/compiler/inferenceAndSelfReferentialConstraint.ts new file mode 100644 index 0000000000..2d991a652c --- /dev/null +++ b/tests/cases/compiler/inferenceAndSelfReferentialConstraint.ts @@ -0,0 +1,29 @@ +// @strict + +// Repro from #29520 + +type Test = { + [P in K | "foo"]: P extends "foo" ? true : () => any +} + +function test>(arg: T) { + return arg; +} + +const res1 = test({ + foo: true, + bar() { + } +}); + +const res2 = test({ + foo: true, + bar: function () { + } +}); + +const res3 = test({ + foo: true, + bar: () => { + } +}); From 390f20f4fc722bd1f96a625f4f33f59f1b2670be Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 30 Jan 2019 08:45:51 -0800 Subject: [PATCH 4/6] Accept new baselines --- .../inferenceAndSelfReferentialConstraint.js | 52 ++++++++++++++ ...erenceAndSelfReferentialConstraint.symbols | 63 +++++++++++++++++ ...nferenceAndSelfReferentialConstraint.types | 67 +++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 tests/baselines/reference/inferenceAndSelfReferentialConstraint.js create mode 100644 tests/baselines/reference/inferenceAndSelfReferentialConstraint.symbols create mode 100644 tests/baselines/reference/inferenceAndSelfReferentialConstraint.types diff --git a/tests/baselines/reference/inferenceAndSelfReferentialConstraint.js b/tests/baselines/reference/inferenceAndSelfReferentialConstraint.js new file mode 100644 index 0000000000..a13f100c2d --- /dev/null +++ b/tests/baselines/reference/inferenceAndSelfReferentialConstraint.js @@ -0,0 +1,52 @@ +//// [inferenceAndSelfReferentialConstraint.ts] +// @strict + +// Repro from #29520 + +type Test = { + [P in K | "foo"]: P extends "foo" ? true : () => any +} + +function test>(arg: T) { + return arg; +} + +const res1 = test({ + foo: true, + bar() { + } +}); + +const res2 = test({ + foo: true, + bar: function () { + } +}); + +const res3 = test({ + foo: true, + bar: () => { + } +}); + + +//// [inferenceAndSelfReferentialConstraint.js] +// @strict +function test(arg) { + return arg; +} +var res1 = test({ + foo: true, + bar: function () { + } +}); +var res2 = test({ + foo: true, + bar: function () { + } +}); +var res3 = test({ + foo: true, + bar: function () { + } +}); diff --git a/tests/baselines/reference/inferenceAndSelfReferentialConstraint.symbols b/tests/baselines/reference/inferenceAndSelfReferentialConstraint.symbols new file mode 100644 index 0000000000..121736623e --- /dev/null +++ b/tests/baselines/reference/inferenceAndSelfReferentialConstraint.symbols @@ -0,0 +1,63 @@ +=== tests/cases/compiler/inferenceAndSelfReferentialConstraint.ts === +// @strict + +// Repro from #29520 + +type Test = { +>Test : Symbol(Test, Decl(inferenceAndSelfReferentialConstraint.ts, 0, 0)) +>K : Symbol(K, Decl(inferenceAndSelfReferentialConstraint.ts, 4, 10)) + + [P in K | "foo"]: P extends "foo" ? true : () => any +>P : Symbol(P, Decl(inferenceAndSelfReferentialConstraint.ts, 5, 3)) +>K : Symbol(K, Decl(inferenceAndSelfReferentialConstraint.ts, 4, 10)) +>P : Symbol(P, Decl(inferenceAndSelfReferentialConstraint.ts, 5, 3)) +} + +function test>(arg: T) { +>test : Symbol(test, Decl(inferenceAndSelfReferentialConstraint.ts, 6, 1)) +>T : Symbol(T, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 14)) +>Test : Symbol(Test, Decl(inferenceAndSelfReferentialConstraint.ts, 0, 0)) +>T : Symbol(T, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 14)) +>arg : Symbol(arg, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 39)) +>T : Symbol(T, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 14)) + + return arg; +>arg : Symbol(arg, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 39)) +} + +const res1 = test({ +>res1 : Symbol(res1, Decl(inferenceAndSelfReferentialConstraint.ts, 12, 5)) +>test : Symbol(test, Decl(inferenceAndSelfReferentialConstraint.ts, 6, 1)) + + foo: true, +>foo : Symbol(foo, Decl(inferenceAndSelfReferentialConstraint.ts, 12, 19)) + + bar() { +>bar : Symbol(bar, Decl(inferenceAndSelfReferentialConstraint.ts, 13, 12)) + } +}); + +const res2 = test({ +>res2 : Symbol(res2, Decl(inferenceAndSelfReferentialConstraint.ts, 18, 5)) +>test : Symbol(test, Decl(inferenceAndSelfReferentialConstraint.ts, 6, 1)) + + foo: true, +>foo : Symbol(foo, Decl(inferenceAndSelfReferentialConstraint.ts, 18, 19)) + + bar: function () { +>bar : Symbol(bar, Decl(inferenceAndSelfReferentialConstraint.ts, 19, 12)) + } +}); + +const res3 = test({ +>res3 : Symbol(res3, Decl(inferenceAndSelfReferentialConstraint.ts, 24, 5)) +>test : Symbol(test, Decl(inferenceAndSelfReferentialConstraint.ts, 6, 1)) + + foo: true, +>foo : Symbol(foo, Decl(inferenceAndSelfReferentialConstraint.ts, 24, 19)) + + bar: () => { +>bar : Symbol(bar, Decl(inferenceAndSelfReferentialConstraint.ts, 25, 12)) + } +}); + diff --git a/tests/baselines/reference/inferenceAndSelfReferentialConstraint.types b/tests/baselines/reference/inferenceAndSelfReferentialConstraint.types new file mode 100644 index 0000000000..c4c723baaa --- /dev/null +++ b/tests/baselines/reference/inferenceAndSelfReferentialConstraint.types @@ -0,0 +1,67 @@ +=== tests/cases/compiler/inferenceAndSelfReferentialConstraint.ts === +// @strict + +// Repro from #29520 + +type Test = { +>Test : Test + + [P in K | "foo"]: P extends "foo" ? true : () => any +>true : true +} + +function test>(arg: T) { +>test : >(arg: T) => T +>arg : T + + return arg; +>arg : T +} + +const res1 = test({ +>res1 : { foo: true; bar(): void; } +>test({ foo: true, bar() { }}) : { foo: true; bar(): void; } +>test : >(arg: T) => T +>{ foo: true, bar() { }} : { foo: true; bar(): void; } + + foo: true, +>foo : true +>true : true + + bar() { +>bar : () => void + } +}); + +const res2 = test({ +>res2 : { foo: true; bar: () => void; } +>test({ foo: true, bar: function () { }}) : { foo: true; bar: () => void; } +>test : >(arg: T) => T +>{ foo: true, bar: function () { }} : { foo: true; bar: () => void; } + + foo: true, +>foo : true +>true : true + + bar: function () { +>bar : () => void +>function () { } : () => void + } +}); + +const res3 = test({ +>res3 : { foo: true; bar: () => void; } +>test({ foo: true, bar: () => { }}) : { foo: true; bar: () => void; } +>test : >(arg: T) => T +>{ foo: true, bar: () => { }} : { foo: true; bar: () => void; } + + foo: true, +>foo : true +>true : true + + bar: () => { +>bar : () => void +>() => { } : () => void + } +}); + From 2a91e7aab2bcf5cf322eb1172c9909a0c8162ab7 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 31 Jan 2019 15:32:52 -0800 Subject: [PATCH 5/6] Revert previous changes, use flag to disable fixing during constraint check --- src/compiler/checker.ts | 29 ++++++++++++++--------------- src/compiler/types.ts | 1 + 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 44f118b3a5..e60d5e4a2a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14112,7 +14112,9 @@ namespace ts { function mapper(t: Type): Type { for (let i = 0; i < inferences.length; i++) { if (t === inferences[i].typeParameter) { - inferences[i].isFixed = true; + if (!(context.flags & InferenceFlags.NoFixing)) { + inferences[i].isFixed = true; + } return getInferredType(context, i); } } @@ -14788,7 +14790,7 @@ namespace ts { return getWidenedType(unwidenedType); } - function getInferredType(context: InferenceContext, index: number, ignoreConstraints = false): Type { + function getInferredType(context: InferenceContext, index: number): Type { const inference = context.inferences[index]; let inferredType = inference.inferredType; if (!inferredType) { @@ -14836,14 +14838,14 @@ namespace ts { inference.inferredType = inferredType; - if (!ignoreConstraints) { - const constraint = getConstraintOfTypeParameter(inference.typeParameter); - if (constraint) { - const instantiatedConstraint = instantiateType(constraint, context); - if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) { - inference.inferredType = inferredType = instantiatedConstraint; - } + const constraint = getConstraintOfTypeParameter(inference.typeParameter); + if (constraint) { + context.flags |= InferenceFlags.NoFixing; + const instantiatedConstraint = instantiateType(constraint, context); + if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) { + inference.inferredType = inferredType = instantiatedConstraint; } + context.flags &= ~InferenceFlags.NoFixing; } } @@ -14854,10 +14856,10 @@ namespace ts { return isInJavaScriptFile ? anyType : emptyObjectType; } - function getInferredTypes(context: InferenceContext, ignoreConstraints = false): Type[] { + function getInferredTypes(context: InferenceContext): Type[] { const result: Type[] = []; for (let i = 0; i < context.inferences.length; i++) { - result.push(getInferredType(context, i, ignoreConstraints)); + result.push(getInferredType(context, i)); } return result; } @@ -19927,10 +19929,7 @@ namespace ts { inferTypes(context.inferences, spreadType, restType); } - // If we are excluding context sensitive arguments we know there is another round of type inference - // coming, so we omit constraint checking. Otherwise, when a type parameter's constraint is self- - // referential, checking the constraint would cause inferences to be fixed prematurely. - return getInferredTypes(context, !!excludeArgument); + return getInferredTypes(context); } function getArrayifiedType(type: Type) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a6690dc252..38d241ce72 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4341,6 +4341,7 @@ namespace ts { None = 0, // No special inference behaviors NoDefault = 1 << 0, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType) AnyDefault = 1 << 1, // Infer anyType for no inferences (otherwise emptyObjectType) + NoFixing = 1 << 2, // Disable type parameter fixing } /** From 51ccfb1201c970691437b039709a540ffd29fda2 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 31 Jan 2019 15:33:00 -0800 Subject: [PATCH 6/6] Accept new baselines --- tests/baselines/reference/jsdocTemplateTag3.types | 2 +- ...spreadOfParamsFromGeneratorMakesRequiredParams.errors.txt | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/baselines/reference/jsdocTemplateTag3.types b/tests/baselines/reference/jsdocTemplateTag3.types index e26b847519..7dd45f47f0 100644 --- a/tests/baselines/reference/jsdocTemplateTag3.types +++ b/tests/baselines/reference/jsdocTemplateTag3.types @@ -52,7 +52,7 @@ function f(t, u, v, w, x) { } f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope'); ->f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope') : string | number +>f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope') : 101 | "nope" >f : (t: T, u: U, v: V, w: W, x: X) => W | X >{ a: 12, b: 'hi', c: null } : { a: number; b: string; c: null; } >a : number diff --git a/tests/baselines/reference/spreadOfParamsFromGeneratorMakesRequiredParams.errors.txt b/tests/baselines/reference/spreadOfParamsFromGeneratorMakesRequiredParams.errors.txt index bca28dbcf5..393e21edb6 100644 --- a/tests/baselines/reference/spreadOfParamsFromGeneratorMakesRequiredParams.errors.txt +++ b/tests/baselines/reference/spreadOfParamsFromGeneratorMakesRequiredParams.errors.txt @@ -1,5 +1,5 @@ error TS2318: Cannot find global type 'IterableIterator'. -tests/cases/compiler/spreadOfParamsFromGeneratorMakesRequiredParams.ts(6,1): error TS2345: Argument of type '[]' is not assignable to parameter of type 'never'. +tests/cases/compiler/spreadOfParamsFromGeneratorMakesRequiredParams.ts(6,1): error TS2554: Expected 2 arguments, but got 1. !!! error TS2318: Cannot find global type 'IterableIterator'. @@ -11,4 +11,5 @@ tests/cases/compiler/spreadOfParamsFromGeneratorMakesRequiredParams.ts(6,1): err call(function* (a: 'a') { }); // error, 2nd argument required ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type '[]' is not assignable to parameter of type 'never'. \ No newline at end of file +!!! error TS2554: Expected 2 arguments, but got 1. +!!! related TS6210 tests/cases/compiler/spreadOfParamsFromGeneratorMakesRequiredParams.ts:3:5: An argument for 'args' was not provided. \ No newline at end of file