Skip to content

Commit 4c34f2c

Browse files
authored
Fix mapper used to instantiate distributive conditional manufactured in declaration emit (microsoft#49737)
1 parent c251d60 commit 4c34f2c

6 files changed

+143
-49
lines changed

src/compiler/checker.ts

Lines changed: 89 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,23 @@ namespace ts {
340340
const Symbol = objectAllocator.getSymbolConstructor();
341341
const Type = objectAllocator.getTypeConstructor();
342342
const Signature = objectAllocator.getSignatureConstructor();
343+
type DebugType = Type & { __debugTypeToString(): string }; // eslint-disable-line @typescript-eslint/naming-convention
344+
class DebugTypeMapper {
345+
declare kind: TypeMapKind;
346+
__debugToString(): string { // eslint-disable-line @typescript-eslint/naming-convention
347+
Debug.type<TypeMapper>(this);
348+
switch (this.kind) {
349+
case TypeMapKind.Function: return this.debugInfo?.() || "(function mapper)";
350+
case TypeMapKind.Simple: return `${(this.source as DebugType).__debugTypeToString()} -> ${(this.target as DebugType).__debugTypeToString()}`;
351+
case TypeMapKind.Array: return zipWith(this.sources, this.targets || map(this.sources, () => anyType), (s, t) => `${(s as DebugType).__debugTypeToString()} -> ${(t as DebugType).__debugTypeToString()}`).join(", ");
352+
case TypeMapKind.Deferred: return zipWith(this.sources, this.targets, (s, t) => `${(s as DebugType).__debugTypeToString()} -> ${(t() as DebugType).__debugTypeToString()}`).join(", ");
353+
case TypeMapKind.Merged:
354+
case TypeMapKind.Composite: return `m1: ${(this.mapper1 as unknown as DebugTypeMapper).__debugToString().split("\n").join("\n ")}
355+
m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n").join("\n ")}`;
356+
default: return Debug.assertNever(this);
357+
}
358+
}
359+
}
343360

344361
let typeCount = 0;
345362
let symbolCount = 0;
@@ -829,10 +846,23 @@ namespace ts {
829846
const templateConstraintType = getUnionType([stringType, numberType, booleanType, bigintType, nullType, undefinedType]) as UnionType;
830847
const numericStringType = getTemplateLiteralType(["", ""], [numberType]); // The `${number}` type
831848

832-
const restrictiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? getRestrictiveTypeParameter(t as TypeParameter) : t);
833-
const permissiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? wildcardType : t);
849+
const restrictiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? getRestrictiveTypeParameter(t as TypeParameter) : t, () => "(restrictive mapper)");
850+
const permissiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? wildcardType : t, () => "(permissive mapper)");
834851
const uniqueLiteralType = createIntrinsicType(TypeFlags.Never, "never"); // `uniqueLiteralType` is a special `never` flagged by union reduction to behave as a literal
835-
const uniqueLiteralMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? uniqueLiteralType : t); // replace all type parameters with the unique literal type (disregarding constraints)
852+
const uniqueLiteralMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? uniqueLiteralType : t, () => "(unique literal mapper)"); // replace all type parameters with the unique literal type (disregarding constraints)
853+
let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined;
854+
const reportUnreliableMapper = makeFunctionTypeMapper(t => {
855+
if (outofbandVarianceMarkerHandler && (t === markerSuperType || t === markerSubType || t === markerOtherType)) {
856+
outofbandVarianceMarkerHandler(/*onlyUnreliable*/ true);
857+
}
858+
return t;
859+
}, () => "(unmeasurable reporter)");
860+
const reportUnmeasurableMapper = makeFunctionTypeMapper(t => {
861+
if (outofbandVarianceMarkerHandler && (t === markerSuperType || t === markerSubType || t === markerOtherType)) {
862+
outofbandVarianceMarkerHandler(/*onlyUnreliable*/ false);
863+
}
864+
return t;
865+
}, () => "(unreliable reporter)");
836866

