From 95bf71f08c114dc22979cfbeed5f1bcddfc6bbbd Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 5 Sep 2017 17:17:04 -0700 Subject: [PATCH 1/2] Use canonicalized forms when comparing signatures --- src/compiler/checker.ts | 31 ++++++++++++++++++++++++++----- src/compiler/types.ts | 2 ++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fa5441f684..bf0d765530 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6632,11 +6632,31 @@ namespace ts { } function getErasedSignature(signature: Signature): Signature { - if (!signature.typeParameters) return signature; - if (!signature.erasedSignatureCache) { - signature.erasedSignatureCache = instantiateSignature(signature, createTypeEraser(signature.typeParameters), /*eraseTypeParameters*/ true); - } - return signature.erasedSignatureCache; + return signature.typeParameters ? + signature.erasedSignatureCache || (signature.erasedSignatureCache = createErasedSignature(signature)) : + signature; + } + + function createErasedSignature(signature: Signature) { + // Create an instantiation of the signature where all type arguments are the any type. + return instantiateSignature(signature, createTypeEraser(signature.typeParameters), /*eraseTypeParameters*/ true); + } + + function getCanonicalSignature(signature: Signature): Signature { + return signature.typeParameters ? + signature.canonicalSignatureCache || (signature.canonicalSignatureCache = createCanonicalSignature(signature)) : + signature; + } + + function createCanonicalSignature(signature: Signature) { + // Create an instantiation of the signature where each unconstrained type parameter is replaced with + // its original. When a generic class or interface is instantiated, each generic method in the class or + // interface is instantiated with a fresh set of cloned type parameters (which we need to handle scenarios + // where different generations of the same type parameter are in scope). This leads to a lot of new type + // identities, and potentially a lot of work comparing those identities, so here we create an instantiation + // that reverts back to the original type identities for all unconstrained type parameters. + const canonicalTypeArguments = map(signature.typeParameters, tp => tp.target && !getConstraintOfTypeParameter(tp.target) ? tp.target : tp); + return instantiateSignature(signature, createTypeMapper(signature.typeParameters, canonicalTypeArguments), /*eraseTypeParameters*/ true); } function getOrCreateTypeFromSignature(signature: Signature): ObjectType { @@ -8473,6 +8493,7 @@ namespace ts { return Ternary.False; } + target = getCanonicalSignature(target); if (source.typeParameters) { source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e2d9977f30..9d5bbf5b08 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3447,6 +3447,8 @@ namespace ts { /* @internal */ erasedSignatureCache?: Signature; // Erased version of signature (deferred) /* @internal */ + canonicalSignatureCache?: Signature; // Canonical version of signature (deferred) + /* @internal */ isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison /* @internal */ typePredicate?: TypePredicate; From fc163300435e4dce17312fe5b7a74ade4d621afb Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 6 Sep 2017 09:48:00 -0700 Subject: [PATCH 2/2] Minor changes --- src/compiler/checker.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bf0d765530..1bbd710e32 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6654,9 +6654,8 @@ namespace ts { // interface is instantiated with a fresh set of cloned type parameters (which we need to handle scenarios // where different generations of the same type parameter are in scope). This leads to a lot of new type // identities, and potentially a lot of work comparing those identities, so here we create an instantiation - // that reverts back to the original type identities for all unconstrained type parameters. - const canonicalTypeArguments = map(signature.typeParameters, tp => tp.target && !getConstraintOfTypeParameter(tp.target) ? tp.target : tp); - return instantiateSignature(signature, createTypeMapper(signature.typeParameters, canonicalTypeArguments), /*eraseTypeParameters*/ true); + // that uses the original type identities for all unconstrained type parameters. + return getSignatureInstantiation(signature, map(signature.typeParameters, tp => tp.target && !getConstraintOfTypeParameter(tp.target) ? tp.target : tp)); } function getOrCreateTypeFromSignature(signature: Signature): ObjectType { @@ -8493,8 +8492,8 @@ namespace ts { return Ternary.False; } - target = getCanonicalSignature(target); - if (source.typeParameters) { + if (source.typeParameters && source.typeParameters !== target.typeParameters) { + target = getCanonicalSignature(target); source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes); }