diff --git a/crates/ruff_benchmark/benches/ty.rs b/crates/ruff_benchmark/benches/ty.rs index eb5a000587..02a10a4100 100644 --- a/crates/ruff_benchmark/benches/ty.rs +++ b/crates/ruff_benchmark/benches/ty.rs @@ -351,6 +351,41 @@ fn benchmark_many_tuple_assignments(criterion: &mut Criterion) { }); } +fn benchmark_tuple_implicit_instance_attributes(criterion: &mut Criterion) { + setup_rayon(); + + criterion.bench_function("ty_micro[many_tuple_assignments]", |b| { + b.iter_batched_ref( + || { + // This is a regression benchmark for a case that used to hang: + // https://github.com/astral-sh/ty/issues/765 + setup_micro_case( + r#" + from typing import Any + + class A: + foo: tuple[Any, ...] + + class B(A): + def __init__(self, parent: "C", x: tuple[Any]): + self.foo = parent.foo + x + + class C(A): + def __init__(self, parent: B, x: tuple[Any]): + self.foo = parent.foo + x + "#, + ) + }, + |case| { + let Case { db, .. } = case; + let result = db.check(); + assert_eq!(result.len(), 0); + }, + BatchSize::SmallInput, + ); + }); +} + fn benchmark_complex_constrained_attributes_1(criterion: &mut Criterion) { setup_rayon(); @@ -630,6 +665,7 @@ criterion_group!( micro, benchmark_many_string_assignments, benchmark_many_tuple_assignments, + benchmark_tuple_implicit_instance_attributes, benchmark_complex_constrained_attributes_1, benchmark_complex_constrained_attributes_2, benchmark_many_enum_members, diff --git a/crates/ty_python_semantic/resources/mdtest/assignment/augmented.md b/crates/ty_python_semantic/resources/mdtest/assignment/augmented.md index ceac281fff..d800c86c6a 100644 --- a/crates/ty_python_semantic/resources/mdtest/assignment/augmented.md +++ b/crates/ty_python_semantic/resources/mdtest/assignment/augmented.md @@ -13,7 +13,7 @@ reveal_type(x) # revealed: int | float x = (1, 2) x += (3, 4) -reveal_type(x) # revealed: tuple[Literal[1], Literal[2], Literal[3], Literal[4]] +reveal_type(x) # revealed: tuple[Literal[1, 2, 3, 4], ...] ``` ## Dunder methods diff --git a/crates/ty_python_semantic/resources/mdtest/binary/tuples.md b/crates/ty_python_semantic/resources/mdtest/binary/tuples.md index 947262fc69..c6a2142044 100644 --- a/crates/ty_python_semantic/resources/mdtest/binary/tuples.md +++ b/crates/ty_python_semantic/resources/mdtest/binary/tuples.md @@ -3,14 +3,14 @@ ## Concatenation for heterogeneous tuples ```py -reveal_type((1, 2) + (3, 4)) # revealed: tuple[Literal[1], Literal[2], Literal[3], Literal[4]] -reveal_type(() + (1, 2)) # revealed: tuple[Literal[1], Literal[2]] -reveal_type((1, 2) + ()) # revealed: tuple[Literal[1], Literal[2]] +reveal_type((1, 2) + (3, 4)) # revealed: tuple[Literal[1, 2, 3, 4], ...] +reveal_type(() + (1, 2)) # revealed: tuple[Literal[1, 2], ...] +reveal_type((1, 2) + ()) # revealed: tuple[Literal[1, 2], ...] reveal_type(() + ()) # revealed: tuple[()] def _(x: tuple[int, str], y: tuple[None, tuple[int]]): - reveal_type(x + y) # revealed: tuple[int, str, None, tuple[int]] - reveal_type(y + x) # revealed: tuple[None, tuple[int], int, str] + reveal_type(x + y) # revealed: tuple[int | str | None | tuple[int], ...] + reveal_type(y + x) # revealed: tuple[None | tuple[int] | int | str, ...] ``` ## Concatenation for homogeneous tuples @@ -19,10 +19,10 @@ def _(x: tuple[int, str], y: tuple[None, tuple[int]]): def _(x: tuple[int, ...], y: tuple[str, ...]): reveal_type(x + x) # revealed: tuple[int, ...] reveal_type(x + y) # revealed: tuple[int | str, ...] - reveal_type((1, 2) + x) # revealed: tuple[Literal[1], Literal[2], *tuple[int, ...]] - reveal_type(x + (3, 4)) # revealed: tuple[*tuple[int, ...], Literal[3], Literal[4]] - reveal_type((1, 2) + x + (3, 4)) # revealed: tuple[Literal[1], Literal[2], *tuple[int, ...], Literal[3], Literal[4]] - reveal_type((1, 2) + y + (3, 4) + x) # revealed: tuple[Literal[1], Literal[2], *tuple[int | str, ...]] + reveal_type((1, 2) + x) # revealed: tuple[int, ...] + reveal_type(x + (3, 4)) # revealed: tuple[int, ...] + reveal_type((1, 2) + x + (3, 4)) # revealed: tuple[int, ...] + reveal_type((1, 2) + y + (3, 4) + x) # revealed: tuple[int | str, ...] ``` We get the same results even when we use a legacy type alias, even though this involves first @@ -41,8 +41,8 @@ StrTuple = tuple[str, ...] def _(one_two: OneTwo, x: IntTuple, y: StrTuple, three_four: ThreeFour): reveal_type(x + x) # revealed: tuple[int, ...] reveal_type(x + y) # revealed: tuple[int | str, ...] - reveal_type(one_two + x) # revealed: tuple[Literal[1], Literal[2], *tuple[int, ...]] - reveal_type(x + three_four) # revealed: tuple[*tuple[int, ...], Literal[3], Literal[4]] - reveal_type(one_two + x + three_four) # revealed: tuple[Literal[1], Literal[2], *tuple[int, ...], Literal[3], Literal[4]] - reveal_type(one_two + y + three_four + x) # revealed: tuple[Literal[1], Literal[2], *tuple[int | str, ...]] + reveal_type(one_two + x) # revealed: tuple[int, ...] + reveal_type(x + three_four) # revealed: tuple[int, ...] + reveal_type(one_two + x + three_four) # revealed: tuple[int, ...] + reveal_type(one_two + y + three_four + x) # revealed: tuple[int | str, ...] ``` diff --git a/crates/ty_python_semantic/resources/mdtest/instance_layout_conflict.md b/crates/ty_python_semantic/resources/mdtest/instance_layout_conflict.md index 4f5bc1dd2b..45f5c5d53c 100644 --- a/crates/ty_python_semantic/resources/mdtest/instance_layout_conflict.md +++ b/crates/ty_python_semantic/resources/mdtest/instance_layout_conflict.md @@ -122,15 +122,14 @@ class A: __slots__ = () __slots__ += ("a", "b") -reveal_type(A.__slots__) # revealed: tuple[Literal["a"], Literal["b"]] +reveal_type(A.__slots__) # revealed: tuple[Literal["a", "b"], ...] class B: __slots__ = ("c", "d") -class C( # error: [instance-layout-conflict] - A, - B, -): ... +# TODO: ideally this would trigger `[instance-layout-conflict]` +# (but it's also not high-priority) +class C(A, B): ... ``` ## Explicitly annotated `__slots__` diff --git a/crates/ty_python_semantic/resources/mdtest/subscript/tuple.md b/crates/ty_python_semantic/resources/mdtest/subscript/tuple.md index 9b345f7978..a5be45f5cb 100644 --- a/crates/ty_python_semantic/resources/mdtest/subscript/tuple.md +++ b/crates/ty_python_semantic/resources/mdtest/subscript/tuple.md @@ -240,9 +240,7 @@ def homogeneous(t: tuple[str, ...]) -> None: reveal_type(t[-3]) # revealed: str reveal_type(t[-4]) # revealed: str -def mixed(s: tuple[str, ...]) -> None: - t = (1, 2, 3) + s + (8, 9, 10) - +def mixed(t: tuple[Literal[1], Literal[2], Literal[3], *tuple[str, ...], Literal[8], Literal[9], Literal[10]]) -> None: reveal_type(t[0]) # revealed: Literal[1] reveal_type(t[1]) # revealed: Literal[2] reveal_type(t[2]) # revealed: Literal[3] diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index 8c395cb31a..064f907edc 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -7061,8 +7061,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { // the result would then become Any or Unknown, respectively). (any @ Type::Dynamic(DynamicType::Any), _, _) | (_, any @ Type::Dynamic(DynamicType::Any), _) => Some(any), + (unknown @ Type::Dynamic(DynamicType::Unknown), _, _) | (_, unknown @ Type::Dynamic(DynamicType::Unknown), _) => Some(unknown), + ( todo @ Type::Dynamic( DynamicType::Todo(_) @@ -7083,6 +7085,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { ), _, ) => Some(todo), + (Type::Never, _, _) | (_, Type::Never, _) => Some(Type::Never), (Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Add) => Some( @@ -7235,13 +7238,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { op, ), - (Type::Tuple(lhs), Type::Tuple(rhs), ast::Operator::Add) => { - Some(Type::tuple(TupleType::new( - self.db(), - lhs.tuple(self.db()).concat(self.db(), rhs.tuple(self.db())), - ))) - } - // We've handled all of the special cases that we support for literals, so we need to // fall back on looking for dunder methods on one of the operand types. (