From c2e6f7aacc507a2351a09d36434e103c6b7b620d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 26 Feb 2018 13:48:40 -0800 Subject: [PATCH] Disallow recursion --- src/compiler/checker.ts | 137 +++++++++------------------ src/compiler/diagnosticMessages.json | 4 - src/compiler/types.ts | 4 +- 3 files changed, 46 insertions(+), 99 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 004fd947edc..84355e32a73 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -58,8 +58,6 @@ namespace ts { let symbolCount = 0; let enumCount = 0; let symbolInstantiationDepth = 0; - let aliasInstantiationDepth = 0; - const aliasInstantiations: Symbol[] = []; const emptySymbols = createSymbolTable(); const identityMapper: (type: Type) => Type = identity; @@ -8189,18 +8187,10 @@ namespace ts { return type.flags & TypeFlags.Substitution ? (type).typeParameter : type; } - function getRootTrueType(root: ConditionalRoot) { - return root.resolvedTrueType || (root.resolvedTrueType = getTypeFromTypeNode(root.node.trueType)); - } - - function getRootFalseType(root: ConditionalRoot) { - return root.resolvedFalseType || (root.resolvedFalseType = getTypeFromTypeNode(root.node.falseType)); - } - function getConditionalType(root: ConditionalRoot, mapper: TypeMapper): Type { let combinedMapper: TypeMapper; - const getTrueType = () => instantiateType(getRootTrueType(root), combinedMapper || mapper); - const getFalseType = () => instantiateType(getRootFalseType(root), mapper); + const getTrueType = () => instantiateType(root.trueType, combinedMapper || mapper); + const getFalseType = () => instantiateType(root.falseType, mapper); const checkType = instantiateType(root.checkType, mapper); const extendsType = instantiateType(root.extendsType, mapper); // Return falseType for a definitely false extends check. We check an instantations of the two @@ -8247,11 +8237,11 @@ namespace ts { } function getTrueTypeFromConditionalType(type: ConditionalType) { - return type.resolvedTrueType || (type.resolvedTrueType = instantiateType(getRootTrueType(type.root), type.mapper)); + return type.resolvedTrueType || (type.resolvedTrueType = instantiateType(type.root.trueType, type.mapper)); } function getFalseTypeFromConditionalType(type: ConditionalType) { - return type.resolvedFalseType || (type.resolvedFalseType = instantiateType(getRootFalseType(type.root), type.mapper)); + return type.resolvedFalseType || (type.resolvedFalseType = instantiateType(type.root.falseType, type.mapper)); } function getInferTypeParameters(node: ConditionalTypeNode): TypeParameter[] { @@ -8275,14 +8265,14 @@ namespace ts { node, checkType, extendsType: getTypeFromTypeNode(node.extendsType), + trueType: getTypeFromTypeNode(node.trueType), + falseType: getTypeFromTypeNode(node.falseType), isDistributive: !!(checkType.flags & TypeFlags.TypeParameter), inferTypeParameters: getInferTypeParameters(node), outerTypeParameters, instantiations: undefined, aliasSymbol: getAliasSymbolForTypeNode(node), - aliasTypeArguments: getAliasTypeArgumentsForTypeNode(node), - resolvedTrueType: undefined, - resolvedFalseType: undefined + aliasTypeArguments: getAliasTypeArgumentsForTypeNode(node) }; links.resolvedType = getConditionalType(root, /*mapper*/ undefined); if (outerTypeParameters) { @@ -8910,89 +8900,50 @@ namespace ts { return getConditionalType(root, mapper); } - function getInstantiationErrorTypeAlias() { - const counted: Symbol[] = []; - let topCount = 0; - let topSymbol: Symbol; - for (let i = 0; i < aliasInstantiationDepth - topCount; i++) { - const symbol = aliasInstantiations[i]; - if (counted.indexOf(symbol) < 0) { - counted.push(symbol); - let count = 0; - for (let j = i; j < aliasInstantiationDepth; j++) { - if (symbol === aliasInstantiations[j]) count++; - } - if (count > topCount) { - topCount = count; - topSymbol = symbol; - } - } - } - return topSymbol && getDeclarationOfKind(topSymbol, SyntaxKind.TypeAliasDeclaration); - } - function instantiateType(type: Type, mapper: TypeMapper): Type { if (type && mapper && mapper !== identityMapper) { - if (aliasInstantiationDepth >= 100) { - const declaration = getInstantiationErrorTypeAlias(); - error(declaration, Diagnostics.Recursive_instantiations_of_type_0_are_excessively_deep_and_possibly_infinite, - declarationNameToString(declaration.name)); - return unknownType; + if (type.flags & TypeFlags.TypeParameter) { + return mapper(type); } - if (type.aliasSymbol) { - aliasInstantiations[aliasInstantiationDepth] = type.aliasSymbol; - aliasInstantiationDepth++; - const result = instantiateTypeWorker(type, mapper); - aliasInstantiationDepth--; - return result; + if (type.flags & TypeFlags.Object) { + if ((type).objectFlags & ObjectFlags.Anonymous) { + // If the anonymous type originates in a declaration of a function, method, class, or + // interface, in an object type literal, or in an object literal expression, we may need + // to instantiate the type because it might reference a type parameter. + return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ? + getAnonymousTypeInstantiation(type, mapper) : type; + } + if ((type).objectFlags & ObjectFlags.Mapped) { + return getAnonymousTypeInstantiation(type, mapper); + } + if ((type).objectFlags & ObjectFlags.Reference) { + const typeArguments = (type).typeArguments; + const newTypeArguments = instantiateTypes(typeArguments, mapper); + return newTypeArguments !== typeArguments ? createTypeReference((type).target, newTypeArguments) : type; + } } - return instantiateTypeWorker(type, mapper); - } - return type; - } - - function instantiateTypeWorker(type: Type, mapper: TypeMapper): Type { - if (type.flags & TypeFlags.TypeParameter) { - return mapper(type); - } - if (type.flags & TypeFlags.Object) { - if ((type).objectFlags & ObjectFlags.Anonymous) { - // If the anonymous type originates in a declaration of a function, method, class, or - // interface, in an object type literal, or in an object literal expression, we may need - // to instantiate the type because it might reference a type parameter. - return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ? - getAnonymousTypeInstantiation(type, mapper) : type; + if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) { + const types = (type).types; + const newTypes = instantiateTypes(types, mapper); + return newTypes !== types ? getUnionType(newTypes, UnionReduction.Literal, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)) : type; } - if ((type).objectFlags & ObjectFlags.Mapped) { - return getAnonymousTypeInstantiation(type, mapper); + if (type.flags & TypeFlags.Intersection) { + const types = (type).types; + const newTypes = instantiateTypes(types, mapper); + return newTypes !== types ? getIntersectionType(newTypes, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)) : type; } - if ((type).objectFlags & ObjectFlags.Reference) { - const typeArguments = (type).typeArguments; - const newTypeArguments = instantiateTypes(typeArguments, mapper); - return newTypeArguments !== typeArguments ? createTypeReference((type).target, newTypeArguments) : type; + if (type.flags & TypeFlags.Index) { + return getIndexType(instantiateType((type).type, mapper)); + } + if (type.flags & TypeFlags.IndexedAccess) { + return getIndexedAccessType(instantiateType((type).objectType, mapper), instantiateType((type).indexType, mapper)); + } + if (type.flags & TypeFlags.Conditional) { + return getConditionalTypeInstantiation(type, combineTypeMappers((type).mapper, mapper)); + } + if (type.flags & TypeFlags.Substitution) { + return mapper((type).typeParameter); } - } - if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) { - const types = (type).types; - const newTypes = instantiateTypes(types, mapper); - return newTypes !== types ? getUnionType(newTypes, UnionReduction.Literal, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)) : type; - } - if (type.flags & TypeFlags.Intersection) { - const types = (type).types; - const newTypes = instantiateTypes(types, mapper); - return newTypes !== types ? getIntersectionType(newTypes, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)) : type; - } - if (type.flags & TypeFlags.Index) { - return getIndexType(instantiateType((type).type, mapper)); - } - if (type.flags & TypeFlags.IndexedAccess) { - return getIndexedAccessType(instantiateType((type).objectType, mapper), instantiateType((type).indexType, mapper)); - } - if (type.flags & TypeFlags.Conditional) { - return getConditionalTypeInstantiation(type, combineTypeMappers((type).mapper, mapper)); - } - if (type.flags & TypeFlags.Substitution) { - return mapper((type).typeParameter); } return type; } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a38fcd4dafa..f120e81283a 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1988,10 +1988,6 @@ "category": "Error", "code": 2567 }, - "Recursive instantiations of type '{0}' are excessively deep and possibly infinite.": { - "category": "Error", - "code": 2568 - }, "JSX element attributes type '{0}' may not be a union type.": { "category": "Error", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cc57d1215f7..758a5e4f1f6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3815,14 +3815,14 @@ namespace ts { node: ConditionalTypeNode; checkType: Type; extendsType: Type; + trueType: Type; + falseType: Type; isDistributive: boolean; inferTypeParameters: TypeParameter[]; outerTypeParameters?: TypeParameter[]; instantiations?: Map; aliasSymbol: Symbol; aliasTypeArguments: Type[]; - resolvedTrueType?: Type; - resolvedFalseType?: Type; } // T extends U ? X : Y (TypeFlags.Conditional)