Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/pyright-internal/src/analyzer/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
isOptionalType,
isTupleClass,
isUnboundedTupleClass,
isUnionableType,
isUnionable,
lookUpClassMember,
makeInferenceContext,
mapSubtypes,
Expand Down Expand Up @@ -365,7 +365,7 @@ export function getTypeOfBinaryOperation(
adjustedLeftType = convertToInstantiable(evaluator.getNoneType());
}

if (isUnionableType([adjustedLeftType, adjustedRightType])) {
if (isUnionable(adjustedLeftType, adjustedRightType)) {
if (isInstantiableClass(adjustedLeftType)) {
adjustedLeftType = specializeWithDefaultTypeArgs(adjustedLeftType);
}
Expand Down
27 changes: 11 additions & 16 deletions packages/pyright-internal/src/analyzer/typeUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -846,26 +846,21 @@ export function preserveUnknown(type1: Type, type2: Type): AnyType | UnknownType
}
}

// Determines whether the specified type is a type that can be
// combined with other types for a union.
export function isUnionableType(subtypes: Type[]): boolean {
// If all of the subtypes are TypeForm types, we know that they
// are unionable.
if (subtypes.every((t) => t.props?.typeForm !== undefined)) {
return true;
// Determines whether the two types will produce a union using the "|" operator.
export function isUnionable(leftType: Type, rightType: Type): boolean {
// If the type of LHS is Any (but is not actually the Any symbol), we
// can't determine whether "|" will produce a union.
if (isAnyOrUnknown(leftType) && !leftType.props?.specialForm) {
return false;
}

let typeFlags = TypeFlags.Instance | TypeFlags.Instantiable;

for (const subtype of subtypes) {
typeFlags &= subtype.flags;
// If the subtypes are TypeForm types, we know that they are unionable.
if (leftType.props?.typeForm && rightType.props?.typeForm) {
return true;
}

// All subtypes need to be instantiable. Some types (like Any
// and None) are both instances and instantiable. It's OK to
// include some of these, but at least one subtype needs to
// be definitively instantiable (not an instance).
return (typeFlags & TypeFlags.Instantiable) !== 0 && (typeFlags & TypeFlags.Instance) === 0;
// Are both types instantiable?
return (leftType.flags & rightType.flags & TypeFlags.Instantiable) !== 0;
}

export function derivesFromAnyOrUnknown(type: Type): boolean {
Expand Down
7 changes: 6 additions & 1 deletion packages/pyright-internal/src/tests/samples/never1.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# This sample tests the handling of the "Never" type,
# ensuring that it's treated as the same as NoReturn.

from typing import NoReturn, TypeVar, Generic
from typing import Any, NoReturn, TypeVar, Generic
from typing_extensions import Never # pyright: ignore[reportMissingModuleSource]

T = TypeVar("T")
Expand Down Expand Up @@ -51,6 +51,11 @@ def func4():
assert_never1()


def func5(x: Any | Never, y: Never | Any):
reveal_type(x, expected_text="Any")
reveal_type(y, expected_text="Any")


reveal_type(assert_never1, expected_text="(val: Never) -> NoReturn")

# This should generate an error.
Expand Down
Loading