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] Rename and rework the CoreStdlibModule enum #15071

Merged
merged 2 commits into from
Dec 19, 2024

Conversation

AlexWaygood
Copy link
Member

Summary

This PR does several things:

  • Renames the CoreStdlibModule enum to KnownModule, since it is a parallel enum to KnownClass, KnownFunction and KnownInstanceType
  • Moves the enum from stdlib.rs to module_resolver::module.rs. This allows it to be stored as a field on ModuleInner objects, and retrieved using a getter method from Module objects. This in turn means that we can then add known() and is_known() methods to the Module struct, which allows us to query whether a given Module represents a known module in much the same way we already do using FunctionType::is_known and Class::is_known.

These changes have several goals:

  • Unify the API of the various Known* enums
  • Put in one place the code required to check whether a Module really represents a specific module from the standard library. We had the same checks written out in several places, which was error-prone, since there are several things that needed to be checked and some are quite subtle. (One recent example: [red-knot] Statically known branches #15019 (comment).)
  • Make our API less stringly typed, and therefore less error-prone (the changes in definition.rs are an example)
  • Exploit some micro-optimisation opportunities in a few places

Test Plan

cargo test -p red_knot_python_semantic. No new tests added -- this should be a pure refactor.

@AlexWaygood AlexWaygood added the red-knot Multi-file analysis & type inference label Dec 19, 2024
Comment on lines 2669 to 2754
pub fn try_from_file_and_symbol(
db: &'db dyn Db,
file: File,
instance_name: &str,
) -> Option<Self> {
let candidate = match instance_name {
"Any" => Self::Any,
"ClassVar" => Self::ClassVar,
"Deque" => Self::Deque,
"List" => Self::List,
"Dict" => Self::Dict,
"DefaultDict" => Self::DefaultDict,
"Set" => Self::Set,
"FrozenSet" => Self::FrozenSet,
"Counter" => Self::Counter,
"ChainMap" => Self::ChainMap,
"OrderedDict" => Self::OrderedDict,
"Optional" => Self::Optional,
"Union" => Self::Union,
"NoReturn" => Self::NoReturn,
"Tuple" => Self::Tuple,
"Type" => Self::Type,
"Callable" => Self::Callable,
"Annotated" => Self::Annotated,
"Literal" => Self::Literal,
"Never" => Self::Never,
"Self" => Self::TypingSelf,
"Final" => Self::Final,
"Unpack" => Self::Unpack,
"Required" => Self::Required,
"TypeAlias" => Self::TypeAlias,
"TypeGuard" => Self::TypeGuard,
"TypeIs" => Self::TypeIs,
"ReadOnly" => Self::ReadOnly,
"Concatenate" => Self::Concatenate,
"NotRequired" => Self::NotRequired,
"LiteralString" => Self::LiteralString,
_ => return None,
};

candidate
.check_module(file_to_module(db, file)?.known()?)
.then_some(candidate)
}

/// Return `true` if `module` is a module from which this `KnownInstance` variant can validly originate.
///
/// Most variants can only exist in one module, which is the same as `self.class().canonical_module()`.
/// Some variants could validly be defined in either `typing` or `typing_extensions`, however.
pub fn check_module(self, module: KnownModule) -> bool {
match self {
Self::Any
| Self::ClassVar
| Self::Deque
| Self::List
| Self::Dict
| Self::DefaultDict
| Self::Set
| Self::FrozenSet
| Self::Counter
| Self::ChainMap
| Self::OrderedDict
| Self::Optional
| Self::Union
| Self::NoReturn
| Self::Tuple
| Self::Type
| Self::Callable => module.is_typing(),
Self::Annotated
| Self::Literal
| Self::LiteralString
| Self::Never
| Self::TypingSelf
| Self::Final
| Self::Concatenate
| Self::Unpack
| Self::Required
| Self::NotRequired
| Self::TypeAlias
| Self::TypeGuard
| Self::TypeIs
| Self::ReadOnly
| Self::TypeAliasType(_)
| Self::TypeVar(_) => {
matches!(module, KnownModule::Typing | KnownModule::TypingExtensions)
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This is functionally the same, but in the try_from*() methods on the other Known* enums, we delay the file_to_module() call as much as possible, as it's probably much more expensive than matching on the value of a string. This PR does the same for this method.

Copy link
Contributor

github-actions bot commented Dec 19, 2024

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

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 is beautiful, what a nice simplification and unification of concepts.

crates/red_knot_python_semantic/src/types.rs Outdated Show resolved Hide resolved
crates/red_knot_python_semantic/src/types.rs Outdated Show resolved Hide resolved
@AlexWaygood
Copy link
Member Author

1% speedup on the incremental benchmark... who knows if that's real, but certainly better than a slowdown whatever the case 😆 https://codspeed.io/astral-sh/ruff/branches/alex%2Fknown-module

@AlexWaygood AlexWaygood enabled auto-merge (squash) December 19, 2024 20:54
@AlexWaygood AlexWaygood merged commit bcec5e6 into main Dec 19, 2024
20 checks passed
@AlexWaygood AlexWaygood deleted the alex/known-module branch December 19, 2024 20:59
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

Successfully merging this pull request may close these issues.

2 participants