Skip to content

Commit 0d99db9

Browse files
committed
wrapped optionals are covariant in their inner type
1 parent 0ad5eca commit 0d99db9

2 files changed

Lines changed: 19 additions & 0 deletions

File tree

crates/ty_python_semantic/resources/mdtest/basedpython_optional_type.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ def h() -> int???:
6969
reveal_type(h()) # revealed: int???
7070
```
7171

72+
## wrapped optionals are covariant in their inner type
73+
74+
a narrower wrapped optional is assignable to a wider one — `Literal[1]??` to `int??`:
75+
76+
```by
77+
def f[T](t: T) -> T??:
78+
return Some(t)
79+
80+
x: int?? = f(1)
81+
```
82+
7283
## force-unwrap `!` peels one optional layer
7384

7485
`expr!` removes one layer of optionality: a wrapped optional yields the next layer in, and a plain

crates/ty_python_semantic/src/types/relation.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2062,6 +2062,14 @@ impl<'a, 'c, 'db> TypeRelationChecker<'a, 'c, 'db> {
20622062
self.check_type_pair(db, source_form.instance_fallback(db), target)
20632063
}
20642064

2065+
// basedpython: wrapped optionals are covariant in their inner type —
2066+
// `Literal[1]??` is assignable to `int??` because every wrapped
2067+
// value of the former is a wrapped value of the latter
2068+
(
2069+
Type::KnownInstance(KnownInstanceType::WrappedOptional(source_inner)),
2070+
Type::KnownInstance(KnownInstanceType::WrappedOptional(target_inner)),
2071+
) => self.check_type_pair(db, source_inner.inner(db), target_inner.inner(db)),
2072+
20652073
// basedpython: a wrapped optional `WrappedOptional(inner)` accepts
20662074
// any value of its decomposition `inner | None` — the wrapped value
20672075
// itself, or `None` for the absent outer state. (Identical wrapped

0 commit comments

Comments
 (0)