diff --git a/mypy/checker.py b/mypy/checker.py index 33705c98e10c..58f1feb8dd9c 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3398,9 +3398,21 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: s.is_final_def and s.type and not has_no_typevars(s.type) - and self.scope.active_class() is not None + and (active_class := self.scope.active_class()) is not None ): - self.fail(message_registry.DEPENDENT_FINAL_IN_CLASS_BODY, s) + # Final[T] instance fields in dataclasses are not class attributes, so + # they are allowed to depend on type variables (PEP 591 / dataclass spec). + # Only suppress the error when the field is a dataclass instance field + # (not explicitly ClassVar). + lv = s.lvalues[0] if s.lvalues else None + is_dataclass_instance_field = ( + "dataclass_tag" in active_class.metadata + and isinstance(lv, RefExpr) + and isinstance(lv.node, Var) + and not lv.node.is_classvar + ) + if not is_dataclass_instance_field: + self.fail(message_registry.DEPENDENT_FINAL_IN_CLASS_BODY, s) if s.unanalyzed_type and not self.in_checked_function(): self.msg.annotation_in_unchecked_function(context=s) diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index 8608962e00a4..86e50bff3892 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -1322,3 +1322,35 @@ class S9(S2, S4): # E: Class "S9" has incompatible disjoint bases pass [builtins fixtures/tuple.pyi] + +[case testFinalDefiningTypevarsDataclassInstanceField] +# Regression test for https://github.com/python/mypy/issues/21637 +# Final[T] instance fields in dataclasses must NOT trigger the +# "cannot depend on type variables" error, because they are instance +# attributes, not class attributes. +from dataclasses import dataclass +from typing import Final, Generic, TypeVar + +T = TypeVar("T") + +@dataclass(frozen=True) +class A(Generic[T]): + value: Final[T] # OK — instance field in a dataclass + +@dataclass(frozen=True) +class B(Generic[T]): + value: T # OK — control case without Final +[builtins fixtures/dataclasses.pyi] +[typing fixtures/typing-medium.pyi] + +[case testFinalDefiningTypevarsNonDataclassStillErrors] +# Final[T] in a plain (non-dataclass) generic class body must still raise the error. +from typing import Any, Final, Generic, TypeVar + +T = TypeVar("T") +d: Any + +class C(Generic[T]): + x: Final[T] = d # E: Final name declared in class body cannot depend on type variables +[builtins fixtures/dataclasses.pyi] +[typing fixtures/typing-medium.pyi]