Skip to content

[red-knot] Corrections and improvements to intersection simplification#15475

Merged
AlexWaygood merged 6 commits intomainfrom
alex/truthy-bools
Jan 14, 2025
Merged

[red-knot] Corrections and improvements to intersection simplification#15475
AlexWaygood merged 6 commits intomainfrom
alex/truthy-bools

Conversation

@AlexWaygood
Copy link
Member

Summary

I noticed that we were incorrectly oversimplifying certain intersections, for example:

from knot_extensions import Intersection, Not, AlwaysTruthy

def g(
    x: Intersection[str, Not[AlwaysTruthy]]
):
    if isinstance(x, bool):
        reveal_type(x)  # revealed: Literal[False]

Here, bool & ~AlwaysTruthy can indeed be simplified to Literal[False], and therefore str & ~AlwaysTruthy & bool can be simplified to str & Literal[False], which then simplifies further to Never since str and Literal[False] are disjoint. But that's not what we're doing in the example above: instead, we're attempting to simplify str out of the intersection as part of the initial simplification, which then means that we never realise further along in the intersection simplification algorithm that the entire intersection simplifies to Never.

This PR fixes the bug, and also adds some more simplifications for intersections that we weren't doing previously:

  • bool & AlwaysTruthy -> Literal[True]
  • bool & AlwaysFalsy -> Literal[False]
  • LiteralString & AlwaysTruthy -> LiteralString & ~Literal[""]
  • LiteralString & AlwaysFalsy -> Literal[""]

This furthers our aim of having equivalent types use identical internal representations wherever it's feasible to do so.

Test Plan

  • cargo test -p red_knot_python_semantic --test mdtest
  • uvx pre-commit run -a

@AlexWaygood AlexWaygood added the ty Multi-file analysis & type inference label Jan 14, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Jan 14, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@AlexWaygood
Copy link
Member Author

AlexWaygood commented Jan 14, 2025

2% speedup on the Codspeed incremental benchmark?? I'll take it! https://codspeed.io/astral-sh/ruff/branches/alex%2Ftruthy-bools

Comment on lines -351 to +424
// bool & ~Literal[True] = Literal[False]
// bool & ~AlwaysTruthy = Literal[False]
Type::BooleanLiteral(_) | Type::AlwaysFalsy | Type::AlwaysTruthy
if self.positive.contains(&KnownClass::Bool.to_instance(db)) =>
{
*self = Self::default();
self.positive.insert(Type::BooleanLiteral(
new_negative.bool(db) != Truthiness::AlwaysTrue,
));
// `bool & ~AlwaysTruthy` -> `bool & Literal[False]`
// `bool & ~Literal[True]` -> `bool & Literal[False]`
Type::AlwaysTruthy | Type::BooleanLiteral(true) if contains_bool() => {
self.add_positive(db, Type::BooleanLiteral(false));
}
Copy link
Member Author

@AlexWaygood AlexWaygood Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect the codspeed improvement is because we're now doing fewer Salsa lookups eagerly due to changes like this (KnownClass::Bool.to_instance(db) eagerly looked up the bool type in the Salsa db) whereas the new contains_bool() closure here is lazier

Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, no notes!

Thank you!

@AlexWaygood AlexWaygood merged commit bcf0a71 into main Jan 14, 2025
21 checks passed
@AlexWaygood AlexWaygood deleted the alex/truthy-bools branch January 14, 2025 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants