From b65ff647c1debc58aa66b20359d644f54048fd56 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 2 Sep 2017 10:27:48 -0700 Subject: [PATCH 1/5] Improved caching scheme for anonymous types --- src/compiler/checker.ts | 281 +++++++++++++--------------------- src/compiler/types.ts | 5 +- src/services/signatureHelp.ts | 2 +- 3 files changed, 107 insertions(+), 181 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 269666d7b9468..7a9931f19a430 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4794,22 +4794,39 @@ namespace ts { return typeParameters; } - // Appends the outer type parameters of a node to a set of type parameters and returns the resulting set. The function - // allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set in-place and - // returns the same array. - function appendOuterTypeParameters(typeParameters: TypeParameter[], node: Node): TypeParameter[] { + // Return the outer type parameters of a node or undefined if the node has no outer type parameters. + function getOuterTypeParameters(node: Node, includeThisTypes?: boolean): TypeParameter[] { while (true) { node = node.parent; if (!node) { - return typeParameters; + return undefined; } - if (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || - node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression || - node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.ArrowFunction) { - const declarations = (node).typeParameters; - if (declarations) { - return appendTypeParameters(appendOuterTypeParameters(typeParameters, node), declarations); - } + switch (node.kind) { + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.MethodSignature: + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + case SyntaxKind.JSDocFunctionType: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.JSDocTemplateTag: + case SyntaxKind.MappedType: + const outerTypeParameters = getOuterTypeParameters(node, includeThisTypes); + if (node.kind === SyntaxKind.MappedType) { + return append(outerTypeParameters, getDeclaredTypeOfTypeParameter(getSymbolOfNode((node).typeParameter))); + } + const outerAndOwnTypeParameters = appendTypeParameters(outerTypeParameters, getEffectiveTypeParameterDeclarations(node) || emptyArray); + const thisType = includeThisTypes && + (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.InterfaceDeclaration) && + getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node)).thisType; + return thisType ? append(outerAndOwnTypeParameters, thisType) : outerAndOwnTypeParameters; } } } @@ -4817,7 +4834,7 @@ namespace ts { // The outer type parameters are those defined by enclosing generic classes, methods, or functions. function getOuterTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] { const declaration = symbol.flags & SymbolFlags.Class ? symbol.valueDeclaration : getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration); - return appendOuterTypeParameters(/*typeParameters*/ undefined, declaration); + return getOuterTypeParameters(declaration); } // The local type parameters are the combined set of type parameters from all declarations of the class, @@ -6800,7 +6817,7 @@ namespace ts { const id = getTypeListId(typeArguments); let instantiation = links.instantiations.get(id); if (!instantiation) { - links.instantiations.set(id, instantiation = instantiateTypeNoAlias(type, createTypeMapper(typeParameters, fillMissingTypeArguments(typeArguments, typeParameters, getMinTypeArgumentCount(typeParameters))))); + links.instantiations.set(id, instantiation = instantiateType(type, createTypeMapper(typeParameters, fillMissingTypeArguments(typeArguments, typeParameters, getMinTypeArgumentCount(typeParameters))))); } return instantiation; } @@ -8024,11 +8041,6 @@ namespace ts { return instantiateList(signatures, mapper, instantiateSignature); } - function instantiateCached(type: T, mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T { - const instantiations = mapper.instantiations || (mapper.instantiations = []); - return instantiations[type.id] || (instantiations[type.id] = instantiator(type, mapper)); - } - function makeUnaryTypeMapper(source: Type, target: Type) { return (t: Type) => t === source ? target : t; } @@ -8050,11 +8062,9 @@ namespace ts { function createTypeMapper(sources: TypeParameter[], targets: Type[]): TypeMapper { Debug.assert(targets === undefined || sources.length === targets.length); - const mapper: TypeMapper = sources.length === 1 ? makeUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) : + return sources.length === 1 ? makeUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) : sources.length === 2 ? makeBinaryTypeMapper(sources[0], targets ? targets[0] : anyType, sources[1], targets ? targets[1] : anyType) : - makeArrayTypeMapper(sources, targets); - mapper.mappedTypes = sources; - return mapper; + makeArrayTypeMapper(sources, targets); } function createTypeEraser(sources: TypeParameter[]): TypeMapper { @@ -8065,10 +8075,8 @@ namespace ts { * Maps forward-references to later types parameters to the empty object type. * This is used during inference when instantiating type parameter defaults. */ - function createBackreferenceMapper(typeParameters: TypeParameter[], index: number) { - const mapper: TypeMapper = t => indexOf(typeParameters, t) >= index ? emptyObjectType : t; - mapper.mappedTypes = typeParameters; - return mapper; + function createBackreferenceMapper(typeParameters: TypeParameter[], index: number): TypeMapper { + return t => indexOf(typeParameters, t) >= index ? emptyObjectType : t; } function isInferenceContext(mapper: TypeMapper): mapper is InferenceContext { @@ -8086,15 +8094,11 @@ namespace ts { } function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper { - const mapper: TypeMapper = t => instantiateType(mapper1(t), mapper2); - mapper.mappedTypes = concatenate(mapper1.mappedTypes, mapper2.mappedTypes); - return mapper; + return t => instantiateType(mapper1(t), mapper2); } - function createReplacementMapper(source: Type, target: Type, baseMapper: TypeMapper) { - const mapper: TypeMapper = t => t === source ? target : baseMapper(t); - mapper.mappedTypes = baseMapper.mappedTypes; - return mapper; + function createReplacementMapper(source: Type, target: Type, baseMapper: TypeMapper): TypeMapper { + return t => t === source ? target : baseMapper(t); } function cloneTypeParameter(typeParameter: TypeParameter): TypeParameter { @@ -8174,13 +8178,39 @@ namespace ts { return result; } - function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType { - const result = createObjectType(ObjectFlags.Anonymous | ObjectFlags.Instantiated, type.symbol); - result.target = type.objectFlags & ObjectFlags.Instantiated ? type.target : type; - result.mapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper; - result.aliasSymbol = type.aliasSymbol; - result.aliasTypeArguments = instantiateTypes(type.aliasTypeArguments, mapper); - return result; + function getAnonymousTypeInstantiation(type: AnonymousType, mapper: TypeMapper) { + if (type.objectFlags & ObjectFlags.Instantiated) { + mapper = combineTypeMappers(type.mapper, mapper); + type = type.target; + } + const symbol = type.symbol; + const links = getSymbolLinks(symbol); + if (!links.typeParameters) { + // This first time an anonymous type is instantiated we compute and store a list of the type + // parameters that are in scope (and therefore potentially referenced). + const typeParameters = getOuterTypeParameters(symbol.declarations[0], /*includeThisTypes*/ true); + links.typeParameters = typeParameters || emptyArray; + if (typeParameters) { + links.instantiations = createMap(); + links.instantiations.set(getTypeListId(typeParameters), type); + } + } + const typeParameters = links.typeParameters; + if (typeParameters.length) { + // We are instantiating an anonymous type that has one or more type parameters in scope. Apply the + // mapper to the type parameters to produce the effective list of type arguments, and compute the + // instantiation cache key from the type IDs of the type arguments. + const typeArguments = map(typeParameters, mapper); + const id = getTypeListId(typeArguments); + let result = links.instantiations.get(id); + if (!result) { + const newMapper = createTypeMapper(typeParameters, typeArguments); + result = type.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(type, newMapper) : instantiateAnonymousType(type, newMapper); + links.instantiations.set(id, result); + } + return result; + } + return type; } function instantiateMappedType(type: MappedType, mapper: TypeMapper): Type { @@ -8197,164 +8227,64 @@ namespace ts { if (typeVariable !== mappedTypeVariable) { return mapType(mappedTypeVariable, t => { if (isMappableType(t)) { - return instantiateMappedObjectType(type, createReplacementMapper(typeVariable, t, mapper)); + return instantiateAnonymousType(type, createReplacementMapper(typeVariable, t, mapper)); } return t; }); } } } - return instantiateMappedObjectType(type, mapper); + return instantiateAnonymousType(type, mapper); } function isMappableType(type: Type) { return type.flags & (TypeFlags.TypeParameter | TypeFlags.Object | TypeFlags.Intersection | TypeFlags.IndexedAccess); } - function instantiateMappedObjectType(type: MappedType, mapper: TypeMapper): Type { - const result = createObjectType(ObjectFlags.Mapped | ObjectFlags.Instantiated, type.symbol); - result.declaration = type.declaration; - result.mapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper; + function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType { + const result = createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol); + if (type.objectFlags & ObjectFlags.Mapped) { + (result).declaration = (type).declaration; + } + result.target = type; + result.mapper = mapper; result.aliasSymbol = type.aliasSymbol; result.aliasTypeArguments = instantiateTypes(type.aliasTypeArguments, mapper); return result; } - function isSymbolInScopeOfMappedTypeParameter(symbol: Symbol, mapper: TypeMapper) { - if (!(symbol.declarations && symbol.declarations.length)) { - return false; - } - const mappedTypes = mapper.mappedTypes; - // Starting with the parent of the symbol's declaration, check if the mapper maps any of - // the type parameters introduced by enclosing declarations. We just pick the first - // declaration since multiple declarations will all have the same parent anyway. - return !!findAncestor(symbol.declarations[0], node => { - if (node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.SourceFile) { - return "quit"; - } - switch (node.kind) { - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - case SyntaxKind.Constructor: - case SyntaxKind.CallSignature: - case SyntaxKind.ConstructSignature: - case SyntaxKind.IndexSignature: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - const typeParameters = getEffectiveTypeParameterDeclarations(node as DeclarationWithTypeParameters); - if (typeParameters) { - for (const d of typeParameters) { - if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode(d)))) { - return true; - } - } - } - if (isClassLike(node) || node.kind === SyntaxKind.InterfaceDeclaration) { - const thisType = getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node)).thisType; - if (thisType && contains(mappedTypes, thisType)) { - return true; - } - } - break; - case SyntaxKind.MappedType: - if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode((node).typeParameter)))) { - return true; - } - break; - case SyntaxKind.JSDocFunctionType: - const func = node as JSDocFunctionType; - for (const p of func.parameters) { - if (contains(mappedTypes, getTypeOfNode(p))) { - return true; - } - } - break; - } - }); - } - - function isTopLevelTypeAlias(symbol: Symbol) { - if (symbol.declarations && symbol.declarations.length) { - const parentKind = symbol.declarations[0].parent.kind; - return parentKind === SyntaxKind.SourceFile || parentKind === SyntaxKind.ModuleBlock; - } - return false; - } - function instantiateType(type: Type, mapper: TypeMapper): Type { if (type && mapper !== identityMapper) { - // If we are instantiating a type that has a top-level type alias, obtain the instantiation through - // the type alias instead in order to share instantiations for the same type arguments. This can - // dramatically reduce the number of structurally identical types we generate. Note that we can only - // perform this optimization for top-level type aliases. Consider: - // - // function f1(x: T) { - // type Foo = { x: X, t: T }; - // let obj: Foo = { x: x }; - // return obj; - // } - // function f2(x: U) { return f1(x); } - // let z = f2(42); - // - // Above, the declaration of f2 has an inferred return type that is an instantiation of f1's Foo - // equivalent to { x: U, t: U }. When instantiating this return type, we can't go back to Foo's - // cache because all cached instantiations are of the form { x: ???, t: T }, i.e. they have not been - // instantiated for T. Instead, we need to further instantiate the { x: U, t: U } form. - if (type.aliasSymbol && isTopLevelTypeAlias(type.aliasSymbol)) { - if (type.aliasTypeArguments) { - return getTypeAliasInstantiation(type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); + 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).objectFlags & ObjectFlags.Mapped) { + return getAnonymousTypeInstantiation(type, mapper); + } + if ((type).objectFlags & ObjectFlags.Reference) { + return createTypeReference((type).target, instantiateTypes((type).typeArguments, mapper)); } - return type; } - return instantiateTypeNoAlias(type, mapper); - } - return type; - } - - function instantiateTypeNoAlias(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. We skip instantiation - // if none of the type parameters that are in scope in the type's declaration are mapped by - // the given mapper, however we can only do that analysis if the type isn't itself an - // instantiation. - return type.symbol && - type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && - ((type).objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ? - instantiateCached(type, mapper, instantiateAnonymousType) : type; + if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) { + return getUnionType(instantiateTypes((type).types, mapper), /*subtypeReduction*/ false, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); } - if ((type).objectFlags & ObjectFlags.Mapped) { - return instantiateCached(type, mapper, instantiateMappedType); + if (type.flags & TypeFlags.Intersection) { + return getIntersectionType(instantiateTypes((type).types, mapper), type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); } - if ((type).objectFlags & ObjectFlags.Reference) { - return createTypeReference((type).target, instantiateTypes((type).typeArguments, mapper)); + 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.Union && !(type.flags & TypeFlags.Primitive)) { - return getUnionType(instantiateTypes((type).types, mapper), /*subtypeReduction*/ false, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); - } - if (type.flags & TypeFlags.Intersection) { - return getIntersectionType(instantiateTypes((type).types, mapper), type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); - } - 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)); } return type; } @@ -10368,7 +10298,6 @@ namespace ts { function createInferenceContext(signature: Signature, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext { const inferences = baseInferences ? map(baseInferences, cloneInferenceInfo) : map(signature.typeParameters, createInferenceInfo); const context = mapper as InferenceContext; - context.mappedTypes = signature.typeParameters; context.signature = signature; context.inferences = inferences; context.flags = flags; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 64444693acfec..99afe5e6e4e2e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3331,13 +3331,12 @@ namespace ts { } /* @internal */ - export interface MappedType extends ObjectType { + export interface MappedType extends AnonymousType { declaration: MappedTypeNode; typeParameter?: TypeParameter; constraintType?: Type; templateType?: Type; modifiersType?: Type; - mapper?: TypeMapper; // Instantiation mapper } export interface EvolvingArrayType extends ObjectType { @@ -3469,8 +3468,6 @@ namespace ts { /* @internal */ export interface TypeMapper { (t: TypeParameter): Type; - mappedTypes?: TypeParameter[]; // Types mapped by this mapper - instantiations?: Type[]; // Cache of instantiations created using this type mapper. } export const enum InferencePriority { diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 2976b0d28ee97..10d5dda7966fb 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -373,7 +373,7 @@ namespace ts.SignatureHelp { isVariadic = false; // type parameter lists are not variadic prefixDisplayParts.push(punctuationPart(SyntaxKind.LessThanToken)); // Use `.mapper` to ensure we get the generic type arguments even if this is an instantiated version of the signature. - const typeParameters = candidateSignature.mapper ? candidateSignature.mapper.mappedTypes : candidateSignature.typeParameters; + const typeParameters = candidateSignature.typeParameters; // !!! candidateSignature.mapper ? candidateSignature.mapper.mappedTypes : candidateSignature.typeParameters; signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray; suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken)); const parameterParts = mapToDisplayParts(writer => From 601a21c77b3af323b293cbcb3483936443bc50b7 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 2 Sep 2017 15:39:14 -0700 Subject: [PATCH 2/5] Fix signature help --- src/services/signatureHelp.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 10d5dda7966fb..7e8e748bb172d 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -372,8 +372,7 @@ namespace ts.SignatureHelp { if (isTypeParameterList) { isVariadic = false; // type parameter lists are not variadic prefixDisplayParts.push(punctuationPart(SyntaxKind.LessThanToken)); - // Use `.mapper` to ensure we get the generic type arguments even if this is an instantiated version of the signature. - const typeParameters = candidateSignature.typeParameters; // !!! candidateSignature.mapper ? candidateSignature.mapper.mappedTypes : candidateSignature.typeParameters; + const typeParameters = (candidateSignature.target || candidateSignature).typeParameters; signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray; suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken)); const parameterParts = mapToDisplayParts(writer => From 319617c5d8f7e496402b1ae746d0d9135bf880b4 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 3 Sep 2017 08:53:04 -0700 Subject: [PATCH 3/5] Optimize caching of type literals --- src/compiler/checker.ts | 46 +++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7a9931f19a430..112bbbf1c0a5b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8179,33 +8179,37 @@ namespace ts { } function getAnonymousTypeInstantiation(type: AnonymousType, mapper: TypeMapper) { - if (type.objectFlags & ObjectFlags.Instantiated) { - mapper = combineTypeMappers(type.mapper, mapper); - type = type.target; - } - const symbol = type.symbol; + const target = type.objectFlags & ObjectFlags.Instantiated ? type.target : type; + const symbol = target.symbol; const links = getSymbolLinks(symbol); - if (!links.typeParameters) { - // This first time an anonymous type is instantiated we compute and store a list of the type - // parameters that are in scope (and therefore potentially referenced). - const typeParameters = getOuterTypeParameters(symbol.declarations[0], /*includeThisTypes*/ true); - links.typeParameters = typeParameters || emptyArray; - if (typeParameters) { + let typeParameters = links.typeParameters; + if (!typeParameters) { + // The first time an anonymous type is instantiated we compute and store a list of the type + // parameters that are in scope (and therefore potentially referenced). For type literals that + // aren't the right hand side of a generic type alias declaration we optimize by reducing the + // set of type parameters to those that are actually referenced somewhere in the literal. + const declaration = symbol.declarations[0]; + const outerTypeParameters = getOuterTypeParameters(declaration, /*includeThisTypes*/ true) || emptyArray; + typeParameters = symbol.flags & SymbolFlags.TypeLiteral && !target.aliasTypeArguments ? + filter(outerTypeParameters, tp => isTypeParameterReferencedWithin(tp, declaration)) : + outerTypeParameters; + links.typeParameters = typeParameters; + if (typeParameters.length) { links.instantiations = createMap(); - links.instantiations.set(getTypeListId(typeParameters), type); + links.instantiations.set(getTypeListId(typeParameters), target); } } - const typeParameters = links.typeParameters; if (typeParameters.length) { // We are instantiating an anonymous type that has one or more type parameters in scope. Apply the // mapper to the type parameters to produce the effective list of type arguments, and compute the // instantiation cache key from the type IDs of the type arguments. - const typeArguments = map(typeParameters, mapper); + const combinedMapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper; + const typeArguments = map(typeParameters, combinedMapper); const id = getTypeListId(typeArguments); let result = links.instantiations.get(id); if (!result) { const newMapper = createTypeMapper(typeParameters, typeArguments); - result = type.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(type, newMapper) : instantiateAnonymousType(type, newMapper); + result = target.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(target, newMapper) : instantiateAnonymousType(target, newMapper); links.instantiations.set(id, result); } return result; @@ -8213,6 +8217,16 @@ namespace ts { return type; } + function isTypeParameterReferencedWithin(tp: TypeParameter, node: Node) { + return tp.isThisType ? forEachChild(node, checkThis) : forEachChild(node, checkIdentifier); + function checkThis(node: Node): boolean { + return node.kind === SyntaxKind.ThisType || forEachChild(node, checkThis); + } + function checkIdentifier(node: Node): boolean { + return node.kind === SyntaxKind.Identifier && isPartOfTypeNode(node) && getTypeFromTypeNode(node) === tp || forEachChild(node, checkIdentifier); + } + } + function instantiateMappedType(type: MappedType, mapper: TypeMapper): Type { // Check if we have a homomorphic mapped type, i.e. a type of the form { [P in keyof T]: X } for some // type variable T. If so, the mapped type is distributive over a union type and when T is instantiated @@ -10420,6 +10434,7 @@ namespace ts { function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) { let symbolStack: Symbol[]; let visited: Map; + //sys.write(typeToString(originalSource) + " ==> " + typeToString(originalTarget) + "\n"); inferFromTypes(originalSource, originalTarget); function inferFromTypes(source: Type, target: Type) { @@ -10487,6 +10502,7 @@ namespace ts { const inference = getInferenceInfoForType(target); if (inference) { if (!inference.isFixed) { + //sys.write(" " + typeToString(source) + "\n"); if (!inference.candidates || priority < inference.priority) { inference.candidates = [source]; inference.priority = priority; From a0c40943feaa4afb1fbbe038d2cf5daf9d642f48 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 3 Sep 2017 08:53:19 -0700 Subject: [PATCH 4/5] Accept new baselines --- .../reference/functionConstraintSatisfaction2.errors.txt | 4 ---- tests/baselines/reference/limitDeepInstantiations.errors.txt | 4 ++-- tests/baselines/reference/promisePermutations.errors.txt | 2 -- tests/baselines/reference/promisePermutations2.errors.txt | 2 -- tests/baselines/reference/promisePermutations3.errors.txt | 2 -- 5 files changed, 2 insertions(+), 12 deletions(-) diff --git a/tests/baselines/reference/functionConstraintSatisfaction2.errors.txt b/tests/baselines/reference/functionConstraintSatisfaction2.errors.txt index f87312634f82f..7558a97511fc6 100644 --- a/tests/baselines/reference/functionConstraintSatisfaction2.errors.txt +++ b/tests/baselines/reference/functionConstraintSatisfaction2.errors.txt @@ -22,8 +22,6 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstrain Type 'void' is not assignable to type 'string'. tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction2.ts(38,10): error TS2345: Argument of type 'U' is not assignable to parameter of type '(x: string) => string'. Type 'T' is not assignable to type '(x: string) => string'. - Type '() => void' is not assignable to type '(x: string) => string'. - Type 'void' is not assignable to type 'string'. ==== tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction2.ts (13 errors) ==== @@ -102,7 +100,5 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstrain ~ !!! error TS2345: Argument of type 'U' is not assignable to parameter of type '(x: string) => string'. !!! error TS2345: Type 'T' is not assignable to type '(x: string) => string'. -!!! error TS2345: Type '() => void' is not assignable to type '(x: string) => string'. -!!! error TS2345: Type 'void' is not assignable to type 'string'. } \ No newline at end of file diff --git a/tests/baselines/reference/limitDeepInstantiations.errors.txt b/tests/baselines/reference/limitDeepInstantiations.errors.txt index 330e5fbc8e43e..70718199d2bdf 100644 --- a/tests/baselines/reference/limitDeepInstantiations.errors.txt +++ b/tests/baselines/reference/limitDeepInstantiations.errors.txt @@ -1,4 +1,4 @@ -tests/cases/compiler/limitDeepInstantiations.ts(3,35): error TS2550: Generic type instantiation is excessively deep and possibly infinite. +tests/cases/compiler/limitDeepInstantiations.ts(3,35): error TS2502: '"true"' is referenced directly or indirectly in its own type annotation. tests/cases/compiler/limitDeepInstantiations.ts(5,13): error TS2344: Type '"false"' does not satisfy the constraint '"true"'. @@ -7,7 +7,7 @@ tests/cases/compiler/limitDeepInstantiations.ts(5,13): error TS2344: Type '"fals type Foo = { "true": Foo> }[T]; ~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2550: Generic type instantiation is excessively deep and possibly infinite. +!!! error TS2502: '"true"' is referenced directly or indirectly in its own type annotation. let f1: Foo<"true", {}>; let f2: Foo<"false", {}>; ~~~~~~~ diff --git a/tests/baselines/reference/promisePermutations.errors.txt b/tests/baselines/reference/promisePermutations.errors.txt index a876345ffa100..c5527d5aa6745 100644 --- a/tests/baselines/reference/promisePermutations.errors.txt +++ b/tests/baselines/reference/promisePermutations.errors.txt @@ -45,7 +45,6 @@ tests/cases/compiler/promisePermutations.ts(134,19): error TS2345: Argument of t tests/cases/compiler/promisePermutations.ts(137,33): error TS2345: Argument of type '(x: any) => IPromise' is not assignable to parameter of type '(error: any) => IPromise'. Type 'IPromise' is not assignable to type 'IPromise'. tests/cases/compiler/promisePermutations.ts(144,35): error TS2345: Argument of type '(x: any) => IPromise' is not assignable to parameter of type '(error: any) => IPromise'. - Type 'IPromise' is not assignable to type 'IPromise'. tests/cases/compiler/promisePermutations.ts(152,36): error TS2345: Argument of type '(x: any) => IPromise' is not assignable to parameter of type '(error: any) => Promise'. Type 'IPromise' is not assignable to type 'Promise'. Types of property 'then' are incompatible. @@ -290,7 +289,6 @@ tests/cases/compiler/promisePermutations.ts(160,21): error TS2345: Argument of t var r10d = r10.then(testFunction, sIPromise, nIPromise); // ok ~~~~~~~~~ !!! error TS2345: Argument of type '(x: any) => IPromise' is not assignable to parameter of type '(error: any) => IPromise'. -!!! error TS2345: Type 'IPromise' is not assignable to type 'IPromise'. var r10e = r10.then(testFunction, nIPromise, sIPromise).then(sIPromise, sIPromise, sIPromise); // ok var s10 = testFunction10P(x => x); var s10a = s10.then(testFunction10, testFunction10, testFunction10); // ok diff --git a/tests/baselines/reference/promisePermutations2.errors.txt b/tests/baselines/reference/promisePermutations2.errors.txt index 871ee2ae2c347..955063797c0e7 100644 --- a/tests/baselines/reference/promisePermutations2.errors.txt +++ b/tests/baselines/reference/promisePermutations2.errors.txt @@ -45,7 +45,6 @@ tests/cases/compiler/promisePermutations2.ts(133,19): error TS2345: Argument of tests/cases/compiler/promisePermutations2.ts(136,33): error TS2345: Argument of type '(x: any) => IPromise' is not assignable to parameter of type '(error: any) => IPromise'. Type 'IPromise' is not assignable to type 'IPromise'. tests/cases/compiler/promisePermutations2.ts(143,35): error TS2345: Argument of type '(x: any) => IPromise' is not assignable to parameter of type '(error: any) => IPromise'. - Type 'IPromise' is not assignable to type 'IPromise'. tests/cases/compiler/promisePermutations2.ts(151,36): error TS2345: Argument of type '(x: any) => IPromise' is not assignable to parameter of type '(error: any) => Promise'. Type 'IPromise' is not assignable to type 'Promise'. Types of property 'then' are incompatible. @@ -289,7 +288,6 @@ tests/cases/compiler/promisePermutations2.ts(159,21): error TS2345: Argument of var r10d = r10.then(testFunction, sIPromise, nIPromise); // error ~~~~~~~~~ !!! error TS2345: Argument of type '(x: any) => IPromise' is not assignable to parameter of type '(error: any) => IPromise'. -!!! error TS2345: Type 'IPromise' is not assignable to type 'IPromise'. var r10e = r10.then(testFunction, nIPromise, sIPromise).then(sIPromise, sIPromise, sIPromise); // ok var s10 = testFunction10P(x => x); var s10a = s10.then(testFunction10, testFunction10, testFunction10); // ok diff --git a/tests/baselines/reference/promisePermutations3.errors.txt b/tests/baselines/reference/promisePermutations3.errors.txt index 9d09559c5d32e..89a4cffe68813 100644 --- a/tests/baselines/reference/promisePermutations3.errors.txt +++ b/tests/baselines/reference/promisePermutations3.errors.txt @@ -48,7 +48,6 @@ tests/cases/compiler/promisePermutations3.ts(133,19): error TS2345: Argument of tests/cases/compiler/promisePermutations3.ts(136,33): error TS2345: Argument of type '(x: any) => IPromise' is not assignable to parameter of type '(error: any) => IPromise'. Type 'IPromise' is not assignable to type 'IPromise'. tests/cases/compiler/promisePermutations3.ts(143,35): error TS2345: Argument of type '(x: any) => IPromise' is not assignable to parameter of type '(error: any) => IPromise'. - Type 'IPromise' is not assignable to type 'IPromise'. tests/cases/compiler/promisePermutations3.ts(151,36): error TS2345: Argument of type '(x: any) => IPromise' is not assignable to parameter of type '(error: any) => Promise'. Type 'IPromise' is not assignable to type 'Promise'. Types of property 'then' are incompatible. @@ -301,7 +300,6 @@ tests/cases/compiler/promisePermutations3.ts(165,21): error TS2345: Argument of var r10d = r10.then(testFunction, sIPromise, nIPromise); // error ~~~~~~~~~ !!! error TS2345: Argument of type '(x: any) => IPromise' is not assignable to parameter of type '(error: any) => IPromise'. -!!! error TS2345: Type 'IPromise' is not assignable to type 'IPromise'. var r10e = r10.then(testFunction, nIPromise, sIPromise).then(sIPromise, sIPromise, sIPromise); // ok var s10 = testFunction10P(x => x); var s10a = s10.then(testFunction10, testFunction10, testFunction10); // ok From 82281d9910e9d5dd3289368dad63b18fb87dbc29 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 3 Sep 2017 11:00:03 -0700 Subject: [PATCH 5/5] Fix linting errors --- src/compiler/checker.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 112bbbf1c0a5b..42b15d1a0ac29 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10434,7 +10434,6 @@ namespace ts { function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) { let symbolStack: Symbol[]; let visited: Map; - //sys.write(typeToString(originalSource) + " ==> " + typeToString(originalTarget) + "\n"); inferFromTypes(originalSource, originalTarget); function inferFromTypes(source: Type, target: Type) { @@ -10502,7 +10501,6 @@ namespace ts { const inference = getInferenceInfoForType(target); if (inference) { if (!inference.isFixed) { - //sys.write(" " + typeToString(source) + "\n"); if (!inference.candidates || priority < inference.priority) { inference.candidates = [source]; inference.priority = priority;