@@ -15581,29 +15581,44 @@ namespace ts {
1558115581 // is some specialization or subtype of Q
1558215582 // This is difficult to detect generally, so we scan for prior comparisons of the same instantiated type, and match up matching
1558315583 // type arguments into sets to create a canonicalization based on those matches
15584- if (relation !== identityRelation && ((source.aliasSymbol && !source.aliasTypeArgumentsContainsMarker && source.aliasTypeArguments) || (getObjectFlags(source) & ObjectFlags.Reference && !!(<TypeReference>source).node && !(getObjectFlags(source) & ObjectFlags.MarkerType))) &&
15585- ((target.aliasSymbol && !target.aliasTypeArgumentsContainsMarker && target.aliasTypeArguments) || (getObjectFlags(target) & ObjectFlags.Reference && !!(<TypeReference>target).node && !(getObjectFlags(target) & ObjectFlags.MarkerType)))) {
15584+ if (relation !== identityRelation && ((source.aliasSymbol && !source.aliasTypeArgumentsContainsMarker && source.aliasTypeArguments) || (getObjectFlags(source) & ObjectFlags.Reference && !!getTypeArguments (<TypeReference>source).length && !(getObjectFlags(source) & ObjectFlags.MarkerType))) &&
15585+ ((target.aliasSymbol && !target.aliasTypeArgumentsContainsMarker && target.aliasTypeArguments) || (getObjectFlags(target) & ObjectFlags.Reference && !!getTypeArguments (<TypeReference>target).length && !(getObjectFlags(target) & ObjectFlags.MarkerType)))) {
1558615586 if (source.aliasSymbol || target.aliasSymbol || (<TypeReference>source).target !== (<TypeReference>target).target) { // ensure like symbols are just handled by standard variance analysis
15587- const originalKey = getRelationKey(source, target, intersectionState, relation);
1558815587 const sourceTypeArguments = source.aliasTypeArguments || getTypeArguments(<TypeReference>source);
15588+ const sourceHasMarker = some(sourceTypeArguments, a => a === markerOtherType);
1558915589 const targetTypeArguments = target.aliasTypeArguments || getTypeArguments(<TypeReference>target);
15590- for (let i = 0; i < sourceTypeArguments.length; i++) {
15591- for (let j = 0; j < targetTypeArguments.length; j++) {
15592- if (!(sourceTypeArguments[i].flags & TypeFlags.TypeParameter) && isTypeIdenticalTo(sourceTypeArguments[i], targetTypeArguments[j])) {
15593- const sourceClone = sourceTypeArguments.slice();
15594- sourceClone[i] = markerOtherType;
15595- const s = getInstanceOfAliasOrReferenceWithMarker(source, sourceClone);
15596- const targetClone = targetTypeArguments.slice();
15597- targetClone[j] = markerOtherType;
15598- const t = getInstanceOfAliasOrReferenceWithMarker(target, targetClone);
15599- // If the marker-instantiated form looks "the same" as the type we already have (eg,
15600- // because we replace unconstrained generics with unconstrained generics), skip the check
15601- // since we'll otherwise deliver a spurious `Maybe` result from the key _just_ set upon
15602- // entry into `recursiveTypeRelatedTo`
15603- if (getRelationKey(s, t, intersectionState, relation) !== originalKey) {
15604- const result = isRelatedTo(s, t, /*reportErrors*/ false);
15605- if (result) {
15606- return result;
15590+ const targetHasMarker = some(targetTypeArguments, a => a === markerOtherType);
15591+ // We're using `markerOtherType` as an existential, so we can't use it again if it's already in use,
15592+ // as we'd get spurious equivalencies - we'd need to use a second existential type, and once we're doing
15593+ // that we lose a lot of the benefit of canonicalizing back to a single-existential comparison, since then
15594+ // we'd need to manufacture new type identities for every new existential we make
15595+ // The above checks don't catch all cases this can occur, as they can only detect when the containing type
15596+ // was flagged during construction as containing a marker; however if a marker enters a type through instantiation
15597+ // we need to catch that here.
15598+ if (!sourceHasMarker && !targetHasMarker) {
15599+ const originalKey = getRelationKey(source, target, intersectionState, relation);
15600+ for (let i = 0; i < sourceTypeArguments.length; i++) {
15601+ for (let j = 0; j < targetTypeArguments.length; j++) {
15602+ if ((!(sourceTypeArguments[i].flags & TypeFlags.TypeParameter) && !isTypeAny(sourceTypeArguments[i]) && isTypeIdenticalTo(sourceTypeArguments[i], targetTypeArguments[j])) ||
15603+ // Similarly, if we're comparing X<Q> to Z<any>, X<Q> is assignable to Z<any> trivially if X<?> is assignable to Z<?>
15604+ (!(sourceTypeArguments[i].flags & TypeFlags.TypeParameter) && isTypeAny(targetTypeArguments[j])) ||
15605+ // Again, but for `X<any>` vs `Z<Q>`
15606+ (isTypeAny(sourceTypeArguments[i]) && !(targetTypeArguments[j].flags & TypeFlags.TypeParameter))) {
15607+ const sourceClone = sourceTypeArguments.slice();
15608+ sourceClone[i] = markerOtherType;
15609+ const s = getInstanceOfAliasOrReferenceWithMarker(source, sourceClone);
15610+ const targetClone = targetTypeArguments.slice();
15611+ targetClone[j] = markerOtherType;
15612+ const t = getInstanceOfAliasOrReferenceWithMarker(target, targetClone);
15613+ // If the marker-instantiated form looks "the same" as the type we already have (eg,
15614+ // because we replace unconstrained generics with unconstrained generics), skip the check
15615+ // since we'll otherwise deliver a spurious `Maybe` result from the key _just_ set upon
15616+ // entry into `recursiveTypeRelatedTo`
15617+ if (getRelationKey(s, t, intersectionState, relation) !== originalKey) {
15618+ const result = isRelatedTo(s, t, /*reportErrors*/ false);
15619+ if (result) {
15620+ return result;
15621+ }
1560715622 }
1560815623 }
1560915624 }
0 commit comments