diff --git a/crates/ty_python_semantic/resources/mdtest/assignment/annotations.md b/crates/ty_python_semantic/resources/mdtest/assignment/annotations.md index 36f53afe4d..d8595041fc 100644 --- a/crates/ty_python_semantic/resources/mdtest/assignment/annotations.md +++ b/crates/ty_python_semantic/resources/mdtest/assignment/annotations.md @@ -511,7 +511,7 @@ def f(self, dt: dict[str, Any], key: str): ```toml [environment] -python-version = "3.12" +python-version = "3.14" ``` ```py @@ -550,7 +550,7 @@ g: list[Any] | dict[Any, Any] = f3(1) reveal_type(g) # revealed: list[int] | dict[int, int] ``` -We currently prefer the generic declared type regardless of its variance: +We only prefer the declared type if it is in non-covariant position. ```py class Bivariant[T]: @@ -594,12 +594,22 @@ x6: Covariant[Any] = covariant(1) x7: Contravariant[Any] = contravariant(1) x8: Invariant[Any] = invariant(1) -reveal_type(x5) # revealed: Bivariant[Any] -reveal_type(x6) # revealed: Covariant[Any] +reveal_type(x5) # revealed: Bivariant[Literal[1]] +reveal_type(x6) # revealed: Covariant[Literal[1]] reveal_type(x7) # revealed: Contravariant[Any] reveal_type(x8) # revealed: Invariant[Any] ``` +```py +class X[T]: + def __init__(self: X[None]): ... + def pop(self) -> T: + raise NotImplementedError + +x1: X[int | None] = X() +reveal_type(x1) # revealed: X[None] +``` + ## Narrow generic unions ```toml diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index cd50607c14..b0134a20c3 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -3016,7 +3016,19 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> { tcx.filter_union(self.db, |ty| ty.class_specialization(self.db).is_some()) .class_specialization(self.db)?; - builder.infer(return_ty, tcx).ok()?; + builder + .infer_map(return_ty, tcx, |(_, variance, inferred_ty)| { + // Avoid unnecessarily widening the return type based on a covariant + // type parameter from the type context, as it can lead to argument + // assignability errors if the type variable is constrained by a narrower + // parameter type. + if variance.is_covariant() { + return None; + } + + Some(inferred_ty) + }) + .ok()?; Some(builder.type_mappings().clone()) });