Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 35 additions & 23 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,7 @@ import {
WideningContext,
WithStatement,
YieldExpression,
voidMap,
} from "./_namespaces/ts.js";
import * as moduleSpecifiers from "./_namespaces/ts.moduleSpecifiers.js";
import * as performance from "./_namespaces/ts.performance.js";
Expand Down Expand Up @@ -19168,7 +19169,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// We don't want inferences from constraints as they may cause us to eagerly resolve the
// conditional type instead of deferring resolution. Also, we always want strict function
// types rules (i.e. proper contravariance) for inferences.
inferTypes(context.inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
inferTypes(context, context.inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
}
// It's possible for 'infer T' type paramteters to be given uninstantiated constraints when the
// those type parameters are used in type references (see getInferredTypeParameterConstraint). For
Expand Down Expand Up @@ -19960,7 +19961,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function makeFunctionTypeMapper(func: (t: Type) => Type, debugInfo: () => string): TypeMapper {
return Debug.attachDebugPrototypeIfDebug({ kind: TypeMapKind.Function, func, debugInfo: Debug.isDebugging ? debugInfo : undefined });
const mapper = Debug.attachDebugPrototypeIfDebug({ kind: TypeMapKind.Function, func, debugInfo: Debug.isDebugging ? debugInfo : undefined });
mapper.instantiations = voidMap;
Comment thread
Andarist marked this conversation as resolved.
Outdated
return mapper;
}

function makeDeferredTypeMapper(sources: readonly TypeParameter[], targets: (() => Type)[]) {
Expand Down Expand Up @@ -20371,10 +20374,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite);
return errorType;
}
const key = type.id + getAliasId(aliasSymbol, aliasTypeArguments);
const cached = (mapper.instantiations ??= new Map()).get(key);
if (cached) {
return cached;
}
totalInstantiationCount++;
instantiationCount++;
instantiationDepth++;
const result = instantiateTypeWorker(type, mapper, aliasSymbol, aliasTypeArguments);
mapper.instantiations.set(key, result);
instantiationDepth--;
return result;
}
Expand Down Expand Up @@ -23265,7 +23274,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (sourceParams) {
// If the source has infer type parameters, we instantiate them in the context of the target
const ctx = createInferenceContext(sourceParams, /*signature*/ undefined, InferenceFlags.None, isRelatedToWorker);
inferTypes(ctx.inferences, (target as ConditionalType).extendsType, sourceExtends, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
inferTypes(ctx, ctx.inferences, (target as ConditionalType).extendsType, sourceExtends, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
sourceExtends = instantiateType(sourceExtends, ctx.mapper);
mapper = ctx.mapper;
}
Expand Down Expand Up @@ -25559,7 +25568,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// Before we commit to a particular inference (and thus lock out any further inferences),
// we infer from any intra-expression inference sites we have collected.
inferFromIntraExpressionSites(context);
clearCachedInferences(context.inferences);
clearCachedInferences(context, context.inferences);
inference.isFixed = true;
}
return getInferredType(context, i);
Expand All @@ -25576,7 +25585,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
);
}

