From 9fc83b3d5beae5800251770f466ad8fbf6986bff Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 12 Jun 2023 11:17:02 -0700 Subject: [PATCH 1/4] Mark base constraints as circular upon reaching depth limit --- src/compiler/checker.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ab963a802354a..90918e01a827c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13719,6 +13719,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { result = computeBaseConstraint(getSimplifiedType(t, /*writing*/ false)); stack.pop(); } + else { + result = circularConstraintType; + } if (!popTypeResolution()) { if (t.flags & TypeFlags.TypeParameter) { const errorNode = getConstraintDeclaration(t as TypeParameter); From 56092920d40615e9e8cfa7c24ad1e4fe8e910477 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 12 Jun 2023 11:24:26 -0700 Subject: [PATCH 2/4] Add regression test --- .../circularBaseConstraint.errors.txt | 22 ++++++++++ .../reference/circularBaseConstraint.symbols | 40 +++++++++++++++++++ .../reference/circularBaseConstraint.types | 25 ++++++++++++ .../cases/compiler/circularBaseConstraint.ts | 18 +++++++++ 4 files changed, 105 insertions(+) create mode 100644 tests/baselines/reference/circularBaseConstraint.errors.txt create mode 100644 tests/baselines/reference/circularBaseConstraint.symbols create mode 100644 tests/baselines/reference/circularBaseConstraint.types create mode 100644 tests/cases/compiler/circularBaseConstraint.ts diff --git a/tests/baselines/reference/circularBaseConstraint.errors.txt b/tests/baselines/reference/circularBaseConstraint.errors.txt new file mode 100644 index 0000000000000..efee09b0ad837 --- /dev/null +++ b/tests/baselines/reference/circularBaseConstraint.errors.txt @@ -0,0 +1,22 @@ +tests/cases/compiler/circularBaseConstraint.ts(14,8): error TS2304: Cannot find name 'a'. + + +==== tests/cases/compiler/circularBaseConstraint.ts (1 errors) ==== + // Repro from #54610 + + type A = T; + + type B = T extends any[] + ? never + : A extends infer key + ? key extends keyof T + ? B + : never + : never; + + function foo() { + `${a}` as B; + ~ +!!! error TS2304: Cannot find name 'a'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/circularBaseConstraint.symbols b/tests/baselines/reference/circularBaseConstraint.symbols new file mode 100644 index 0000000000000..75f6be65fd603 --- /dev/null +++ b/tests/baselines/reference/circularBaseConstraint.symbols @@ -0,0 +1,40 @@ +=== tests/cases/compiler/circularBaseConstraint.ts === +// Repro from #54610 + +type A = T; +>A : Symbol(A, Decl(circularBaseConstraint.ts, 0, 0)) +>T : Symbol(T, Decl(circularBaseConstraint.ts, 2, 7)) +>T : Symbol(T, Decl(circularBaseConstraint.ts, 2, 7)) + +type B = T extends any[] +>B : Symbol(B, Decl(circularBaseConstraint.ts, 2, 14)) +>T : Symbol(T, Decl(circularBaseConstraint.ts, 4, 7)) +>T : Symbol(T, Decl(circularBaseConstraint.ts, 4, 7)) + + ? never + : A extends infer key +>A : Symbol(A, Decl(circularBaseConstraint.ts, 0, 0)) +>T : Symbol(T, Decl(circularBaseConstraint.ts, 4, 7)) +>key : Symbol(key, Decl(circularBaseConstraint.ts, 6, 24)) + + ? key extends keyof T +>key : Symbol(key, Decl(circularBaseConstraint.ts, 6, 24)) +>T : Symbol(T, Decl(circularBaseConstraint.ts, 4, 7)) + + ? B +>B : Symbol(B, Decl(circularBaseConstraint.ts, 2, 14)) +>T : Symbol(T, Decl(circularBaseConstraint.ts, 4, 7)) +>key : Symbol(key, Decl(circularBaseConstraint.ts, 6, 24)) + + : never + : never; + +function foo() { +>foo : Symbol(foo, Decl(circularBaseConstraint.ts, 10, 12)) +>T : Symbol(T, Decl(circularBaseConstraint.ts, 12, 13)) + + `${a}` as B; +>B : Symbol(B, Decl(circularBaseConstraint.ts, 2, 14)) +>T : Symbol(T, Decl(circularBaseConstraint.ts, 12, 13)) +} + diff --git a/tests/baselines/reference/circularBaseConstraint.types b/tests/baselines/reference/circularBaseConstraint.types new file mode 100644 index 0000000000000..1a53288497d8a --- /dev/null +++ b/tests/baselines/reference/circularBaseConstraint.types @@ -0,0 +1,25 @@ +=== tests/cases/compiler/circularBaseConstraint.ts === +// Repro from #54610 + +type A = T; +>A : T + +type B = T extends any[] +>B : B + + ? never + : A extends infer key + ? key extends keyof T + ? B + : never + : never; + +function foo() { +>foo : () => void + + `${a}` as B; +>`${a}` as B : B +>`${a}` : string +>a : any +} + diff --git a/tests/cases/compiler/circularBaseConstraint.ts b/tests/cases/compiler/circularBaseConstraint.ts new file mode 100644 index 0000000000000..df41734bb0d11 --- /dev/null +++ b/tests/cases/compiler/circularBaseConstraint.ts @@ -0,0 +1,18 @@ +// @strict: true +// @noEmit: true + +// Repro from #54610 + +type A = T; + +type B = T extends any[] + ? never + : A extends infer key + ? key extends keyof T + ? B + : never + : never; + +function foo() { + `${a}` as B; +} From a13ce4a8c3ac0802d4cf7bdcd717a454cda1bd7e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 12 Jun 2023 12:16:39 -0700 Subject: [PATCH 3/4] Accept new baselines --- tests/baselines/reference/circularBaseConstraint.errors.txt | 4 ++-- tests/baselines/reference/circularBaseConstraint.symbols | 4 +++- tests/baselines/reference/circularBaseConstraint.types | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/circularBaseConstraint.errors.txt b/tests/baselines/reference/circularBaseConstraint.errors.txt index efee09b0ad837..d5bbc1abff4a8 100644 --- a/tests/baselines/reference/circularBaseConstraint.errors.txt +++ b/tests/baselines/reference/circularBaseConstraint.errors.txt @@ -1,7 +1,7 @@ -tests/cases/compiler/circularBaseConstraint.ts(14,8): error TS2304: Cannot find name 'a'. +circularBaseConstraint.ts(14,8): error TS2304: Cannot find name 'a'. -==== tests/cases/compiler/circularBaseConstraint.ts (1 errors) ==== +==== circularBaseConstraint.ts (1 errors) ==== // Repro from #54610 type A = T; diff --git a/tests/baselines/reference/circularBaseConstraint.symbols b/tests/baselines/reference/circularBaseConstraint.symbols index 75f6be65fd603..1c34bbb9b0451 100644 --- a/tests/baselines/reference/circularBaseConstraint.symbols +++ b/tests/baselines/reference/circularBaseConstraint.symbols @@ -1,4 +1,6 @@ -=== tests/cases/compiler/circularBaseConstraint.ts === +//// [tests/cases/compiler/circularBaseConstraint.ts] //// + +=== circularBaseConstraint.ts === // Repro from #54610 type A = T; diff --git a/tests/baselines/reference/circularBaseConstraint.types b/tests/baselines/reference/circularBaseConstraint.types index 1a53288497d8a..86d26e29612a0 100644 --- a/tests/baselines/reference/circularBaseConstraint.types +++ b/tests/baselines/reference/circularBaseConstraint.types @@ -1,4 +1,6 @@ -=== tests/cases/compiler/circularBaseConstraint.ts === +//// [tests/cases/compiler/circularBaseConstraint.ts] //// + +=== circularBaseConstraint.ts === // Repro from #54610 type A = T; From 3dda751d0683204ea81df7e5c2c958e618079326 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 13 Jun 2023 16:39:44 -0700 Subject: [PATCH 4/4] Add depth limiter to isConstTypeVariable --- src/compiler/checker.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b75e9ffed3f32..d262fe5609a6b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13538,14 +13538,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return hasNonCircularBaseConstraint(typeParameter) ? getConstraintFromTypeParameter(typeParameter) : undefined; } - function isConstTypeVariable(type: Type | undefined): boolean { - return !!(type && ( + function isConstTypeVariable(type: Type | undefined, depth = 0): boolean { + return depth < 5 && !!(type && ( type.flags & TypeFlags.TypeParameter && some((type as TypeParameter).symbol?.declarations, d => hasSyntacticModifier(d, ModifierFlags.Const)) || - type.flags & TypeFlags.Union && some((type as UnionType).types, isConstTypeVariable) || - type.flags & TypeFlags.IndexedAccess && isConstTypeVariable((type as IndexedAccessType).objectType) || - type.flags & TypeFlags.Conditional && isConstTypeVariable(getConstraintOfConditionalType(type as ConditionalType)) || - type.flags & TypeFlags.Substitution && isConstTypeVariable((type as SubstitutionType).baseType) || - isGenericTupleType(type) && findIndex(getElementTypes(type), (t, i) => !!(type.target.elementFlags[i] & ElementFlags.Variadic) && isConstTypeVariable(t)) >= 0)); + type.flags & TypeFlags.Union && some((type as UnionType).types, t => isConstTypeVariable(t, depth)) || + type.flags & TypeFlags.IndexedAccess && isConstTypeVariable((type as IndexedAccessType).objectType, depth + 1) || + type.flags & TypeFlags.Conditional && isConstTypeVariable(getConstraintOfConditionalType(type as ConditionalType), depth + 1) || + type.flags & TypeFlags.Substitution && isConstTypeVariable((type as SubstitutionType).baseType, depth) || + isGenericTupleType(type) && findIndex(getElementTypes(type), (t, i) => !!(type.target.elementFlags[i] & ElementFlags.Variadic) && isConstTypeVariable(t, depth)) >= 0)); } function getConstraintOfIndexedAccess(type: IndexedAccessType) { @@ -13719,9 +13719,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { result = computeBaseConstraint(getSimplifiedType(t, /*writing*/ false)); stack.pop(); } - else { - result = circularConstraintType; - } if (!popTypeResolution()) { if (t.flags & TypeFlags.TypeParameter) { const errorNode = getConstraintDeclaration(t as TypeParameter);