Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[red-knot] handle sealed types #12694

Open
2 of 3 tasks
carljm opened this issue Aug 5, 2024 · 2 comments
Open
2 of 3 tasks

[red-knot] handle sealed types #12694

carljm opened this issue Aug 5, 2024 · 2 comments
Labels
red-knot Multi-file analysis & type inference

Comments

@carljm
Copy link
Contributor

carljm commented Aug 5, 2024

Some types have a finite number of possible inhabitants. Examples include builtins.bool (the inhabitants are Literal[True] and Literal[False]) and enum types. For such types, we should consider the union of all possible inhabitants as equivalent to the type itself (so for example Literal[True, False]) is equivalent to builtins.bool). We should also take this equivalence into account when resolving negated intersections (so for example builtins.bool & ~Literal[True] is equivalent to Literal[False]; practically this will come up in type narrowing.)

Tasks here, in the order we'll likely get to them:

  • resolve Literal[True] | Literal[False] to builtins.bool
  • resolve builtins.bool back to Literal[True] | Literal[False] when intersection eliminates a type inhabitant
  • extend the above handling to enum types, when we add them
@carljm carljm added the red-knot Multi-file analysis & type inference label Aug 5, 2024
@AlexWaygood
Copy link
Member

Other examples of sealed types are the various singletons available in builtins and the stdlib:

  • NotImplemented (the only possible instance of types.NotImplementedType)
  • None (the only possible instance of types.NoneType)
  • ... (also aliased as Ellipsis: the only possible instance of types.EllipsisType)
  • typing.NoDefault (opaque; no public type exposed anywhere)

carljm added a commit that referenced this issue Sep 1, 2024
The `UnionBuilder` builds `builtins.bool` when handed `Literal[True]`
and `Literal[False]`.

Caveat: If the builtins module is unfindable somehow, the builder falls
back to the union type of these two literals.

First task from #12694

---------

Co-authored-by: Carl Meyer <[email protected]>
carljm added a commit that referenced this issue Oct 18, 2024
…ons (#13775)

## Summary

- Add `Type::is_disjoint_from` as a way to test whether two types
overlap
- Add a first set of simplification rules for intersection types
  - `S & T = S` for `S <: T`
  - `S & ~T = Never` for `S <: T`
  - `~S & ~T = ~T` for `S <: T`
  - `A & ~B = A` for `A` disjoint from `B`
  - `A & B = Never` for `A` disjoint from `B`
  - `bool & ~Literal[bool] = Literal[!bool]`

resolves one item in #12694

## Open questions:

- Can we somehow leverage the (anti) symmetry between `positive` and
`negative` contributions? I could imagine that there would be a way if
we had `Type::Not(type)`/`Type::Negative(type)`, but with the
`positive`/`negative` architecture, I'm not sure. Note that there is a
certain duplication in the `add_positive`/`add_negative` functions (e.g.
`S & ~T = Never` is implemented twice), but other rules are actually not
perfectly symmetric: `S & T = S` vs `~S & ~T = ~T`.
- I'm not particularly proud of the way `add_positive`/`add_negative`
turned out. They are long imperative-style functions with some
mutability mixed in (`to_remove`). I'm happy to look into ways to
improve this code *if we decide to go with this approach* of
implementing a set of ad-hoc rules for simplification.
- ~~Is it useful to perform simplifications eagerly in
`add_positive`/`add_negative`? (@carljm)~~ This is what I did for now.

## Test Plan

- Unit tests for `Type::is_disjoint_from`
- Observe changes in Markdown-based tests
- Unit tests for `IntersectionBuilder::build()`

---------

Co-authored-by: Carl Meyer <[email protected]>
@carljm
Copy link
Contributor Author

carljm commented Oct 18, 2024

Singleton types are a degenerate case of sealed types. They are sealed, but they don't require the handling discussed in this issue, since there is no equivalence to a union (except in the sense that all types are equivalent to a union of just that type, but we don't allow building size-one unions.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
red-knot Multi-file analysis & type inference
Projects
None yet
Development

No branches or pull requests

2 participants