Fixed recent regression that results in a false positive under certain specific circumstances involving higher-order functions that return generic callable types. This addresses #8852. (#8892)

This commit is contained in:
Eric Traut 2024-09-04 10:55:00 -07:00 committed by GitHub
parent bf2c2bd7c5
commit 56183a3de4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 41 additions and 5 deletions

View File

@ -2158,8 +2158,9 @@ export function synthesizeTypeVarForSelfCls(classType: ClassType, isClsParam: bo
const scopeId = getTypeVarScopeId(classType) ?? '';
selfType.shared.isSynthesized = true;
selfType.shared.isSynthesizedSelf = true;
selfType.priv.nameWithScope = TypeVarType.makeNameWithScope(selfType.shared.name, scopeId);
selfType.priv.scopeId = scopeId;
selfType.priv.scopeName = '';
selfType.priv.nameWithScope = TypeVarType.makeNameWithScope(selfType.shared.name, scopeId, selfType.priv.scopeName);
const boundType = ClassType.specialize(
classType,

View File

@ -2854,7 +2854,11 @@ export namespace TypeVarType {
newInstance.shared.name = name;
if (newInstance.priv.scopeId) {
newInstance.priv.nameWithScope = makeNameWithScope(name, newInstance.priv.scopeId);
newInstance.priv.nameWithScope = makeNameWithScope(
name,
newInstance.priv.scopeId,
newInstance.priv.scopeName ?? ''
);
}
return newInstance;
@ -2867,7 +2871,7 @@ export namespace TypeVarType {
scopeType: TypeVarScopeType | undefined
): TypeVarType {
const newInstance = TypeBase.cloneType(type);
newInstance.priv.nameWithScope = makeNameWithScope(type.shared.name, scopeId);
newInstance.priv.nameWithScope = makeNameWithScope(type.shared.name, scopeId, scopeName ?? '');
newInstance.priv.scopeId = scopeId;
newInstance.priv.scopeName = scopeName;
newInstance.priv.scopeType = scopeType;
@ -2957,8 +2961,12 @@ export namespace TypeVarType {
return newInstance;
}
export function makeNameWithScope(name: string, scopeId: string) {
return `${name}.${scopeId}`;
export function makeNameWithScope(name: string, scopeId: string, scopeName: string) {
// We include the scopeName here even though it's normally already part
// of the scopeId. There are cases where it can diverge, specifically
// in scenarios involving higher-order functions that return generic
// callable types. See adjustCallableReturnType for details.
return `${name}.${scopeId}.${scopeName}`;
}
// When solving the TypeVars for a callable, we need to distinguish between

View File

@ -0,0 +1,21 @@
# This sample tests a particular situation that regressed.
from typing import Any, Generic, TypeVar, TypeVarTuple, Callable
D = TypeVar("D")
S = TypeVarTuple("S")
class N(Generic[*S, D]): ...
def func1[*S1, D1, *S2, D2, Dim1](
c: Callable[[N[*S1, D1], N[*S2, D2]], Any],
) -> Callable[[N[Dim1, *S1, D1], N[Dim1, *S2, D2]], Any]: ...
def func2[X, Y, Z](x: N[X, Y, Z], y: N[X, Y, Z]):
func1(func3)(x, y)
def func3[Dim1, T](x: N[Dim1, T], y: N[Dim1, T]) -> N[T]: ...

View File

@ -909,6 +909,12 @@ test('SolverHigherOrder12', () => {
TestUtils.validateResults(analysisResults, 0);
});
test('SolverHigherOrder13', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['solverHigherOrder13.py']);
TestUtils.validateResults(analysisResults, 0);
});
test('SolverLiteral1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['solverLiteral1.py']);