diff --git a/packages/pyright-internal/src/analyzer/operations.ts b/packages/pyright-internal/src/analyzer/operations.ts index d4ed00b39d5c..83e85af395e0 100644 --- a/packages/pyright-internal/src/analyzer/operations.ts +++ b/packages/pyright-internal/src/analyzer/operations.ts @@ -36,7 +36,7 @@ import { isOptionalType, isTupleClass, isUnboundedTupleClass, - isUnionableType, + isUnionable, lookUpClassMember, makeInferenceContext, mapSubtypes, @@ -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); } diff --git a/packages/pyright-internal/src/analyzer/typeUtils.ts b/packages/pyright-internal/src/analyzer/typeUtils.ts index 0d50ad5d2e7b..f84c2dc62fc6 100644 --- a/packages/pyright-internal/src/analyzer/typeUtils.ts +++ b/packages/pyright-internal/src/analyzer/typeUtils.ts @@ -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 { diff --git a/packages/pyright-internal/src/tests/samples/never1.py b/packages/pyright-internal/src/tests/samples/never1.py index 76d7b387afd4..f7db547709b9 100644 --- a/packages/pyright-internal/src/tests/samples/never1.py +++ b/packages/pyright-internal/src/tests/samples/never1.py @@ -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") @@ -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.