Skip to content

Commit 1a6a10b

Browse files
authored
[red-knot] Empty tuple is always-falsy (#17213)
## Summary Fix assignability of `tuple[()]` to `AlwaysFalsy`. closes #17202 ## Test Plan Ran the property tests for a while
1 parent b3243b5 commit 1a6a10b

File tree

2 files changed

+16
-4
lines changed

2 files changed

+16
-4
lines changed

crates/red_knot_python_semantic/resources/mdtest/type_properties/is_assignable_to.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ static_assert(is_assignable_to(Meta, type[Unknown]))
183183
## Tuple types
184184

185185
```py
186-
from knot_extensions import static_assert, is_assignable_to
186+
from knot_extensions import static_assert, is_assignable_to, AlwaysTruthy, AlwaysFalsy
187187
from typing import Literal, Any
188188

189189
static_assert(is_assignable_to(tuple[()], tuple[()]))
@@ -198,6 +198,14 @@ static_assert(is_assignable_to(tuple[()], tuple))
198198
static_assert(is_assignable_to(tuple[int, str], tuple))
199199
static_assert(is_assignable_to(tuple[Any], tuple))
200200

201+
# TODO: It is not yet clear if we want the following two assertions to hold.
202+
# See https://github.com/astral-sh/ruff/issues/15528 for more details. The
203+
# short version is: We either need to special-case enforcement of the Liskov
204+
# substitution principle on `__bool__` and `__len__` for tuple subclasses,
205+
# or we need to negate these assertions.
206+
static_assert(is_assignable_to(tuple[()], AlwaysFalsy))
207+
static_assert(is_assignable_to(tuple[int], AlwaysTruthy))
208+
201209
static_assert(not is_assignable_to(tuple[()], tuple[int]))
202210
static_assert(not is_assignable_to(tuple[int], tuple[str]))
203211
static_assert(not is_assignable_to(tuple[int], tuple[int, str]))

crates/red_knot_python_semantic/src/types.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -1071,9 +1071,13 @@ impl<'db> Type<'db> {
10711071
// This special case is required because the left-hand side tuple might be a
10721072
// gradual type, so we can not rely on subtyping. This allows us to assign e.g.
10731073
// `tuple[Any, int]` to `tuple`.
1074-
(Type::Tuple(_), _) => KnownClass::Tuple
1075-
.to_instance(db)
1076-
.is_assignable_to(db, target),
1074+
(Type::Tuple(_), _)
1075+
if KnownClass::Tuple
1076+
.to_instance(db)
1077+
.is_assignable_to(db, target) =>
1078+
{
1079+
true
1080+
}
10771081

10781082
// `type[Any]` is assignable to any `type[...]` type, because `type[Any]` can
10791083
// materialize to any `type[...]` type.

0 commit comments

Comments
 (0)