Skip to content

Commit

Permalink
Fixed bug that led to an incorrect type evaluation for nested call ex…
Browse files Browse the repository at this point in the history
…pressions where an inner call expression used a ParamSpec. This addresses microsoft#5281. (microsoft#5322)

Co-authored-by: Eric Traut <[email protected]>
  • Loading branch information
erictraut and msfterictraut authored Jun 17, 2023
1 parent b90abab commit 2829429
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 34 deletions.
54 changes: 27 additions & 27 deletions packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1170,7 +1170,16 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
const diag = new DiagnosticAddendum();

// Make sure the resulting type is assignable to the expected type.
if (!assignType(inferenceContext.expectedType, typeResult.type, diag)) {
if (
!assignType(
inferenceContext.expectedType,
typeResult.type,
diag,
/* destTypeVarContext */ undefined,
/* srcTypeVarContext */ undefined,
AssignTypeFlags.IgnoreTypeVarScope
)
) {
typeResult.typeErrors = true;
typeResult.expectedTypeDiagAddendum = diag;
diag.addTextRange(node);
Expand Down Expand Up @@ -23234,9 +23243,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
/* diag */ undefined,
srcTypeVarContext,
destTypeVarContext,
(flags ^ AssignTypeFlags.ReverseTypeVarMatching) |
AssignTypeFlags.IgnoreTypeVarScope |
AssignTypeFlags.RetainLiteralsForTypeVar,
(flags ^ AssignTypeFlags.ReverseTypeVarMatching) | AssignTypeFlags.RetainLiteralsForTypeVar,
recursionCount
);

Expand All @@ -23252,9 +23259,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
/* diag */ undefined,
srcTypeVarContext,
destTypeVarContext,
(flags ^ AssignTypeFlags.ReverseTypeVarMatching) |
AssignTypeFlags.IgnoreTypeVarScope |
AssignTypeFlags.RetainLiteralsForTypeVar,
(flags ^ AssignTypeFlags.ReverseTypeVarMatching) | AssignTypeFlags.RetainLiteralsForTypeVar,
recursionCount
);

Expand Down Expand Up @@ -23979,11 +23984,14 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions

const srcParamSpec = effectiveSrcType.details.paramSpec;
const destParamSpec = effectiveDestType.details.paramSpec;
const targetTypeVarContext =
(flags & AssignTypeFlags.ReverseTypeVarMatching) === 0 ? destTypeVarContext : srcTypeVarContext;

if (targetTypeVarContext.hasSolveForScope(destParamSpec.scopeId)) {
// Synthesize a function based on the remaining parameters.
// If there are remaining parameters and the source and dest do not contain
// the same ParamSpec, synthesize a function for the remaining parameters.
if (
remainingParams.length > 0 ||
!srcParamSpec ||
!isTypeSame(srcParamSpec, destParamSpec, { ignoreTypeFlags: true })
) {
const remainingFunction = FunctionType.createInstance(
'',
'',
Expand All @@ -24000,12 +24008,13 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
: undefined;

if (
!assignTypeToTypeVar(
evaluatorInterface,
!assignType(
destParamSpec,
remainingFunction,
/* diag */ undefined,
targetTypeVarContext
destTypeVarContext,
srcTypeVarContext,
flags
)
) {
// If we couldn't assign the function to the ParamSpec, see if we can
Expand All @@ -24014,27 +24023,18 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (
remainingParams.length > 0 ||
!srcParamSpec ||
!assignTypeToTypeVar(
evaluatorInterface,
!assignType(
destParamSpec,
convertToInstance(srcParamSpec) as TypeVarType,
/* diag */ undefined,
targetTypeVarContext
destTypeVarContext,
srcTypeVarContext,
flags
)
) {
canAssign = false;
}
}
} else {
// If there are any remaining parameters or the source doesn't include the
// dest param spec itself, it is not assignable in this case.
if (
!srcParamSpec ||
!isTypeSame(srcParamSpec, destParamSpec, { ignoreTypeFlags: true }) ||
remainingParams.length > 0
) {
canAssign = false;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,11 @@ def test_5(a: Any, *args: int, b: Any = ...) -> Any:
val3 = test_5(test_5, **{})
reveal_type(
val3,
expected_text="Unknown",
)

val4 = test_5(test_5, b=True)
reveal_type(
val4,
expected_text="Type[list[Overload[(a: Type[(**P(1)@test_5) -> Type[T(1)@test_5]], *, b: Literal[False] | None = ...) -> Type[list[Type[T(1)@test_5]]], (a: T(1)@test_5, *args: int, b: Literal[False] | None = ...) -> Type[list[T(1)@test_5]], (a: T(1)@test_5, *args: int, b: Literal[True] = ...) -> Type[list[T(1)@test_5]], (a: Any, *args: int, b: Any = ...) -> Any]]]",
)
38 changes: 32 additions & 6 deletions packages/pyright-internal/src/tests/samples/genericTypes108.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,48 @@
# This sample tests the handling of nested calls to generic functions
# when bidirectional type inference is involved.

from typing import Literal, TypeVar
from typing import Callable, Generic, Literal, ParamSpec, TypeVar

T = TypeVar("T")
_T = TypeVar("_T")
_P = ParamSpec("_P")


def identity(x: T) -> T:
def identity1(x: _T) -> _T:
return x


def identity2(x: T) -> T:
def identity2(x: _T) -> _T:
return x


def test(x: Literal[2]) -> Literal[2]:
return identity(identity2(x))
def test1(x: Literal[2]) -> Literal[2]:
return identity1(identity2(x))


v1 = min(1, max(2, 0.5))
reveal_type(v1, expected_text="float")


class Future(Generic[_T]):
...


def func1(future: Future[_T]) -> Future[_T]:
...


def func2(__fn: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> Future[_T]:
...


def func3() -> int:
...


def func4(a: int, b: int) -> str:
...


reveal_type(func1(func2(func3)), expected_text="Future[int]")
reveal_type(func1(func2(func4, 1, 2)), expected_text="Future[str]")
reveal_type(func1(func2(func4, a=1, b=2)), expected_text="Future[str]")
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ class A:
...


T = TypeVar("T")
T_A = TypeVar("T_A", bound=A)


Expand Down

0 comments on commit 2829429

Please sign in to comment.