Use type arguments instead of flags to detect variance marker types

This commit is contained in:
Anders Hejlsberg
2019-11-20 13:45:18 -08:00
parent 75301c8e2c
commit 337ab38348
2 changed files with 36 additions and 40 deletions

View File

@@ -15189,12 +15189,14 @@ namespace ts {
// is more predictable than other, interned types, which may or may not have an alias depending on
// the order in which things were checked.
if (source.flags & (TypeFlags.Object | TypeFlags.Conditional) && source.aliasSymbol &&
source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol &&
!(source.aliasTypeArgumentsContainsMarker || target.aliasTypeArgumentsContainsMarker)) {
source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) {
// Compute variances eagerly to ensure reliability flags are properly propagated.
const variances = getAliasVariances(source.aliasSymbol);
const varianceResult = relateVariances(source.aliasTypeArguments, target.aliasTypeArguments, variances, isIntersectionConstituent);
if (varianceResult !== undefined) {
return varianceResult;
if (!(containsMarkerType(source.aliasTypeArguments) || containsMarkerType(target.aliasTypeArguments))) {
const varianceResult = relateVariances(source.aliasTypeArguments, target.aliasTypeArguments, variances, isIntersectionConstituent);
if (varianceResult !== undefined) {
return varianceResult;
}
}
}
@@ -15383,7 +15385,7 @@ namespace ts {
return Ternary.False;
}
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target &&
!(getObjectFlags(source) & ObjectFlags.MarkerType || getObjectFlags(target) & ObjectFlags.MarkerType)) {
!(containsMarkerType(getTypeArguments(<TypeReference>source)) || containsMarkerType(getTypeArguments(<TypeReference>target)))) {
// We have type references to the same generic type, and the type references are not marker
// type references (which are intended by be compared structurally). Obtain the variance
// information for the type parameters and relate the type arguments accordingly.
@@ -16157,21 +16159,24 @@ namespace ts {
return false;
}
// Return a type reference where the source type parameter is replaced with the target marker
// type, and flag the result as a marker type reference.
function getMarkerTypeReference(type: GenericType, source: TypeParameter, target: Type) {
const result = createTypeReference(type, map(type.typeParameters, t => t === source ? target : t));
result.objectFlags |= ObjectFlags.MarkerType;
return result;
function containsMarkerType(types: readonly Type[] | undefined) {
return some(types, t => t === markerSuperType || t === markerSubType || t === markerOtherType);
}
function getVariances(type: GenericType): VarianceFlags[] {
// Arrays and tuples are known to be covariant, no need to spend time computing this (emptyArray implies covariance for all parameters)
if (type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple) {
return emptyArray;
}
return getVariancesWorker(type, type, createTypeReference);
}
function getAliasVariances(symbol: Symbol) {
const links = getSymbolLinks(symbol);
return getVariancesWorker(links.typeParameters, links, (_links, param, marker) => {
const type = getTypeAliasInstantiation(symbol, instantiateTypes(links.typeParameters!, makeUnaryTypeMapper(param, marker)));
type.aliasTypeArgumentsContainsMarker = true;
return type;
});
return getVariancesWorker(symbol, getSymbolLinks(symbol), getTypeAliasInstantiation);
}
function getTypeArgumentsWithMarker(typeParameters: TypeParameter[], source: TypeParameter, marker: TypeParameter) {
return map(typeParameters, makeUnaryTypeMapper(source, marker));
}
// Return an array containing the variance of each type parameter. The variance is effectively
@@ -16180,12 +16185,13 @@ namespace ts {
// instantiations of the generic type for type arguments with known relations. The function
// returns the emptyArray singleton if we're not in strictFunctionTypes mode or if the function
// has been invoked recursively for the given generic type.
function getVariancesWorker<TCache extends { variances?: VarianceFlags[] }>(typeParameters: readonly TypeParameter[] = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): VarianceFlags[] {
function getVariancesWorker<T extends GenericType | Symbol>(target: T, cache: GenericType | SymbolLinks, getInstance: (target: T, typeArguments: readonly Type[]) => Type): VarianceFlags[] {
let variances = cache.variances;
if (!variances) {
// The emptyArray singleton is used to signal a recursive invocation.
cache.variances = emptyArray;
variances = [];
const typeParameters = cache.typeParameters || emptyArray;
for (const tp of typeParameters) {
let unmeasurable = false;
let unreliable = false;
@@ -16194,15 +16200,15 @@ namespace ts {
// We first compare instantiations where the type parameter is replaced with
// marker types that have a known subtype relationship. From this we can infer
// invariance, covariance, contravariance or bivariance.
const typeWithSuper = createMarkerType(cache, tp, markerSuperType);
const typeWithSub = createMarkerType(cache, tp, markerSubType);
const typeWithSuper = getInstance(target, getTypeArgumentsWithMarker(typeParameters, tp, markerSuperType));
const typeWithSub = getInstance(target, getTypeArgumentsWithMarker(typeParameters, tp, markerSubType));
let variance = (isTypeAssignableTo(typeWithSub, typeWithSuper) ? VarianceFlags.Covariant : 0) |
(isTypeAssignableTo(typeWithSuper, typeWithSub) ? VarianceFlags.Contravariant : 0);
// If the instantiations appear to be related bivariantly it may be because the
// type parameter is independent (i.e. it isn't witnessed anywhere in the generic
// type). To determine this we compare instantiations where the type parameter is
// replaced with marker types that are known to be unrelated.
if (variance === VarianceFlags.Bivariant && isTypeAssignableTo(createMarkerType(cache, tp, markerOtherType), typeWithSuper)) {
if (variance === VarianceFlags.Bivariant && isTypeAssignableTo(getInstance(target, getTypeArgumentsWithMarker(typeParameters, tp, markerOtherType)), typeWithSuper)) {
variance = VarianceFlags.Independent;
}
outofbandVarianceMarkerHandler = oldHandler;
@@ -16221,14 +16227,6 @@ namespace ts {
return variances;
}
function getVariances(type: GenericType): VarianceFlags[] {
// Arrays and tuples are known to be covariant, no need to spend time computing this (emptyArray implies covariance for all parameters)
if (type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple) {
return emptyArray;
}
return getVariancesWorker(type.typeParameters, type, getMarkerTypeReference);
}
// Return true if the given type reference has a 'void' type argument for a covariant type parameter.
// See comment at call in recursiveTypeRelatedTo for when this case matters.
function hasCovariantVoidArgument(typeArguments: readonly Type[], variances: VarianceFlags[]): boolean {

View File

@@ -4271,7 +4271,6 @@ namespace ts {
pattern?: DestructuringPattern; // Destructuring pattern represented by type (if any)
aliasSymbol?: Symbol; // Alias associated with type
aliasTypeArguments?: readonly Type[]; // Alias type arguments (if any)
/* @internal */ aliasTypeArgumentsContainsMarker?: boolean; // Alias type arguments (if any)
/* @internal */
permissiveInstantiation?: Type; // Instantiation with type parameters mapped to wildcard type
/* @internal */
@@ -4348,19 +4347,18 @@ namespace ts {
ContainsSpread = 1 << 10, // Object literal contains spread operation
ReverseMapped = 1 << 11, // Object contains a property from a reverse-mapped type
JsxAttributes = 1 << 12, // Jsx attributes type
MarkerType = 1 << 13, // Marker type used for variance probing
JSLiteral = 1 << 14, // Object type declared in JS - disables errors on read/write of nonexisting members
FreshLiteral = 1 << 15, // Fresh object literal
ArrayLiteral = 1 << 16, // Originates in an array literal
ObjectRestType = 1 << 17, // Originates in object rest declaration
JSLiteral = 1 << 13, // Object type declared in JS - disables errors on read/write of nonexisting members
FreshLiteral = 1 << 14, // Fresh object literal
ArrayLiteral = 1 << 15, // Originates in an array literal
ObjectRestType = 1 << 16, // Originates in object rest declaration
/* @internal */
PrimitiveUnion = 1 << 18, // Union of only primitive types
PrimitiveUnion = 1 << 17, // Union of only primitive types
/* @internal */
ContainsWideningType = 1 << 19, // Type is or contains undefined or null widening type
ContainsWideningType = 1 << 18, // Type is or contains undefined or null widening type
/* @internal */
ContainsObjectOrArrayLiteral = 1 << 20, // Type is or contains object literal type
ContainsObjectOrArrayLiteral = 1 << 19, // Type is or contains object literal type
/* @internal */
NonInferrableType = 1 << 21, // Type is or contains anyFunctionType or silentNeverType
NonInferrableType = 1 << 20, // Type is or contains anyFunctionType or silentNeverType
ClassOrInterface = Class | Interface,
/* @internal */
RequiresWidening = ContainsWideningType | ContainsObjectOrArrayLiteral,