[ty] Avoid UnionBuilder overhead when creating a new union from the filtered elements of an existing union#22352
Conversation
Typing conformance resultsNo changes detected ✅ |
|
Merging this PR will not alter performance
Comparing Footnotes
|
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
invalid-argument-type |
0 | 0 | 17 |
unresolved-attribute |
0 | 0 | 14 |
unsupported-operator |
0 | 0 | 2 |
invalid-assignment |
0 | 0 | 1 |
invalid-return-type |
0 | 0 | 1 |
not-iterable |
0 | 0 | 1 |
unused-type-ignore-comment |
0 | 1 | 0 |
| Total | 0 | 1 | 36 |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
e08e60c to
daa878c
Compare
This reproduction demonstrates the behavior difference between main branch and PR #22352 on the prefect codebase. Main branch shows: error[unknown-argument]: Argument `_sync` does not match any known parameter PR branch (1ab630c): All checks passed The key components: - async_dispatch decorator with Union type parameter - Block class with decorated load method returning Self - inject_client decorator using ParamSpec Run from repro/ directory: ty check integrations/prefect-azure/prefect_azure/experimental/bundles/execute.py
This test verifies that decorators using ParamSpec can internally handle kwargs that aren't in the original function signature (via kwargs.pop()) without causing false positive unknown-argument errors. The test fails on main with: - error[invalid-await]: union type not awaitable - error[unknown-argument]: _sync not recognized The test passes after PR #22352's fix, which preserves cycle recovery state when filtering union types.
This comment was marked as resolved.
This comment was marked as resolved.
aae5dd3 to
9a5b6b1
Compare
f02aaa2 to
3b62be2
Compare
This comment was marked as outdated.
This comment was marked as outdated.
3b62be2 to
2670a8c
Compare
|
Okay, this PR now has no changes to diagnostics across the ecosystem after being rebased on top of #23014 -- so it's ready for review! |
| .build() | ||
| pub(crate) fn filter(self, db: &'db dyn Db, f: impl FnMut(&Type<'db>) -> bool) -> Type<'db> { | ||
| let current = self.elements(db); | ||
| let new: Box<[Type<'db>]> = current.iter().copied().filter(f).collect(); |
There was a problem hiding this comment.
I experimented with .collect()ing into a SmallVec<[Type<'db>; 1]>, since allocating feels unnecessary here if there is only one element after filtering out elements that don't match the predicate. But this appeared slightly slower -- probably because in the common case, we do still have >1 element after applying the filter, and so you just add a bunch of SmallVec branches that need to check whether we're in the fast path or the slow path, and you don't gain much.
2670a8c to
eff4294
Compare
uff, you're right. I assumed that those were some of our standard flakes, but they are indeed consistently different on this PR branch than they are on |
… filtered elements of an existing union
eff4294 to
263114b
Compare
Memory usage reportSummary
Significant changesClick to expand detailed breakdownflake8
trio
sphinx
prefect
|
* main: (209 commits) [ty] Defer base inference for functional `type(...)` classes (#22792) flake8-executable: allow global flags in uv shebangs (EXE003) (#22582) [ty] Add `replace-imports-with-any` option (#23122) Update html comments in mdtests (#23269) Apply ruff formatting to mdtests (#22935) [ty] Exclude test-related symbols from non-first-party packages in auto-import completions [ty] Refactor `CursorTest` helper to support site-packages [ty] Qualify inlay hint edit symbol when possibly referencing another variable (#23265) [ty] Avoid `UnionBuilder` overhead when creating a new union from the filtered elements of an existing union (#22352) [ty] Refactor TypedDict key assignment validation (#23262) [ty] Improve Python environment path documentation (#23256) [ty] loop control flow analysis using loop header definitions Prepare for 0.15.1 (#23253) Remove docker-run-action (#23254) [ty] Allow discovering dependencies in system Python environments (#22994) Ensure pending suppression diagnostics are reported (#23242) [`isort`] support for configurable import section heading comments (#23151) [ty] Fix method calls on subclasses of `Any` (#23248) [ty] Fix bound method access on `None` (#23246) Make range suppression test snapshot actually useful (#23251) ...




Note: this PR is stacked on top of #22845. Applied directly tomain, this PR would lead to a change in behaviour on at least one ecosystem project. See the PR summary in #22845 for more details.Summary
In order to ensure that unions are kept in a simplified form everywhere, the
UnionBuilderperforms a number of expensive subtyping/redundancy checks between elements. But when creating a new union by filtering the elements of an existing union, these subtyping/redundancy checks are (theoretically) unnecessary. We already know that the existing union will have upheld the invariants maintained by theUnionBuilder, and that therefore it cannot contain a pair of types where one type is a subtype of the other.This PR reduces overhead in
UnionType::filter()by callingUnionType::new()directly instead of going via theUnionBuilder. It also adjusts several other callsites to useUnionType::filter()rather than manually filtering the union elements and then callingUnionType::from_elements().Test Plan
Existing tests all pass, and (following #23014) there are no longer any changes to diagnostics across the ecosystem.