Skip to content

Commit 337ab38

Browse files
committed
Use type arguments instead of flags to detect variance marker types
1 parent 75301c8 commit 337ab38

File tree

2 files changed

+36
-40
lines changed

2 files changed

+36
-40
lines changed

src/compiler/checker.ts

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15189,12 +15189,14 @@ namespace ts {
1518915189
// is more predictable than other, interned types, which may or may not have an alias depending on
1519015190
// the order in which things were checked.
1519115191
if (source.flags & (TypeFlags.Object | TypeFlags.Conditional) && source.aliasSymbol &&
15192-
source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol &&
15193-
!(source.aliasTypeArgumentsContainsMarker || target.aliasTypeArgumentsContainsMarker)) {
15192+
source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) {
15193+
// Compute variances eagerly to ensure reliability flags are properly propagated.
1519415194
const variances = getAliasVariances(source.aliasSymbol);
15195-
const varianceResult = relateVariances(source.aliasTypeArguments, target.aliasTypeArguments, variances, isIntersectionConstituent);
15196-
if (varianceResult !== undefined) {
15197-
return varianceResult;
15195+
if (!(containsMarkerType(source.aliasTypeArguments) || containsMarkerType(target.aliasTypeArguments))) {
15196+
const varianceResult = relateVariances(source.aliasTypeArguments, target.aliasTypeArguments, variances, isIntersectionConstituent);
15197+
if (varianceResult !== undefined) {
15198+
return varianceResult;
15199+
}
1519815200
}
1519915201
}
1520015202

@@ -15383,7 +15385,7 @@ namespace ts {
1538315385
return Ternary.False;
1538415386
}
1538515387
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target &&
15386-
!(getObjectFlags(source) & ObjectFlags.MarkerType || getObjectFlags(target) & ObjectFlags.MarkerType)) {
15388+
!(containsMarkerType(getTypeArguments(<TypeReference>source)) || containsMarkerType(getTypeArguments(<TypeReference>target)))) {
1538715389
// We have type references to the same generic type, and the type references are not marker
1538815390
// type references (which are intended by be compared structurally). Obtain the variance
1538915391
// information for the type parameters and relate the type arguments accordingly.
@@ -16157,21 +16159,24 @@ namespace ts {
1615716159
return false;
1615816160
}
1615916161

16160-
// Return a type reference where the source type parameter is replaced with the target marker
16161-
// type, and flag the result as a marker type reference.
16162-
function getMarkerTypeReference(type: GenericType, source: TypeParameter, target: Type) {
16163-
const result = createTypeReference(type, map(type.typeParameters, t => t === source ? target : t));
16164-
result.objectFlags |= ObjectFlags.MarkerType;
16165-
return result;
16162+
function containsMarkerType(types: readonly Type[] | undefined) {
16163+
return some(types, t => t === markerSuperType || t === markerSubType || t === markerOtherType);
16164+
}
16165+
16166+
function getVariances(type: GenericType): VarianceFlags[] {
16167+
// Arrays and tuples are known to be covariant, no need to spend time computing this (emptyArray implies covariance for all parameters)
16168+
if (type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple) {
16169+
return emptyArray;
16170+
}
16171+
return getVariancesWorker(type, type, createTypeReference);
1616616172
}
1616716173

1616816174
function getAliasVariances(symbol: Symbol) {
16169-
const links = getSymbolLinks(symbol);
16170-
return getVariancesWorker(links.typeParameters, links, (_links, param, marker) => {
16171-
const type = getTypeAliasInstantiation(symbol, instantiateTypes(links.typeParameters!, makeUnaryTypeMapper(param, marker)));
16172-
type.aliasTypeArgumentsContainsMarker = true;
16173-
return type;
16174-
});
16175+
return getVariancesWorker(symbol, getSymbolLinks(symbol), getTypeAliasInstantiation);
16176+
}
16177+
16178+
function getTypeArgumentsWithMarker(typeParameters: TypeParameter[], source: TypeParameter, marker: TypeParameter) {
16179+
return map(typeParameters, makeUnaryTypeMapper(source, marker));
1617516180
}
1617616181

1617716182
// Return an array containing the variance of each type parameter. The variance is effectively
@@ -16180,12 +16185,13 @@ namespace ts {
1618016185
// instantiations of the generic type for type arguments with known relations. The function
1618116186
// returns the emptyArray singleton if we're not in strictFunctionTypes mode or if the function
1618216187
// has been invoked recursively for the given generic type.
16183-
function getVariancesWorker<TCache extends { variances?: VarianceFlags[] }>(typeParameters: readonly TypeParameter[] = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): VarianceFlags[] {
16188+
function getVariancesWorker<T extends GenericType | Symbol>(target: T, cache: GenericType | SymbolLinks, getInstance: (target: T, typeArguments: readonly Type[]) => Type): VarianceFlags[] {
1618416189
let variances = cache.variances;
1618516190
if (!variances) {
1618616191
// The emptyArray singleton is used to signal a recursive invocation.
1618716192
cache.variances = emptyArray;
1618816193
variances = [];
16194+
const typeParameters = cache.typeParameters || emptyArray;
1618916195
for (const tp of typeParameters) {
1619016196
let unmeasurable = false;
1619116197
let unreliable = false;
@@ -16194,15 +16200,15 @@ namespace ts {
1619416200
// We first compare instantiations where the type parameter is replaced with
1619516201
// marker types that have a known subtype relationship. From this we can infer
1619616202
// invariance, covariance, contravariance or bivariance.
16197-
const typeWithSuper = createMarkerType(cache, tp, markerSuperType);
16198-
const typeWithSub = createMarkerType(cache, tp, markerSubType);
16203+
const typeWithSuper = getInstance(target, getTypeArgumentsWithMarker(typeParameters, tp, markerSuperType));
16204+
const typeWithSub = getInstance(target, getTypeArgumentsWithMarker(typeParameters, tp, markerSubType));
1619916205
let variance = (isTypeAssignableTo(typeWithSub, typeWithSuper) ? VarianceFlags.Covariant : 0) |
1620016206
(isTypeAssignableTo(typeWithSuper, typeWithSub) ? VarianceFlags.Contravariant : 0);
1620116207
// If the instantiations appear to be related bivariantly it may be because the
1620216208
// type parameter is independent (i.e. it isn't witnessed anywhere in the generic
1620316209
// type). To determine this we compare instantiations where the type parameter is
1620416210
// replaced with marker types that are known to be unrelated.
16205-
if (variance === VarianceFlags.Bivariant && isTypeAssignableTo(createMarkerType(cache, tp, markerOtherType), typeWithSuper)) {
16211+
if (variance === VarianceFlags.Bivariant && isTypeAssignableTo(getInstance(target, getTypeArgumentsWithMarker(typeParameters, tp, markerOtherType)), typeWithSuper)) {
1620616212
variance = VarianceFlags.Independent;
1620716213
}
1620816214
outofbandVarianceMarkerHandler = oldHandler;
@@ -16221,14 +16227,6 @@ namespace ts {
1622116227
return variances;
1622216228
}
1622316229

16224-
function getVariances(type: GenericType): VarianceFlags[] {
16225-
// Arrays and tuples are known to be covariant, no need to spend time computing this (emptyArray implies covariance for all parameters)
16226-
if (type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple) {
16227-
return emptyArray;
16228-
}
16229-
return getVariancesWorker(type.typeParameters, type, getMarkerTypeReference);
16230-
}
16231-
1623216230
// Return true if the given type reference has a 'void' type argument for a covariant type parameter.
1623316231
// See comment at call in recursiveTypeRelatedTo for when this case matters.
1623416232
function hasCovariantVoidArgument(typeArguments: readonly Type[], variances: VarianceFlags[]): boolean {

src/compiler/types.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4271,7 +4271,6 @@ namespace ts {
42714271
pattern?: DestructuringPattern; // Destructuring pattern represented by type (if any)
42724272
aliasSymbol?: Symbol; // Alias associated with type
42734273
aliasTypeArguments?: readonly Type[]; // Alias type arguments (if any)
4274-
/* @internal */ aliasTypeArgumentsContainsMarker?: boolean; // Alias type arguments (if any)
42754274
/* @internal */
42764275
permissiveInstantiation?: Type; // Instantiation with type parameters mapped to wildcard type
42774276
/* @internal */
@@ -4348,19 +4347,18 @@ namespace ts {
43484347
ContainsSpread = 1 << 10, // Object literal contains spread operation
43494348
ReverseMapped = 1 << 11, // Object contains a property from a reverse-mapped type
43504349
JsxAttributes = 1 << 12, // Jsx attributes type
4351-
MarkerType = 1 << 13, // Marker type used for variance probing
4352-
JSLiteral = 1 << 14, // Object type declared in JS - disables errors on read/write of nonexisting members
4353-
FreshLiteral = 1 << 15, // Fresh object literal
4354-
ArrayLiteral = 1 << 16, // Originates in an array literal
4355-
ObjectRestType = 1 << 17, // Originates in object rest declaration
4350+
JSLiteral = 1 << 13, // Object type declared in JS - disables errors on read/write of nonexisting members
4351+
FreshLiteral = 1 << 14, // Fresh object literal
4352+
ArrayLiteral = 1 << 15, // Originates in an array literal
4353+
ObjectRestType = 1 << 16, // Originates in object rest declaration
43564354
/* @internal */
4357-
PrimitiveUnion = 1 << 18, // Union of only primitive types
4355+
PrimitiveUnion = 1 << 17, // Union of only primitive types
43584356
/* @internal */
4359-
ContainsWideningType = 1 << 19, // Type is or contains undefined or null widening type
4357+
ContainsWideningType = 1 << 18, // Type is or contains undefined or null widening type
43604358
/* @internal */
4361-
ContainsObjectOrArrayLiteral = 1 << 20, // Type is or contains object literal type
4359+
ContainsObjectOrArrayLiteral = 1 << 19, // Type is or contains object literal type
43624360
/* @internal */
4363-
NonInferrableType = 1 << 21, // Type is or contains anyFunctionType or silentNeverType
4361+
NonInferrableType = 1 << 20, // Type is or contains anyFunctionType or silentNeverType
43644362
ClassOrInterface = Class | Interface,
43654363
/* @internal */
43664364
RequiresWidening = ContainsWideningType | ContainsObjectOrArrayLiteral,

0 commit comments

Comments
 (0)