Skip to content

Commit 3cd18a2

Browse files
committed
[ty] Move Salsa caching "down a layer" in tuple.rs
1 parent 498a048 commit 3cd18a2

File tree

11 files changed

+422
-344
lines changed

11 files changed

+422
-344
lines changed

crates/ty_python_semantic/src/types.rs

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use infer::nearest_enclosing_class;
22
use itertools::{Either, Itertools};
33
use ruff_db::parsed::parsed_module;
44

5-
use std::borrow::Cow;
65
use std::slice::Iter;
76

87
use bitflags::bitflags;
@@ -687,7 +686,7 @@ impl<'db> Type<'db> {
687686
///
688687
/// I.e., for the type `tuple[int, str]`, this will return the tuple spec `[int, str]`.
689688
/// For a subclass of `tuple[int, str]`, it will return the same tuple spec.
690-
fn tuple_instance_spec(&self, db: &'db dyn Db) -> Option<Cow<'db, TupleSpec<'db>>> {
689+
fn tuple_instance_spec(&self, db: &'db dyn Db) -> Option<TupleSpec<'db>> {
691690
self.into_nominal_instance()
692691
.and_then(|instance| instance.tuple_spec(db))
693692
}
@@ -702,9 +701,9 @@ impl<'db> Type<'db> {
702701
///
703702
/// I.e., for the type `tuple[int, str]`, this will return the tuple spec `[int, str]`.
704703
/// But for a subclass of `tuple[int, str]`, it will return `None`.
705-
fn exact_tuple_instance_spec(&self, db: &'db dyn Db) -> Option<Cow<'db, TupleSpec<'db>>> {
704+
fn exact_tuple_instance_spec(&self) -> Option<TupleSpec<'db>> {
706705
self.into_nominal_instance()
707-
.and_then(|instance| instance.own_tuple_spec(db))
706+
.and_then(|instance| instance.own_tuple_spec())
708707
}
709708

710709
/// Returns the materialization of this type depending on the given `variance`.
@@ -4622,9 +4621,9 @@ impl<'db> Type<'db> {
46224621
///
46234622
/// This method should only be used outside of type checking because it omits any errors.
46244623
/// For type checking, use [`try_iterate`](Self::try_iterate) instead.
4625-
fn iterate(self, db: &'db dyn Db) -> Cow<'db, TupleSpec<'db>> {
4624+
fn iterate(self, db: &'db dyn Db) -> TupleSpec<'db> {
46264625
self.try_iterate(db)
4627-
.unwrap_or_else(|err| Cow::Owned(TupleSpec::homogeneous(err.fallback_element_type(db))))
4626+
.unwrap_or_else(|err| TupleSpec::homogeneous(db, err.fallback_element_type(db)))
46284627
}
46294628

46304629
/// Given the type of an object that is iterated over in some way,
@@ -4635,15 +4634,15 @@ impl<'db> Type<'db> {
46354634
/// ```python
46364635
/// y(*x)
46374636
/// ```
4638-
fn try_iterate(self, db: &'db dyn Db) -> Result<Cow<'db, TupleSpec<'db>>, IterationError<'db>> {
4637+
fn try_iterate(self, db: &'db dyn Db) -> Result<TupleSpec<'db>, IterationError<'db>> {
46394638
self.try_iterate_with_mode(db, EvaluationMode::Sync)
46404639
}
46414640

46424641
fn try_iterate_with_mode(
46434642
self,
46444643
db: &'db dyn Db,
46454644
mode: EvaluationMode,
4646-
) -> Result<Cow<'db, TupleSpec<'db>>, IterationError<'db>> {
4645+
) -> Result<TupleSpec<'db>, IterationError<'db>> {
46474646
if mode.is_async() {
46484647
let try_call_dunder_anext_on_iterator = |iterator: Type<'db>| {
46494648
iterator
@@ -4657,7 +4656,7 @@ impl<'db> Type<'db> {
46574656
Ok(dunder_aiter_bindings) => {
46584657
let iterator = dunder_aiter_bindings.return_type(db);
46594658
match try_call_dunder_anext_on_iterator(iterator) {
4660-
Ok(result) => Ok(Cow::Owned(TupleSpec::homogeneous(result))),
4659+
Ok(result) => Ok(TupleSpec::homogeneous(db, result)),
46614660
Err(dunder_anext_error) => {
46624661
Err(IterationError::IterReturnsInvalidIterator {
46634662
iterator,
@@ -4702,27 +4701,28 @@ impl<'db> Type<'db> {
47024701
}
47034702
}
47044703
Type::GenericAlias(alias) if alias.origin(db).is_tuple(db) => {
4705-
return Ok(Cow::Owned(TupleSpec::homogeneous(todo_type!(
4706-
"*tuple[] annotations"
4707-
))));
4704+
return Ok(TupleSpec::homogeneous(
4705+
db,
4706+
todo_type!("*tuple[] annotations"),
4707+
));
47084708
}
47094709
Type::StringLiteral(string_literal_ty) => {
47104710
// We could go further and deconstruct to an array of `StringLiteral`
47114711
// with each individual character, instead of just an array of
47124712
// `LiteralString`, but there would be a cost and it's not clear that
47134713
// it's worth it.
4714-
return Ok(Cow::Owned(TupleSpec::from_elements(std::iter::repeat_n(
4715-
Type::LiteralString,
4716-
string_literal_ty.python_len(db),
4717-
))));
4714+
return Ok(TupleSpec::heterogeneous(
4715+
db,
4716+
std::iter::repeat_n(Type::LiteralString, string_literal_ty.python_len(db)),
4717+
));
47184718
}
47194719
Type::Never => {
47204720
// The dunder logic below would have us return `tuple[Never, ...]`, which eagerly
47214721
// simplifies to `tuple[()]`. That will will cause us to emit false positives if we
47224722
// index into the tuple. Using `tuple[Unknown, ...]` avoids these false positives.
47234723
// TODO: Consider removing this special case, and instead hide the indexing
47244724
// diagnostic in unreachable code.
4725-
return Ok(Cow::Owned(TupleSpec::homogeneous(Type::unknown())));
4725+
return Ok(TupleSpec::homogeneous(db, Type::unknown()));
47264726
}
47274727
_ => {}
47284728
}
@@ -4751,7 +4751,7 @@ impl<'db> Type<'db> {
47514751
// `__iter__` is definitely bound and calling it succeeds.
47524752
// See what calling `__next__` on the object returned by `__iter__` gives us...
47534753
try_call_dunder_next_on_iterator(iterator)
4754-
.map(|ty| Cow::Owned(TupleSpec::homogeneous(ty)))
4754+
.map(|ty| TupleSpec::homogeneous(db, ty))
47554755
.map_err(
47564756
|dunder_next_error| IterationError::IterReturnsInvalidIterator {
47574757
iterator,
@@ -4776,10 +4776,13 @@ impl<'db> Type<'db> {
47764776
// and the type returned by the `__getitem__` method.
47774777
//
47784778
// No diagnostic is emitted; iteration will always succeed!
4779-
Cow::Owned(TupleSpec::homogeneous(UnionType::from_elements(
4779+
TupleSpec::homogeneous(
47804780
db,
4781-
[dunder_next_return, dunder_getitem_return_type],
4782-
)))
4781+
UnionType::from_elements(
4782+
db,
4783+
[dunder_next_return, dunder_getitem_return_type],
4784+
),
4785+
)
47834786
})
47844787
.map_err(|dunder_getitem_error| {
47854788
IterationError::PossiblyUnboundIterAndGetitemError {
@@ -4806,7 +4809,7 @@ impl<'db> Type<'db> {
48064809

48074810
// There's no `__iter__` method. Try `__getitem__` instead...
48084811
Err(CallDunderError::MethodNotAvailable) => try_call_dunder_getitem()
4809-
.map(|ty| Cow::Owned(TupleSpec::homogeneous(ty)))
4812+
.map(|ty| TupleSpec::homogeneous(db, ty))
48104813
.map_err(
48114814
|dunder_getitem_error| IterationError::UnboundIterAndGetitemError {
48124815
dunder_getitem_error,

crates/ty_python_semantic/src/types/call/arguments.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl<'a, 'db> CallArguments<'a, 'db> {
5353
let ty = infer_argument_type(arg, value);
5454
let length = ty
5555
.try_iterate(db)
56-
.map(|tuple| tuple.len())
56+
.map(|tuple| tuple.len(db))
5757
.unwrap_or(TupleLength::unknown());
5858
(Argument::Variadic(length), Some(ty))
5959
}
@@ -225,7 +225,7 @@ fn expand_type<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<Vec<Type<'db>>> {
225225

226226
// If the class is a fixed-length tuple subtype, we expand it to its elements.
227227
if let Some(spec) = instance.tuple_spec(db) {
228-
return match &*spec {
228+
return match spec.inner(db) {
229229
Tuple::Fixed(fixed_length_tuple) => {
230230
let expanded = fixed_length_tuple
231231
.all_elements()
@@ -237,7 +237,7 @@ fn expand_type<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<Vec<Type<'db>>> {
237237
}
238238
})
239239
.multi_cartesian_product()
240-
.map(|types| Type::tuple(TupleType::from_elements(db, types)))
240+
.map(|types| Type::tuple(TupleType::heterogeneous(db, types)))
241241
.collect::<Vec<_>>();
242242

243243
if expanded.len() == 1 {

crates/ty_python_semantic/src/types/call/bind.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
//! [signatures][crate::types::signatures], we have to handle the fact that the callable might be a
44
//! union of types, each of which might contain multiple overloads.
55
6-
use std::borrow::Cow;
76
use std::collections::HashSet;
87
use std::fmt;
98

@@ -1034,10 +1033,8 @@ impl<'db> Bindings<'db> {
10341033
);
10351034
continue;
10361035
};
1037-
overload.set_return_type(Type::tuple(TupleType::new(
1038-
db,
1039-
tuple_spec.as_ref(),
1040-
)));
1036+
overload
1037+
.set_return_type(Type::tuple(TupleType::new(db, tuple_spec)));
10411038
}
10421039
}
10431040

@@ -2151,9 +2148,9 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
21512148
// arbitrary number of `Unknown`s at the end, so that if the expanded value has a
21522149
// smaller arity than the unexpanded value, we still have enough values to assign to
21532150
// the already matched parameters.
2154-
let argument_types = match argument_types.as_ref() {
2151+
let argument_types = match argument_types.inner(self.db) {
21552152
Tuple::Fixed(_) => {
2156-
Cow::Owned(argument_types.concat(self.db, &Tuple::homogeneous(Type::unknown())))
2153+
argument_types.concat(self.db, &Tuple::homogeneous(Type::unknown()))
21572154
}
21582155
Tuple::Variable(_) => argument_types,
21592156
};
@@ -2173,13 +2170,13 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
21732170

21742171
// Check the types by zipping through the splatted argument types and their matched
21752172
// parameters.
2176-
for (argument_type, parameter_index) in (argument_types.all_elements())
2173+
for (argument_type, parameter_index) in (argument_types.all_elements(self.db))
21772174
.zip(&self.argument_matches[argument_index].parameters)
21782175
{
21792176
self.check_argument_type(
21802177
adjusted_argument_index,
21812178
argument,
2182-
*argument_type,
2179+
argument_type,
21832180
*parameter_index,
21842181
);
21852182
}

crates/ty_python_semantic/src/types/class.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::types::function::{DataclassTransformerParams, KnownFunction};
2020
use crate::types::generics::{GenericContext, Specialization, walk_specialization};
2121
use crate::types::infer::nearest_enclosing_class;
2222
use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature};
23-
use crate::types::tuple::TupleSpec;
23+
use crate::types::tuple::Tuple;
2424
use crate::types::{
2525
BareTypeAliasType, Binding, BoundSuperError, BoundSuperType, CallableType, DataclassParams,
2626
DeprecatedInstance, KnownInstanceType, StringLiteralType, TypeAliasType, TypeMapping,
@@ -661,7 +661,7 @@ impl<'db> ClassType<'db> {
661661
"__len__" if class_literal.is_tuple(db) => {
662662
let return_type = specialization
663663
.and_then(|spec| spec.tuple(db))
664-
.and_then(|tuple| tuple.len().into_fixed_length())
664+
.and_then(|tuple| tuple.len(db).into_fixed_length())
665665
.and_then(|len| i64::try_from(len).ok())
666666
.map(Type::IntLiteral)
667667
.unwrap_or_else(|| KnownClass::Int.to_instance(db));
@@ -672,7 +672,7 @@ impl<'db> ClassType<'db> {
672672
"__bool__" if class_literal.is_tuple(db) => {
673673
let return_type = specialization
674674
.and_then(|spec| spec.tuple(db))
675-
.map(|tuple| tuple.truthiness().into_type(db))
675+
.map(|tuple| tuple.truthiness(db).into_type(db))
676676
.unwrap_or_else(|| KnownClass::Bool.to_instance(db));
677677

678678
synthesize_simple_tuple_method(return_type)
@@ -685,13 +685,13 @@ impl<'db> ClassType<'db> {
685685
let mut element_type_to_indices: FxIndexMap<Type<'db>, Vec<i64>> =
686686
FxIndexMap::default();
687687

688-
match tuple {
688+
match tuple.inner(db) {
689689
// E.g. for `tuple[int, str]`, we will generate the following overloads:
690690
//
691691
// __getitem__(self, index: Literal[0, -2], /) -> int
692692
// __getitem__(self, index: Literal[1, -1], /) -> str
693693
//
694-
TupleSpec::Fixed(fixed_length_tuple) => {
694+
Tuple::Fixed(fixed_length_tuple) => {
695695
let tuple_length = fixed_length_tuple.len();
696696

697697
for (index, ty) in fixed_length_tuple.elements().enumerate() {
@@ -714,7 +714,7 @@ impl<'db> ClassType<'db> {
714714
// __getitem__(self, index: Literal[-2], /) -> bytes
715715
// __getitem__(self, index: Literal[-3], /) -> float | str
716716
//
717-
TupleSpec::Variable(variable_length_tuple) => {
717+
Tuple::Variable(variable_length_tuple) => {
718718
for (index, ty) in variable_length_tuple.prefix.iter().enumerate() {
719719
if let Ok(index) = i64::try_from(index) {
720720
element_type_to_indices.entry(*ty).or_default().push(index);
@@ -778,7 +778,7 @@ impl<'db> ClassType<'db> {
778778
}
779779

780780
let all_elements_unioned =
781-
UnionType::from_elements(db, tuple.all_elements());
781+
UnionType::from_elements(db, tuple.all_elements(db));
782782

783783
let mut overload_signatures =
784784
Vec::with_capacity(element_type_to_indices.len().saturating_add(2));
@@ -850,15 +850,15 @@ impl<'db> ClassType<'db> {
850850
Some(tuple) => {
851851
// TODO: Once we support PEP 646 annotations for `*args` parameters, we can
852852
// use the tuple itself as the argument type.
853-
let tuple_len = tuple.len();
853+
let tuple_len = tuple.len(db);
854854

855855
if tuple_len.minimum() == 0 && tuple_len.maximum().is_none() {
856856
// If the tuple has no length restrictions,
857857
// any iterable is allowed as long as the iterable has the correct element type.
858-
let mut tuple_elements = tuple.all_elements();
858+
let mut tuple_elements = tuple.all_elements(db);
859859
iterable_parameter = iterable_parameter.with_annotated_type(
860860
KnownClass::Iterable
861-
.to_specialized_instance(db, [*tuple_elements.next().unwrap()]),
861+
.to_specialized_instance(db, [tuple_elements.next().unwrap()]),
862862
);
863863
assert_eq!(
864864
tuple_elements.next(),
@@ -884,7 +884,7 @@ impl<'db> ClassType<'db> {
884884
// - a zero-length tuple
885885
// - an unspecialized tuple
886886
// - a tuple with no minimum length
887-
if tuple.is_none_or(|tuple| tuple.len().minimum() == 0) {
887+
if tuple.is_none_or(|tuple| tuple.len(db).minimum() == 0) {
888888
iterable_parameter =
889889
iterable_parameter.with_default_type(Type::empty_tuple(db));
890890
}
@@ -4707,7 +4707,7 @@ impl SlotsKind {
47074707
// __slots__ = ("a", "b")
47084708
Type::NominalInstance(nominal) => match nominal
47094709
.tuple_spec(db)
4710-
.and_then(|spec| spec.len().into_fixed_length())
4710+
.and_then(|spec| spec.len(db).into_fixed_length())
47114711
{
47124712
Some(0) => Self::Empty,
47134713
Some(_) => Self::NotEmpty,

crates/ty_python_semantic/src/types/display.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::types::class::{ClassLiteral, ClassType, GenericAlias};
1212
use crate::types::function::{FunctionType, OverloadLiteral};
1313
use crate::types::generics::{GenericContext, Specialization};
1414
use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature};
15-
use crate::types::tuple::TupleSpec;
15+
use crate::types::tuple::{Tuple, TupleSpec};
1616
use crate::types::{
1717
BoundTypeVarInstance, CallableType, IntersectionType, KnownClass, MethodWrapperKind, Protocol,
1818
StringLiteralType, SubclassOfInner, Type, TypeVarBoundOrConstraints, TypeVarInstance,
@@ -263,8 +263,8 @@ pub(crate) struct DisplayTuple<'db> {
263263
impl Display for DisplayTuple<'_> {
264264
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
265265
f.write_str("tuple[")?;
266-
match self.tuple {
267-
TupleSpec::Fixed(tuple) => {
266+
match self.tuple.inner(self.db) {
267+
Tuple::Fixed(tuple) => {
268268
let elements = tuple.elements_slice();
269269
if elements.is_empty() {
270270
f.write_str("()")?;
@@ -287,7 +287,7 @@ impl Display for DisplayTuple<'_> {
287287
// above only an S is included only if there's a suffix; anything about both a P and an
288288
// S is included if there is either a prefix or a suffix. The initial `tuple[` and
289289
// trailing `]` are printed elsewhere. The `yyy, ...` is printed no matter what.)
290-
TupleSpec::Variable(tuple) => {
290+
Tuple::Variable(tuple) => {
291291
if !tuple.prefix.is_empty() {
292292
tuple.prefix.display(self.db).fmt(f)?;
293293
f.write_str(", ")?;

0 commit comments

Comments
 (0)