-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[ty] Generate the top and bottom materialization of a type #18594
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
Conversation
|
e4abd62 to
7342d3c
Compare
carljm
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good! I think the main thing is we need to ensure nesting is right, by always flipping the polarity of the "outer" variance when we hit a contravariant context, not hardcoding variance.
Another observation here is that the implementation of top_materialization and bottom_materialization should be identical -- the only difference is whether we start with a variance of "Covariant" (top materialization) or start with a variance of "Contravariant" (bottom materialization). It might make sense to rename the method added in the current PR to just materialize, and then have Type::top_materialization and Type::bottom_materialization just be thin wrappers that call materialize with the right initial variance.
crates/ty_python_semantic/resources/mdtest/type_properties/top_materialization.md
Outdated
Show resolved
Hide resolved
crates/ty_python_semantic/resources/mdtest/type_properties/top_materialization.md
Outdated
Show resolved
Hide resolved
crates/ty_python_semantic/resources/mdtest/type_properties/top_materialization.md
Outdated
Show resolved
Hide resolved
crates/ty_python_semantic/resources/mdtest/type_properties/top_materialization.md
Outdated
Show resolved
Hide resolved
77d8c4b to
4a9739f
Compare
Post review changes
Edit:
|
crates/ty_python_semantic/resources/mdtest/type_properties/materialization.md
Show resolved
Hide resolved
crates/ty_python_semantic/resources/mdtest/type_properties/materialization.md
Show resolved
Hide resolved
* Rename `T` to `T_all` * Pass in the surrounding context for materializing the specialization * Update test suite for the above and add additional test using callable * Add TODO for materializing generic protocol class
carljm
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
* main: [ty] Add some "inside string" tests for `object.<CURSOR>` completions [ty] Pull types on synthesized Python files created by mdtest (#18539) Update Rust crate anstyle to v1.0.11 (#18583) [`pyupgrade`] Fix `super(__class__, self)` detection in UP008 (super-call-with-parameters) (#18478) [ty] Generate the top and bottom materialization of a type (#18594) `SourceOrderVisitor` should visit the `Identifier` part of the `PatternKeyword` node (#18635) Update salsa (#18636) [ty] Update mypy_primer doc (#18638) [ty] Improve support for `object.<CURSOR>` completions [ty] Add `CoveringNode::find_last` [ty] Refactor covering node representation [ty] Infer the Python version from `--python=<system installation>` on Unix (#18550) [`flake8-return`] Fix `RET504` autofix generating a syntax error (#18428) Fix incorrect salsa `return_ref` attribute (#18605) Move corpus tests to `ty_python_semantic` (#18609) [`pyupgrade`] Don't offer fix for `Optional[None]` in non-pep604-annotation-optional (`UP045)` or non-pep604-annotation-union (`UP007`) (#18545) [`pep8-naming`] Suppress fix for `N804` and `N805` if the recommend name is already used (#18472) [`ruff`] skip fix for `RUF059` if dummy name is already bound (unused-unpacked-variable) (#18509)
| Type::Callable(callable_type) => { | ||
| Type::Callable(callable_type.materialize(db, variance)) | ||
| } | ||
| Type::SubclassOf(subclass_of_type) => subclass_of_type.materialize(db, variance), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just snooping around here but is this missing a Type::SubclassOf in the return value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nah, the function here delegates to SubclassOfType::materialize(), which does work to figure out which Type variant it needs to return (it won't always be a Type::SubclassOf type due to some eager normalizations we do):
ruff/crates/ty_python_semantic/src/types/subclass_of.rs
Lines 80 to 104 in a2cd6df
| pub(super) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Type<'db> { | |
| match self.subclass_of { | |
| SubclassOfInner::Dynamic(_) => match variance { | |
| TypeVarVariance::Covariant => KnownClass::Type.to_instance(db), | |
| TypeVarVariance::Contravariant => Type::Never, | |
| TypeVarVariance::Invariant => { | |
| // We need to materialize this to `type[T]` but that isn't representable so | |
| // we instead use a type variable with an upper bound of `type`. | |
| Type::TypeVar(TypeVarInstance::new( | |
| db, | |
| Name::new_static("T_all"), | |
| None, | |
| Some(TypeVarBoundOrConstraints::UpperBound( | |
| KnownClass::Type.to_instance(db), | |
| )), | |
| variance, | |
| None, | |
| TypeVarKind::Pep695, | |
| )) | |
| } | |
| TypeVarVariance::Bivariant => unreachable!(), | |
| }, | |
| SubclassOfInner::Class(_) => Type::SubclassOf(self), | |
| } | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, but this is subtle and probably should have a comment. SubclassOfType::materialize returns a Type for the overall materialization of the subclass-of type, not just for the inner type. The reason for this is that we don't currently have a direct representation for type[T] (that is, subclass-of a typevar), but the materialization of type[Any] in an invariant position should be type[T] where T has no upper bound or constraints, and it turns out that this is equivalent to just T' where T': type -- so that's how we represent this type[T] we need to synthesize.
I think I did just spot another bug in looking at EDIT: never mind, this is wrong -- I was thinking in terms of collecting constraints in variance inference, but here we are just flipping variance, not collecting constraints. So SubclassOfType::materialize, though. That method currently acts as if type[...] itself were bivariant (imposed no variance constraints). But in fact it is covariant. So both the Contravariant and Invariant branches there should be returning the type variable.type[] being covariant just means it doesn't change the outer variance context.
Summary
This is to support #18607.
This PR adds support for generating the top materialization (or upper bound materialization) and the bottom materialization (or lower bound materialization) of a type. This is the most general and the most specific form of the type which is fully static, respectively.
More concretely,
T', the top materialization ofT, is the typeTwith all occurrencesof dynamic type (
Any,Unknown,@Todo) replaced as follows:objectNever(For an invariant position, it should actually be replaced with an existential type, but this is not currently representable in our type system, so we use an unresolved type variable for now instead.)
The bottom materialization is implemented in the same way, except we start out in "contravariant" position.
Test Plan
Add test cases for various types.