function clearCachedInferences(inferences: InferenceInfo[]) {
function clearCachedInferences(context: InferenceContext | undefined, inferences: InferenceInfo[]) {
if (context) {
context.nonFixingMapper.instantiations = undefined;
}
Comment thread
Andarist marked this conversation as resolved.
Outdated
for (const inference of inferences) {
if (!inference.isFixed) {
inference.inferredType = undefined;
Expand Down Expand Up @@ -25608,7 +25620,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
getContextualTypeForObjectLiteralMethod(node as MethodDeclaration, ContextFlags.NoConstraints) :
getContextualType(node, ContextFlags.NoConstraints);
if (contextualType) {
inferTypes(context.inferences, type, contextualType);
inferTypes(context, context.inferences, type, contextualType);
}
}
context.intraExpressionInferenceSites = undefined;
Expand Down Expand Up @@ -25789,7 +25801,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const typeParameter = getIndexedAccessType(constraint.type, getTypeParameterFromMappedType(target)) as TypeParameter;
const templateType = getTemplateTypeFromMappedType(target);
const inference = createInferenceInfo(typeParameter);
inferTypes([inference], sourceType, templateType);
inferTypes(/*context*/ undefined, [inference], sourceType, templateType);
return getTypeFromInference(inference) || unknownType;
}

Expand Down Expand Up @@ -26052,7 +26064,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return isTupleType(type) && getTupleElementType(type, 0) === getIndexedAccessType(typeParameter, getNumberLiteralType(0)) && !getTypeOfPropertyOfType(type, "1" as __String);
}

function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority = InferencePriority.None, contravariant = false) {
function inferTypes(context: InferenceContext | undefined, inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority = InferencePriority.None, contravariant = false) {
let bivariant = false;
let propagationType: Type;
let inferencePriority: number = InferencePriority.MaxValue;
Expand Down Expand Up @@ -26190,17 +26202,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (contravariant && !bivariant) {
if (!contains(inference.contraCandidates, candidate)) {
inference.contraCandidates = append(inference.contraCandidates, candidate);
clearCachedInferences(inferences);
clearCachedInferences(context, inferences);
}
}
else if (!contains(inference.candidates, candidate)) {
inference.candidates = append(inference.candidates, candidate);
clearCachedInferences(inferences);
clearCachedInferences(context, inferences);
}
}
if (!(priority & InferencePriority.ReturnType) && target.flags & TypeFlags.TypeParameter && inference.topLevel && !isTypeParameterAtTopLevel(originalTarget, target as TypeParameter)) {
inference.topLevel = false;
clearCachedInferences(inferences);
clearCachedInferences(context, inferences);
}
}
inferencePriority = Math.min(inferencePriority, priority);
Expand Down Expand Up @@ -35045,11 +35057,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const sourceSignature = mapper ? instantiateSignature(contextualSignature, mapper) : contextualSignature;
applyToParameterTypes(sourceSignature, signature, (source, target) => {
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
inferTypes(context.inferences, source, target);
inferTypes(context, context.inferences, source, target);
});
if (!inferenceContext) {
applyToReturnTypes(contextualSignature, signature, (source, target) => {
inferTypes(context.inferences, source, target, InferencePriority.ReturnType);
inferTypes(context, context.inferences, source, target, InferencePriority.ReturnType);
});
}
return getSignatureInstantiation(signature, getInferredTypes(context), isInJSFile(contextualSignature.declaration));
Expand All @@ -35058,7 +35070,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function inferJsxTypeArguments(node: JsxOpeningLikeElement, signature: Signature, checkMode: CheckMode, context: InferenceContext): Type[] {
const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node);
const checkAttrType = checkExpressionWithContextualType(node.attributes, paramType, context, checkMode);
inferTypes(context.inferences, checkAttrType, paramType);
inferTypes(context, context.inferences, checkAttrType, paramType);
return getInferredTypes(context);
}

Expand Down Expand Up @@ -35118,15 +35130,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters)) :
instantiatedType;
// Inferences made from return types have lower priority than all other inferences.
inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType);
inferTypes(context, context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType);
}
// Create a type mapper for instantiating generic contextual types using the inferences made
// from the return type. We need a separate inference pass here because (a) instantiation of
// the source type uses the outer context's return mapper (which excludes inferences made from
// outer arguments), and (b) we don't want any further inferences going into this context.
const returnContext = createInferenceContext(signature.typeParameters!, signature, context.flags);
const returnSourceType = instantiateType(contextualType, outerContext && outerContext.returnMapper);
inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType);
inferTypes(returnContext, returnContext.inferences, returnSourceType, inferenceTargetType);
context.returnMapper = some(returnContext.inferences, hasInferenceCandidates) ? getMapperFromContext(cloneInferredPartOfContext(returnContext)) : undefined;
}
}
Expand All @@ -35144,7 +35156,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const thisType = getThisTypeOfSignature(signature);
if (thisType && couldContainTypeVariables(thisType)) {
const thisArgumentNode = getThisArgumentOfCall(node);
inferTypes(context.inferences, getThisArgumentType(thisArgumentNode), thisType);
inferTypes(context, context.inferences, getThisArgumentType(thisArgumentNode), thisType);
}

