Skip to content

Commit

Permalink
Fixed a bug that resulted in a false negative when assigning a `T | N…
Browse files Browse the repository at this point in the history
…one` type to `object | None` in an invariant context. This same bug led to issues with the validation of TypeVar variance within a Protocol. This addresses microsoft/pylance-release#4613. (#5518)

Co-authored-by: Eric Traut <[email protected]>
  • Loading branch information
erictraut and msfterictraut authored Jul 17, 2023
1 parent bfe2128 commit 7fcdc92
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 34 deletions.
70 changes: 36 additions & 34 deletions packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20904,6 +20904,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
});

if (!isAssignable) {
return false;
}

// Now handle generic base classes.
destType.details.baseClasses.forEach((baseClass) => {
if (
Expand Down Expand Up @@ -21652,42 +21656,40 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}

if (isUnion(destType)) {
// If both the source and dest are unions, use assignFromUnionType which has
// special-case logic to handle this case.
if (isUnion(srcType)) {
if (
assignFromUnionType(
destType,
srcType,
/* diag */ undefined,
destTypeVarContext,
srcTypeVarContext,
originalFlags,
recursionCount
)
) {
return true;
return assignFromUnionType(
destType,
srcType,
/* diag */ undefined,
destTypeVarContext,
srcTypeVarContext,
originalFlags,
recursionCount
);
}

const clonedDestTypeVarContext = destTypeVarContext?.clone();
const clonedSrcTypeVarContext = srcTypeVarContext?.clone();
if (
assignToUnionType(
destType,
srcType,
/* diag */ undefined,
clonedDestTypeVarContext,
clonedSrcTypeVarContext,
originalFlags,
recursionCount
)
) {
if (destTypeVarContext && clonedDestTypeVarContext) {
destTypeVarContext.copyFromClone(clonedDestTypeVarContext);
}
} else {
const clonedDestTypeVarContext = destTypeVarContext?.clone();
const clonedSrcTypeVarContext = srcTypeVarContext?.clone();
if (
assignToUnionType(
destType,
srcType,
/* diag */ undefined,
clonedDestTypeVarContext,
clonedSrcTypeVarContext,
originalFlags,
recursionCount
)
) {
if (destTypeVarContext && clonedDestTypeVarContext) {
destTypeVarContext.copyFromClone(clonedDestTypeVarContext);
}
if (srcTypeVarContext && clonedSrcTypeVarContext) {
srcTypeVarContext.copyFromClone(clonedSrcTypeVarContext);
}
return true;
if (srcTypeVarContext && clonedSrcTypeVarContext) {
srcTypeVarContext.copyFromClone(clonedSrcTypeVarContext);
}
return true;
}
}

Expand Down Expand Up @@ -22429,7 +22431,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
!isSubtypeSubsumed &&
!assignType(
destType,
concreteSubtype,
subtype,
diag?.createAddendum(),
destTypeVarContext,
srcTypeVarContext,
Expand Down
4 changes: 4 additions & 0 deletions packages/pyright-internal/src/tests/samples/protocol17.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,7 @@ def prop1(self) -> _T1_co:
class Protocol10(Protocol[_T1_co]):
def m1(self) -> type[_T1_co]:
...


class Protocol11(Protocol[_T1]):
x: _T1 | None

0 comments on commit 7fcdc92

Please sign in to comment.