mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-18 03:36:18 +00:00
[ty] Implement constraint implication for compound types (#21366)
Some checks are pending
CI / cargo test (${{ github.repository == 'astral-sh/ruff' && 'depot-windows-2022-16' || 'windows-latest' }}) (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (macos-latest) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / ty completion evaluation (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / benchmarks walltime (medium|multithreaded) (push) Blocked by required conditions
CI / benchmarks walltime (small|large) (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
Some checks are pending
CI / cargo test (${{ github.repository == 'astral-sh/ruff' && 'depot-windows-2022-16' || 'windows-latest' }}) (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (macos-latest) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / ty completion evaluation (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / benchmarks walltime (medium|multithreaded) (push) Blocked by required conditions
CI / benchmarks walltime (small|large) (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
This PR updates the constraint implication type relationship to work on compound types as well. (A compound type is a non-atomic type, like `list[T]`.) The goal of constraint implication is to check whether the requirements of a constraint imply that a particular subtyping relationship holds. Before, we were only checking atomic typevars. That would let us verify that the constraint set `T ≤ bool` implies that `T` is always a subtype of `int`. (In this case, the lhs of the subtyping check, `T`, is an atomic typevar.) But we weren't recursing into compound types, to look for nested occurrences of typevars. That means that we weren't able to see that `T ≤ bool` implies that `Covariant[T]` is always a subtype of `Covariant[int]`. Doing this recursion means that we have to carry the constraint set along with us as we recurse into types as part of `has_relation_to`, by adding constraint implication as a new `TypeRelation` variant. (Before it was just a method on `ConstraintSet`.) --------- Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
This commit is contained in:
parent
d63b4b0383
commit
698231a47a
12 changed files with 321 additions and 111 deletions
|
|
@ -12,8 +12,7 @@ a particular constraint set hold_.
|
||||||
## Concrete types
|
## Concrete types
|
||||||
|
|
||||||
For concrete types, constraint implication is exactly the same as subtyping. (A concrete type is any
|
For concrete types, constraint implication is exactly the same as subtyping. (A concrete type is any
|
||||||
fully static type that is not a typevar. It can _contain_ a typevar, though — `list[T]` is
|
fully static type that does not contain a typevar.)
|
||||||
considered concrete.)
|
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from ty_extensions import ConstraintSet, is_subtype_of, static_assert
|
from ty_extensions import ConstraintSet, is_subtype_of, static_assert
|
||||||
|
|
@ -191,4 +190,163 @@ def mutually_constrained[T, U]():
|
||||||
static_assert(not given_int.implies_subtype_of(T, str))
|
static_assert(not given_int.implies_subtype_of(T, str))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Compound types
|
||||||
|
|
||||||
|
All of the relationships in the above section also apply when a typevar appears in a compound type.
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Never
|
||||||
|
from ty_extensions import ConstraintSet, static_assert
|
||||||
|
|
||||||
|
class Covariant[T]:
|
||||||
|
def get(self) -> T:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
def given_constraints[T]():
|
||||||
|
static_assert(not ConstraintSet.always().implies_subtype_of(Covariant[T], Covariant[int]))
|
||||||
|
static_assert(not ConstraintSet.always().implies_subtype_of(Covariant[T], Covariant[bool]))
|
||||||
|
static_assert(not ConstraintSet.always().implies_subtype_of(Covariant[T], Covariant[str]))
|
||||||
|
|
||||||
|
# These are vacuously true; false implies anything
|
||||||
|
static_assert(ConstraintSet.never().implies_subtype_of(Covariant[T], Covariant[int]))
|
||||||
|
static_assert(ConstraintSet.never().implies_subtype_of(Covariant[T], Covariant[bool]))
|
||||||
|
static_assert(ConstraintSet.never().implies_subtype_of(Covariant[T], Covariant[str]))
|
||||||
|
|
||||||
|
# For a covariant typevar, (T ≤ int) implies that (Covariant[T] ≤ Covariant[int]).
|
||||||
|
given_int = ConstraintSet.range(Never, T, int)
|
||||||
|
static_assert(given_int.implies_subtype_of(Covariant[T], Covariant[int]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Covariant[T], Covariant[bool]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Covariant[T], Covariant[str]))
|
||||||
|
|
||||||
|
given_bool = ConstraintSet.range(Never, T, bool)
|
||||||
|
static_assert(given_bool.implies_subtype_of(Covariant[T], Covariant[int]))
|
||||||
|
static_assert(given_bool.implies_subtype_of(Covariant[T], Covariant[bool]))
|
||||||
|
static_assert(not given_bool.implies_subtype_of(Covariant[T], Covariant[str]))
|
||||||
|
|
||||||
|
given_bool_int = ConstraintSet.range(bool, T, int)
|
||||||
|
static_assert(not given_bool_int.implies_subtype_of(Covariant[int], Covariant[T]))
|
||||||
|
static_assert(given_bool_int.implies_subtype_of(Covariant[bool], Covariant[T]))
|
||||||
|
static_assert(not given_bool_int.implies_subtype_of(Covariant[str], Covariant[T]))
|
||||||
|
|
||||||
|
def mutually_constrained[T, U]():
|
||||||
|
# If (T = U ∧ U ≤ int), then (T ≤ int) must be true as well, and therefore
|
||||||
|
# (Covariant[T] ≤ Covariant[int]).
|
||||||
|
given_int = ConstraintSet.range(U, T, U) & ConstraintSet.range(Never, U, int)
|
||||||
|
static_assert(given_int.implies_subtype_of(Covariant[T], Covariant[int]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Covariant[T], Covariant[bool]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Covariant[T], Covariant[str]))
|
||||||
|
|
||||||
|
# If (T ≤ U ∧ U ≤ int), then (T ≤ int) must be true as well, and therefore
|
||||||
|
# (Covariant[T] ≤ Covariant[int]).
|
||||||
|
given_int = ConstraintSet.range(Never, T, U) & ConstraintSet.range(Never, U, int)
|
||||||
|
static_assert(given_int.implies_subtype_of(Covariant[T], Covariant[int]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Covariant[T], Covariant[bool]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Covariant[T], Covariant[str]))
|
||||||
|
```
|
||||||
|
|
||||||
|
Many of the relationships are reversed for typevars that appear in contravariant types.
|
||||||
|
|
||||||
|
```py
|
||||||
|
class Contravariant[T]:
|
||||||
|
def set(self, value: T):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def given_constraints[T]():
|
||||||
|
static_assert(not ConstraintSet.always().implies_subtype_of(Contravariant[int], Contravariant[T]))
|
||||||
|
static_assert(not ConstraintSet.always().implies_subtype_of(Contravariant[bool], Contravariant[T]))
|
||||||
|
static_assert(not ConstraintSet.always().implies_subtype_of(Contravariant[str], Contravariant[T]))
|
||||||
|
|
||||||
|
# These are vacuously true; false implies anything
|
||||||
|
static_assert(ConstraintSet.never().implies_subtype_of(Contravariant[int], Contravariant[T]))
|
||||||
|
static_assert(ConstraintSet.never().implies_subtype_of(Contravariant[bool], Contravariant[T]))
|
||||||
|
static_assert(ConstraintSet.never().implies_subtype_of(Contravariant[str], Contravariant[T]))
|
||||||
|
|
||||||
|
# For a contravariant typevar, (T ≤ int) implies that (Contravariant[int] ≤ Contravariant[T]).
|
||||||
|
# (The order of the comparison is reversed because of contravariance.)
|
||||||
|
given_int = ConstraintSet.range(Never, T, int)
|
||||||
|
static_assert(given_int.implies_subtype_of(Contravariant[int], Contravariant[T]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Contravariant[bool], Contravariant[T]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Contravariant[str], Contravariant[T]))
|
||||||
|
|
||||||
|
given_bool = ConstraintSet.range(Never, T, int)
|
||||||
|
static_assert(given_bool.implies_subtype_of(Contravariant[int], Contravariant[T]))
|
||||||
|
static_assert(not given_bool.implies_subtype_of(Contravariant[bool], Contravariant[T]))
|
||||||
|
static_assert(not given_bool.implies_subtype_of(Contravariant[str], Contravariant[T]))
|
||||||
|
|
||||||
|
def mutually_constrained[T, U]():
|
||||||
|
# If (T = U ∧ U ≤ int), then (T ≤ int) must be true as well, and therefore
|
||||||
|
# (Contravariant[int] ≤ Contravariant[T]).
|
||||||
|
given_int = ConstraintSet.range(U, T, U) & ConstraintSet.range(Never, U, int)
|
||||||
|
static_assert(given_int.implies_subtype_of(Contravariant[int], Contravariant[T]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Contravariant[bool], Contravariant[T]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Contravariant[str], Contravariant[T]))
|
||||||
|
|
||||||
|
# If (T ≤ U ∧ U ≤ int), then (T ≤ int) must be true as well, and therefore
|
||||||
|
# (Contravariant[int] ≤ Contravariant[T]).
|
||||||
|
given_int = ConstraintSet.range(Never, T, U) & ConstraintSet.range(Never, U, int)
|
||||||
|
static_assert(given_int.implies_subtype_of(Contravariant[int], Contravariant[T]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Contravariant[bool], Contravariant[T]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Contravariant[str], Contravariant[T]))
|
||||||
|
```
|
||||||
|
|
||||||
|
For invariant typevars, subtyping of the typevar does not imply subtyping of the compound type in
|
||||||
|
either direction. But an equality constraint on the typevar does.
|
||||||
|
|
||||||
|
```py
|
||||||
|
class Invariant[T]:
|
||||||
|
def get(self) -> T:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
def set(self, value: T):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def given_constraints[T]():
|
||||||
|
static_assert(not ConstraintSet.always().implies_subtype_of(Invariant[T], Invariant[int]))
|
||||||
|
static_assert(not ConstraintSet.always().implies_subtype_of(Invariant[T], Invariant[bool]))
|
||||||
|
static_assert(not ConstraintSet.always().implies_subtype_of(Invariant[T], Invariant[str]))
|
||||||
|
|
||||||
|
# These are vacuously true; false implies anything
|
||||||
|
static_assert(ConstraintSet.never().implies_subtype_of(Invariant[T], Invariant[int]))
|
||||||
|
static_assert(ConstraintSet.never().implies_subtype_of(Invariant[T], Invariant[bool]))
|
||||||
|
static_assert(ConstraintSet.never().implies_subtype_of(Invariant[T], Invariant[str]))
|
||||||
|
|
||||||
|
# For an invariant typevar, (T ≤ int) does not imply that (Invariant[T] ≤ Invariant[int]).
|
||||||
|
given_int = ConstraintSet.range(Never, T, int)
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[T], Invariant[int]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[T], Invariant[bool]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[T], Invariant[str]))
|
||||||
|
|
||||||
|
# It also does not imply the contravariant ordering (Invariant[int] ≤ Invariant[T]).
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[int], Invariant[T]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[bool], Invariant[T]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[str], Invariant[T]))
|
||||||
|
|
||||||
|
# But (T = int) does imply both.
|
||||||
|
given_int = ConstraintSet.range(int, T, int)
|
||||||
|
static_assert(given_int.implies_subtype_of(Invariant[T], Invariant[int]))
|
||||||
|
static_assert(given_int.implies_subtype_of(Invariant[int], Invariant[T]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[bool], Invariant[T]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[T], Invariant[bool]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[str], Invariant[T]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[T], Invariant[str]))
|
||||||
|
|
||||||
|
def mutually_constrained[T, U]():
|
||||||
|
# If (T = U ∧ U ≤ int), then (T ≤ int) must be true as well. But because T is invariant, that
|
||||||
|
# does _not_ imply that (Invariant[T] ≤ Invariant[int]).
|
||||||
|
given_int = ConstraintSet.range(U, T, U) & ConstraintSet.range(Never, U, int)
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[T], Invariant[int]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[T], Invariant[bool]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[T], Invariant[str]))
|
||||||
|
|
||||||
|
# If (T = U ∧ U = int), then (T = int) must be true as well. That is an equality constraint, so
|
||||||
|
# even though T is invariant, it does imply that (Invariant[T] ≤ Invariant[int]).
|
||||||
|
given_int = ConstraintSet.range(U, T, U) & ConstraintSet.range(int, U, int)
|
||||||
|
static_assert(given_int.implies_subtype_of(Invariant[T], Invariant[int]))
|
||||||
|
static_assert(given_int.implies_subtype_of(Invariant[int], Invariant[T]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[T], Invariant[bool]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[bool], Invariant[T]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[T], Invariant[str]))
|
||||||
|
static_assert(not given_int.implies_subtype_of(Invariant[str], Invariant[T]))
|
||||||
|
```
|
||||||
|
|
||||||
[subtyping]: https://typing.python.org/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence
|
[subtyping]: https://typing.python.org/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ pub(crate) type ApplyTypeMappingVisitor<'db> = TypeTransformer<'db, TypeMapping<
|
||||||
|
|
||||||
/// A [`PairVisitor`] that is used in `has_relation_to` methods.
|
/// A [`PairVisitor`] that is used in `has_relation_to` methods.
|
||||||
pub(crate) type HasRelationToVisitor<'db> =
|
pub(crate) type HasRelationToVisitor<'db> =
|
||||||
CycleDetector<TypeRelation, (Type<'db>, Type<'db>, TypeRelation), ConstraintSet<'db>>;
|
CycleDetector<TypeRelation<'db>, (Type<'db>, Type<'db>, TypeRelation<'db>), ConstraintSet<'db>>;
|
||||||
|
|
||||||
impl Default for HasRelationToVisitor<'_> {
|
impl Default for HasRelationToVisitor<'_> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
|
@ -1623,6 +1623,25 @@ impl<'db> Type<'db> {
|
||||||
self.has_relation_to(db, target, inferable, TypeRelation::Subtyping)
|
self.has_relation_to(db, target, inferable, TypeRelation::Subtyping)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the constraints under which this type is a subtype of type `target`, assuming that
|
||||||
|
/// all of the restrictions in `constraints` hold.
|
||||||
|
///
|
||||||
|
/// See [`TypeRelation::SubtypingAssuming`] for more details.
|
||||||
|
fn when_subtype_of_assuming(
|
||||||
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
target: Type<'db>,
|
||||||
|
assuming: ConstraintSet<'db>,
|
||||||
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
|
) -> ConstraintSet<'db> {
|
||||||
|
self.has_relation_to(
|
||||||
|
db,
|
||||||
|
target,
|
||||||
|
inferable,
|
||||||
|
TypeRelation::SubtypingAssuming(assuming),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Return true if this type is assignable to type `target`.
|
/// Return true if this type is assignable to type `target`.
|
||||||
///
|
///
|
||||||
/// See [`TypeRelation::Assignability`] for more details.
|
/// See [`TypeRelation::Assignability`] for more details.
|
||||||
|
|
@ -1654,7 +1673,7 @@ impl<'db> Type<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
target: Type<'db>,
|
target: Type<'db>,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
self.has_relation_to_impl(
|
self.has_relation_to_impl(
|
||||||
db,
|
db,
|
||||||
|
|
@ -1671,7 +1690,7 @@ impl<'db> Type<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
target: Type<'db>,
|
target: Type<'db>,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
@ -1684,6 +1703,14 @@ impl<'db> Type<'db> {
|
||||||
return ConstraintSet::from(true);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle constraint implication first. If either `self` or `target` is a typevar, check
|
||||||
|
// the constraint set to see if the corresponding constraint is satisfied.
|
||||||
|
if let TypeRelation::SubtypingAssuming(constraints) = relation
|
||||||
|
&& (self.is_type_var() || target.is_type_var())
|
||||||
|
{
|
||||||
|
return constraints.implies_subtype_of(db, self, target);
|
||||||
|
}
|
||||||
|
|
||||||
match (self, target) {
|
match (self, target) {
|
||||||
// Everything is a subtype of `object`.
|
// Everything is a subtype of `object`.
|
||||||
(_, Type::NominalInstance(instance)) if instance.is_object() => {
|
(_, Type::NominalInstance(instance)) if instance.is_object() => {
|
||||||
|
|
@ -1763,7 +1790,7 @@ impl<'db> Type<'db> {
|
||||||
"DynamicType::Divergent should have been handled in an earlier branch"
|
"DynamicType::Divergent should have been handled in an earlier branch"
|
||||||
);
|
);
|
||||||
ConstraintSet::from(match relation {
|
ConstraintSet::from(match relation {
|
||||||
TypeRelation::Subtyping => false,
|
TypeRelation::Subtyping | TypeRelation::SubtypingAssuming(_) => false,
|
||||||
TypeRelation::Assignability => true,
|
TypeRelation::Assignability => true,
|
||||||
TypeRelation::Redundancy => match target {
|
TypeRelation::Redundancy => match target {
|
||||||
Type::Dynamic(_) => true,
|
Type::Dynamic(_) => true,
|
||||||
|
|
@ -1773,7 +1800,7 @@ impl<'db> Type<'db> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
(_, Type::Dynamic(_)) => ConstraintSet::from(match relation {
|
(_, Type::Dynamic(_)) => ConstraintSet::from(match relation {
|
||||||
TypeRelation::Subtyping => false,
|
TypeRelation::Subtyping | TypeRelation::SubtypingAssuming(_) => false,
|
||||||
TypeRelation::Assignability => true,
|
TypeRelation::Assignability => true,
|
||||||
TypeRelation::Redundancy => match self {
|
TypeRelation::Redundancy => match self {
|
||||||
Type::Dynamic(_) => true,
|
Type::Dynamic(_) => true,
|
||||||
|
|
@ -1970,12 +1997,16 @@ impl<'db> Type<'db> {
|
||||||
// to non-transitivity (highly undesirable); and pragmatically, a full implementation
|
// to non-transitivity (highly undesirable); and pragmatically, a full implementation
|
||||||
// of redundancy may not generally lead to simpler types in many situations.
|
// of redundancy may not generally lead to simpler types in many situations.
|
||||||
let self_ty = match relation {
|
let self_ty = match relation {
|
||||||
TypeRelation::Subtyping | TypeRelation::Redundancy => self,
|
TypeRelation::Subtyping
|
||||||
|
| TypeRelation::Redundancy
|
||||||
|
| TypeRelation::SubtypingAssuming(_) => self,
|
||||||
TypeRelation::Assignability => self.bottom_materialization(db),
|
TypeRelation::Assignability => self.bottom_materialization(db),
|
||||||
};
|
};
|
||||||
intersection.negative(db).iter().when_all(db, |&neg_ty| {
|
intersection.negative(db).iter().when_all(db, |&neg_ty| {
|
||||||
let neg_ty = match relation {
|
let neg_ty = match relation {
|
||||||
TypeRelation::Subtyping | TypeRelation::Redundancy => neg_ty,
|
TypeRelation::Subtyping
|
||||||
|
| TypeRelation::Redundancy
|
||||||
|
| TypeRelation::SubtypingAssuming(_) => neg_ty,
|
||||||
TypeRelation::Assignability => neg_ty.bottom_materialization(db),
|
TypeRelation::Assignability => neg_ty.bottom_materialization(db),
|
||||||
};
|
};
|
||||||
self_ty.is_disjoint_from_impl(
|
self_ty.is_disjoint_from_impl(
|
||||||
|
|
@ -10322,7 +10353,7 @@ impl<'db> ConstructorCallError<'db> {
|
||||||
|
|
||||||
/// A non-exhaustive enumeration of relations that can exist between types.
|
/// A non-exhaustive enumeration of relations that can exist between types.
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
pub(crate) enum TypeRelation {
|
pub(crate) enum TypeRelation<'db> {
|
||||||
/// The "subtyping" relation.
|
/// The "subtyping" relation.
|
||||||
///
|
///
|
||||||
/// A [fully static] type `B` is a subtype of a fully static type `A` if and only if
|
/// A [fully static] type `B` is a subtype of a fully static type `A` if and only if
|
||||||
|
|
@ -10446,9 +10477,46 @@ pub(crate) enum TypeRelation {
|
||||||
/// [fully static]: https://typing.python.org/en/latest/spec/glossary.html#term-fully-static-type
|
/// [fully static]: https://typing.python.org/en/latest/spec/glossary.html#term-fully-static-type
|
||||||
/// [materializations]: https://typing.python.org/en/latest/spec/glossary.html#term-materialize
|
/// [materializations]: https://typing.python.org/en/latest/spec/glossary.html#term-materialize
|
||||||
Redundancy,
|
Redundancy,
|
||||||
|
|
||||||
|
/// The "constraint implication" relationship, aka "implies subtype of".
|
||||||
|
///
|
||||||
|
/// This relationship tests whether one type is a [subtype][Self::Subtyping] of another,
|
||||||
|
/// assuming that the constraints in a particular constraint set hold.
|
||||||
|
///
|
||||||
|
/// For concrete types (types that do not contain typevars), this relationship is the same as
|
||||||
|
/// [subtyping][Self::Subtyping]. (Constraint sets place restrictions on typevars, so if you
|
||||||
|
/// are not comparing typevars, the constraint set can have no effect on whether subtyping
|
||||||
|
/// holds.)
|
||||||
|
///
|
||||||
|
/// If you're comparing a typevar, we have to consider what restrictions the constraint set
|
||||||
|
/// places on that typevar to determine if subtyping holds. For instance, if you want to check
|
||||||
|
/// whether `T ≤ int`, then the answer will depend on what constraint set you are considering:
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// implies_subtype_of(T ≤ bool, T, int) ⇒ true
|
||||||
|
/// implies_subtype_of(T ≤ int, T, int) ⇒ true
|
||||||
|
/// implies_subtype_of(T ≤ str, T, int) ⇒ false
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// In the first two cases, the constraint set ensures that `T` will always specialize to a
|
||||||
|
/// type that is a subtype of `int`. In the final case, the constraint set requires `T` to
|
||||||
|
/// specialize to a subtype of `str`, and there is no such type that is also a subtype of
|
||||||
|
/// `int`.
|
||||||
|
///
|
||||||
|
/// There are two constraint sets that deserve special consideration.
|
||||||
|
///
|
||||||
|
/// - The "always true" constraint set does not place any restrictions on any typevar. In this
|
||||||
|
/// case, `implies_subtype_of` will return the same result as `when_subtype_of`, even if
|
||||||
|
/// you're comparing against a typevar.
|
||||||
|
///
|
||||||
|
/// - The "always false" constraint set represents an impossible situation. In this case, every
|
||||||
|
/// subtype check will be vacuously true, even if you're comparing two concrete types that
|
||||||
|
/// are not actually subtypes of each other. (That is, `implies_subtype_of(false, int, str)`
|
||||||
|
/// will return true!)
|
||||||
|
SubtypingAssuming(ConstraintSet<'db>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeRelation {
|
impl TypeRelation<'_> {
|
||||||
pub(crate) const fn is_assignability(self) -> bool {
|
pub(crate) const fn is_assignability(self) -> bool {
|
||||||
matches!(self, TypeRelation::Assignability)
|
matches!(self, TypeRelation::Assignability)
|
||||||
}
|
}
|
||||||
|
|
@ -10615,7 +10683,7 @@ impl<'db> BoundMethodType<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Self,
|
other: Self,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
@ -10782,7 +10850,7 @@ impl<'db> CallableType<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Self,
|
other: Self,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
@ -10891,7 +10959,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Self,
|
other: Self,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
|
||||||
|
|
@ -1216,10 +1216,10 @@ impl<'db> Bindings<'db> {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = tracked.constraints(db).when_subtype_of_given(
|
let result = ty_a.when_subtype_of_assuming(
|
||||||
db,
|
db,
|
||||||
*ty_a,
|
|
||||||
*ty_b,
|
*ty_b,
|
||||||
|
tracked.constraints(db),
|
||||||
InferableTypeVars::None,
|
InferableTypeVars::None,
|
||||||
);
|
);
|
||||||
let tracked = TrackedConstraintSet::new(db, result);
|
let tracked = TrackedConstraintSet::new(db, result);
|
||||||
|
|
|
||||||
|
|
@ -541,14 +541,16 @@ impl<'db> ClassType<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Self,
|
other: Self,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
self.iter_mro(db).when_any(db, |base| {
|
self.iter_mro(db).when_any(db, |base| {
|
||||||
match base {
|
match base {
|
||||||
ClassBase::Dynamic(_) => match relation {
|
ClassBase::Dynamic(_) => match relation {
|
||||||
TypeRelation::Subtyping | TypeRelation::Redundancy => {
|
TypeRelation::Subtyping
|
||||||
|
| TypeRelation::Redundancy
|
||||||
|
| TypeRelation::SubtypingAssuming(_) => {
|
||||||
ConstraintSet::from(other.is_object(db))
|
ConstraintSet::from(other.is_object(db))
|
||||||
}
|
}
|
||||||
TypeRelation::Assignability => ConstraintSet::from(!other.is_final(db)),
|
TypeRelation::Assignability => ConstraintSet::from(!other.is_final(db)),
|
||||||
|
|
|
||||||
|
|
@ -185,11 +185,12 @@ impl<'db> ConstraintSet<'db> {
|
||||||
typevar: BoundTypeVarInstance<'db>,
|
typevar: BoundTypeVarInstance<'db>,
|
||||||
lower: Type<'db>,
|
lower: Type<'db>,
|
||||||
upper: Type<'db>,
|
upper: Type<'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (lower, upper) = match relation {
|
let (lower, upper) = match relation {
|
||||||
// TODO: Is this the correct constraint for redundancy?
|
TypeRelation::Subtyping
|
||||||
TypeRelation::Subtyping | TypeRelation::Redundancy => (
|
| TypeRelation::Redundancy
|
||||||
|
| TypeRelation::SubtypingAssuming(_) => (
|
||||||
lower.top_materialization(db),
|
lower.top_materialization(db),
|
||||||
upper.bottom_materialization(db),
|
upper.bottom_materialization(db),
|
||||||
),
|
),
|
||||||
|
|
@ -215,47 +216,16 @@ impl<'db> ConstraintSet<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the constraints under which `lhs` is a subtype of `rhs`, assuming that the
|
/// Returns the constraints under which `lhs` is a subtype of `rhs`, assuming that the
|
||||||
/// constraints in this constraint set hold.
|
/// constraints in this constraint set hold. Panics if neither of the types being compared are
|
||||||
///
|
/// a typevar. (That case is handled by `Type::has_relation_to`.)
|
||||||
/// For concrete types (types that are not typevars), this returns the same result as
|
pub(crate) fn implies_subtype_of(
|
||||||
/// [`when_subtype_of`][Type::when_subtype_of]. (Constraint sets place restrictions on
|
|
||||||
/// typevars, so if you are not comparing typevars, the constraint set can have no effect on
|
|
||||||
/// whether subtyping holds.)
|
|
||||||
///
|
|
||||||
/// If you're comparing a typevar, we have to consider what restrictions the constraint set
|
|
||||||
/// places on that typevar to determine if subtyping holds. For instance, if you want to check
|
|
||||||
/// whether `T ≤ int`, then answer will depend on what constraint set you are considering:
|
|
||||||
///
|
|
||||||
/// ```text
|
|
||||||
/// when_subtype_of_given(T ≤ bool, T, int) ⇒ true
|
|
||||||
/// when_subtype_of_given(T ≤ int, T, int) ⇒ true
|
|
||||||
/// when_subtype_of_given(T ≤ str, T, int) ⇒ false
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// In the first two cases, the constraint set ensures that `T` will always specialize to a
|
|
||||||
/// type that is a subtype of `int`. In the final case, the constraint set requires `T` to
|
|
||||||
/// specialize to a subtype of `str`, and there is no such type that is also a subtype of
|
|
||||||
/// `int`.
|
|
||||||
///
|
|
||||||
/// There are two constraint sets that deserve special consideration.
|
|
||||||
///
|
|
||||||
/// - The "always true" constraint set does not place any restrictions on any typevar. In this
|
|
||||||
/// case, `when_subtype_of_given` will return the same result as `when_subtype_of`, even if
|
|
||||||
/// you're comparing against a typevar.
|
|
||||||
///
|
|
||||||
/// - The "always false" constraint set represents an impossible situation. In this case, every
|
|
||||||
/// subtype check will be vacuously true, even if you're comparing two concrete types that
|
|
||||||
/// are not actually subtypes of each other. (That is,
|
|
||||||
/// `when_subtype_of_given(false, int, str)` will return true!)
|
|
||||||
pub(crate) fn when_subtype_of_given(
|
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
lhs: Type<'db>,
|
lhs: Type<'db>,
|
||||||
rhs: Type<'db>,
|
rhs: Type<'db>,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
node: self.node.when_subtype_of_given(db, lhs, rhs, inferable),
|
node: self.node.implies_subtype_of(db, lhs, rhs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -829,13 +799,7 @@ impl<'db> Node<'db> {
|
||||||
simplified.and(db, domain)
|
simplified.and(db, domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn when_subtype_of_given(
|
fn implies_subtype_of(self, db: &'db dyn Db, lhs: Type<'db>, rhs: Type<'db>) -> Self {
|
||||||
self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
lhs: Type<'db>,
|
|
||||||
rhs: Type<'db>,
|
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
|
||||||
) -> Self {
|
|
||||||
// When checking subtyping involving a typevar, we can turn the subtyping check into a
|
// When checking subtyping involving a typevar, we can turn the subtyping check into a
|
||||||
// constraint (i.e, "is `T` a subtype of `int` becomes the constraint `T ≤ int`), and then
|
// constraint (i.e, "is `T` a subtype of `int` becomes the constraint `T ≤ int`), and then
|
||||||
// check when the BDD implies that constraint.
|
// check when the BDD implies that constraint.
|
||||||
|
|
@ -846,8 +810,7 @@ impl<'db> Node<'db> {
|
||||||
(_, Type::TypeVar(bound_typevar)) => {
|
(_, Type::TypeVar(bound_typevar)) => {
|
||||||
ConstrainedTypeVar::new_node(db, bound_typevar, lhs, Type::object())
|
ConstrainedTypeVar::new_node(db, bound_typevar, lhs, Type::object())
|
||||||
}
|
}
|
||||||
// If neither type is a typevar, then we fall back on a normal subtyping check.
|
_ => panic!("at least one type should be a typevar"),
|
||||||
_ => return lhs.when_subtype_of(db, rhs, inferable).node,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.satisfies(db, constraint)
|
self.satisfies(db, constraint)
|
||||||
|
|
@ -1464,10 +1427,12 @@ impl<'db> InteriorNode<'db> {
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_node = Node::new_constraint(
|
let new_constraint =
|
||||||
db,
|
ConstrainedTypeVar::new(db, constrained_typevar, new_lower, new_upper);
|
||||||
ConstrainedTypeVar::new(db, constrained_typevar, new_lower, new_upper),
|
if seen_constraints.contains(&new_constraint) {
|
||||||
);
|
continue;
|
||||||
|
}
|
||||||
|
let new_node = Node::new_constraint(db, new_constraint);
|
||||||
let positive_left_node =
|
let positive_left_node =
|
||||||
Node::new_satisfied_constraint(db, left_constraint.when_true());
|
Node::new_satisfied_constraint(db, left_constraint.when_true());
|
||||||
let positive_right_node =
|
let positive_right_node =
|
||||||
|
|
@ -1481,7 +1446,18 @@ impl<'db> InteriorNode<'db> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// From here on out we know that both constraints constrain the same typevar.
|
// From here on out we know that both constraints constrain the same typevar. The
|
||||||
|
// clause above will propagate all that we know about the current typevar relative to
|
||||||
|
// other typevars, producing constraints on this typevar that have concrete lower/upper
|
||||||
|
// bounds. That means we can skip the simplifications below if any bound is another
|
||||||
|
// typevar.
|
||||||
|
if left_constraint.lower(db).is_type_var()
|
||||||
|
|| left_constraint.upper(db).is_type_var()
|
||||||
|
|| right_constraint.lower(db).is_type_var()
|
||||||
|
|| right_constraint.upper(db).is_type_var()
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Containment: The range of one constraint might completely contain the range of the
|
// Containment: The range of one constraint might completely contain the range of the
|
||||||
// other. If so, there are several potential simplifications.
|
// other. If so, there are several potential simplifications.
|
||||||
|
|
|
||||||
|
|
@ -971,7 +971,7 @@ impl<'db> FunctionType<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Self,
|
other: Self,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
@ -979,8 +979,10 @@ impl<'db> FunctionType<'db> {
|
||||||
// our representation of a function type includes any specialization that should be applied
|
// our representation of a function type includes any specialization that should be applied
|
||||||
// to the signature. Different specializations of the same function type are only subtypes
|
// to the signature. Different specializations of the same function type are only subtypes
|
||||||
// of each other if they result in subtype signatures.
|
// of each other if they result in subtype signatures.
|
||||||
if matches!(relation, TypeRelation::Subtyping | TypeRelation::Redundancy)
|
if matches!(
|
||||||
&& self.normalized(db) == other.normalized(db)
|
relation,
|
||||||
|
TypeRelation::Subtyping | TypeRelation::Redundancy | TypeRelation::SubtypingAssuming(_)
|
||||||
|
) && self.normalized(db) == other.normalized(db)
|
||||||
{
|
{
|
||||||
return ConstraintSet::from(true);
|
return ConstraintSet::from(true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -732,7 +732,7 @@ fn has_relation_in_invariant_position<'db>(
|
||||||
base_type: &Type<'db>,
|
base_type: &Type<'db>,
|
||||||
base_materialization: Option<MaterializationKind>,
|
base_materialization: Option<MaterializationKind>,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
@ -781,30 +781,34 @@ fn has_relation_in_invariant_position<'db>(
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
// For gradual types, A <: B (subtyping) is defined as Top[A] <: Bottom[B]
|
// For gradual types, A <: B (subtyping) is defined as Top[A] <: Bottom[B]
|
||||||
(None, Some(base_mat), TypeRelation::Subtyping | TypeRelation::Redundancy) => {
|
(
|
||||||
is_subtype_in_invariant_position(
|
None,
|
||||||
db,
|
Some(base_mat),
|
||||||
derived_type,
|
TypeRelation::Subtyping | TypeRelation::Redundancy | TypeRelation::SubtypingAssuming(_),
|
||||||
MaterializationKind::Top,
|
) => is_subtype_in_invariant_position(
|
||||||
base_type,
|
db,
|
||||||
base_mat,
|
derived_type,
|
||||||
inferable,
|
MaterializationKind::Top,
|
||||||
relation_visitor,
|
base_type,
|
||||||
disjointness_visitor,
|
base_mat,
|
||||||
)
|
inferable,
|
||||||
}
|
relation_visitor,
|
||||||
(Some(derived_mat), None, TypeRelation::Subtyping | TypeRelation::Redundancy) => {
|
disjointness_visitor,
|
||||||
is_subtype_in_invariant_position(
|
),
|
||||||
db,
|
(
|
||||||
derived_type,
|
Some(derived_mat),
|
||||||
derived_mat,
|
None,
|
||||||
base_type,
|
TypeRelation::Subtyping | TypeRelation::Redundancy | TypeRelation::SubtypingAssuming(_),
|
||||||
MaterializationKind::Bottom,
|
) => is_subtype_in_invariant_position(
|
||||||
inferable,
|
db,
|
||||||
relation_visitor,
|
derived_type,
|
||||||
disjointness_visitor,
|
derived_mat,
|
||||||
)
|
base_type,
|
||||||
}
|
MaterializationKind::Bottom,
|
||||||
|
inferable,
|
||||||
|
relation_visitor,
|
||||||
|
disjointness_visitor,
|
||||||
|
),
|
||||||
// And A <~ B (assignability) is Bottom[A] <: Top[B]
|
// And A <~ B (assignability) is Bottom[A] <: Top[B]
|
||||||
(None, Some(base_mat), TypeRelation::Assignability) => is_subtype_in_invariant_position(
|
(None, Some(base_mat), TypeRelation::Assignability) => is_subtype_in_invariant_position(
|
||||||
db,
|
db,
|
||||||
|
|
@ -1072,7 +1076,7 @@ impl<'db> Specialization<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Self,
|
other: Self,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ impl<'db> Type<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
protocol: ProtocolInstanceType<'db>,
|
protocol: ProtocolInstanceType<'db>,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
@ -370,7 +370,7 @@ impl<'db> NominalInstanceType<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Self,
|
other: Self,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ impl<'db> ProtocolInterface<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Self,
|
other: Self,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
@ -634,7 +634,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Type<'db>,
|
other: Type<'db>,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ impl<'db> CallableSignature<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: &Self,
|
other: &Self,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
@ -256,7 +256,7 @@ impl<'db> CallableSignature<'db> {
|
||||||
self_signatures: &[Signature<'db>],
|
self_signatures: &[Signature<'db>],
|
||||||
other_signatures: &[Signature<'db>],
|
other_signatures: &[Signature<'db>],
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
@ -732,7 +732,7 @@ impl<'db> Signature<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: &Signature<'db>,
|
other: &Signature<'db>,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ impl<'db> SubclassOfType<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: SubclassOfType<'db>,
|
other: SubclassOfType<'db>,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
|
||||||
|
|
@ -260,7 +260,7 @@ impl<'db> TupleType<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: Self,
|
other: Self,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
@ -442,7 +442,7 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: &Tuple<Type<'db>>,
|
other: &Tuple<Type<'db>>,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
@ -799,7 +799,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: &Tuple<Type<'db>>,
|
other: &Tuple<Type<'db>>,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
@ -1191,7 +1191,7 @@ impl<'db> Tuple<Type<'db>> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
other: &Self,
|
other: &Self,
|
||||||
inferable: InferableTypeVars<'_, 'db>,
|
inferable: InferableTypeVars<'_, 'db>,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation<'db>,
|
||||||
relation_visitor: &HasRelationToVisitor<'db>,
|
relation_visitor: &HasRelationToVisitor<'db>,
|
||||||
disjointness_visitor: &IsDisjointVisitor<'db>,
|
disjointness_visitor: &IsDisjointVisitor<'db>,
|
||||||
) -> ConstraintSet<'db> {
|
) -> ConstraintSet<'db> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue