Fixed bug that results in the incorrect default value of a variadic type variable when the type argument is not provided. It should default to *tuple[Any, ...], not *tuple[Any]. This addresses #8285.

This commit is contained in:
Eric Traut 2024-07-01 08:13:44 -07:00
parent 74846de568
commit 21efa0ecf8
7 changed files with 91 additions and 27 deletions

View File

@ -290,7 +290,7 @@ function validateNewAndInitMethods(
newMethodReturnType = applySolvedTypeVars(
ClassType.cloneAsInstance(type),
new TypeVarContext(getTypeVarScopeId(type)),
{ unknownIfNotFound: true }
{ unknownIfNotFound: true, tupleClassType: evaluator.getTupleClassType() }
) as ClassType;
}
@ -824,6 +824,7 @@ function applyExpectedTypeForConstructor(
const specializedType = applySolvedTypeVars(type, typeVarContext, {
unknownIfNotFound: unsolvedTypeVarsAreUnknown,
tupleClassType: evaluator.getTupleClassType(),
}) as ClassType;
return ClassType.cloneAsInstance(specializedType);
}
@ -1118,7 +1119,10 @@ function createFunctionFromInitMethod(
}
});
returnType = applySolvedTypeVars(objectType, typeVarContext, { unknownIfNotFound: true }) as ClassType;
returnType = applySolvedTypeVars(objectType, typeVarContext, {
unknownIfNotFound: true,
tupleClassType: evaluator.getTupleClassType(),
}) as ClassType;
}
}

View File

@ -821,7 +821,10 @@ function getConverterInputType(
const inputTypeVarContext = new TypeVarContext(typeVar.scopeId);
if (evaluator.assignType(targetFunction, signature, diagAddendum, inputTypeVarContext)) {
const overloadSolution = applySolvedTypeVars(typeVar, inputTypeVarContext, { unknownIfNotFound: true });
const overloadSolution = applySolvedTypeVars(typeVar, inputTypeVarContext, {
unknownIfNotFound: true,
tupleClassType: evaluator.getTupleClassType(),
});
acceptedTypes.push(overloadSolution);
}
});

View File

@ -685,7 +685,7 @@ function narrowTypeBasedOnClassPattern(
// specialize it with Unknown type arguments.
if (isClass(exprType) && !exprType.typeAliasInfo) {
exprType = ClassType.cloneRemoveTypePromotions(exprType);
exprType = specializeWithUnknownTypeArgs(exprType);
exprType = specializeWithUnknownTypeArgs(exprType, evaluator.getTupleClassType());
}
// Are there any positional arguments? If so, try to get the mappings for
@ -932,6 +932,7 @@ function narrowTypeBasedOnClassPattern(
) {
resultType = applySolvedTypeVars(matchTypeInstance, typeVarContext, {
unknownIfNotFound: true,
tupleClassType: evaluator.getTupleClassType(),
}) as ClassType;
}
}

View File

@ -4926,7 +4926,10 @@ export function createTypeEvaluator(
let defaultType: Type;
if (param.details.isDefaultExplicit || param.details.isParamSpec) {
defaultType = applySolvedTypeVars(param, typeVarContext, { unknownIfNotFound: true });
defaultType = applySolvedTypeVars(param, typeVarContext, {
unknownIfNotFound: true,
tupleClassType: getTupleClassType(),
});
} else if (param.details.isVariadic && tupleClass && isInstantiableClass(tupleClass)) {
defaultType = makeTupleObject(
[{ type: UnknownType.create(), isUnbounded: true }],
@ -4951,7 +4954,10 @@ export function createTypeEvaluator(
}
type = TypeBase.cloneForTypeAlias(
applySolvedTypeVars(type, typeVarContext, { unknownIfNotFound: true }),
applySolvedTypeVars(type, typeVarContext, {
unknownIfNotFound: true,
tupleClassType: getTupleClassType(),
}),
type.typeAliasInfo.name,
type.typeAliasInfo.fullName,
type.typeAliasInfo.moduleName,
@ -6855,7 +6861,10 @@ export function createTypeEvaluator(
if (index < typeArgs.length) {
typeArgType = convertToInstance(typeArgs[index].type);
} else if (param.details.isDefaultExplicit) {
typeArgType = applySolvedTypeVars(param, typeVarContext, { unknownIfNotFound: true });
typeArgType = applySolvedTypeVars(param, typeVarContext, {
unknownIfNotFound: true,
tupleClassType: getTupleClassType(),
});
} else {
typeArgType = UnknownType.create();
}
@ -11333,6 +11342,7 @@ export function createTypeEvaluator(
effectiveExpectedType = applySolvedTypeVars(genericReturnType, tempTypeVarContext, {
unknownIfNotFound: true,
tupleClassType: getTupleClassType(),
});
effectiveFlags |= AssignTypeFlags.SkipPopulateUnknownExpectedType;
@ -11633,6 +11643,7 @@ export function createTypeEvaluator(
let specializedReturnType = applySolvedTypeVars(returnType, typeVarContext, {
unknownIfNotFound,
tupleClassType: getTupleClassType(),
unknownExemptTypeVars: getUnknownExemptTypeVarsForReturnType(type, returnType),
eliminateUnsolvedInUnions,
applyInScopePlaceholders: true,
@ -12516,6 +12527,7 @@ export function createTypeEvaluator(
const concreteDefaultType = makeTopLevelTypeVarsConcrete(
applySolvedTypeVars(typeVar.details.defaultType, typeVarContext, {
unknownIfNotFound: true,
tupleClassType: getTupleClassType(),
})
);
@ -18334,6 +18346,7 @@ export function createTypeEvaluator(
// Replace any unsolved TypeVars with Unknown (including all function-scoped TypeVars).
inferredParamType = applySolvedTypeVars(inferredParamType, typeVarContext, {
unknownIfNotFound: true,
tupleClassType: getTupleClassType(),
});
}
@ -20388,7 +20401,10 @@ export function createTypeEvaluator(
return;
}
const solvedDefaultType = applySolvedTypeVars(typeParam, typeVarContext, { unknownIfNotFound: true });
const solvedDefaultType = applySolvedTypeVars(typeParam, typeVarContext, {
unknownIfNotFound: true,
tupleClassType: getTupleClassType(),
});
typeArgTypes.push(solvedDefaultType);
if (isParamSpec(typeParam)) {
typeVarContext.setTypeVarType(typeParam, convertTypeToParamSpecValue(solvedDefaultType));
@ -22360,7 +22376,7 @@ export function createTypeEvaluator(
getEffectiveTypeOfSymbol(member.symbol),
member.classType,
/* selfClass */ undefined,
typeClass ?? UnknownType.create()
typeClass && isInstantiableClass(typeClass) ? typeClass : undefined
);
}
return UnknownType.create();

View File

@ -638,7 +638,7 @@ export function getTypeNarrowingCallback(
);
const arg1Type = arg1TypeResult.type;
const classTypeList = getIsInstanceClassTypes(arg1Type);
const classTypeList = getIsInstanceClassTypes(evaluator, arg1Type);
const isIncomplete = !!callTypeResult.isIncomplete || !!arg1TypeResult.isIncomplete;
if (classTypeList) {
@ -1146,7 +1146,10 @@ function narrowTypeForIsEllipsis(evaluator: TypeEvaluator, type: Type, isPositiv
// that accepts a single class, and a more complex form that accepts a tuple
// of classes (including arbitrarily-nested tuples). This method determines
// which form and returns a list of classes or undefined.
function getIsInstanceClassTypes(argType: Type): (ClassType | TypeVarType | FunctionType)[] | undefined {
function getIsInstanceClassTypes(
evaluator: TypeEvaluator,
argType: Type
): (ClassType | TypeVarType | FunctionType)[] | undefined {
let foundNonClassType = false;
const classTypeList: (ClassType | TypeVarType | FunctionType)[] = [];
@ -1155,7 +1158,7 @@ function getIsInstanceClassTypes(argType: Type): (ClassType | TypeVarType | Func
const addClassTypesToList = (types: Type[]) => {
types.forEach((subtype) => {
if (isClass(subtype)) {
subtype = specializeWithUnknownTypeArgs(subtype);
subtype = specializeWithUnknownTypeArgs(subtype, evaluator.getTupleClassType());
if (isInstantiableClass(subtype) && ClassType.isBuiltIn(subtype, 'Callable')) {
subtype = convertToInstantiable(getUnknownTypeForCallable());
@ -1359,7 +1362,8 @@ function narrowTypeForIsInstanceInternal(
concreteFilterType,
/* typeArguments */ undefined,
/* isTypeArgumentExplicit */ false
)
),
evaluator.getTupleClassType()
);
}
@ -1463,7 +1467,11 @@ function narrowTypeForIsInstanceInternal(
specializedFilterType = applySolvedTypeVars(
unspecializedFilterType,
typeVarContext,
{ unknownIfNotFound: true, useUnknownOverDefault: true }
{
unknownIfNotFound: true,
useUnknownOverDefault: true,
tupleClassType: evaluator.getTupleClassType(),
}
) as ClassType;
}
}

View File

@ -245,12 +245,13 @@ export const enum AssignTypeFlags {
}
export interface ApplyTypeVarOptions {
typeClassType?: ClassType;
tupleClassType?: ClassType;
unknownIfNotFound?: boolean;
useUnknownOverDefault?: boolean;
unknownExemptTypeVars?: TypeVarType[];
useNarrowBoundOnly?: boolean;
eliminateUnsolvedInUnions?: boolean;
typeClassType?: Type;
applyInScopePlaceholders?: boolean;
}
@ -1073,7 +1074,7 @@ export function specializeWithDefaultTypeArgs(type: ClassType): ClassType {
// Specializes the class with "Unknown" type args (or the equivalent for ParamSpecs
// or TypeVarTuples).
export function specializeWithUnknownTypeArgs(type: ClassType): ClassType {
export function specializeWithUnknownTypeArgs(type: ClassType, tupleClassType?: ClassType): ClassType {
if (type.details.typeParameters.length === 0) {
return type;
}
@ -1091,18 +1092,22 @@ export function specializeWithUnknownTypeArgs(type: ClassType): ClassType {
return ClassType.cloneForSpecialization(
type,
type.details.typeParameters.map((param) => getUnknownTypeForTypeVar(param)),
type.details.typeParameters.map((param) => getUnknownTypeForTypeVar(param, tupleClassType)),
/* isTypeArgumentExplicit */ false,
/* includeSubclasses */ type.includeSubclasses
);
}
// Returns "Unknown" for simple TypeVars or the equivalent for a ParamSpec.
export function getUnknownTypeForTypeVar(typeVar: TypeVarType): Type {
export function getUnknownTypeForTypeVar(typeVar: TypeVarType, tupleClassType?: ClassType): Type {
if (typeVar.details.isParamSpec) {
return getUnknownTypeForParamSpec();
}
if (typeVar.details.isVariadic && tupleClassType) {
return getUnknownTypeForVariadicTypeVar(tupleClassType);
}
return UnknownType.create();
}
@ -1118,6 +1123,19 @@ export function getUnknownTypeForParamSpec(): FunctionType {
return newFunction;
}
export function getUnknownTypeForVariadicTypeVar(tupleClassType: ClassType): Type {
assert(isInstantiableClass(tupleClassType) && ClassType.isBuiltIn(tupleClassType, 'tuple'));
return ClassType.cloneAsInstance(
specializeTupleClass(
tupleClassType,
[{ type: UnknownType.create(), isUnbounded: true }],
/* isTypeArgumentExplicit */ true,
/* isUnpackedTuple */ true
)
);
}
// Returns the equivalent of "Callable[..., Unknown]".
export function getUnknownTypeForCallable(): FunctionType {
const newFunction = FunctionType.createSynthesizedInstance('', FunctionTypeFlags.GradualCallableForm);
@ -1374,7 +1392,7 @@ export function partiallySpecializeType(
type: Type,
contextClassType: ClassType,
selfClass?: ClassType | TypeVarType,
typeClassType?: Type
typeClassType?: ClassType
): Type {
// If the context class is not specialized (or doesn't need specialization),
// then there's no need to do any more work.
@ -4133,7 +4151,7 @@ class ApplySolvedTypeVarsTransformer extends TypeVarTransformer {
if (this._options.unknownIfNotFound) {
return this._options.useUnknownOverDefault
? specializeWithUnknownTypeArgs(subtype)
? specializeWithUnknownTypeArgs(subtype, this._options.tupleClassType)
: specializeWithDefaultTypeArgs(subtype);
}
}
@ -4171,7 +4189,7 @@ class ApplySolvedTypeVarsTransformer extends TypeVarTransformer {
return this._solveDefaultType(typeVar.details.defaultType, recursionCount);
}
return UnknownType.create();
return getUnknownTypeForTypeVar(typeVar, this._options.tupleClassType);
}
}

View File

@ -18,13 +18,16 @@ class Array(Generic[Unpack[_Xs]]):
reveal_type(args, expected_text="tuple[*_Xs@Array]")
# This should generate an error because _Xs is not unpacked.
def foo(self, *args: _Xs) -> None: ...
def foo(self, *args: _Xs) -> None:
...
def linearize(value: Array[Unpack[_Xs]]) -> tuple[Unpack[_Xs]]: ...
def linearize(value: Array[Unpack[_Xs]]) -> tuple[Unpack[_Xs]]:
...
def array_to_tuple(value: Array[Unpack[_Xs]]) -> tuple[complex, Unpack[_Xs]]: ...
def array_to_tuple(value: Array[Unpack[_Xs]]) -> tuple[complex, Unpack[_Xs]]:
...
def func1(x: Array[int, str, str, float], y: Array[()]):
@ -88,12 +91,23 @@ def func3(x: Array[Unpack[_Xs]]) -> Array[Unpack[_Xs]]:
@overload
def func4(signal: Array[*_Xs], *args: *_Xs) -> None: ...
def func4(signal: Array[*_Xs], *args: *_Xs) -> None:
...
@overload
def func4(signal: str, *args: Any) -> None: ...
def func4(signal: Array[*_Xs] | str, *args: *_Xs) -> None: ...
def func4(signal: str, *args: Any) -> None:
...
def func4(signal: Array[*_Xs] | str, *args: *_Xs) -> None:
...
def func5(a1: Array[Literal["a", "b"]], a2: Array[Literal["a"], Literal["b"]]):
func4(a1, "a")
func4(a2, "a", "b")
def func6(a: Array):
reveal_type(a, expected_text="Array[*tuple[Unknown, ...]]")