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

View File

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

View File

@ -1250,7 +1250,11 @@ function getMappingPatternInfo(evaluator: TypeEvaluator, type: Type, node: Patte
} }
if (mroClassToSpecialize) { 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) { if (specializedMapping.priv.typeArguments && specializedMapping.priv.typeArguments.length >= 2) {
mappingInfo.push({ mappingInfo.push({
@ -1346,7 +1350,11 @@ function getSequencePatternInfo(
} }
if (mroClassToSpecialize) { if (mroClassToSpecialize) {
const specializedSequence = partiallySpecializeType(mroClassToSpecialize, concreteSubtype) as ClassType; const specializedSequence = partiallySpecializeType(
mroClassToSpecialize,
concreteSubtype,
evaluator.getTypeClassType()
) as ClassType;
if (isTupleClass(specializedSequence)) { if (isTupleClass(specializedSequence)) {
const typeArgs = specializedSequence.priv.tupleTypeArguments ?? [ 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 // We can skip this if it's the dest class because it is already
// specialized. // specialized.
if (!ClassType.isSameGenericClass(mroClass, destType)) { if (!ClassType.isSameGenericClass(mroClass, destType)) {
destMemberType = partiallySpecializeType(destMemberType, mroClass, selfType); destMemberType = partiallySpecializeType(
destMemberType,
mroClass,
evaluator.getTypeClassType(),
selfType
);
} }
if (isInstantiableClass(srcMemberInfo.classType)) { if (isInstantiableClass(srcMemberInfo.classType)) {
@ -439,7 +444,12 @@ function assignClassToProtocolInternal(
evaluator.inferReturnTypeIfNecessary(symbolType); evaluator.inferReturnTypeIfNecessary(symbolType);
} }
srcMemberType = partiallySpecializeType(symbolType, srcMemberInfo.classType, selfType); srcMemberType = partiallySpecializeType(
symbolType,
srcMemberInfo.classType,
evaluator.getTypeClassType(),
selfType
);
} else { } else {
srcMemberType = UnknownType.create(); srcMemberType = UnknownType.create();
} }
@ -574,7 +584,7 @@ function assignClassToProtocolInternal(
let getterType = evaluator.getGetterTypeFromProperty(destMemberType, /* inferTypeIfNeeded */ true); let getterType = evaluator.getGetterTypeFromProperty(destMemberType, /* inferTypeIfNeeded */ true);
if (getterType) { if (getterType) {
getterType = partiallySpecializeType(getterType, mroClass); getterType = partiallySpecializeType(getterType, mroClass, evaluator.getTypeClassType());
} }
if ( if (

View File

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

View File

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

View File

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

View File

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