Skip to content

Commit 24a25fd

Browse files
committed
Revise type inference data structures
1 parent 2068192 commit 24a25fd

File tree

2 files changed

+90
-81
lines changed

2 files changed

+90
-81
lines changed

src/compiler/checker.ts

Lines changed: 75 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -7921,10 +7921,10 @@ namespace ts {
79217921
function getInferenceMapper(context: InferenceContext): TypeMapper {
79227922
if (!context.mapper) {
79237923
const mapper: TypeMapper = t => {
7924-
const typeParameters = context.signature.typeParameters;
7925-
for (let i = 0; i < typeParameters.length; i++) {
7926-
if (t === typeParameters[i]) {
7927-
context.inferences[i].isFixed = true;
7924+
const inferences = context.inferences;
7925+
for (let i = 0; i < inferences.length; i++) {
7926+
if (t === inferences[i].typeParameter) {
7927+
inferences[i].isFixed = true;
79287928
return getInferredType(context, i);
79297929
}
79307930
}
@@ -10131,22 +10131,22 @@ namespace ts {
1013110131
}
1013210132

1013310133
function createInferenceContext(signature: Signature, inferUnionTypes: boolean, useAnyForNoInferences: boolean): InferenceContext {
10134-
const inferences = map(signature.typeParameters, createTypeInferencesObject);
1013510134
return {
1013610135
signature,
10136+
inferences: map(signature.typeParameters, createInferenceInfo),
1013710137
inferUnionTypes,
10138-
inferences,
10139-
inferredTypes: new Array(signature.typeParameters.length),
1014010138
useAnyForNoInferences
1014110139
};
1014210140
}
1014310141

10144-
function createTypeInferencesObject(): TypeInferences {
10142+
function createInferenceInfo(typeParameter: TypeParameter): InferenceInfo {
1014510143
return {
10146-
primary: undefined,
10147-
secondary: undefined,
10144+
typeParameter,
10145+
candidates: undefined,
10146+
inferredType: undefined,
10147+
priority: undefined,
1014810148
topLevel: true,
10149-
isFixed: false,
10149+
isFixed: false
1015010150
};
1015110151
}
1015210152

@@ -10183,10 +10183,9 @@ namespace ts {
1018310183
if (properties.length === 0 && !indexInfo) {
1018410184
return undefined;
1018510185
}
10186-
const typeVariable = <TypeVariable>getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
10187-
const typeVariableArray = [typeVariable];
10188-
const typeInferences = createTypeInferencesObject();
10189-
const typeInferencesArray = [typeInferences];
10186+
const typeParameter = <TypeParameter>getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
10187+
const inference = createInferenceInfo(typeParameter);
10188+
const inferences = [inference];
1019010189
const templateType = getTemplateTypeFromMappedType(target);
1019110190
const readonlyMask = target.declaration.readonlyToken ? false : true;
1019210191
const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
@@ -10212,22 +10211,20 @@ namespace ts {
1021210211
return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined);
1021310212

1021410213
function inferTargetType(sourceType: Type): Type {
10215-
typeInferences.primary = undefined;
10216-
typeInferences.secondary = undefined;
10217-
inferTypes(typeVariableArray, typeInferencesArray, sourceType, templateType);
10218-
const inferences = typeInferences.primary || typeInferences.secondary;
10219-
return inferences && getUnionType(inferences, /*subtypeReduction*/ true);
10214+
inference.candidates = undefined;
10215+
inferTypes(inferences, sourceType, templateType);
10216+
return inference.candidates && getUnionType(inference.candidates, /*subtypeReduction*/ true);
1022010217
}
1022110218
}
1022210219

1022310220
function inferTypesWithContext(context: InferenceContext, originalSource: Type, originalTarget: Type) {
10224-
inferTypes(context.signature.typeParameters, context.inferences, originalSource, originalTarget);
10221+
inferTypes(context.inferences, originalSource, originalTarget);
1022510222
}
1022610223

10227-
function inferTypes(typeVariables: TypeVariable[], typeInferences: TypeInferences[], originalSource: Type, originalTarget: Type) {
10224+
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type) {
1022810225
let symbolStack: Symbol[];
1022910226
let visited: Map<boolean>;
10230-
let inferiority = 0;
10227+
let priority = 0;
1023110228
inferFromTypes(originalSource, originalTarget);
1023210229

1023310230
function inferFromTypes(source: Type, target: Type) {
@@ -10291,24 +10288,24 @@ namespace ts {
1029110288
if (source.flags & TypeFlags.ContainsAnyFunctionType) {
1029210289
return;
1029310290
}
10294-
for (let i = 0; i < typeVariables.length; i++) {
10295-
if (target === typeVariables[i]) {
10296-
const inferences = typeInferences[i];
10297-
if (!inferences.isFixed) {
10291+
for (const inference of inferences) {
10292+
if (target === inference.typeParameter) {
10293+
if (!inference.isFixed) {
1029810294
// Any inferences that are made to a type parameter in a union type are inferior
1029910295
// to inferences made to a flat (non-union) type. This is because if we infer to
1030010296
// T | string[], we really don't know if we should be inferring to T or not (because
1030110297
// the correct constituent on the target side could be string[]). Therefore, we put
1030210298
// such inferior inferences into a secondary bucket, and only use them if the primary
1030310299
// bucket is empty.
10304-
const candidates = inferiority ?
10305-
inferences.secondary || (inferences.secondary = []) :
10306-
inferences.primary || (inferences.primary = []);
10307-
if (!contains(candidates, source)) {
10308-
candidates.push(source);
10300+
if (!inference.candidates || priority < inference.priority) {
10301+
inference.candidates = [source];
10302+
inference.priority = priority;
10303+
}
10304+
else if (priority === inference.priority) {
10305+
inference.candidates.push(source);
1030910306
}
1031010307
if (target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
10311-
inferences.topLevel = false;
10308+
inference.topLevel = false;
1031210309
}
1031310310
}
1031410311
return;
@@ -10330,7 +10327,7 @@ namespace ts {
1033010327
let typeVariable: TypeVariable;
1033110328
// First infer to each type in union or intersection that isn't a type variable
1033210329
for (const t of targetTypes) {
10333-
if (t.flags & TypeFlags.TypeVariable && contains(typeVariables, t)) {
10330+
if (getInferenceInfoForType(t)) {
1033410331
typeVariable = <TypeVariable>t;
1033510332
typeVariableCount++;
1033610333
}
@@ -10342,9 +10339,10 @@ namespace ts {
1034210339
// variable. This gives meaningful results for union types in co-variant positions and intersection
1034310340
// types in contra-variant positions (such as callback parameters).
1034410341
if (typeVariableCount === 1) {
10345-
inferiority++;
10342+
const savePriority = priority;
10343+
priority |= InferencePriority.NakedTypeVariable;
1034610344
inferFromTypes(source, typeVariable);
10347-
inferiority--;
10345+
priority = savePriority;
1034810346
}
1034910347
}
1035010348
else if (source.flags & TypeFlags.UnionOrIntersection) {
@@ -10384,6 +10382,17 @@ namespace ts {
1038410382
}
1038510383
}
1038610384

10385+
function getInferenceInfoForType(type: Type) {
10386+
if (type.flags & TypeFlags.TypeVariable) {
10387+
for (const inference of inferences) {
10388+
if (type === inference.typeParameter) {
10389+
return inference;
10390+
}
10391+
}
10392+
}
10393+
return undefined;
10394+
}
10395+
1038710396
function inferFromObjectTypes(source: Type, target: Type) {
1038810397
if (getObjectFlags(target) & ObjectFlags.Mapped) {
1038910398
const constraintType = getConstraintTypeFromMappedType(<MappedType>target);
@@ -10392,13 +10401,14 @@ namespace ts {
1039210401
// where T is a type variable. Use inferTypeForHomomorphicMappedType to infer a suitable source
1039310402
// type and then make a secondary inference from that type to T. We make a secondary inference
1039410403
// such that direct inferences to T get priority over inferences to Partial<T>, for example.
10395-
const index = indexOf(typeVariables, (<IndexType>constraintType).type);
10396-
if (index >= 0 && !typeInferences[index].isFixed) {
10404+
const inference = getInferenceInfoForType((<IndexType>constraintType).type);
10405+
if (inference && !inference.isFixed) {
1039710406
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target);
1039810407
if (inferredType) {
10399-
inferiority++;
10400-
inferFromTypes(inferredType, typeVariables[index]);
10401-
inferiority--;
10408+
const savePriority = priority;
10409+
priority |= InferencePriority.MappedType;
10410+
inferFromTypes(inferredType, inference.typeParameter);
10411+
priority = savePriority;
1040210412
}
1040310413
}
1040410414
return;
@@ -10497,33 +10507,29 @@ namespace ts {
1049710507
return type.flags & TypeFlags.Union ? getUnionType(reducedTypes) : getIntersectionType(reducedTypes);
1049810508
}
1049910509

10500-
function getInferenceCandidates(context: InferenceContext, index: number): Type[] {
10501-
const inferences = context.inferences[index];
10502-
return inferences.primary || inferences.secondary || emptyArray;
10503-
}
10504-
1050510510
function hasPrimitiveConstraint(type: TypeParameter): boolean {
1050610511
const constraint = getConstraintOfTypeParameter(type);
1050710512
return constraint && maybeTypeOfKind(constraint, TypeFlags.Primitive | TypeFlags.Index);
1050810513
}
1050910514

1051010515
function getInferredType(context: InferenceContext, index: number): Type {
10511-
let inferredType = context.inferredTypes[index];
10516+
const inference = context.inferences[index];
10517+
let inferredType = inference.inferredType;
1051210518
let inferenceSucceeded: boolean;
1051310519
if (!inferredType) {
10514-
const inferences = getInferenceCandidates(context, index);
10515-
if (inferences.length) {
10520+
const candidates = inference.candidates;
10521+
if (candidates) {
1051610522
// We widen inferred literal types if
1051710523
// all inferences were made to top-level ocurrences of the type parameter, and
1051810524
// the type parameter has no constraint or its constraint includes no primitive or literal types, and
1051910525
// the type parameter was fixed during inference or does not occur at top-level in the return type.
1052010526
const signature = context.signature;
10521-
const widenLiteralTypes = context.inferences[index].topLevel &&
10522-
!hasPrimitiveConstraint(signature.typeParameters[index]) &&
10523-
(context.inferences[index].isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), signature.typeParameters[index]));
10524-
const baseInferences = widenLiteralTypes ? sameMap(inferences, getWidenedLiteralType) : inferences;
10527+
const widenLiteralTypes = inference.topLevel &&
10528+
!hasPrimitiveConstraint(inference.typeParameter) &&
10529+
(inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter));
10530+
const baseCandidates = widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) : candidates;
1052510531
// Infer widened union or supertype, or the unknown type for no common supertype
10526-
const unionOrSuperType = context.inferUnionTypes ? getUnionType(baseInferences, /*subtypeReduction*/ true) : getCommonSupertype(baseInferences);
10532+
const unionOrSuperType = context.inferUnionTypes ? getUnionType(baseCandidates, /*subtypeReduction*/ true) : getCommonSupertype(baseCandidates);
1052710533
inferredType = unionOrSuperType ? getWidenedType(unionOrSuperType) : unknownType;
1052810534
inferenceSucceeded = !!unionOrSuperType;
1052910535
}
@@ -10533,7 +10539,7 @@ namespace ts {
1053310539
// succeeds, meaning there is no error for not having inference candidates. An
1053410540
// inference error only occurs when there are *conflicting* candidates, i.e.
1053510541
// candidates with no common supertype.
10536-
const defaultType = getDefaultFromTypeParameter(context.signature.typeParameters[index]);
10542+
const defaultType = getDefaultFromTypeParameter(inference.typeParameter);
1053710543
if (defaultType) {
1053810544
// Instantiate the default type. Any forward reference to a type
1053910545
// parameter should be instantiated to the empty object type.
@@ -10548,15 +10554,15 @@ namespace ts {
1054810554

1054910555
inferenceSucceeded = true;
1055010556
}
10551-
context.inferredTypes[index] = inferredType;
10557+
inference.inferredType = inferredType;
1055210558

1055310559
// Only do the constraint check if inference succeeded (to prevent cascading errors)
1055410560
if (inferenceSucceeded) {
1055510561
const constraint = getConstraintOfTypeParameter(context.signature.typeParameters[index]);
1055610562
if (constraint) {
1055710563
const instantiatedConstraint = instantiateType(constraint, getInferenceMapper(context));
1055810564
if (!isTypeAssignableTo(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
10559-
context.inferredTypes[index] = inferredType = instantiatedConstraint;
10565+
inference.inferredType = inferredType = instantiatedConstraint;
1056010566
}
1056110567
}
1056210568
}
@@ -10571,10 +10577,11 @@ namespace ts {
1057110577
}
1057210578

1057310579
function getInferredTypes(context: InferenceContext): Type[] {
10574-
for (let i = 0; i < context.inferredTypes.length; i++) {
10575-
getInferredType(context, i);
10580+
let result = [];
10581+
for (let i = 0; i < context.inferences.length; i++) {
10582+
result.push(getInferredType(context, i));
1057610583
}
10577-
return context.inferredTypes;
10584+
return result;
1057810585
}
1057910586

1058010587
// EXPRESSION TYPE CHECKING
@@ -14837,18 +14844,18 @@ namespace ts {
1483714844
return getSignatureInstantiation(signature, getInferredTypes(context));
1483814845
}
1483914846

14840-
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: Expression[], excludeArgument: boolean[], context: InferenceContext): void {
14841-
const typeParameters = signature.typeParameters;
14847+
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: Expression[], excludeArgument: boolean[], context: InferenceContext): Type[] {
14848+
const inferences = context.inferences;
1484214849
const inferenceMapper = getInferenceMapper(context);
1484314850

1484414851
// Clear out all the inference results from the last time inferTypeArguments was called on this context
14845-
for (let i = 0; i < typeParameters.length; i++) {
14852+
for (let i = 0; i < inferences.length; i++) {
1484614853
// As an optimization, we don't have to clear (and later recompute) inferred types
1484714854
// for type parameters that have already been fixed on the previous call to inferTypeArguments.
1484814855
// It would be just as correct to reset all of them. But then we'd be repeating the same work
1484914856
// for the type parameters that were fixed, namely the work done by getInferredType.
14850-
if (!context.inferences[i].isFixed) {
14851-
context.inferredTypes[i] = undefined;
14857+
if (!inferences[i].isFixed) {
14858+
inferences[i].inferredType = undefined;
1485214859
}
1485314860
}
1485414861

@@ -14908,8 +14915,7 @@ namespace ts {
1490814915
}
1490914916
}
1491014917
}
14911-
14912-
getInferredTypes(context);
14918+
return getInferredTypes(context);
1491314919
}
1491414920

1491514921
function checkTypeArguments(signature: Signature, typeArgumentNodes: TypeNode[], typeArgumentTypes: Type[], reportErrors: boolean, headMessage?: DiagnosticMessage): boolean {
@@ -15491,7 +15497,7 @@ namespace ts {
1549115497
else {
1549215498
Debug.assert(resultOfFailedInference.failedTypeParameterIndex >= 0);
1549315499
const failedTypeParameter = candidateForTypeArgumentError.typeParameters[resultOfFailedInference.failedTypeParameterIndex];
15494-
const inferenceCandidates = getInferenceCandidates(resultOfFailedInference, resultOfFailedInference.failedTypeParameterIndex);
15500+
const inferenceCandidates = resultOfFailedInference.inferences[resultOfFailedInference.failedTypeParameterIndex].candidates;
1549515501

1549615502
let diagnosticChainHead = chainDiagnosticMessages(/*details*/ undefined, // details will be provided by call to reportNoCommonSupertypeError
1549715503
Diagnostics.The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly,
@@ -15576,8 +15582,7 @@ namespace ts {
1557615582
typeArgumentsAreValid = checkTypeArguments(candidate, typeArguments, typeArgumentTypes, /*reportErrors*/ false);
1557715583
}
1557815584
else {
15579-
inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext);
15580-
typeArgumentTypes = inferenceContext.inferredTypes;
15585+
typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext);
1558115586
typeArgumentsAreValid = inferenceContext.failedTypeParameterIndex === undefined;
1558215587
}
1558315588
if (!typeArgumentsAreValid) {

0 commit comments

Comments
 (0)