in packages/pyright-internal/src/analyzer/typeEvaluator.ts [19937:20257]
sourceType: printType(updatedType),
destType: printType(destType.details.boundType),
name: TypeVarType.getReadableName(destType),
})
);
}
}
return false;
}
}
if (!typeVarMap.isLocked() && isTypeVarInScope) {
typeVarMap.setTypeVarType(destType, newNarrowTypeBound, newWideTypeBound, retainLiterals);
}
return true;
}
function canAssignTypeToParamSpec(
destType: TypeVarType,
srcType: Type,
diag: DiagnosticAddendum | undefined,
typeVarMap: TypeVarMap,
recursionCount = 0
) {
if (isTypeVar(srcType) && srcType.details.isParamSpec) {
const existingEntry = typeVarMap.getParamSpec(destType);
if (existingEntry) {
if (existingEntry.parameters.length === 0 && existingEntry.paramSpec) {
// If there's an existing entry that matches, that's fine.
if (
isTypeSame(
existingEntry.paramSpec,
srcType,
/* ignorePseudoGeneric */ undefined,
/* ignoreTypeFlags */ undefined,
recursionCount
)
) {
return true;
}
}
} else {
if (!typeVarMap.isLocked() && typeVarMap.hasSolveForScope(destType.scopeId)) {
typeVarMap.setParamSpec(destType, {
flags: FunctionTypeFlags.None,
parameters: [],
typeVarScopeId: undefined,
docString: undefined,
paramSpec: srcType,
});
}
return true;
}
} else if (isFunction(srcType)) {
const functionSrcType = srcType;
const parameters = srcType.details.parameters.map((p, index) => {
const paramSpecEntry: ParamSpecEntry = {
category: p.category,
name: p.name,
isNameSynthesized: p.isNameSynthesized,
hasDefault: !!p.hasDefault,
type: FunctionType.getEffectiveParameterType(functionSrcType, index),
};
return paramSpecEntry;
});
const existingEntry = typeVarMap.getParamSpec(destType);
if (existingEntry) {
// Verify that the existing entry matches the new entry.
if (
!existingEntry.paramSpec &&
existingEntry.parameters.length === parameters.length &&
!existingEntry.parameters.some((existingParam, index) => {
const newParam = parameters[index];
return (
existingParam.category !== newParam.category ||
existingParam.name !== newParam.name ||
existingParam.hasDefault !== newParam.hasDefault ||
!isTypeSame(
existingParam.type,
newParam.type,
/* ignorePseudoGeneric */ undefined,
/* ignoreTypeFlags */ undefined,
recursionCount
)
);
})
) {
return true;
}
} else {
if (!typeVarMap.isLocked() && typeVarMap.hasSolveForScope(destType.scopeId)) {
typeVarMap.setParamSpec(destType, {
parameters,
typeVarScopeId: srcType.details.typeVarScopeId,
flags: srcType.details.flags,
docString: srcType.details.docString,
paramSpec: undefined,
});
}
return true;
}
} else if (isAnyOrUnknown(srcType)) {
return true;
}
if (diag) {
diag.addMessage(
Localizer.DiagnosticAddendum.typeParamSpec().format({
type: printType(srcType),
name: destType.details.name,
})
);
}
return false;
}
// Determines if the source type can be assigned to the dest type.
// If typeVarMap is provided, type variables within the destType are
// matched against existing type variables in the map. If a type variable
// in the dest type is not in the type map already, it is assigned a type
// and added to the map.
function canAssignType(
destType: Type,
srcType: Type,
diag?: DiagnosticAddendum,
typeVarMap?: TypeVarMap,
flags = CanAssignFlags.Default,
recursionCount = 0
): boolean {
destType = transformPossibleRecursiveTypeAlias(destType);
srcType = transformPossibleRecursiveTypeAlias(srcType);
// If this is a one-element union that contains a variadic type variable,
// pull out the subtype.
if (isUnion(destType) && destType.subtypes.length === 1 && isVariadicTypeVar(destType.subtypes[0])) {
destType = destType.subtypes[0];
}
if (isUnion(srcType) && srcType.subtypes.length === 1 && isVariadicTypeVar(srcType.subtypes[0])) {
srcType = srcType.subtypes[0];
}
if (destType === srcType) {
// If the dest type is a TypeVar and a TypeVarMap was provided, we may
// need to assign the TypeVar to itself under certain circumstances.
// This is needed for cases where generic class A[T] calls its own
// constructor with an argument of type T.
if (
isTypeVar(destType) &&
!destType.details.isParamSpec &&
!destType.details.isVariadic &&
destType.scopeType === TypeVarScopeType.Class &&
typeVarMap &&
!typeVarMap.isLocked() &&
typeVarMap.hasSolveForScope(destType.scopeId) &&
!typeVarMap.getTypeVar(destType) &&
(flags & (CanAssignFlags.SkipSolveTypeVars | CanAssignFlags.ReverseTypeVarMatching)) === 0
) {
typeVarMap.setTypeVarType(destType, srcType);
}
return true;
}
if (recursionCount > maxTypeRecursionCount) {
return true;
}
recursionCount++;
// If the source or dest is unbound, allow the assignment. The
// error will be reported elsewhere.
if (isUnbound(destType) || isUnbound(srcType)) {
return true;
}
// If we're in "overload overlap" mode, convert top-level type variables
// to their concrete forms in the source.
if ((flags & CanAssignFlags.OverloadOverlapCheck) !== 0) {
srcType = makeTopLevelTypeVarsConcrete(srcType);
}
// Strip a few of the flags we don't want to propagate to other calls.
const originalFlags = flags;
flags &= ~(CanAssignFlags.AllowBoolTypeGuard | CanAssignFlags.AllowTypeVarNarrowing);
// Before performing any other checks, see if the dest type is a
// TypeVar that we are attempting to match.
if (isTypeVar(destType)) {
// If the dest is a constrained or bound type variable and all of the
// types in the source are conditioned on that same type variable
// and have compatible types, we'll consider it assignable.
if (canAssignConditionalTypeToTypeVar(destType, srcType, recursionCount)) {
return true;
}
if (isTypeSame(destType, srcType)) {
return true;
}
// Handle the special case where both types are Self types. We'll allow
// them to be treated as equivalent to handle certain common idioms.
if (
isTypeVar(srcType) &&
srcType.details.isSynthesizedSelf &&
srcType.details.boundType &&
destType.details.isSynthesizedSelf &&
destType.details.boundType
) {
if ((flags & CanAssignFlags.ReverseTypeVarMatching) === 0 && typeVarMap) {
canAssignTypeToTypeVar(destType, srcType, diag, typeVarMap, originalFlags, recursionCount);
}
return true;
}
// If the dest is a variadic type variable, and the source is a tuple
// with a single entry that is the same variadic type variable, it's a match.
if (
isVariadicTypeVar(destType) &&
isClassInstance(srcType) &&
isTupleClass(srcType) &&
srcType.tupleTypeArguments &&
srcType.tupleTypeArguments.length === 1
) {
if (
isTypeSame(
destType,
srcType.tupleTypeArguments[0].type,
/* ignorePseudoGeneric */ undefined,
/* ignoreTypeFlags */ undefined,
recursionCount
)
) {
return true;
}
}
// If we're using ReverseTypeVarMatching and the source is a TypeVar,
// the logic below will handle this case.
if ((flags & CanAssignFlags.ReverseTypeVarMatching) === 0 || !isTypeVar(srcType)) {
if (flags & CanAssignFlags.SkipSolveTypeVars) {
return canAssignType(
makeTopLevelTypeVarsConcrete(destType),
makeTopLevelTypeVarsConcrete(srcType),
diag,
/* typeVarMap */ undefined,
originalFlags,
recursionCount
);
} else {
if (
!canAssignTypeToTypeVar(
destType,
srcType,
diag,
typeVarMap ?? new TypeVarMap(),
originalFlags,
recursionCount
)
) {
return false;
}
if (isAnyOrUnknown(srcType) && (flags & CanAssignFlags.OverloadOverlapCheck) !== 0) {
return false;
}
return true;
}
}
}
if (isTypeVar(srcType)) {
if ((flags & CanAssignFlags.ReverseTypeVarMatching) !== 0) {
// The caller has requested that we solve for source type variables
// rather than dest. If the type variable is not in the scope of the
// provided TypeVarMap, simply verify that the concrete types are
// compatible.
if (!typeVarMap || !typeVarMap.hasSolveForScope(getTypeVarScopeId(srcType))) {
return canAssignType(
makeTopLevelTypeVarsConcrete(destType),
makeTopLevelTypeVarsConcrete(srcType),
diag,
/* typeVarMap */ undefined,
originalFlags,
recursionCount
);
} else {
// Reverse the order of assignment to populate the TypeVarMap for
// the source TypeVar. Normally we set the AllowTypeVarNarrowing flag
// so the wide type bound of the TypeVar is set rather than the narrow
// type bound. This allows the type to be further narrowed through other
// assignments. However, if we're populating the expected type in the
// TypeVarMap, we don't want to allow further narrowing.
let effectiveFlags = originalFlags;
if ((originalFlags & CanAssignFlags.PopulatingExpectedType) !== 0) {
effectiveFlags &= ~(
CanAssignFlags.ReverseTypeVarMatching | CanAssignFlags.AllowTypeVarNarrowing
);
} else {
effectiveFlags |= CanAssignFlags.AllowTypeVarNarrowing;
}
if (
canAssignTypeToTypeVar(
srcType as TypeVarType,
destType,
diag,
typeVarMap,
effectiveFlags,
recursionCount
)
) {
return true;
}
// If the dest type is a union, only one of the subtypes needs to match.
let isAssignable = false;
if (isUnion(destType)) {
doForEachSubtype(destType, (destSubtype) => {