837867
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
838868
const emptyJsxObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
@@ -1029,7 +1059,6 @@ namespace ts {
10291059

10301060
let _jsxNamespace: __String;
10311061
let _jsxFactoryEntity: EntityName | undefined;
1032-
let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined;
10331062

10341063
const subtypeRelation = new Map<string, RelationComparisonResult>();
10351064
const strictSubtypeRelation = new Map<string, RelationComparisonResult>();
@@ -5124,7 +5153,7 @@ namespace ts {
51245153
const name = typeParameterToName(newParam, context);
51255154
const newTypeVariable = factory.createTypeReferenceNode(name);
51265155
context.approximateLength += 37; // 15 each for two added conditionals, 7 for an added infer type
5127-
const newMapper = prependTypeMapping(type.root.checkType, newParam, type.combinedMapper || type.mapper);
5156+
const newMapper = prependTypeMapping(type.root.checkType, newParam, type.mapper);
51285157
const saveInferTypeParameters = context.inferTypeParameters;
51295158
context.inferTypeParameters = type.root.inferTypeParameters;
51305159
const extendsTypeNode = typeToTypeNodeHelper(instantiateType(type.root.extendsType, newMapper), context);
@@ -16774,7 +16803,7 @@ namespace ts {
1677416803
switch (mapper.kind) {
1677516804
case TypeMapKind.Simple:
1677616805
return type === mapper.source ? mapper.target : type;
16777-
case TypeMapKind.Array:
16806+
case TypeMapKind.Array: {
1677816807
const sources = mapper.sources;
1677916808
const targets = mapper.targets;
1678016809
for (let i = 0; i < sources.length; i++) {
@@ -16783,6 +16812,17 @@ namespace ts {
1678316812
}
1678416813
}
1678516814
return type;
16815+
}
16816+
case TypeMapKind.Deferred: {
16817+
const sources = mapper.sources;
16818+
const targets = mapper.targets;
16819+
for (let i = 0; i < sources.length; i++) {
16820+
if (type === sources[i]) {
16821+
return targets[i]();
16822+
}
16823+
}
16824+
return type;
16825+
}
1678616826
case TypeMapKind.Function:
1678716827
return mapper.func(type);
1678816828
case TypeMapKind.Composite:
@@ -16792,20 +16832,31 @@ namespace ts {
1679216832
}
1679316833
}
1679416834

16835+
function attachDebugPrototypeIfDebug(mapper: TypeMapper): TypeMapper {
16836+
if (Debug.isDebugging) {
16837+
return Object.setPrototypeOf(mapper, DebugTypeMapper.prototype);
16838+
}
16839+
return mapper;
16840+
}
16841+
1679516842
function makeUnaryTypeMapper(source: Type, target: Type): TypeMapper {
16796-
return { kind: TypeMapKind.Simple, source, target };
16843+
return attachDebugPrototypeIfDebug({ kind: TypeMapKind.Simple, source, target });
1679716844
}
1679816845

1679916846
function makeArrayTypeMapper(sources: readonly TypeParameter[], targets: readonly Type[] | undefined): TypeMapper {
16800-
return { kind: TypeMapKind.Array, sources, targets };
16847+
return attachDebugPrototypeIfDebug({ kind: TypeMapKind.Array, sources, targets });
16848+
}
16849+
16850+
function makeFunctionTypeMapper(func: (t: Type) => Type, debugInfo: () => string): TypeMapper {
16851+
return attachDebugPrototypeIfDebug({ kind: TypeMapKind.Function, func, debugInfo: Debug.isDebugging ? debugInfo : undefined });
1680116852
}
1680216853

16803-
function makeFunctionTypeMapper(func: (t: Type) => Type): TypeMapper {
16804-
return { kind: TypeMapKind.Function, func };
16854+
function makeDeferredTypeMapper(sources: readonly TypeParameter[], targets: (() => Type)[]) {
16855+
return attachDebugPrototypeIfDebug({ kind: TypeMapKind.Deferred, sources, targets });
1680516856
}
1680616857

1680716858
function makeCompositeTypeMapper(kind: TypeMapKind.Composite | TypeMapKind.Merged, mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
16808-
return { kind, mapper1, mapper2 };
16859+
return attachDebugPrototypeIfDebug({ kind, mapper1, mapper2 });
1680916860
}
1681016861

1681116862
function createTypeEraser(sources: readonly TypeParameter[]): TypeMapper {
@@ -16817,7 +16868,8 @@ namespace ts {
1681716868
* This is used during inference when instantiating type parameter defaults.
1681816869
*/
1681916870
function createBackreferenceMapper(context: InferenceContext, index: number): TypeMapper {
16820-
return makeFunctionTypeMapper(t => findIndex(context.inferences, info => info.typeParameter === t) >= index ? unknownType : t);
16871+
const forwardInferences = context.inferences.slice(index);
16872+
return createTypeMapper(map(forwardInferences, i => i.typeParameter), map(forwardInferences, () => unknownType));
1682116873
}
1682216874

1682316875
function combineTypeMappers(mapper1: TypeMapper | undefined, mapper2: TypeMapper): TypeMapper {
@@ -19183,10 +19235,10 @@ namespace ts {
1918319235
// We're in the middle of variance checking - integrate any unmeasurable/unreliable flags from this cached component
1918419236
const saved = entry & RelationComparisonResult.ReportsMask;
1918519237
if (saved & RelationComparisonResult.ReportsUnmeasurable) {
19186-
instantiateType(source, makeFunctionTypeMapper(reportUnmeasurableMarkers));
19238+
instantiateType(source, reportUnmeasurableMapper);
1918719239
}
1918819240
if (saved & RelationComparisonResult.ReportsUnreliable) {
19189-
instantiateType(source, makeFunctionTypeMapper(reportUnreliableMarkers));
19241+
instantiateType(source, reportUnreliableMapper);
1919019242
}
1919119243
}
1919219244
return entry & RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False;
@@ -19624,7 +19676,7 @@ namespace ts {
1962419676
}
1962519677
// Report unreliable variance for type variables referenced in template literal type placeholders.
1962619678
// For example, `foo-${number}` is related to `foo-${string}` even though number isn't related to string.
19627-
instantiateType(source, makeFunctionTypeMapper(reportUnreliableMarkers));
19679+
instantiateType(source, reportUnreliableMapper);
1962819680
}
1962919681
if (isTypeMatchedByTemplateLiteralType(source, target as TemplateLiteralType)) {
1963019682
return Ternary.True;
@@ -19900,20 +19952,6 @@ namespace ts {
1990019952
}
1990119953
}
1990219954

19903-
function reportUnmeasurableMarkers(p: TypeParameter) {
19904-
if (outofbandVarianceMarkerHandler && (p === markerSuperType || p === markerSubType || p === markerOtherType)) {
19905-
outofbandVarianceMarkerHandler(/*onlyUnreliable*/ false);
19906-
}
19907-
return p;
19908-
}
19909-
19910-
function reportUnreliableMarkers(p: TypeParameter) {
19911-
if (outofbandVarianceMarkerHandler && (p === markerSuperType || p === markerSubType || p === markerOtherType)) {
19912-
outofbandVarianceMarkerHandler(/*onlyUnreliable*/ true);
19913-
}
19914-
return p;
19915-
}
19916-
1991719955
// A type [P in S]: X is related to a type [Q in T]: Y if T is related to S and X' is
1991819956
// related to Y, where X' is an instantiation of X in which P is replaced with Q. Notice
1991919957
// that S and T are contra-variant whereas X and Y are co-variant.
@@ -19923,7 +19961,7 @@ namespace ts {
1992319961
if (modifiersRelated) {
1992419962
let result: Ternary;
1992519963
const targetConstraint = getConstraintTypeFromMappedType(target);
19926-
const sourceConstraint = instantiateType(getConstraintTypeFromMappedType(source), makeFunctionTypeMapper(getCombinedMappedTypeOptionality(source) < 0 ? reportUnmeasurableMarkers : reportUnreliableMarkers));
19964+
const sourceConstraint = instantiateType(getConstraintTypeFromMappedType(source), getCombinedMappedTypeOptionality(source) < 0 ? reportUnmeasurableMapper : reportUnreliableMapper);
1992719965
if (result = isRelatedTo(targetConstraint, sourceConstraint, RecursionFlags.Both, reportErrors)) {
1992819966
const mapper = createTypeMapper([getTypeParameterFromMappedType(source)], [getTypeParameterFromMappedType(target)]);
1992919967
if (instantiateType(getNameTypeFromMappedType(source), mapper) === instantiateType(getNameTypeFromMappedType(target), mapper)) {
@@ -20470,7 +20508,7 @@ namespace ts {
2047020508
*/
2047120509
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
2047220510
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
20473-
relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedToWorker, makeFunctionTypeMapper(reportUnreliableMarkers));
20511+
relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedToWorker, reportUnreliableMapper);
2047420512
}
2047520513

2047620514
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
@@ -21855,28 +21893,31 @@ namespace ts {
2185521893
signature,
2185621894
flags,
2185721895
compareTypes,
21858-
mapper: makeFunctionTypeMapper(t => mapToInferredType(context, t, /*fix*/ true)),
21859-
nonFixingMapper: makeFunctionTypeMapper(t => mapToInferredType(context, t, /*fix*/ false)),
21896+
mapper: reportUnmeasurableMapper, // initialize to a noop mapper so the context object is available, but the underlying object shape is right upon construction
21897+
nonFixingMapper: reportUnmeasurableMapper,
2186021898
};
21899+
context.mapper = makeFixingMapperForContext(context);
21900+
context.nonFixingMapper = makeNonFixingMapperForContext(context);
2186121901
return context;
2186221902
}
2186321903

21864-
function mapToInferredType(context: InferenceContext, t: Type, fix: boolean): Type {
21865-
const inferences = context.inferences;
21866-
for (let i = 0; i < inferences.length; i++) {
21867-
const inference = inferences[i];
21868-
if (t === inference.typeParameter) {
21869-
if (fix && !inference.isFixed) {
21870-
// Before we commit to a particular inference (and thus lock out any further inferences),
21871-
// we infer from any intra-expression inference sites we have collected.
21872-
inferFromIntraExpressionSites(context);
21873-
clearCachedInferences(inferences);
21874-
inference.isFixed = true;
21875-
}
21876-
return getInferredType(context, i);
21904+
function makeFixingMapperForContext(context: InferenceContext) {
21905+
return makeDeferredTypeMapper(map(context.inferences, i => i.typeParameter), map(context.inferences, (inference, i) => () => {
21906+
if (!inference.isFixed) {
21907+
// Before we commit to a particular inference (and thus lock out any further inferences),
21908+
// we infer from any intra-expression inference sites we have collected.
21909+
inferFromIntraExpressionSites(context);
21910+
clearCachedInferences(context.inferences);
21911+
inference.isFixed = true;
2187721912
}
21878-
}
21879-
return t;
21913+
return getInferredType(context, i);
21914+
}));
21915+
}
21916+
21917+
function makeNonFixingMapperForContext(context: InferenceContext) {
21918+
return makeDeferredTypeMapper(map(context.inferences, i => i.typeParameter), map(context.inferences, (_, i) => () => {
21919+
return getInferredType(context, i);
21920+
}));
2188021921
}
2188121922

2188221923
function clearCachedInferences(inferences: InferenceInfo[]) {

src/compiler/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6025,6 +6025,7 @@ namespace ts {
60256025
export const enum TypeMapKind {
60266026
Simple,
60276027
Array,
6028+
Deferred,
60286029
Function,
60296030
Composite,
60306031
Merged,
@@ -6034,7 +6035,8 @@ namespace ts {
60346035
export type TypeMapper =
60356036
| { kind: TypeMapKind.Simple, source: Type, target: Type }
60366037
| { kind: TypeMapKind.Array, sources: readonly Type[], targets: readonly Type[] | undefined }
6037-
| { kind: TypeMapKind.Function, func: (t: Type) => Type }
6038+
| { kind: TypeMapKind.Deferred, sources: readonly Type[], targets: (() => Type)[] }
6039+
| { kind: TypeMapKind.Function, func: (t: Type) => Type, debugInfo?: () => string }
60386040
| { kind: TypeMapKind.Composite | TypeMapKind.Merged, mapper1: TypeMapper, mapper2: TypeMapper };
60396041

60406042
export const enum InferencePriority {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//// [declarationEmitDistributiveConditionalWithInfer.ts]
2+
// This function's type is changed on declaration
3+
export const fun = (
4+
subFun: <Collection, Field extends keyof Collection>()
5+
=> FlatArray<Collection[Field], 0>[]) => { };
6+
7+
8+
//// [declarationEmitDistributiveConditionalWithInfer.js]
9+
"use strict";
10+
exports.__esModule = true;
11+
exports.fun = void 0;
12+
// This function's type is changed on declaration
13+
var fun = function (subFun) { };
14+
exports.fun = fun;
15+
16+
17+
//// [declarationEmitDistributiveConditionalWithInfer.d.ts]
18+
export declare const fun: (subFun: <Collection, Field extends keyof Collection>() => (Collection[Field] extends infer T ? T extends Collection[Field] ? T extends readonly (infer InnerArr)[] ? InnerArr : T : never : never)[]) => void;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
=== tests/cases/compiler/declarationEmitDistributiveConditionalWithInfer.ts ===
2+
// This function's type is changed on declaration
3+
export const fun = (
4+
>fun : Symbol(fun, Decl(declarationEmitDistributiveConditionalWithInfer.ts, 1, 12))
5+
6+
subFun: <Collection, Field extends keyof Collection>()
7+
>subFun : Symbol(subFun, Decl(declarationEmitDistributiveConditionalWithInfer.ts, 1, 20))
8+
>Collection : Symbol(Collection, Decl(declarationEmitDistributiveConditionalWithInfer.ts, 2, 13))
9+
>Field : Symbol(Field, Decl(declarationEmitDistributiveConditionalWithInfer.ts, 2, 24))
10+
>Collection : Symbol(Collection, Decl(declarationEmitDistributiveConditionalWithInfer.ts, 2, 13))
11+
12+
=> FlatArray<Collection[Field], 0>[]) => { };
13+
>FlatArray : Symbol(FlatArray, Decl(lib.es2019.array.d.ts, --, --))
14+
>Collection : Symbol(Collection, Decl(declarationEmitDistributiveConditionalWithInfer.ts, 2, 13))
15+
>Field : Symbol(Field, Decl(declarationEmitDistributiveConditionalWithInfer.ts, 2, 24))
16+

0 commit comments

Comments
 (0)