for (let i = 0; i < argCount; i++) {
Expand All @@ -35153,14 +35165,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const paramType = getTypeAtPosition(signature, i);
if (couldContainTypeVariables(paramType)) {
const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode);
inferTypes(context.inferences, argType, paramType);
inferTypes(context, context.inferences, argType, paramType);
}
}
}

if (restType && couldContainTypeVariables(restType)) {
const spreadType = getSpreadArgumentType(args, argCount, args.length, restType, context, checkMode);
inferTypes(context.inferences, spreadType, restType);
inferTypes(context, context.inferences, spreadType, restType);
}

return getInferredTypes(context);
Expand Down Expand Up @@ -37886,14 +37898,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (typeNode) {
const source = addOptionality(getTypeFromTypeNode(typeNode), /*isProperty*/ false, isOptionalDeclaration(declaration));
const target = getTypeAtPosition(context, i);
inferTypes(inferenceContext.inferences, source, target);
inferTypes(inferenceContext, inferenceContext.inferences, source, target);
}
}
const typeNode = signature.declaration && getEffectiveReturnTypeNode(signature.declaration);
if (typeNode) {
const source = getTypeFromTypeNode(typeNode);
const target = getReturnTypeOfSignature(context);
inferTypes(inferenceContext.inferences, source, target);
inferTypes(inferenceContext, inferenceContext.inferences, source, target);
}
}

