Skip to content

Commit a7a27e3

Browse files
committed
Rework inference depth check to depend on if an inference has been found yet, use more complete depth checking machinery
1 parent 37fce9f commit a7a27e3

File tree

1 file changed

+56
-47
lines changed

1 file changed

+56
-47
lines changed

src/compiler/checker.ts

+56-47
Original file line numberDiff line numberDiff line change
@@ -13113,26 +13113,28 @@ namespace ts {
1311313113
// type arguments into sets to create a canonicalization based on those matches
1311413114
if (relation !== identityRelation && ((source.aliasSymbol && !source.aliasTypeArgumentsContainsMarker && source.aliasTypeArguments) || (getObjectFlags(source) & ObjectFlags.Reference && (<TypeReference>source).typeArguments && !(getObjectFlags(source) & ObjectFlags.MarkerType))) &&
1311513115
((target.aliasSymbol && !target.aliasTypeArgumentsContainsMarker && target.aliasTypeArguments) || (getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>target).typeArguments && !(getObjectFlags(target) & ObjectFlags.MarkerType)))) {
13116-
const originalKey = getRelationKey(source, target, relation);
13117-
const sourceTypeArguments = source.aliasTypeArguments || (<TypeReference>source).typeArguments!;
13118-
const targetTypeArguments = target.aliasTypeArguments || (<TypeReference>target).typeArguments!;
13119-
for (let i = 0; i < sourceTypeArguments.length; i++) {
13120-
for (let j = 0; j < targetTypeArguments.length; j++) {
13121-
if (!(sourceTypeArguments[i].flags & TypeFlags.TypeParameter) && isTypeIdenticalTo(sourceTypeArguments[i], targetTypeArguments[j])) {
13122-
const sourceClone = sourceTypeArguments.slice();
13123-
sourceClone[i] = markerOtherType;
13124-
const s = getInstanceOfAliasOrReferenceWithMarker(source, sourceClone);
13125-
const targetClone = targetTypeArguments.slice();
13126-
targetClone[j] = markerOtherType;
13127-
const t = getInstanceOfAliasOrReferenceWithMarker(target, targetClone);
13128-
// If the marker-instantiated form looks "the same" as the type we already have (eg,
13129-
// because we replace unconstrained generics with unconstrained generics), skip the check
13130-
// since we'll otherwise deliver a spurious `Maybe` result from the key _just_ set upon
13131-
// entry into `recursiveTypeRelatedTo`
13132-
if (getRelationKey(s, t, relation) !== originalKey) {
13133-
const result = isRelatedTo(s, t, /*reportErrors*/ false);
13134-
if (result) {
13135-
return result;
13116+
if (source.aliasSymbol || target.aliasSymbol || (<TypeReference>source).target !== (<TypeReference>target).target) { // ensure like symbols are just handled by standard variance analysis
13117+
const originalKey = getRelationKey(source, target, relation);
13118+
const sourceTypeArguments = source.aliasTypeArguments || (<TypeReference>source).typeArguments!;
13119+
const targetTypeArguments = target.aliasTypeArguments || (<TypeReference>target).typeArguments!;
13120+
for (let i = 0; i < sourceTypeArguments.length; i++) {
13121+
for (let j = 0; j < targetTypeArguments.length; j++) {
13122+
if (!(sourceTypeArguments[i].flags & TypeFlags.TypeParameter) && isTypeIdenticalTo(sourceTypeArguments[i], targetTypeArguments[j])) {
13123+
const sourceClone = sourceTypeArguments.slice();
13124+
sourceClone[i] = markerOtherType;
13125+
const s = getInstanceOfAliasOrReferenceWithMarker(source, sourceClone);
13126+
const targetClone = targetTypeArguments.slice();
13127+
targetClone[j] = markerOtherType;
13128+
const t = getInstanceOfAliasOrReferenceWithMarker(target, targetClone);
13129+
// If the marker-instantiated form looks "the same" as the type we already have (eg,
13130+
// because we replace unconstrained generics with unconstrained generics), skip the check
13131+
// since we'll otherwise deliver a spurious `Maybe` result from the key _just_ set upon
13132+
// entry into `recursiveTypeRelatedTo`
13133+
if (getRelationKey(s, t, relation) !== originalKey) {
13134+
const result = isRelatedTo(s, t, /*reportErrors*/ false);
13135+
if (result) {
13136+
return result;
13137+
}
1313613138
}
1313713139
}
1313813140
}
@@ -14242,7 +14244,7 @@ namespace ts {
1424214244
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely
1424314245
// expanding. Effectively, we will generate a false positive when two types are structurally equal to at least 5
1424414246
// levels, but unequal at some level beyond that.
14245-
function isDeeplyNestedType(type: Type, stack: Type[], depth: number): boolean {
14247+
function isDeeplyNestedType(type: Type, stack: Type[], depth: number, maxCount = 5): boolean {
1424614248
// We track all object types that have an associated symbol (representing the origin of the type)
1424714249
if (depth >= 5 && type.flags & TypeFlags.Object) {
1424814250
const symbol = type.symbol;
@@ -14252,7 +14254,7 @@ namespace ts {
1425214254
const t = stack[i];
1425314255
if (t.flags & TypeFlags.Object && t.symbol === symbol) {
1425414256
count++;
14255-
if (count >= 5) return true;
14257+
if (count >= maxCount) return true;
1425614258
}
1425714259
}
1425814260
}
@@ -15180,23 +15182,49 @@ namespace ts {
1518015182
}
1518115183

1518215184
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) {
15183-
let symbolDepth: Map<number>;
15185+
let sourceStack: Type[];
15186+
let targetStack: Type[];
15187+
let depth = 0;
1518415188
let visited: Map<boolean>;
1518515189
let bivariant = false;
1518615190
let propagationType: Type;
1518715191
let allowComplexConstraintInference = true;
15192+
let expandingFlags = ExpandingFlags.None;
1518815193
inferFromTypes(originalSource, originalTarget);
1518915194

1519015195
function inferFromTypes(source: Type, target: Type): void {
15191-
if (!couldContainTypeVariables(target)) {
15192-
return;
15193-
}
15194-
1519515196
const key = source.id + "," + target.id;
1519615197
if (visited && visited.get(key)) {
1519715198
return;
1519815199
}
1519915200
(visited || (visited = createMap<boolean>())).set(key, true);
15201+
15202+
const maxdepth = every(inferences, i => !!(length(i.candidates) || length(i.contraCandidates))) ? 1 : 5; // Expand up to 5 layers deep unless we've found an inference, in which case stop at 1
15203+
const saveExpandingFlags = expandingFlags;
15204+
if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, depth, maxdepth)) expandingFlags |= ExpandingFlags.Source;
15205+
if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, depth, maxdepth)) expandingFlags |= ExpandingFlags.Target;
15206+
if (expandingFlags === ExpandingFlags.Both) {
15207+
expandingFlags = saveExpandingFlags;
15208+
return;
15209+
}
15210+
15211+
if (!sourceStack) {
15212+
sourceStack = [];
15213+
targetStack = [];
15214+
}
15215+
sourceStack[depth] = source;
15216+
targetStack[depth] = target;
15217+
depth++;
15218+
inferFromTypesWorker(source, target);
15219+
depth--;
15220+
expandingFlags = saveExpandingFlags;
15221+
}
15222+
15223+
function inferFromTypesWorker(source: Type, target: Type): void {
15224+
if (!couldContainTypeVariables(target)) {
15225+
return;
15226+
}
15227+
1520015228
if (source === wildcardType) {
1520115229
// We are inferring from an 'any' type. We want to infer this type for every type parameter
1520215230
// referenced in the target type, so we record it as the propagation type and infer from the
@@ -15416,26 +15444,7 @@ namespace ts {
1541615444
source = apparentSource;
1541715445
}
1541815446
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
15419-
// If we are already processing another target type with the same associated symbol (such as
15420-
// an instantiation of the same generic type), we do not explore this target as it would yield
15421-
// no further inferences. We exclude the static side of classes from this check since it shares
15422-
// its symbol with the instance side which would lead to false positives.
15423-
const isNonConstructorObject = target.flags & TypeFlags.Object &&
15424-
!(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class);
15425-
const symbol = isNonConstructorObject ? target.symbol : undefined;
15426-
if (symbol) {
15427-
const id = "" + getSymbolId(symbol);
15428-
const depth = symbolDepth && symbolDepth.get(id) || 0;
15429-
if (depth > 5) {
15430-
return;
15431-
}
15432-
(symbolDepth || (symbolDepth = createMap())).set(id, depth + 1);
15433-
inferFromObjectTypes(source, target);
15434-
symbolDepth.set(id, depth);
15435-
}
15436-
else {
15437-
inferFromObjectTypes(source, target);
15438-
}
15447+
inferFromObjectTypes(source, target);
1543915448
}
1544015449
}
1544115450
}

0 commit comments

Comments
 (0)