mirror of
https://github.com/microsoft/pyright.git
synced 2024-10-26 10:55:06 +03:00
Moved more functionality out of typeEvaluator.ts into constraintSolver.ts. No functional change.
This commit is contained in:
parent
53198149dd
commit
e860737771
@ -18,6 +18,7 @@ import {
|
||||
combineTypes,
|
||||
FunctionType,
|
||||
FunctionTypeFlags,
|
||||
isAny,
|
||||
isAnyOrUnknown,
|
||||
isClass,
|
||||
isClassInstance,
|
||||
@ -33,22 +34,27 @@ import {
|
||||
ParamSpecEntry,
|
||||
Type,
|
||||
TypeBase,
|
||||
TypeVarScopeId,
|
||||
TypeVarType,
|
||||
UnknownType,
|
||||
Variance,
|
||||
} from './types';
|
||||
import {
|
||||
addConditionToType,
|
||||
buildTypeVarContextFromSpecializedClass,
|
||||
CanAssignFlags,
|
||||
containsLiteralType,
|
||||
convertParamSpecValueToType,
|
||||
convertToInstance,
|
||||
convertToInstantiable,
|
||||
getTypeCondition,
|
||||
getTypeVarScopeId,
|
||||
isEffectivelyInstantiable,
|
||||
isPartlyUnknown,
|
||||
mapSubtypes,
|
||||
specializeTupleClass,
|
||||
stripLiteralValue,
|
||||
transformExpectedTypeForConstructor,
|
||||
} from './typeUtils';
|
||||
import { TypeVarContext } from './typeVarContext';
|
||||
|
||||
@ -758,3 +764,144 @@ function canAssignTypeToParamSpec(
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// In cases where the expected type is a specialized base class of the
|
||||
// source type, we need to determine which type arguments in the derived
|
||||
// class will make it compatible with the specialized base class. This method
|
||||
// performs this reverse mapping of type arguments and populates the type var
|
||||
// map for the target type. If the type is not assignable to the expected type,
|
||||
// it returns false.
|
||||
export function populateTypeVarContextBasedOnExpectedType(
|
||||
evaluator: TypeEvaluator,
|
||||
type: ClassType,
|
||||
expectedType: Type,
|
||||
typeVarContext: TypeVarContext,
|
||||
liveTypeVarScopes: TypeVarScopeId[] | undefined
|
||||
): boolean {
|
||||
if (isAny(expectedType)) {
|
||||
type.details.typeParameters.forEach((typeParam) => {
|
||||
typeVarContext.setTypeVarType(typeParam, expectedType);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isClass(expectedType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the expected type is generic (but not specialized), we can't proceed.
|
||||
const expectedTypeArgs = expectedType.typeArguments;
|
||||
if (!expectedTypeArgs) {
|
||||
return evaluator.canAssignType(
|
||||
type,
|
||||
expectedType,
|
||||
/* diag */ undefined,
|
||||
typeVarContext,
|
||||
CanAssignFlags.PopulatingExpectedType
|
||||
);
|
||||
}
|
||||
|
||||
// If the expected type is the same as the target type (commonly the case),
|
||||
// we can use a faster method.
|
||||
if (ClassType.isSameGenericClass(expectedType, type)) {
|
||||
const sameClassTypeVarContext = buildTypeVarContextFromSpecializedClass(expectedType);
|
||||
sameClassTypeVarContext.getTypeVars().forEach((entry) => {
|
||||
const typeVarType = sameClassTypeVarContext.getTypeVarType(entry.typeVar);
|
||||
|
||||
if (typeVarType) {
|
||||
// Skip this if the type argument is a TypeVar defined by the class scope because
|
||||
// we're potentially solving for these TypeVars.
|
||||
if (!isTypeVar(typeVarType) || typeVarType.scopeId !== type.details.typeVarScopeId) {
|
||||
typeVarContext.setTypeVarType(
|
||||
entry.typeVar,
|
||||
entry.typeVar.details.variance === Variance.Covariant ? undefined : typeVarType,
|
||||
entry.typeVar.details.variance === Variance.Contravariant ? undefined : typeVarType,
|
||||
entry.retainLiteral
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create a generic version of the expected type.
|
||||
const expectedTypeScopeId = getTypeVarScopeId(expectedType);
|
||||
const synthExpectedTypeArgs = ClassType.getTypeParameters(expectedType).map((typeParam, index) => {
|
||||
const typeVar = TypeVarType.createInstance(`__dest${index}`);
|
||||
typeVar.details.isSynthesized = true;
|
||||
|
||||
// Use invariance here so we set the narrow and wide values on the TypeVar.
|
||||
typeVar.details.variance = Variance.Invariant;
|
||||
typeVar.scopeId = expectedTypeScopeId;
|
||||
return typeVar;
|
||||
});
|
||||
const genericExpectedType = ClassType.cloneForSpecialization(
|
||||
expectedType,
|
||||
synthExpectedTypeArgs,
|
||||
/* isTypeArgumentExplicit */ true
|
||||
);
|
||||
|
||||
// For each type param in the target type, create a placeholder type variable.
|
||||
const typeArgs = ClassType.getTypeParameters(type).map((_, index) => {
|
||||
const typeVar = TypeVarType.createInstance(`__source${index}`);
|
||||
typeVar.details.isSynthesized = true;
|
||||
typeVar.details.synthesizedIndex = index;
|
||||
typeVar.details.isExemptFromBoundCheck = true;
|
||||
return typeVar;
|
||||
});
|
||||
|
||||
const specializedType = ClassType.cloneForSpecialization(type, typeArgs, /* isTypeArgumentExplicit */ true);
|
||||
const syntheticTypeVarContext = new TypeVarContext(expectedTypeScopeId);
|
||||
if (
|
||||
evaluator.canAssignType(
|
||||
genericExpectedType,
|
||||
specializedType,
|
||||
/* diag */ undefined,
|
||||
syntheticTypeVarContext,
|
||||
CanAssignFlags.PopulatingExpectedType
|
||||
)
|
||||
) {
|
||||
let isResultValid = true;
|
||||
|
||||
synthExpectedTypeArgs.forEach((typeVar, index) => {
|
||||
const synthTypeVar = syntheticTypeVarContext.getTypeVarType(typeVar);
|
||||
|
||||
// Is this one of the synthesized type vars we allocated above? If so,
|
||||
// the type arg that corresponds to this type var maps back to the target type.
|
||||
if (
|
||||
synthTypeVar &&
|
||||
isTypeVar(synthTypeVar) &&
|
||||
synthTypeVar.details.isSynthesized &&
|
||||
synthTypeVar.details.synthesizedIndex !== undefined
|
||||
) {
|
||||
const targetTypeVar =
|
||||
ClassType.getTypeParameters(specializedType)[synthTypeVar.details.synthesizedIndex];
|
||||
if (index < expectedTypeArgs.length) {
|
||||
let expectedTypeArgValue: Type | undefined = expectedTypeArgs[index];
|
||||
|
||||
if (liveTypeVarScopes) {
|
||||
expectedTypeArgValue = transformExpectedTypeForConstructor(
|
||||
expectedTypeArgValue,
|
||||
typeVarContext,
|
||||
liveTypeVarScopes
|
||||
);
|
||||
}
|
||||
|
||||
if (expectedTypeArgValue) {
|
||||
typeVarContext.setTypeVarType(
|
||||
targetTypeVar,
|
||||
typeVar.details.variance === Variance.Covariant ? undefined : expectedTypeArgValue,
|
||||
typeVar.details.variance === Variance.Contravariant ? undefined : expectedTypeArgValue
|
||||
);
|
||||
} else {
|
||||
isResultValid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return isResultValid;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import {
|
||||
PatternValueNode,
|
||||
} from '../parser/parseNodes';
|
||||
import { getFileInfo } from './analyzerNodeInfo';
|
||||
import { populateTypeVarContextBasedOnExpectedType } from './constraintSolver';
|
||||
import { getTypedDictMembersForClass } from './typedDicts';
|
||||
import { EvaluatorFlags, TypeEvaluator } from './typeEvaluatorTypes';
|
||||
import { enumerateLiteralsForType } from './typeGuards';
|
||||
@ -606,7 +607,8 @@ function narrowTypeBasedOnClassPattern(
|
||||
|
||||
const matchTypeInstance = ClassType.cloneAsInstance(unspecializedMatchType);
|
||||
if (
|
||||
evaluator.populateTypeVarContextBasedOnExpectedType(
|
||||
populateTypeVarContextBasedOnExpectedType(
|
||||
evaluator,
|
||||
matchTypeInstance,
|
||||
subjectSubtypeExpanded,
|
||||
typeVarContext,
|
||||
|
@ -87,7 +87,7 @@ import {
|
||||
FlowNode,
|
||||
isCodeFlowSupportedForReference,
|
||||
} from './codeFlowTypes';
|
||||
import { canAssignTypeToTypeVar } from './constraintSolver';
|
||||
import { canAssignTypeToTypeVar, populateTypeVarContextBasedOnExpectedType } from './constraintSolver';
|
||||
import { applyConstructorTransform } from './constructorTransform';
|
||||
import {
|
||||
applyDataClassClassBehaviorOverrides,
|
||||
@ -271,7 +271,6 @@ import {
|
||||
specializeTupleClass,
|
||||
stripLiteralValue,
|
||||
synthesizeTypeVarForSelfCls,
|
||||
transformExpectedTypeForConstructor,
|
||||
transformPossibleRecursiveTypeAlias,
|
||||
VirtualParameterDetails,
|
||||
} from './typeUtils';
|
||||
@ -6645,6 +6644,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
const tupleTypeVarContext = new TypeVarContext(getTypeVarScopeId(tupleClassType));
|
||||
if (
|
||||
!populateTypeVarContextBasedOnExpectedType(
|
||||
evaluatorInterface,
|
||||
ClassType.cloneAsInstance(tupleClassType),
|
||||
expectedType,
|
||||
tupleTypeVarContext,
|
||||
@ -7654,6 +7654,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
const typeVarContext = new TypeVarContext(getTypeVarScopeId(type));
|
||||
if (
|
||||
populateTypeVarContextBasedOnExpectedType(
|
||||
evaluatorInterface,
|
||||
ClassType.cloneAsInstance(type),
|
||||
expectedSubType,
|
||||
typeVarContext,
|
||||
@ -7901,6 +7902,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
|
||||
if (expectedType) {
|
||||
populateTypeVarContextBasedOnExpectedType(
|
||||
evaluatorInterface,
|
||||
ClassType.cloneAsInstance(type),
|
||||
expectedType,
|
||||
typeVarContext,
|
||||
@ -7992,146 +7994,6 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
return specializedType;
|
||||
}
|
||||
|
||||
// In cases where the expected type is a specialized base class of the
|
||||
// source type, we need to determine which type arguments in the derived
|
||||
// class will make it compatible with the specialized base class. This method
|
||||
// performs this reverse mapping of type arguments and populates the type var
|
||||
// map for the target type. If the type is not assignable to the expected type,
|
||||
// it returns false.
|
||||
function populateTypeVarContextBasedOnExpectedType(
|
||||
type: ClassType,
|
||||
expectedType: Type,
|
||||
typeVarContext: TypeVarContext,
|
||||
liveTypeVarScopes: TypeVarScopeId[] | undefined
|
||||
): boolean {
|
||||
if (isAny(expectedType)) {
|
||||
type.details.typeParameters.forEach((typeParam) => {
|
||||
typeVarContext.setTypeVarType(typeParam, expectedType);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isClass(expectedType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the expected type is generic (but not specialized), we can't proceed.
|
||||
const expectedTypeArgs = expectedType.typeArguments;
|
||||
if (!expectedTypeArgs) {
|
||||
return canAssignType(
|
||||
type,
|
||||
expectedType,
|
||||
/* diag */ undefined,
|
||||
typeVarContext,
|
||||
CanAssignFlags.PopulatingExpectedType
|
||||
);
|
||||
}
|
||||
|
||||
// If the expected type is the same as the target type (commonly the case),
|
||||
// we can use a faster method.
|
||||
if (ClassType.isSameGenericClass(expectedType, type)) {
|
||||
const sameClassTypeVarContext = buildTypeVarContextFromSpecializedClass(expectedType);
|
||||
sameClassTypeVarContext.getTypeVars().forEach((entry) => {
|
||||
const typeVarType = sameClassTypeVarContext.getTypeVarType(entry.typeVar);
|
||||
|
||||
if (typeVarType) {
|
||||
// Skip this if the type argument is a TypeVar defined by the class scope because
|
||||
// we're potentially solving for these TypeVars.
|
||||
if (!isTypeVar(typeVarType) || typeVarType.scopeId !== type.details.typeVarScopeId) {
|
||||
typeVarContext.setTypeVarType(
|
||||
entry.typeVar,
|
||||
entry.typeVar.details.variance === Variance.Covariant ? undefined : typeVarType,
|
||||
entry.typeVar.details.variance === Variance.Contravariant ? undefined : typeVarType,
|
||||
entry.retainLiteral
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create a generic version of the expected type.
|
||||
const expectedTypeScopeId = getTypeVarScopeId(expectedType);
|
||||
const synthExpectedTypeArgs = ClassType.getTypeParameters(expectedType).map((typeParam, index) => {
|
||||
const typeVar = TypeVarType.createInstance(`__dest${index}`);
|
||||
typeVar.details.isSynthesized = true;
|
||||
|
||||
// Use invariance here so we set the narrow and wide values on the TypeVar.
|
||||
typeVar.details.variance = Variance.Invariant;
|
||||
typeVar.scopeId = expectedTypeScopeId;
|
||||
return typeVar;
|
||||
});
|
||||
const genericExpectedType = ClassType.cloneForSpecialization(
|
||||
expectedType,
|
||||
synthExpectedTypeArgs,
|
||||
/* isTypeArgumentExplicit */ true
|
||||
);
|
||||
|
||||
// For each type param in the target type, create a placeholder type variable.
|
||||
const typeArgs = ClassType.getTypeParameters(type).map((_, index) => {
|
||||
const typeVar = TypeVarType.createInstance(`__source${index}`);
|
||||
typeVar.details.isSynthesized = true;
|
||||
typeVar.details.synthesizedIndex = index;
|
||||
typeVar.details.isExemptFromBoundCheck = true;
|
||||
return typeVar;
|
||||
});
|
||||
|
||||
const specializedType = ClassType.cloneForSpecialization(type, typeArgs, /* isTypeArgumentExplicit */ true);
|
||||
const syntheticTypeVarContext = new TypeVarContext(expectedTypeScopeId);
|
||||
if (
|
||||
canAssignType(
|
||||
genericExpectedType,
|
||||
specializedType,
|
||||
/* diag */ undefined,
|
||||
syntheticTypeVarContext,
|
||||
CanAssignFlags.PopulatingExpectedType
|
||||
)
|
||||
) {
|
||||
let isResultValid = true;
|
||||
|
||||
synthExpectedTypeArgs.forEach((typeVar, index) => {
|
||||
const synthTypeVar = syntheticTypeVarContext.getTypeVarType(typeVar);
|
||||
|
||||
// Is this one of the synthesized type vars we allocated above? If so,
|
||||
// the type arg that corresponds to this type var maps back to the target type.
|
||||
if (
|
||||
synthTypeVar &&
|
||||
isTypeVar(synthTypeVar) &&
|
||||
synthTypeVar.details.isSynthesized &&
|
||||
synthTypeVar.details.synthesizedIndex !== undefined
|
||||
) {
|
||||
const targetTypeVar =
|
||||
ClassType.getTypeParameters(specializedType)[synthTypeVar.details.synthesizedIndex];
|
||||
if (index < expectedTypeArgs.length) {
|
||||
let expectedTypeArgValue: Type | undefined = expectedTypeArgs[index];
|
||||
|
||||
if (liveTypeVarScopes) {
|
||||
expectedTypeArgValue = transformExpectedTypeForConstructor(
|
||||
expectedTypeArgValue,
|
||||
typeVarContext,
|
||||
liveTypeVarScopes
|
||||
);
|
||||
}
|
||||
|
||||
if (expectedTypeArgValue) {
|
||||
typeVarContext.setTypeVarType(
|
||||
targetTypeVar,
|
||||
typeVar.details.variance === Variance.Covariant ? undefined : expectedTypeArgValue,
|
||||
typeVar.details.variance === Variance.Contravariant ? undefined : expectedTypeArgValue
|
||||
);
|
||||
} else {
|
||||
isResultValid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return isResultValid;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validates that the arguments can be assigned to the call's parameter
|
||||
// list, specializes the call based on arg types, and returns the
|
||||
// specialized type of the return value. If it detects an error along
|
||||
@ -9637,6 +9499,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
) {
|
||||
const tempTypeVarContext = new TypeVarContext(getTypeVarScopeId(effectiveReturnType));
|
||||
populateTypeVarContextBasedOnExpectedType(
|
||||
evaluatorInterface,
|
||||
effectiveReturnType,
|
||||
effectiveExpectedType,
|
||||
tempTypeVarContext,
|
||||
@ -11929,6 +11792,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
const dictTypeVarContext = new TypeVarContext(getTypeVarScopeId(builtInDict));
|
||||
if (
|
||||
!populateTypeVarContextBasedOnExpectedType(
|
||||
evaluatorInterface,
|
||||
builtInDict,
|
||||
expectedType,
|
||||
dictTypeVarContext,
|
||||
@ -12251,6 +12115,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
const typeVarContext = new TypeVarContext(getTypeVarScopeId(builtInListOrSet));
|
||||
if (
|
||||
!populateTypeVarContextBasedOnExpectedType(
|
||||
evaluatorInterface,
|
||||
builtInListOrSet,
|
||||
expectedType,
|
||||
typeVarContext,
|
||||
@ -21999,6 +21864,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
) {
|
||||
const typeVarContext = new TypeVarContext(getTypeVarScopeId(assignedType));
|
||||
populateTypeVarContextBasedOnExpectedType(
|
||||
evaluatorInterface,
|
||||
ClassType.cloneForSpecialization(
|
||||
assignedType,
|
||||
/* typeArguments */ undefined,
|
||||
@ -22961,7 +22827,6 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
getScopeIdForNode,
|
||||
makeTopLevelTypeVarsConcrete,
|
||||
mapSubtypesExpandTypeVars,
|
||||
populateTypeVarContextBasedOnExpectedType,
|
||||
lookUpSymbolRecursive,
|
||||
getDeclaredTypeOfSymbol,
|
||||
getEffectiveTypeOfSymbol,
|
||||
|
@ -41,7 +41,6 @@ import {
|
||||
OverloadedFunctionType,
|
||||
Type,
|
||||
TypeCondition,
|
||||
TypeVarScopeId,
|
||||
TypeVarType,
|
||||
} from './types';
|
||||
import { CanAssignFlags, ClassMember } from './typeUtils';
|
||||
@ -338,12 +337,6 @@ export interface TypeEvaluator {
|
||||
conditionFilter: TypeCondition[] | undefined,
|
||||
callback: (expandedSubtype: Type, unexpandedSubtype: Type) => Type | undefined
|
||||
) => Type;
|
||||
populateTypeVarContextBasedOnExpectedType: (
|
||||
type: ClassType,
|
||||
expectedType: Type,
|
||||
typeVarContext: TypeVarContext,
|
||||
liveTypeVarScopes: TypeVarScopeId[] | undefined
|
||||
) => boolean;
|
||||
lookUpSymbolRecursive: (node: ParseNode, name: string, honorCodeFlow: boolean) => SymbolWithScope | undefined;
|
||||
getDeclaredTypeOfSymbol: (symbol: Symbol) => Type | undefined;
|
||||
getEffectiveTypeOfSymbol: (symbol: Symbol) => Type;
|
||||
|
@ -105,7 +105,6 @@ export function createTypeEvaluatorWithTracker(
|
||||
makeTopLevelTypeVarsConcrete: (t) =>
|
||||
run('makeTopLevelTypeVarsConcrete', () => typeEvaluator.makeTopLevelTypeVarsConcrete(t), t),
|
||||
mapSubtypesExpandTypeVars: typeEvaluator.mapSubtypesExpandTypeVars,
|
||||
populateTypeVarContextBasedOnExpectedType: typeEvaluator.populateTypeVarContextBasedOnExpectedType,
|
||||
lookUpSymbolRecursive: typeEvaluator.lookUpSymbolRecursive,
|
||||
getDeclaredTypeOfSymbol: typeEvaluator.getDeclaredTypeOfSymbol,
|
||||
getEffectiveTypeOfSymbol: (s) =>
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
} from '../parser/parseNodes';
|
||||
import { KeywordType, OperatorType } from '../parser/tokenizerTypes';
|
||||
import { getFileInfo } from './analyzerNodeInfo';
|
||||
import { populateTypeVarContextBasedOnExpectedType } from './constraintSolver';
|
||||
import { Declaration, DeclarationType } from './declaration';
|
||||
import * as ParseTreeUtils from './parseTreeUtils';
|
||||
import { ScopeType } from './scope';
|
||||
@ -992,7 +993,8 @@ function narrowTypeForIsInstance(
|
||||
);
|
||||
|
||||
if (
|
||||
evaluator.populateTypeVarContextBasedOnExpectedType(
|
||||
populateTypeVarContextBasedOnExpectedType(
|
||||
evaluator,
|
||||
unspecializedFilterType,
|
||||
varType,
|
||||
typeVarContext,
|
||||
|
Loading…
Reference in New Issue
Block a user