diff --git a/mypy/checker.py b/mypy/checker.py index 612d2face5b8a..36c9377af0bc8 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1487,7 +1487,8 @@ def check_func_def( # TODO: check recursively for inner type variables if ( arg_type.variance == COVARIANT - and defn.name not in ("__init__", "__new__", "__post_init__") + and defn.name + not in {"__init__", "__new__", "__post_init__", "__replace__"} and not is_private(defn.name) # private methods are not inherited and (i != 0 or not found_self) ): diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 5ac0318421167..3710384934913 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2580,6 +2580,23 @@ Gen(2).__replace__(x="not an int") # E: Argument "x" to "__replace__" of "Gen" [builtins fixtures/tuple.pyi] +[case testDunderReplaceAllowsCovariantDataclassField] +# flags: --python-version 3.13 +from dataclasses import dataclass +from typing import Generic, TypeVar + +T_co = TypeVar("T_co", covariant=True) + +@dataclass(frozen=True) +class A(Generic[T_co]): + value: T_co + +class B(Generic[T_co]): + def __replace__(self, *, value: T_co) -> B[T_co]: + return self + +[builtins fixtures/tuple.pyi] + [case testDunderReplaceCovariantOverride] # flags: --python-version 3.13 --enable-error-code mutable-override from dataclasses import dataclass