Skip to content

Commit af8cf90

Browse files
committed
InferenceContext has-a (not is-a) mapper and has-a new nonFixingMapper
1 parent 42b6ef0 commit af8cf90

File tree

2 files changed

+52
-60
lines changed

2 files changed

+52
-60
lines changed

src/compiler/checker.ts

Lines changed: 48 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10169,7 +10169,7 @@ namespace ts {
1016910169
// types rules (i.e. proper contravariance) for inferences.
1017010170
inferTypes(context.inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
1017110171
}
10172-
combinedMapper = combineTypeMappers(mapper, context);
10172+
combinedMapper = combineTypeMappers(mapper, context.mapper);
1017310173
}
1017410174
// Instantiate the extends type including inferences for 'infer T' type parameters
1017510175
const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType;
@@ -10778,8 +10778,8 @@ namespace ts {
1077810778
return t => typeParameters.indexOf(t) >= index ? emptyObjectType : t;
1077910779
}
1078010780

10781-
function cloneInferenceContext(context: InferenceContext, extraFlags: InferenceFlags = 0): InferenceContext {
10782-
return createInferenceContext(context.typeParameters, context.signature, context.flags | extraFlags, context.compareTypes, context.inferences);
10781+
function cloneInferenceContext<T extends InferenceContext | undefined>(context: T, extraFlags: InferenceFlags = 0): InferenceContext | T & undefined {
10782+
return context && createInferenceContext(context.typeParameters, context.signature, context.flags | extraFlags, context.compareTypes, context.inferences);
1078310783
}
1078410784

1078510785
function cloneInferredPartOfContext(context: InferenceContext): InferenceContext | undefined {
@@ -10793,10 +10793,9 @@ namespace ts {
1079310793
inferences.push(info);
1079410794
}
1079510795
}
10796-
if (!params.length) {
10797-
return undefined;
10798-
}
10799-
return createInferenceContext(params, context.signature, context.flags | InferenceFlags.NoDefault, context.compareTypes, inferences);
10796+
return params.length ?
10797+
createInferenceContext(params, context.signature, context.flags | InferenceFlags.NoDefault, context.compareTypes, inferences) :
10798+
undefined;
1080010799
}
1080110800

1080210801
function combineTypeMappers(mapper1: TypeMapper | undefined, mapper2: TypeMapper): TypeMapper;
@@ -14303,26 +14302,31 @@ namespace ts {
1430314302
}
1430414303

