diff --git a/mypy/erasetype.py b/mypy/erasetype.py index 9e8dc42fb924..cb8d66f292dd 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -246,10 +246,6 @@ class LastKnownValueEraser(TypeTranslator): def visit_instance(self, t: Instance) -> Type: if not t.last_known_value and not t.args: return t - if t.last_known_value is not None and t.last_known_value.is_sentinel_literal(): - # Sentinel values (PEP 661) have no other way to identify themselves than - # via their literal, unlike e.g. enum members, so it must be preserved. - return t return t.copy_modified(args=[a.accept(self) for a in t.args], last_known_value=None) def visit_type_alias_type(self, t: TypeAliasType) -> Type: diff --git a/mypy/expandtype.py b/mypy/expandtype.py index d98ab582cc83..fd507216a6be 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -244,10 +244,6 @@ def visit_type_var(self, t: TypeVarType) -> Type: t = t.copy_modified(upper_bound=t.upper_bound.accept(self)) repl = self.variables.get(t.id, t) if isinstance(repl, ProperType) and isinstance(repl, Instance): - if repl.last_known_value is not None and repl.last_known_value.is_sentinel_literal(): - # Sentinel values (PEP 661) have no other way to identify themselves than - # via their literal, unlike e.g. enum members, so it must survive expansion. - return repl # TODO: do we really need to do this? # If I try to remove this special-casing ~40 tests fail on reveal_type(). return repl.copy_modified(last_known_value=None) diff --git a/mypy/messages.py b/mypy/messages.py index 6103ecfac4c6..b58c9e7ac4b6 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2712,12 +2712,6 @@ def format_literal_value(typ: LiteralType) -> str: if isinstance(typ, Instance): itype = typ - if itype.last_known_value is not None and itype.last_known_value.is_sentinel_literal(): - # Sentinel values (PEP 661) have no other way to identify themselves - # than via their literal, so use it instead of the shared fallback - # class name (unlike other literals, sentinels are always formatted - # this way, e.g. "MISSING" rather than "Literal[MISSING]"). - return format_literal_value(itype.last_known_value) # Get the short name of the type. if itype.type.fullname == "types.ModuleType": # Make some common error messages simpler and tidier. @@ -3531,12 +3525,6 @@ def ignore_last_known_values(t: UnionType) -> Type: seen_instances = set() for item in t.items: if isinstance(item, ProperType) and isinstance(item, Instance): - if item.last_known_value is not None and item.last_known_value.is_sentinel_literal(): - # Sentinel values (PEP 661) have no other way to identify themselves - # than via their literal, unlike e.g. enum members, so it must be - # preserved (see mypy/erasetype.py for the same exemption). - union_items.append(item) - continue erased = item.copy_modified(last_known_value=None) if erased in seen_instances: continue diff --git a/mypy/semanal.py b/mypy/semanal.py index 7f961687a8ae..cd1b0a738974 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3422,13 +3422,14 @@ def setup_sentinel_var(self, s: AssignmentStmt) -> None: assert isinstance(lvalue, NameExpr) if not isinstance(lvalue.node, Var): return + lvalue.is_special_form = True var = lvalue.node var.is_sentinel = True typ = self.sentinel_type_for_var(var, s.rvalue) if typ is not None: s.type = typ - def sentinel_type_for_var(self, var: Var, rvalue: Expression) -> Instance | None: + def sentinel_type_for_var(self, var: Var, rvalue: Expression) -> LiteralType | None: assert isinstance(rvalue, CallExpr) callee = rvalue.callee assert isinstance(callee, RefExpr) @@ -3436,13 +3437,8 @@ def sentinel_type_for_var(self, var: Var, rvalue: Expression) -> Instance | None if typ is None: return None name = f"{self.type.name}.{var.name}" if self.type is not None else var.name - return typ.copy_modified( - last_known_value=LiteralType( - SentinelValue(var.fullname, name), - fallback=typ, - line=rvalue.line, - column=rvalue.column, - ) + return LiteralType( + SentinelValue(var.fullname, name), fallback=typ, line=rvalue.line, column=rvalue.column ) def analyze_identity_global_assignment(self, s: AssignmentStmt) -> bool: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 3e493502d0da..8d500c54364a 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1060,13 +1060,10 @@ def analyze_unbound_type_without_type_info( if isinstance(sym.node, Var) and sym.node.is_sentinel: typ = get_proper_type(sym.node.type) - if isinstance(typ, Instance) and typ.last_known_value is not None: - return LiteralType( - value=typ.last_known_value.value, - fallback=typ.last_known_value.fallback, - line=t.line, - column=t.column, - ) + assert isinstance(typ, LiteralType) + return LiteralType( + value=typ.value, fallback=typ.fallback, line=t.line, column=t.column + ) # None of the above options worked. We parse the args (if there are any) # to make sure there are no remaining semanal-only types, then give up. diff --git a/test-data/unit/check-sentinels.test b/test-data/unit/check-sentinels.test index 6bc8013cfa2f..7a85cda95e81 100644 --- a/test-data/unit/check-sentinels.test +++ b/test-data/unit/check-sentinels.test @@ -224,8 +224,7 @@ X = sentinel("X") def inspect_sentinel(arg: sentinel) -> None: ... -reveal_type(X) # N: Revealed type is "X?" +reveal_type(X) # N: Revealed type is "X" reveal_type(inspect_sentinel) # N: Revealed type is "def (arg: typing_extensions.Sentinel)" inspect_sentinel(arg=X) - [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/deps-types.test b/test-data/unit/deps-types.test index f4c20a7664ae..ae2c851b299c 100644 --- a/test-data/unit/deps-types.test +++ b/test-data/unit/deps-types.test @@ -1036,4 +1036,4 @@ def f(x: MISSING) -> None: pass [builtins fixtures/tuple.pyi] [out] - -> , , m, m.f + -> , m, m.f