Skip to content

Commit d82279e

Browse files
committed
Fix crash when serializing the return type of a generic call to Array.prototype.flat (microsoft#38904)
* Add declaration emit error and checking for circularly referential unions produced by recursive conditionals * Allow indexed accesses to produce alias symbols on types * Add test that still triggers the declaration emit error * Fix spelling
1 parent 2fff325 commit d82279e

File tree

47 files changed

+503
-222
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+503
-222
lines changed

Diff for: src/compiler/checker.ts

+28-10
Original file line numberDiff line numberDiff line change
@@ -4397,8 +4397,8 @@ namespace ts {
43974397
context.inferTypeParameters = (<ConditionalType>type).root.inferTypeParameters;
43984398
const extendsTypeNode = typeToTypeNodeHelper((<ConditionalType>type).extendsType, context);
43994399
context.inferTypeParameters = saveInferTypeParameters;
4400-
const trueTypeNode = typeToTypeNodeHelper(getTrueTypeFromConditionalType(<ConditionalType>type), context);
4401-
const falseTypeNode = typeToTypeNodeHelper(getFalseTypeFromConditionalType(<ConditionalType>type), context);
4400+
const trueTypeNode = typeToTypeNodeOrCircularityElision(getTrueTypeFromConditionalType(<ConditionalType>type));
4401+
const falseTypeNode = typeToTypeNodeOrCircularityElision(getFalseTypeFromConditionalType(<ConditionalType>type));
44024402
context.approximateLength += 15;
44034403
return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode);
44044404
}
@@ -4408,6 +4408,21 @@ namespace ts {
44084408

44094409
return Debug.fail("Should be unreachable.");
44104410

4411+
4412+
function typeToTypeNodeOrCircularityElision(type: Type) {
4413+
if (type.flags & TypeFlags.Union) {
4414+
if (context.visitedTypes && context.visitedTypes.has("" + getTypeId(type))) {
4415+
if (!(context.flags & NodeBuilderFlags.AllowAnonymousIdentifier)) {
4416+
context.encounteredError = true;
4417+
context.tracker?.reportCyclicStructureError?.();
4418+
}
4419+
return createElidedInformationPlaceholder(context);
4420+
}
4421+
return visitAndTransformType(type, type => typeToTypeNodeHelper(type, context));
4422+
}
4423+
return typeToTypeNodeHelper(type, context);
4424+
}
4425+
44114426
function createMappedTypeNodeFromType(type: MappedType) {
44124427
Debug.assert(!!(type.flags & TypeFlags.Object));
44134428
const readonlyToken = type.declaration.readonlyToken ? <ReadonlyToken | PlusToken | MinusToken>createToken(type.declaration.readonlyToken.kind) : undefined;
@@ -12794,10 +12809,12 @@ namespace ts {
1279412809
return links.resolvedType;
1279512810
}
1279612811

12797-
function createIndexedAccessType(objectType: Type, indexType: Type) {
12812+
function createIndexedAccessType(objectType: Type, indexType: Type, aliasSymbol: Symbol | undefined, aliasTypeArguments: readonly Type[] | undefined) {
1279812813
const type = <IndexedAccessType>createType(TypeFlags.IndexedAccess);
1279912814
type.objectType = objectType;
1280012815
type.indexType = indexType;
12816+
type.aliasSymbol = aliasSymbol;
12817+
type.aliasTypeArguments = aliasTypeArguments;
1280112818
return type;
1280212819
}
1280312820

@@ -13129,11 +13146,11 @@ namespace ts {
1312913146
return instantiateType(getTemplateTypeFromMappedType(objectType), templateMapper);
1313013147
}
1313113148

13132-
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression): Type {
13133-
return getIndexedAccessTypeOrUndefined(objectType, indexType, accessNode, AccessFlags.None) || (accessNode ? errorType : unknownType);
13149+
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type {
13150+
return getIndexedAccessTypeOrUndefined(objectType, indexType, accessNode, AccessFlags.None, aliasSymbol, aliasTypeArguments) || (accessNode ? errorType : unknownType);
1313413151
}
1313513152

13136-
function getIndexedAccessTypeOrUndefined(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, accessFlags = AccessFlags.None): Type | undefined {
13153+
function getIndexedAccessTypeOrUndefined(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, accessFlags = AccessFlags.None, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type | undefined {
1313713154
if (objectType === wildcardType || indexType === wildcardType) {
1313813155
return wildcardType;
1313913156
}
@@ -13155,7 +13172,7 @@ namespace ts {
1315513172
const id = objectType.id + "," + indexType.id;
1315613173
let type = indexedAccessTypes.get(id);
1315713174
if (!type) {
13158-
indexedAccessTypes.set(id, type = createIndexedAccessType(objectType, indexType));
13175+
indexedAccessTypes.set(id, type = createIndexedAccessType(objectType, indexType, aliasSymbol, aliasTypeArguments));
1315913176
}
1316013177
return type;
1316113178
}
@@ -13183,7 +13200,7 @@ namespace ts {
1318313200
if (wasMissingProp) {
1318413201
return undefined;
1318513202
}
13186-
return accessFlags & AccessFlags.Writing ? getIntersectionType(propTypes) : getUnionType(propTypes);
13203+
return accessFlags & AccessFlags.Writing ? getIntersectionType(propTypes, aliasSymbol, aliasTypeArguments) : getUnionType(propTypes, UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
1318713204
}
1318813205
return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, /* supressNoImplicitAnyError */ false, accessNode, accessFlags | AccessFlags.CacheSymbol);
1318913206
}
@@ -13193,7 +13210,8 @@ namespace ts {
1319313210
if (!links.resolvedType) {
1319413211
const objectType = getTypeFromTypeNode(node.objectType);
1319513212
const indexType = getTypeFromTypeNode(node.indexType);
13196-
const resolved = getIndexedAccessType(objectType, indexType, node);
13213+
const potentialAlias = getAliasSymbolForTypeNode(node);
13214+
const resolved = getIndexedAccessType(objectType, indexType, node, potentialAlias, getTypeArgumentsForAliasSymbol(potentialAlias));
1319713215
links.resolvedType = resolved.flags & TypeFlags.IndexedAccess &&
1319813216
(<IndexedAccessType>resolved).objectType === objectType &&
1319913217
(<IndexedAccessType>resolved).indexType === indexType ?
@@ -14341,7 +14359,7 @@ namespace ts {
1434114359
return getIndexType(instantiateType((<IndexType>type).type, mapper));
1434214360
}
1434314361
if (flags & TypeFlags.IndexedAccess) {
14344-
return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper));
14362+
return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper), /*accessNode*/ undefined, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
1434514363
}
1434614364
if (flags & TypeFlags.Conditional) {
1434714365
return getConditionalTypeInstantiation(<ConditionalType>type, combineTypeMappers((<ConditionalType>type).mapper, mapper));

Diff for: src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -3509,6 +3509,10 @@
35093509
"category": "Error",
35103510
"code": 5083
35113511
},
3512+
"The inferred type of '{0}' references a type with a cyclic structure which cannot be trivially serialized. A type annotation is necessary.": {
3513+
"category": "Error",
3514+
"code": 5088
3515+
},
35123516

35133517
"Generates a sourcemap for each corresponding '.d.ts' file.": {
35143518
"category": "Message",

Diff for: src/compiler/transformers/declarations.ts

+8
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ namespace ts {
7272
trackSymbol,
7373
reportInaccessibleThisError,
7474
reportInaccessibleUniqueSymbolError,
75+
reportCyclicStructureError,
7576
reportPrivateInBaseOfClassExpression,
7677
reportLikelyUnsafeImportRequiredError,
7778
moduleResolverHost: host,
@@ -175,6 +176,13 @@ namespace ts {
175176
}
176177
}
177178

179+
function reportCyclicStructureError() {
180+
if (errorNameNode) {
181+
context.addDiagnostic(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_a_type_with_a_cyclic_structure_which_cannot_be_trivially_serialized_A_type_annotation_is_necessary,
182+
declarationNameToString(errorNameNode)));
183+
}
184+
}
185+
178186
function reportInaccessibleThisError() {
179187
if (errorNameNode) {
180188
context.addDiagnostic(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary,

Diff for: src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6479,6 +6479,7 @@ namespace ts {
64796479
reportInaccessibleThisError?(): void;
64806480
reportPrivateInBaseOfClassExpression?(propertyName: string): void;
64816481
reportInaccessibleUniqueSymbolError?(): void;
6482+
reportCyclicStructureError?(): void;
64826483
reportLikelyUnsafeImportRequiredError?(specifier: string): void;
64836484
moduleResolverHost?: ModuleSpecifierResolutionHost & { getCommonSourceDirectory(): string };
64846485
trackReferencedAmbientModule?(decl: ModuleDeclaration, symbol: Symbol): void;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
tests/cases/compiler/arrayFakeFlatNoCrashInferenceDeclarations.ts(13,10): error TS5088: The inferred type of 'foo' references a type with a cyclic structure which cannot be trivially serialized. A type annotation is necessary.
2+
3+
4+
==== tests/cases/compiler/arrayFakeFlatNoCrashInferenceDeclarations.ts (1 errors) ====
5+
type BadFlatArray<Arr, Depth extends number> = {obj: {
6+
"done": Arr,
7+
"recur": Arr extends ReadonlyArray<infer InnerArr>
8+
? BadFlatArray<InnerArr, [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][Depth]>
9+
: Arr
10+
}[Depth extends -1 ? "done" : "recur"]}["obj"];
11+
12+
declare function flat<A, D extends number = 1>(
13+
arr: A,
14+
depth?: D
15+
): BadFlatArray<A, D>[]
16+
17+
function foo<T>(arr: T[], depth: number) {
18+
~~~
19+
!!! error TS5088: The inferred type of 'foo' references a type with a cyclic structure which cannot be trivially serialized. A type annotation is necessary.
20+
return flat(arr, depth);
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//// [arrayFakeFlatNoCrashInferenceDeclarations.ts]
2+
type BadFlatArray<Arr, Depth extends number> = {obj: {
3+
"done": Arr,
4+
"recur": Arr extends ReadonlyArray<infer InnerArr>
5+
? BadFlatArray<InnerArr, [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][Depth]>
6+
: Arr
7+
}[Depth extends -1 ? "done" : "recur"]}["obj"];
8+
9+
declare function flat<A, D extends number = 1>(
10+
arr: A,
11+
depth?: D
12+
): BadFlatArray<A, D>[]
13+
14+
function foo<T>(arr: T[], depth: number) {
15+
return flat(arr, depth);
16+
}
17+
18+
//// [arrayFakeFlatNoCrashInferenceDeclarations.js]
19+
"use strict";
20+
function foo(arr, depth) {
21+
return flat(arr, depth);
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
=== tests/cases/compiler/arrayFakeFlatNoCrashInferenceDeclarations.ts ===
2+
type BadFlatArray<Arr, Depth extends number> = {obj: {
3+
>BadFlatArray : Symbol(BadFlatArray, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 0, 0))
4+
>Arr : Symbol(Arr, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 0, 18))
5+
>Depth : Symbol(Depth, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 0, 22))
6+
>obj : Symbol(obj, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 0, 48))
7+
8+
"done": Arr,
9+
>"done" : Symbol("done", Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 0, 54))
10+
>Arr : Symbol(Arr, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 0, 18))
11+
12+
"recur": Arr extends ReadonlyArray<infer InnerArr>
13+
>"recur" : Symbol("recur", Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 1, 16))
14+
>Arr : Symbol(Arr, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 0, 18))
15+
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2016.array.include.d.ts, --, --), Decl(lib.es2019.array.d.ts, --, --))
16+
>InnerArr : Symbol(InnerArr, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 2, 44))
17+
18+
? BadFlatArray<InnerArr, [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][Depth]>
19+
>BadFlatArray : Symbol(BadFlatArray, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 0, 0))
20+
>InnerArr : Symbol(InnerArr, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 2, 44))
21+
>Depth : Symbol(Depth, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 0, 22))
22+
23+
: Arr
24+
>Arr : Symbol(Arr, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 0, 18))
25+
26+
}[Depth extends -1 ? "done" : "recur"]}["obj"];
27+
>Depth : Symbol(Depth, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 0, 22))
28+
29+
declare function flat<A, D extends number = 1>(
30+
>flat : Symbol(flat, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 5, 47))
31+
>A : Symbol(A, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 7, 22))
32+
>D : Symbol(D, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 7, 24))
33+
34+
arr: A,
35+
>arr : Symbol(arr, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 7, 47))
36+
>A : Symbol(A, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 7, 22))
37+
38+
depth?: D
39+
>depth : Symbol(depth, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 8, 11))
40+
>D : Symbol(D, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 7, 24))
41+
42+
): BadFlatArray<A, D>[]
43+
>BadFlatArray : Symbol(BadFlatArray, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 0, 0))
44+
>A : Symbol(A, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 7, 22))
45+
>D : Symbol(D, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 7, 24))
46+
47+
function foo<T>(arr: T[], depth: number) {
48+
>foo : Symbol(foo, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 10, 23))
49+
>T : Symbol(T, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 12, 13))
50+
>arr : Symbol(arr, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 12, 16))
51+
>T : Symbol(T, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 12, 13))
52+
>depth : Symbol(depth, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 12, 25))
53+
54+
return flat(arr, depth);
55+
>flat : Symbol(flat, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 5, 47))
56+
>arr : Symbol(arr, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 12, 16))
57+
>depth : Symbol(depth, Decl(arrayFakeFlatNoCrashInferenceDeclarations.ts, 12, 25))
58+
}

0 commit comments

Comments
 (0)