Expand Down Expand Up @@ -40801,13 +40813,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// contextual signature starting with an empty set of inference candidates.
const inferences = map(context.inferences, info => createInferenceInfo(info.typeParameter));
applyToParameterTypes(instantiatedSignature, contextualSignature, (source, target) => {
inferTypes(inferences, source, target, /*priority*/ 0, /*contravariant*/ true);
inferTypes(context, inferences, source, target, /*priority*/ 0, /*contravariant*/ true);
});
if (some(inferences, hasInferenceCandidates)) {
// We have inference candidates, indicating that one or more type parameters are referenced
// in the parameter types of the contextual signature. Now also infer from the return type.
applyToReturnTypes(instantiatedSignature, contextualSignature, (source, target) => {
inferTypes(inferences, source, target);
inferTypes(context, inferences, source, target);
});
// If the type parameters for which we produced candidates do not have any inferences yet,
// we adopt the new inference candidates and add the type parameters of the expression type
Expand Down
6 changes: 6 additions & 0 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ export const emptyArray: never[] = [] as never[];
/** @internal */
export const emptyMap: ReadonlyMap<never, never> = new Map<never, never>();

/** @internal */
export const voidMap: Map<never, never> = new Map<never, never>();
voidMap.set = function () {
return this;
};

/** @internal */
export function length(array: readonly any[] | undefined): number {
return array !== undefined ? array.length : 0;
Expand Down
10 changes: 5 additions & 5 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7029,11 +7029,11 @@ export const enum TypeMapKind {

/** @internal */
export type TypeMapper =
| { kind: TypeMapKind.Simple; source: Type; target: Type; }
| { kind: TypeMapKind.Array; sources: readonly Type[]; targets: readonly Type[] | undefined; }
| { kind: TypeMapKind.Deferred; sources: readonly Type[]; targets: (() => Type)[]; }
| { kind: TypeMapKind.Function; func: (t: Type) => Type; debugInfo?: () => string; }
| { kind: TypeMapKind.Composite | TypeMapKind.Merged; mapper1: TypeMapper; mapper2: TypeMapper; };
| { kind: TypeMapKind.Simple; source: Type; target: Type; instantiations?: Map<string, Type>; }
| { kind: TypeMapKind.Array; sources: readonly Type[]; targets: readonly Type[] | undefined; instantiations?: Map<string, Type>; }
| { kind: TypeMapKind.Deferred; sources: readonly Type[]; targets: (() => Type)[]; instantiations?: Map<string, Type>; }
| { kind: TypeMapKind.Function; func: (t: Type) => Type; debugInfo?: () => string; instantiations?: Map<string, Type>; }
| { kind: TypeMapKind.Composite | TypeMapKind.Merged; mapper1: TypeMapper; mapper2: TypeMapper; instantiations?: Map<string, Type>; };

// dprint-ignore
export const enum InferencePriority {
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/callsOnComplexSignatures.types
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
=== Performance Stats ===
Assignability cache: 2,500
Type Count: 10,000
Instantiation count: 100,000
Instantiation count: 10,000
Symbol count: 50,000

=== callsOnComplexSignatures.tsx ===
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
=== Performance Stats ===
Assignability cache: 2,500
Type Count: 10,000
Instantiation count: 100,000
Instantiation count: 10,000
Symbol count: 50,000

=== checkJsxChildrenCanBeTupleType.tsx ===
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/checkJsxChildrenProperty16.types
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
=== Performance Stats ===
Assignability cache: 2,500
Type Count: 10,000
Instantiation count: 100,000
Instantiation count: 10,000
Symbol count: 50,000

=== checkJsxChildrenProperty16.tsx ===
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
=== Performance Stats ===
Assignability cache: 2,500
Type Count: 10,000
Instantiation count: 100,000
Instantiation count: 10,000
Symbol count: 50,000

=== checkJsxUnionSFXContextualTypeInferredCorrectly.tsx ===
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
//// [tests/cases/compiler/circularlySimplifyingConditionalTypesNoCrash.ts] ////

=== Performance Stats ===
Instantiation count: 2,500

=== circularlySimplifyingConditionalTypesNoCrash.ts ===
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
>Omit : Omit<T, K>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Strict subtype cache: 2,500
Assignability cache: 10,000
Type Count: 10,000
Instantiation count: 100,000
Instantiation count: 25,000

=== conditionalTypeDiscriminatingLargeUnionRegularTypeFetchingSpeedReasonable.ts ===
type BigUnion =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

=== Performance Stats ===
Type Count: 1,000
Instantiation count: 2,500 -> 5,000
Instantiation count: 2,500

=== conditionalTypeDoesntSpinForever.ts ===
// A *self-contained* demonstration of the problem follows...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
=== Performance Stats ===
Assignability cache: 1,000
Type Count: 5,000
Instantiation count: 100,000
Instantiation count: 5,000
Symbol count: 50,000

=== conditionalTypeVarianceBigArrayConstraintsPerformance.ts ===
Expand Down
3 changes: 0 additions & 3 deletions tests/baselines/reference/conditionalTypes1.types
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
//// [tests/cases/conformance/types/conditional/conditionalTypes1.ts] ////

=== Performance Stats ===
Instantiation count: 1,000

=== conditionalTypes1.ts ===
type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
>T00 : T00
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
=== Performance Stats ===
Assignability cache: 5,000
Type Count: 10,000
Instantiation count: 250,000
Instantiation count: 100,000
Symbol count: 100,000

=== contextuallyTypedJsxAttribute2.tsx ===
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
=== Performance Stats ===
Assignability cache: 2,500
Type Count: 10,000
Instantiation count: 100,000
Instantiation count: 10,000
Symbol count: 50,000

=== contextuallyTypedJsxChildren.tsx ===
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/controlFlowOptionalChain3.types
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
=== Performance Stats ===
Assignability cache: 2,500
Type Count: 10,000
Instantiation count: 100,000
Instantiation count: 10,000
Symbol count: 50,000

=== controlFlowOptionalChain3.tsx ===
Expand Down
Loading
Loading