[ty] don't expand type aliases in implicit tuple aliases

This commit is contained in:
Shunsuke Shibayama 2025-12-17 13:54:20 +09:00
parent b0bc990cbf
commit 60a7af5a55
2 changed files with 20 additions and 9 deletions

View File

@ -0,0 +1,8 @@
# Regression test for https://github.com/astral-sh/ty/issues/1848
T = tuple[int, 'U']
class C(set['U']):
pass
type U = T | C

View File

@ -411,7 +411,8 @@ impl<'db> FixedLengthTuple<Type<'db>> {
// suffix. // suffix.
let mut elements = self.elements().copied(); let mut elements = self.elements().copied();
let prefix = elements.by_ref().take(prefix).collect(); let prefix = elements.by_ref().take(prefix).collect();
let variable = UnionType::from_elements(db, elements.by_ref().take(variable)); let variable =
UnionType::from_elements_leave_aliases(db, elements.by_ref().take(variable));
let suffix = elements.by_ref().take(suffix).collect(); let suffix = elements.by_ref().take(suffix).collect();
Ok(Tuple::Variable(VariableLengthTuple { Ok(Tuple::Variable(VariableLengthTuple {
prefix, prefix,
@ -788,7 +789,7 @@ impl<'db> VariableLengthTuple<Type<'db>> {
let suffix_underflow = suffix_length.saturating_sub(self_suffix_length); let suffix_underflow = suffix_length.saturating_sub(self_suffix_length);
let prefix = (self.prefix_elements().copied().take(prefix_length)) let prefix = (self.prefix_elements().copied().take(prefix_length))
.chain(std::iter::repeat_n(self.variable, prefix_underflow)); .chain(std::iter::repeat_n(self.variable, prefix_underflow));
let variable = UnionType::from_elements( let variable = UnionType::from_elements_leave_aliases(
db, db,
(self.prefix_elements().copied().skip(prefix_length)) (self.prefix_elements().copied().skip(prefix_length))
.chain(std::iter::once(self.variable)) .chain(std::iter::once(self.variable))
@ -1158,7 +1159,7 @@ impl<'db> PyIndex<'db> for &VariableLengthTuple<Type<'db>> {
// large enough that it lands in the variable-length portion. It might also be // large enough that it lands in the variable-length portion. It might also be
// small enough to land in the suffix. // small enough to land in the suffix.
let index_past_prefix = index - self.prefix.len() + 1; let index_past_prefix = index - self.prefix.len() + 1;
Ok(UnionType::from_elements( Ok(UnionType::from_elements_leave_aliases(
db, db,
std::iter::once(self.variable) std::iter::once(self.variable)
.chain(self.suffix_elements().copied().take(index_past_prefix)), .chain(self.suffix_elements().copied().take(index_past_prefix)),
@ -1175,7 +1176,7 @@ impl<'db> PyIndex<'db> for &VariableLengthTuple<Type<'db>> {
// large enough that it lands in the variable-length portion. It might also be // large enough that it lands in the variable-length portion. It might also be
// small enough to land in the prefix. // small enough to land in the prefix.
let index_past_suffix = index_from_end - self.suffix.len() + 1; let index_past_suffix = index_from_end - self.suffix.len() + 1;
Ok(UnionType::from_elements( Ok(UnionType::from_elements_leave_aliases(
db, db,
(self.prefix_elements().rev().copied()) (self.prefix_elements().rev().copied())
.take(index_past_suffix) .take(index_past_suffix)
@ -1260,7 +1261,7 @@ impl<T> Tuple<T> {
impl<'db> Tuple<Type<'db>> { impl<'db> Tuple<Type<'db>> {
pub(crate) fn homogeneous_element_type(&self, db: &'db dyn Db) -> Type<'db> { pub(crate) fn homogeneous_element_type(&self, db: &'db dyn Db) -> Type<'db> {
UnionType::from_elements(db, self.all_elements()) UnionType::from_elements_leave_aliases(db, self.all_elements())
} }
/// Resizes this tuple to a different length, if possible. If this tuple cannot satisfy the /// Resizes this tuple to a different length, if possible. If this tuple cannot satisfy the
@ -1718,7 +1719,7 @@ impl<'db> TupleSpecBuilder<'db> {
suffix: right_suffix, suffix: right_suffix,
}), }),
) => { ) => {
let variable = UnionType::from_elements( let variable = UnionType::from_elements_leave_aliases(
db, db,
left_suffix left_suffix
.iter() .iter()
@ -1763,7 +1764,7 @@ impl<'db> TupleSpecBuilder<'db> {
if our_elements.len() == new_elements.len() => if our_elements.len() == new_elements.len() =>
{ {
for (existing, new) in our_elements.iter_mut().zip(new_elements.elements()) { for (existing, new) in our_elements.iter_mut().zip(new_elements.elements()) {
*existing = UnionType::from_elements(db, [*existing, *new]); *existing = UnionType::from_elements_leave_aliases(db, [*existing, *new]);
} }
self self
} }
@ -1776,8 +1777,10 @@ impl<'db> TupleSpecBuilder<'db> {
// would actually lead to more precise inference, so it's probably not worth the // would actually lead to more precise inference, so it's probably not worth the
// complexity. // complexity.
_ => { _ => {
let unioned = let unioned = UnionType::from_elements_leave_aliases(
UnionType::from_elements(db, self.all_elements().chain(other.all_elements())); db,
self.all_elements().chain(other.all_elements()),
);
TupleSpecBuilder::Variable { TupleSpecBuilder::Variable {
prefix: vec![], prefix: vec![],
variable: unioned, variable: unioned,