Further simplified and cleaned up type var application code. (#8605)

This commit is contained in:
Eric Traut 2024-07-30 11:39:31 -07:00 committed by GitHub
parent 6913347aa8
commit 8b5c1da2b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 144 additions and 90 deletions

View File

@ -95,7 +95,6 @@ export function assignTypeToTypeVar(
console.log(`${indent}destType: ${evaluator.printType(destType)}`);
console.log(`${indent}srcType: ${evaluator.printType(srcType)}`);
console.log(`${indent}flags: ${flags}`);
console.log(`${indent}scopes: ${(typeVarContext.getSolveForScopes() || []).join(', ')}`);
logTypeVarContext(evaluator, typeVarContext, indent);
}

View File

@ -122,7 +122,9 @@ export function validateConstructorArgs(
const aliasInfo = type.props?.typeAliasInfo;
if (aliasInfo?.typeParams && !aliasInfo.typeArgs) {
const typeAliasTypeVarContext = new TypeVarContext(aliasInfo.typeVarScopeId);
type = applySolvedTypeVars(type, typeAliasTypeVarContext, { useDefaultForUnsolved: true }) as ClassType;
type = applySolvedTypeVars(type, typeAliasTypeVarContext, {
replaceUnsolved: { scopeIds: [aliasInfo.typeVarScopeId], tupleClassType: evaluator.getTupleClassType() },
}) as ClassType;
}
const metaclassResult = validateMetaclassCall(
@ -277,7 +279,12 @@ function validateNewAndInitMethods(
newMethodReturnType = applySolvedTypeVars(
ClassType.cloneAsInstance(type),
new TypeVarContext(getTypeVarScopeId(type)),
{ useDefaultForUnsolved: true, tupleClassType: evaluator.getTupleClassType() }
{
replaceUnsolved: {
scopeIds: getTypeVarScopeIds(type) ?? [],
tupleClassType: evaluator.getTupleClassType(),
},
}
) as ClassType;
}
@ -597,7 +604,11 @@ function applyExpectedSubtypeForConstructor(
typeVarContext: TypeVarContext
): Type | undefined {
const specializedType = applySolvedTypeVars(ClassType.cloneAsInstance(type), typeVarContext, {
applyUnificationVars: true,
replaceUnsolved: {
scopeIds: [],
tupleClassType: evaluator.getTupleClassType(),
applyUnificationVars: true,
},
});
if (!evaluator.assignType(expectedSubtype, specializedType)) {
@ -625,7 +636,13 @@ function applyExpectedTypeForConstructor(
// If this isn't a generic type or it's a type that has already been
// explicitly specialized, the expected type isn't applicable.
if (type.shared.typeParams.length === 0 || type.priv.typeArgs) {
return applySolvedTypeVars(ClassType.cloneAsInstance(type), typeVarContext, { applyUnificationVars: true });
return applySolvedTypeVars(ClassType.cloneAsInstance(type), typeVarContext, {
replaceUnsolved: {
scopeIds: [],
tupleClassType: evaluator.getTupleClassType(),
applyUnificationVars: true,
},
});
}
if (inferenceContext) {
@ -646,8 +663,12 @@ function applyExpectedTypeForConstructor(
}
const specializedType = applySolvedTypeVars(type, typeVarContext, {
useDefaultForUnsolved: defaultIfNotFound,
tupleClassType: evaluator.getTupleClassType(),
replaceUnsolved: defaultIfNotFound
? {
scopeIds: getTypeVarScopeIds(type) ?? [],
tupleClassType: evaluator.getTupleClassType(),
}
: undefined,
}) as ClassType;
return ClassType.cloneAsInstance(specializedType);
}
@ -939,8 +960,10 @@ function createFunctionFromInitMethod(
});
returnType = applySolvedTypeVars(objectType, typeVarContext, {
useDefaultForUnsolved: true,
tupleClassType: evaluator.getTupleClassType(),
replaceUnsolved: {
scopeIds: getTypeVarScopeIds(objectType) ?? [],
tupleClassType: evaluator.getTupleClassType(),
},
}) as ClassType;
}
}

View File

@ -824,8 +824,10 @@ function getConverterInputType(
if (evaluator.assignType(targetFunction, signature, diagAddendum, inputTypeVarContext)) {
const overloadSolution = applySolvedTypeVars(typeVar, inputTypeVarContext, {
useDefaultForUnsolved: true,
tupleClassType: evaluator.getTupleClassType(),
replaceUnsolved: {
scopeIds: getTypeVarScopeIds(typeVar) ?? [],
tupleClassType: evaluator.getTupleClassType(),
},
});
acceptedTypes.push(overloadSolution);
}

View File

@ -46,6 +46,7 @@ import {
doForEachSubtype,
getTypeCondition,
getTypeVarScopeId,
getTypeVarScopeIds,
getUnknownTypeForCallable,
isLiteralType,
isLiteralTypeOrUnion,
@ -966,8 +967,10 @@ function narrowTypeBasedOnClassPattern(
)
) {
resultType = applySolvedTypeVars(matchTypeInstance, typeVarContext, {
useDefaultForUnsolved: true,
tupleClassType: evaluator.getTupleClassType(),
replaceUnsolved: {
scopeIds: getTypeVarScopeIds(unexpandedSubtype) ?? [],
tupleClassType: evaluator.getTupleClassType(),
},
}) as ClassType;
}
}

View File

@ -4963,8 +4963,10 @@ export function createTypeEvaluator(
let defaultType: Type;
if (param.shared.isDefaultExplicit || isParamSpec(param)) {
defaultType = applySolvedTypeVars(param, typeVarContext, {
useDefaultForUnsolved: true,
tupleClassType: getTupleClassType(),
replaceUnsolved: {
scopeIds: [aliasInfo.typeVarScopeId],
tupleClassType: getTupleClassType(),
},
});
} else if (isTypeVarTuple(param) && tupleClass && isInstantiableClass(tupleClass)) {
defaultType = makeTupleObject(
@ -4991,8 +4993,10 @@ export function createTypeEvaluator(
type = TypeBase.cloneForTypeAlias(
applySolvedTypeVars(type, typeVarContext, {
useDefaultForUnsolved: true,
tupleClassType: getTupleClassType(),
replaceUnsolved: {
scopeIds: [aliasInfo.typeVarScopeId],
tupleClassType: getTupleClassType(),
},
}),
aliasInfo.name,
aliasInfo.fullName,
@ -6932,8 +6936,10 @@ export function createTypeEvaluator(
typeArgType = convertToInstance(typeArgs[index].type);
} else if (param.shared.isDefaultExplicit) {
typeArgType = applySolvedTypeVars(param, typeVarContext, {
useDefaultForUnsolved: true,
tupleClassType: getTupleClassType(),
replaceUnsolved: {
scopeIds: [aliasInfo.typeVarScopeId],
tupleClassType: getTupleClassType(),
},
});
} else {
typeArgType = UnknownType.create();
@ -11333,8 +11339,11 @@ export function createTypeEvaluator(
});
expectedType = applySolvedTypeVars(genericReturnType, tempTypeVarContext, {
useUnknownForUnsolved: true,
tupleClassType: getTupleClassType(),
replaceUnsolved: {
scopeIds: getTypeVarScopeIds(returnType) ?? [],
useUnknown: true,
tupleClassType: getTupleClassType(),
},
});
assignFlags |= AssignTypeFlags.SkipPopulateUnknownExpectedType;
@ -11581,11 +11590,13 @@ export function createTypeEvaluator(
}
let specializedReturnType = applySolvedTypeVars(returnType, typeVarContext, {
useDefaultForUnsolved: true,
tupleClassType: getTupleClassType(),
unsolvedExemptTypeVars: getUnknownExemptTypeVarsForReturnType(type, returnType),
eliminateUnsolvedInUnions,
applyUnificationVars: true,
replaceUnsolved: {
scopeIds: getTypeVarScopeIds(type) ?? [],
unsolvedExemptTypeVars: getUnknownExemptTypeVarsForReturnType(type, returnType),
tupleClassType: getTupleClassType(),
eliminateUnsolvedInUnions,
applyUnificationVars: true,
},
});
specializedReturnType = addConditionToType(specializedReturnType, typeCondition);
@ -12372,8 +12383,10 @@ export function createTypeEvaluator(
const typeVarContext = new TypeVarContext(typeVar.priv.scopeId);
const concreteDefaultType = makeTopLevelTypeVarsConcrete(
applySolvedTypeVars(typeVar.shared.defaultType, typeVarContext, {
useDefaultForUnsolved: true,
tupleClassType: getTupleClassType(),
replaceUnsolved: {
scopeIds: getTypeVarScopeIds(typeVar) ?? [],
tupleClassType: getTupleClassType(),
},
})
);
@ -14046,7 +14059,13 @@ export function createTypeEvaluator(
}
return mapSubtypes(
applySolvedTypeVars(inferenceContext.expectedType, typeVarContext, { applyUnificationVars: true }),
applySolvedTypeVars(inferenceContext.expectedType, typeVarContext, {
replaceUnsolved: {
scopeIds: [],
tupleClassType: getTupleClassType(),
applyUnificationVars: true,
},
}),
(subtype) => {
if (entryTypes.length !== 1) {
return subtype;
@ -14346,7 +14365,11 @@ export function createTypeEvaluator(
)
) {
functionType = applySolvedTypeVars(functionType, typeVarContext, {
applyUnificationVars: true,
replaceUnsolved: {
scopeIds: [],
tupleClassType: getTupleClassType(),
applyUnificationVars: true,
},
}) as FunctionType;
}
}
@ -18301,17 +18324,22 @@ export function createTypeEvaluator(
// If the parameter type is generic, specialize it in the context
// of the child class.
if (requiresSpecialization(inferredParamType) && isClass(baseClassMemberInfo.classType)) {
const scopeIds: TypeVarScopeId[] =
getTypeVarScopeIds(baseClassMemberInfo.classType) ?? [];
const typeVarContext = buildTypeVarContextFromSpecializedClass(
baseClassMemberInfo.classType
);
// Add the scope of the method to handle any function-scoped TypeVars.
typeVarContext.addSolveForScope(ParseTreeUtils.getScopeIdForNode(baseClassMethodNode));
scopeIds.push(ParseTreeUtils.getScopeIdForNode(baseClassMethodNode));
// Replace any unsolved TypeVars with Unknown (including all function-scoped TypeVars).
inferredParamType = applySolvedTypeVars(inferredParamType, typeVarContext, {
useDefaultForUnsolved: true,
tupleClassType: getTupleClassType(),
replaceUnsolved: {
scopeIds,
tupleClassType: getTupleClassType(),
},
});
}
@ -20366,8 +20394,10 @@ export function createTypeEvaluator(
}
const solvedDefaultType = applySolvedTypeVars(typeParam, typeVarContext, {
useDefaultForUnsolved: true,
tupleClassType: getTupleClassType(),
replaceUnsolved: {
scopeIds: getTypeVarScopeIds(classType) ?? [],
tupleClassType: getTupleClassType(),
},
});
typeArgTypes.push(solvedDefaultType);
setTypeVarType(typeVarContext, typeParam, solvedDefaultType);

View File

@ -75,6 +75,7 @@ import {
getSpecializedTupleType,
getTypeCondition,
getTypeVarScopeId,
getTypeVarScopeIds,
getUnknownTypeForCallable,
isInstantiableMetaclass,
isLiteralType,
@ -1485,8 +1486,11 @@ function narrowTypeForIsInstanceInternal(
unspecializedFilterType,
typeVarContext,
{
useUnknownForUnsolved: true,
tupleClassType: evaluator.getTupleClassType(),
replaceUnsolved: {
scopeIds: getTypeVarScopeIds(filterType) ?? [],
useUnknown: true,
tupleClassType: evaluator.getTupleClassType(),
},
}
) as ClassType;
}

View File

@ -243,13 +243,15 @@ export const enum AssignTypeFlags {
export interface ApplyTypeVarOptions {
typeClassType?: ClassType;
tupleClassType?: ClassType;
useDefaultForUnsolved?: boolean;
useUnknownForUnsolved?: boolean;
unsolvedExemptTypeVars?: TypeVarType[];
replaceUnsolved?: {
scopeIds: TypeVarScopeId[];
tupleClassType: ClassType | undefined;
unsolvedExemptTypeVars?: TypeVarType[];
useUnknown?: boolean;
eliminateUnsolvedInUnions?: boolean;
applyUnificationVars?: boolean;
};
useLowerBoundOnly?: boolean;
eliminateUnsolvedInUnions?: boolean;
applyUnificationVars?: boolean;
}
export interface InferenceContext {
@ -1540,17 +1542,11 @@ export function applySolvedTypeVars(
options: ApplyTypeVarOptions = {}
): Type {
// Use a shortcut if the typeVarContext is empty and no transform is necessary.
if (
typeVarContext.isEmpty() &&
!options.useDefaultForUnsolved &&
!options.useUnknownForUnsolved &&
!options.eliminateUnsolvedInUnions &&
!options.applyUnificationVars
) {
if (typeVarContext.isEmpty() && !options.replaceUnsolved) {
return type;
}
if (options.applyUnificationVars) {
if (options.replaceUnsolved?.applyUnificationVars) {
applyUnificationVars(typeVarContext);
}
@ -4268,9 +4264,12 @@ class ApplySolvedTypeVarsTransformer extends TypeVarTransformer {
}
if (subtype.shared.typeParams && !subtype.priv.typeArgs) {
if (this._options.useDefaultForUnsolved || this._options.useUnknownForUnsolved) {
return this._options.useUnknownForUnsolved
? specializeWithUnknownTypeArgs(subtype, this._options.tupleClassType)
if (this._options.replaceUnsolved) {
return this._options.replaceUnsolved.useUnknown
? specializeWithUnknownTypeArgs(
subtype,
this._options.replaceUnsolved.tupleClassType
)
: specializeWithDefaultTypeArgs(subtype);
}
}
@ -4299,7 +4298,7 @@ class ApplySolvedTypeVarsTransformer extends TypeVarTransformer {
return replacement;
}
if (!this._options.useDefaultForUnsolved && !this._options.useUnknownForUnsolved) {
if (!this._options.replaceUnsolved) {
return replacement;
}
}
@ -4308,24 +4307,12 @@ class ApplySolvedTypeVarsTransformer extends TypeVarTransformer {
return undefined;
}
// If this typeVar is in scope for what we're solving but the type
// var map doesn't contain any entry for it, replace with the
// default or Unknown.
let useDefaultOrUnknown = false;
if (this._options.useDefaultForUnsolved || this._options.useUnknownForUnsolved) {
useDefaultOrUnknown = true;
} else if (this._options.applyUnificationVars && typeVar.priv.isUnificationVar) {
useDefaultOrUnknown = true;
// Use the default value if there is one.
if (typeVar.shared.isDefaultExplicit && !this._options.replaceUnsolved?.useUnknown) {
return this._solveDefaultType(typeVar, recursionCount);
}
if (useDefaultOrUnknown) {
// Use the default value if there is one.
if (typeVar.shared.isDefaultExplicit && !this._options.useUnknownForUnsolved) {
return this._solveDefaultType(typeVar, recursionCount);
}
return getUnknownForTypeVar(typeVar, this._options.tupleClassType);
}
return getUnknownForTypeVar(typeVar, this._options.replaceUnsolved?.tupleClassType);
}
// If we're solving a default type, handle type variables with no scope ID.
@ -4354,7 +4341,7 @@ class ApplySolvedTypeVarsTransformer extends TypeVarTransformer {
// in cases where TypeVars can go unsolved due to unions in parameter
// annotations, like this:
// def test(x: Union[str, T]) -> Union[str, T]
if (this._options.eliminateUnsolvedInUnions) {
if (this._options.replaceUnsolved?.eliminateUnsolvedInUnions) {
if (
isTypeVar(preTransform) &&
this._shouldReplaceTypeVar(preTransform) &&
@ -4374,7 +4361,7 @@ class ApplySolvedTypeVarsTransformer extends TypeVarTransformer {
// If useDefaultForUnsolved or useUnknownForUnsolved is true, the postTransform type will
// be Unknown, which we want to eliminate.
if (this._options.useDefaultForUnsolved || this._options.useUnknownForUnsolved) {
if (this._options.replaceUnsolved) {
if (isUnknown(postTransform)) {
return undefined;
}
@ -4438,24 +4425,13 @@ class ApplySolvedTypeVarsTransformer extends TypeVarTransformer {
return undefined;
}
let useDefaultOrUnknown = false;
if (this._options.useDefaultForUnsolved || this._options.useUnknownForUnsolved) {
useDefaultOrUnknown = true;
} else if (this._options.applyUnificationVars && paramSpec.priv.isUnificationVar) {
useDefaultOrUnknown = true;
// Use the default value if there is one.
if (paramSpec.shared.isDefaultExplicit && !this._options.replaceUnsolved?.useUnknown) {
return convertTypeToParamSpecValue(this._solveDefaultType(paramSpec, recursionCount));
}
if (useDefaultOrUnknown) {
// Use the default value if there is one.
if (paramSpec.shared.isDefaultExplicit && !this._options.useUnknownForUnsolved) {
return convertTypeToParamSpecValue(this._solveDefaultType(paramSpec, recursionCount));
}
// Convert to the ParamSpec equivalent of "Unknown".
return ParamSpecType.getUnknown();
}
return undefined;
// Convert to the ParamSpec equivalent of "Unknown".
return ParamSpecType.getUnknown();
}
override transformConditionalType(type: Type, recursionCount: number): Type {
@ -4531,18 +4507,35 @@ class ApplySolvedTypeVarsTransformer extends TypeVarTransformer {
}
private _shouldReplaceUnsolvedTypeVar(typeVar: TypeVarType): boolean {
// Never replace nested TypeVars with unknown.
if (this.pendingTypeVarTransformations.size > 0) {
return false;
}
const exemptTypeVars = this._options.unsolvedExemptTypeVars;
if (!typeVar.priv.scopeId) {
return false;
}
if (!this._options.replaceUnsolved) {
return false;
}
if (TypeVarType.isUnification(typeVar) && this._options.replaceUnsolved.applyUnificationVars) {
return true;
}
if (!this._options.replaceUnsolved.scopeIds.includes(typeVar.priv.scopeId)) {
return false;
}
const exemptTypeVars = this._options.replaceUnsolved?.unsolvedExemptTypeVars;
if (exemptTypeVars) {
if (exemptTypeVars.some((t) => isTypeSame(t, typeVar, { ignoreTypeFlags: true }))) {
return false;
}
}
return this._typeVarContext.hasSolveForScope(typeVar.priv.scopeId);
return true;
}
private _solveDefaultType(typeVar: TypeVarType, recursionCount: number) {