1430514304
function createInferenceContext(typeParameters: ReadonlyArray<TypeParameter>, signature: Signature | undefined, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext {
14306-
const inferences = baseInferences ? baseInferences.map(cloneInferenceInfo) : typeParameters.map(createInferenceInfo);
14307-
const context = mapper as InferenceContext;
14308-
context.typeParameters = typeParameters;
14309-
context.signature = signature;
14310-
context.inferences = inferences;
14311-
context.flags = flags;
14312-
context.compareTypes = compareTypes || compareTypesAssignable;
14305+
const context: InferenceContext = {
14306+
typeParameters,
14307+
signature,
14308+
inferences: baseInferences ? baseInferences.map(cloneInferenceInfo) : typeParameters.map(createInferenceInfo),
14309+
flags,
14310+
compareTypes: compareTypes || compareTypesAssignable,
14311+
mapper: t => mapToInferredType(context, t, /*fix*/ true),
14312+
nonFixingMapper: t => mapToInferredType(context, t, /*fix*/ false),
14313+
};
1431314314
return context;
14315+
}
1431414316

14315-
function mapper(t: Type): Type {
14316-
for (let i = 0; i < inferences.length; i++) {
14317-
if (t === inferences[i].typeParameter) {
14318-
if (!(context.flags & InferenceFlags.NoFixing)) {
14319-
inferences[i].isFixed = true;
14320-
}
14321-
return getInferredType(context, i);
14317+
function mapToInferredType(context: InferenceContext, t: Type, fix: boolean): Type {
14318+
const inferences = context.inferences;
14319+
for (let i = 0; i < inferences.length; i++) {
14320+
const inference = inferences[i];
14321+
if (t === inference.typeParameter) {
14322+
if (fix && !inference.isFixed) {
14323+
inference.isFixed = true;
14324+
inference.inferredType = undefined;
1432214325
}
14326+
return getInferredType(context, i);
1432314327
}
14324-
return t;
1432514328
}
14329+
return t;
1432614330
}
1432714331

1432814332
function createInferenceInfo(typeParameter: TypeParameter): InferenceInfo {
@@ -14349,6 +14353,10 @@ namespace ts {
1434914353
};
1435014354
}
1435114355

14356+
function getMapperFromContext<T extends InferenceContext | undefined>(context: T): TypeMapper | T & undefined {
14357+
return context && context.mapper;
14358+
}
14359+
1435214360
// Return true if the given type could possibly reference a type parameter for which
1435314361
// we perform type inference (i.e. a type parameter of a generic function). We cache
1435414362
// results for union and intersection types for performance reasons.
@@ -14591,15 +14599,18 @@ namespace ts {
1459114599
const candidate = propagationType || source;
1459214600
// We make contravariant inferences only if we are in a pure contravariant position,
1459314601
// i.e. only if we have not descended into a bivariant position.
14594-
if (contravariant && !bivariant) {
14595-
inference.contraCandidates = appendIfUnique(inference.contraCandidates, candidate);
14602+
if (contravariant && !bivariant && !contains(inference.contraCandidates, candidate)) {
14603+
inference.contraCandidates = append(inference.contraCandidates, candidate);
14604+
inference.inferredType = undefined;
1459614605
}
14597-
else {
14598-
inference.candidates = appendIfUnique(inference.candidates, candidate);
14606+
else if (!contains(inference.candidates, candidate)) {
14607+
inference.candidates = append(inference.candidates, candidate);
14608+
inference.inferredType = undefined;
1459914609
}
1460014610
}
14601-
if (!(priority & InferencePriority.ReturnType) && target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
14611+
if (!(priority & InferencePriority.ReturnType) && target.flags & TypeFlags.TypeParameter && inference.topLevel && !isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
1460214612
inference.topLevel = false;
14613+
inference.inferredType = undefined;
1460314614
}
1460414615
}
1460514616
return;
@@ -15032,7 +15043,7 @@ namespace ts {
1503215043
inferredType = instantiateType(defaultType,
1503315044
combineTypeMappers(
1503415045
createBackreferenceMapper(context.typeParameters, index),
15035-
context));
15046+
context.nonFixingMapper));
1503615047
}
1503715048
else {
1503815049
inferredType = getDefaultTypeArgumentType(!!(context.flags & InferenceFlags.AnyDefault));
@@ -15047,12 +15058,10 @@ namespace ts {
1504715058

1504815059
const constraint = getConstraintOfTypeParameter(inference.typeParameter);
1504915060
if (constraint) {
15050-
context.flags |= InferenceFlags.NoFixing;
15051-
const instantiatedConstraint = instantiateType(constraint, context);
15061+
const instantiatedConstraint = instantiateType(constraint, context.nonFixingMapper);
1505215062
if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
1505315063
inference.inferredType = inferredType = instantiatedConstraint;
1505415064
}
15055-
context.flags &= ~InferenceFlags.NoFixing;
1505615065
}
1505715066
}
1505815067

@@ -17525,7 +17534,7 @@ namespace ts {
1752517534
while (type) {
1752617535
const thisType = getThisTypeFromContextualType(type);
1752717536
if (thisType) {
17528-
return instantiateType(thisType, getInferenceContext(containingLiteral));
17537+
return instantiateType(thisType, getMapperFromContext(getInferenceContext(containingLiteral)));
1752917538
}
1753017539
if (literal.parent.kind !== SyntaxKind.PropertyAssignment) {
1753117540
break;
@@ -20135,7 +20144,7 @@ namespace ts {
2013520144
// the contextual signature is (...args: A) => B, we want to infer the element type of A's constraint (say 'any')
2013620145
// for T but leave it possible to later infer '[any]' back to A.
2013720146
const restType = getEffectiveRestType(contextualSignature);
20138-
const mapper = inferenceContext && restType && restType.flags & TypeFlags.TypeParameter ? cloneInferenceContext(inferenceContext) : inferenceContext;
20147+
const mapper = inferenceContext && (restType && restType.flags & TypeFlags.TypeParameter ? inferenceContext.nonFixingMapper : inferenceContext.mapper);
2013920148
const sourceSignature = mapper ? instantiateSignature(contextualSignature, mapper) : contextualSignature;
2014020149
forEachMatchingParameterType(sourceSignature, signature, (source, target) => {
2014120150
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
@@ -20161,17 +20170,6 @@ namespace ts {
2016120170
}
2016220171

2016320172
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray<Expression>, checkMode: CheckMode, context: InferenceContext): Type[] {
20164-
// Clear out all the inference results from the last time inferTypeArguments was called on this context
20165-
for (const inference of context.inferences) {
20166-
// As an optimization, we don't have to clear (and later recompute) inferred types
20167-
// for type parameters that have already been fixed on the previous call to inferTypeArguments.
20168-
// It would be just as correct to reset all of them. But then we'd be repeating the same work
20169-
// for the type parameters that were fixed, namely the work done by getInferredType.
20170-
if (!inference.isFixed) {
20171-
inference.inferredType = undefined;
20172-
}
20173-
}
20174-
2017520173
if (isJsxOpeningLikeElement(node)) {
2017620174
return inferJsxTypeArguments(node, signature, checkMode, context);
2017720175
}
@@ -20183,11 +20181,11 @@ namespace ts {
2018320181
if (node.kind !== SyntaxKind.Decorator) {
2018420182
const contextualType = getContextualType(node);
2018520183
if (contextualType) {
20186-
// We clone the contextual mapper to avoid disturbing a resolution in progress for an
20184+
// We clone the inference context to avoid disturbing a resolution in progress for an
2018720185
// outer call expression. Effectively we just want a snapshot of whatever has been
2018820186
// inferred for any outer call expression so far.
20189-
const outerContext = getInferenceContext(node);
20190-
const instantiatedType = instantiateType(contextualType, outerContext && cloneInferenceContext(outerContext, InferenceFlags.NoDefault));
20187+
const outerMapper = getMapperFromContext(cloneInferenceContext(getInferenceContext(node), InferenceFlags.NoDefault));
20188+
const instantiatedType = instantiateType(contextualType, outerMapper);
2019120189
// If the contextual type is a generic function type with a single call signature, we
2019220190
// instantiate the type with its own type parameters and type arguments. This ensures that
2019320191
// the type parameters are not erased to type any during type inference such that they can
@@ -20204,7 +20202,7 @@ namespace ts {
2020420202
inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType);
2020520203
// Create a type mapper for instantiating generic contextual types using the inferences made
2020620204
// from the return type.
20207-
context.returnMapper = cloneInferredPartOfContext(context);
20205+
context.returnMapper = getMapperFromContext(cloneInferredPartOfContext(context));
2020820206
}
2020920207
}
2021020208

@@ -21865,7 +21863,7 @@ namespace ts {
2186521863
if (restType && restType.flags & TypeFlags.TypeParameter) {
2186621864
// The contextual signature has a generic rest parameter. We first instantiate the contextual
2186721865
// signature (without fixing type parameters) and assign types to contextually typed parameters.
21868-
const instantiatedContext = instantiateSignature(context, cloneInferenceContext(inferenceContext));
21866+
const instantiatedContext = instantiateSignature(context, inferenceContext.nonFixingMapper);
2186921867
assignContextualParameterTypes(signature, instantiatedContext);
2187021868
// We then infer from a tuple type representing the parameters that correspond to the contextual
2187121869
// rest parameter.
@@ -22316,7 +22314,7 @@ namespace ts {
2231622314
inferFromAnnotatedParameters(signature, contextualSignature, inferenceContext!);
2231722315
}
2231822316
const instantiatedContextualSignature = inferenceContext ?
22319-
instantiateSignature(contextualSignature, inferenceContext) : contextualSignature;
22317+
instantiateSignature(contextualSignature, inferenceContext.mapper) : contextualSignature;
2232022318
assignContextualParameterTypes(signature, instantiatedContextualSignature);
2232122319
}
2232222320
if (!getReturnTypeFromAnnotation(node) && !signature.resolvedReturnType) {
@@ -23617,13 +23615,6 @@ namespace ts {
2361723615
return type;
2361823616
}
2361923617

23620-
// Checks an expression and returns its type. The contextualMapper parameter serves two purposes: When
23621-
// contextualMapper is not undefined and not equal to the identityMapper function object it indicates that the
23622-
// expression is being inferentially typed (section 4.15.2 in spec) and provides the type mapper to use in
23623-
// conjunction with the generic contextual type. When contextualMapper is equal to the identityMapper function
23624-
// object, it serves as an indicator that all contained function and arrow expressions should be considered to
23625-
// have the wildcard function type; this form of type check is used during overload resolution to exclude
23626-
// contextually typed function and arrow expressions in the initial phase.
2362723618
function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type {
2362823619
const saveCurrentNode = currentNode;
2362923620
currentNode = node;

src/compiler/types.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4423,8 +4423,7 @@ namespace ts {
44234423
None = 0, // No special inference behaviors
44244424
NoDefault = 1 << 0, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType)
44254425
AnyDefault = 1 << 1, // Infer anyType for no inferences (otherwise emptyObjectType)
4426-
NoFixing = 1 << 2, // Disable type parameter fixing
4427-
SkippedGenericFunction = 1 << 3,
4426+
SkippedGenericFunction = 1 << 2, // A generic function was skipped during inference
44284427
}
44294428

44304429
/**
@@ -4447,12 +4446,14 @@ namespace ts {
44474446
export type TypeComparer = (s: Type, t: Type, reportErrors?: boolean) => Ternary;
44484447

44494448
/* @internal */
4450-
export interface InferenceContext extends TypeMapper {
4449+
export interface InferenceContext {
44514450
typeParameters: ReadonlyArray<TypeParameter>; // Type parameters for which inferences are made
44524451
signature?: Signature; // Generic signature for which inferences are made (if any)
44534452
inferences: InferenceInfo[]; // Inferences made for each type parameter
44544453
flags: InferenceFlags; // Inference flags
44554454
compareTypes: TypeComparer; // Type comparer function
4455+
mapper: TypeMapper; // Mapper that fixes inferences
4456+
nonFixingMapper: TypeMapper; // Mapper that doesn't fix inferences
44564457
returnMapper?: TypeMapper; // Type mapper for inferences from return types (if any)
44574458
inferredTypeParameters?: ReadonlyArray<TypeParameter>; // Inferred type parameters for function result
44584459
}

0 commit comments

Comments
 (0)