Fixed bug that results in some circumstances in incorrect specialization of type[T] when T evaluates to Any. This addresses #8429. (#8431)

This commit is contained in:
Eric Traut 2024-07-14 23:40:44 -07:00 committed by GitHub
parent 6990c98c12
commit 042d2ea582
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 93 additions and 26 deletions

View File

@ -5937,11 +5937,19 @@ export class Checker extends ParseTreeWalker {
}
let overriddenType = this._evaluator.getEffectiveTypeOfSymbol(overriddenClassAndSymbol.symbol);
overriddenType = partiallySpecializeType(overriddenType, overriddenClassAndSymbol.classType);
overriddenType = partiallySpecializeType(
overriddenType,
overriddenClassAndSymbol.classType,
this._evaluator.getTypeClassType()
);
const overrideSymbol = overrideClassAndSymbol.symbol;
let overrideType = this._evaluator.getEffectiveTypeOfSymbol(overrideSymbol);
overrideType = partiallySpecializeType(overrideType, overrideClassAndSymbol.classType);
overrideType = partiallySpecializeType(
overrideType,
overrideClassAndSymbol.classType,
this._evaluator.getTypeClassType()
);
const childOverrideSymbol = ClassType.getSymbolTable(childClassType).get(memberName);
const childOverrideType = childOverrideSymbol
@ -6174,7 +6182,11 @@ export class Checker extends ParseTreeWalker {
// Is the method present on the base class but missing in the subclass?
if (baseClassPropMethod) {
const baseClassMethodType = partiallySpecializeType(baseClassPropMethod, overriddenClassType);
const baseClassMethodType = partiallySpecializeType(
baseClassPropMethod,
overriddenClassType,
this._evaluator.getTypeClassType()
);
if (isFunction(baseClassMethodType)) {
if (!subclassPropMethod) {
@ -6212,7 +6224,11 @@ export class Checker extends ParseTreeWalker {
}
}
} else {
const subclassMethodType = partiallySpecializeType(subclassPropMethod, overrideClassType);
const subclassMethodType = partiallySpecializeType(
subclassPropMethod,
overrideClassType,
this._evaluator.getTypeClassType()
);
if (isFunction(subclassMethodType)) {
if (
@ -6634,10 +6650,16 @@ export class Checker extends ParseTreeWalker {
const baseType = partiallySpecializeType(
this._evaluator.getEffectiveTypeOfSymbol(baseClassAndSymbol.symbol),
baseClass,
this._evaluator.getTypeClassType(),
childClassSelf
);
overrideType = partiallySpecializeType(overrideType, childClassType, childClassSelf);
overrideType = partiallySpecializeType(
overrideType,
childClassType,
this._evaluator.getTypeClassType(),
childClassSelf
);
if (isFunction(baseType) || isOverloadedFunction(baseType)) {
const diagAddendum = new DiagnosticAddendum();
@ -6992,7 +7014,11 @@ export class Checker extends ParseTreeWalker {
// Is the method present on the base class but missing in the subclass?
if (baseClassPropMethod) {
const baseClassMethodType = partiallySpecializeType(baseClassPropMethod, baseClassType);
const baseClassMethodType = partiallySpecializeType(
baseClassPropMethod,
baseClassType,
this._evaluator.getTypeClassType()
);
if (isFunction(baseClassMethodType)) {
if (!subclassPropMethod) {
@ -7022,7 +7048,11 @@ export class Checker extends ParseTreeWalker {
}
}
} else {
const subclassMethodType = partiallySpecializeType(subclassPropMethod, childClassType);
const subclassMethodType = partiallySpecializeType(
subclassPropMethod,
childClassType,
this._evaluator.getTypeClassType()
);
if (isFunction(subclassMethodType)) {
if (

View File

@ -244,7 +244,11 @@ export function getParameterListDetails(type: FunctionType): ParameterListDetail
const typedDictType = paramType;
paramType.shared.typedDictEntries.knownItems.forEach((entry, name) => {
const specializedParamType = partiallySpecializeType(entry.valueType, typedDictType);
const specializedParamType = partiallySpecializeType(
entry.valueType,
typedDictType,
/* typeClassType */ undefined
);
addVirtualParameter(
FunctionParam.create(

View File

@ -1250,7 +1250,11 @@ function getMappingPatternInfo(evaluator: TypeEvaluator, type: Type, node: Patte
}
if (mroClassToSpecialize) {
const specializedMapping = partiallySpecializeType(mroClassToSpecialize, concreteSubtype) as ClassType;
const specializedMapping = partiallySpecializeType(
mroClassToSpecialize,
concreteSubtype,
evaluator.getTypeClassType()
) as ClassType;
if (specializedMapping.priv.typeArguments && specializedMapping.priv.typeArguments.length >= 2) {
mappingInfo.push({
@ -1346,7 +1350,11 @@ function getSequencePatternInfo(
}
if (mroClassToSpecialize) {
const specializedSequence = partiallySpecializeType(mroClassToSpecialize, concreteSubtype) as ClassType;
const specializedSequence = partiallySpecializeType(
mroClassToSpecialize,
concreteSubtype,
evaluator.getTypeClassType()
) as ClassType;
if (isTupleClass(specializedSequence)) {
const typeArgs = specializedSequence.priv.tupleTypeArguments ?? [

View File

@ -428,7 +428,12 @@ function assignClassToProtocolInternal(
// We can skip this if it's the dest class because it is already
// specialized.
if (!ClassType.isSameGenericClass(mroClass, destType)) {
destMemberType = partiallySpecializeType(destMemberType, mroClass, selfType);
destMemberType = partiallySpecializeType(
destMemberType,
mroClass,
evaluator.getTypeClassType(),
selfType
);
}
if (isInstantiableClass(srcMemberInfo.classType)) {
@ -439,7 +444,12 @@ function assignClassToProtocolInternal(
evaluator.inferReturnTypeIfNecessary(symbolType);
}
srcMemberType = partiallySpecializeType(symbolType, srcMemberInfo.classType, selfType);
srcMemberType = partiallySpecializeType(
symbolType,
srcMemberInfo.classType,
evaluator.getTypeClassType(),
selfType
);
} else {
srcMemberType = UnknownType.create();
}
@ -574,7 +584,7 @@ function assignClassToProtocolInternal(
let getterType = evaluator.getGetterTypeFromProperty(destMemberType, /* inferTypeIfNeeded */ true);
if (getterType) {
getterType = partiallySpecializeType(getterType, mroClass);
getterType = partiallySpecializeType(getterType, mroClass, evaluator.getTypeClassType());
}
if (

View File

@ -2697,7 +2697,7 @@ export function createTypeEvaluator(
if (classOrObjectBase) {
if (memberAccessClass && isInstantiableClass(memberAccessClass)) {
declaredType = partiallySpecializeType(declaredType, memberAccessClass);
declaredType = partiallySpecializeType(declaredType, memberAccessClass, getTypeClassType());
}
if (isFunction(declaredType) || isOverloadedFunction(declaredType)) {
@ -3017,6 +3017,13 @@ export function createTypeEvaluator(
return unionTypeClass ?? UnknownType.create();
}
function getTypeClassType(): ClassType | undefined {
if (typeClass && isInstantiableClass(typeClass)) {
return typeClass;
}
return undefined;
}
function getTypingType(node: ParseNode, symbolName: string): Type | undefined {
return (
getTypeOfModule(node, symbolName, ['typing']) ?? getTypeOfModule(node, symbolName, ['typing_extensions'])
@ -5720,7 +5727,7 @@ export function createTypeEvaluator(
) {
type = getDeclaredTypeOfSymbol(memberInfo.symbol)?.type;
if (type && isInstantiableClass(memberInfo.classType)) {
type = partiallySpecializeType(type, memberInfo.classType);
type = partiallySpecializeType(type, memberInfo.classType, /* typeClassType */ undefined);
}
// If we're setting a class variable via a write through an object,
@ -6088,6 +6095,7 @@ export function createTypeEvaluator(
const specializedType = partiallySpecializeType(
methodType,
accessMethodClass,
getTypeClassType(),
selfType ? (convertToInstantiable(selfType) as ClassType | TypeVarType) : classType
);
@ -22308,8 +22316,8 @@ export function createTypeEvaluator(
return partiallySpecializeType(
getEffectiveTypeOfSymbol(member.symbol),
member.classType,
/* selfClass */ undefined,
typeClass && isInstantiableClass(typeClass) ? typeClass : undefined
getTypeClassType(),
/* selfClass */ undefined
);
}
return UnknownType.create();
@ -22365,6 +22373,7 @@ export function createTypeEvaluator(
const specializedType = partiallySpecializeType(
typeResult.type,
member.unspecializedClassType,
getTypeClassType(),
selfSpecializeClass(selfClass, { overrideTypeArgs: true })
);
@ -22386,7 +22395,7 @@ export function createTypeEvaluator(
}
return {
type: partiallySpecializeType(typeResult.type, member.classType, selfClass),
type: partiallySpecializeType(typeResult.type, member.classType, getTypeClassType(), selfClass),
isIncomplete: !!typeResult.isIncomplete,
};
}
@ -22637,7 +22646,7 @@ export function createTypeEvaluator(
let destMemberType = getEffectiveTypeOfSymbol(symbol);
const srcMemberType = getTypeOfMember(memberInfo);
destMemberType = partiallySpecializeType(destMemberType, destType);
destMemberType = partiallySpecializeType(destMemberType, destType, getTypeClassType());
// Properties require special processing.
if (
@ -27401,6 +27410,7 @@ export function createTypeEvaluator(
getObjectType,
getNoneType,
getUnionClassType,
getTypeClassType,
getBuiltInObject,
getTypingType,
assignTypeArguments,

View File

@ -651,6 +651,7 @@ export interface TypeEvaluator {
getObjectType: () => Type;
getNoneType: () => Type;
getUnionClassType(): Type;
getTypeClassType(): ClassType | undefined;
getTypingType: (node: ParseNode, symbolName: string) => Type | undefined;
inferReturnTypeIfNecessary: (type: Type) => void;
inferTypeParameterVarianceForClass: (type: ClassType) => void;

View File

@ -1446,8 +1446,8 @@ export function isTupleIndexUnambiguous(type: ClassType, index: number) {
export function partiallySpecializeType(
type: Type,
contextClassType: ClassType,
selfClass?: ClassType | TypeVarType,
typeClassType?: ClassType
typeClassType: ClassType | undefined,
selfClass?: ClassType | TypeVarType
): Type {
// If the context class is not specialized (or doesn't need specialization),
// then there's no need to do any more work.
@ -1477,8 +1477,8 @@ export function partiallySpecializeType(
methodType: partiallySpecializeType(
methodInfo.methodType,
contextClassType,
selfClass,
typeClassType
typeClassType,
selfClass
) as FunctionType,
classType: methodInfo.classType,
};
@ -1965,7 +1965,7 @@ export function* getClassIterator(classType: Type, flags = ClassIteratorFlags.De
// If mroClass is an ancestor of classType, partially specialize
// it in the context of classType.
const specializedMroClass = partiallySpecializeType(mroClass, classType);
const specializedMroClass = partiallySpecializeType(mroClass, classType, /* typeClassType */ undefined);
// Should we ignore members on the 'object' base class?
if (flags & ClassIteratorFlags.SkipObjectBaseClass) {
@ -2001,7 +2001,7 @@ export function getClassFieldsRecursive(classType: ClassType): Map<string, Class
// Evaluate the types of members from the end of the MRO to the beginning.
ClassType.getReverseMro(classType).forEach((mroClass) => {
const specializedMroClass = partiallySpecializeType(mroClass, classType);
const specializedMroClass = partiallySpecializeType(mroClass, classType, /* typeClassType */ undefined);
if (isClass(specializedMroClass)) {
ClassType.getSymbolTable(specializedMroClass).forEach((symbol, name) => {

View File

@ -974,7 +974,11 @@ function getTypedDictMembersForClassRecursive(
classType.shared.baseClasses.forEach((baseClassType) => {
if (isInstantiableClass(baseClassType) && ClassType.isTypedDictClass(baseClassType)) {
const specializedBaseClassType = partiallySpecializeType(baseClassType, classType);
const specializedBaseClassType = partiallySpecializeType(
baseClassType,
classType,
evaluator.getTypeClassType()
);
assert(isClass(specializedBaseClassType));
// Recursively gather keys from parent classes. Don't report any errors