diff --git a/crates/ty_python_semantic/resources/mdtest/function/return_type.md b/crates/ty_python_semantic/resources/mdtest/function/return_type.md index d5d570ab97..fe44fe3b9d 100644 --- a/crates/ty_python_semantic/resources/mdtest/function/return_type.md +++ b/crates/ty_python_semantic/resources/mdtest/function/return_type.md @@ -505,3 +505,16 @@ class Abstract(Protocol): class Concrete(Abstract): def method(self) -> str: ... # error: [invalid-return-type] ``` + +## Diagnostics for `invalid-return-type` on dynamic type + +```toml +environment.python-version = "3.12" +``` + +```py +from typing import Never, Any + +def f(func: Any) -> Never: # error: [invalid-return-type] + func() +``` diff --git a/crates/ty_python_semantic/src/semantic_index/reachability_constraints.rs b/crates/ty_python_semantic/src/semantic_index/reachability_constraints.rs index 2d1609b7a9..9042e919be 100644 --- a/crates/ty_python_semantic/src/semantic_index/reachability_constraints.rs +++ b/crates/ty_python_semantic/src/semantic_index/reachability_constraints.rs @@ -815,6 +815,17 @@ impl ReachabilityConstraints { // very large in number, since we add them on all statement level function calls. let ty = infer_expression_type(db, callable); + // Short-circuit for well known types that are known not to return `Never` when called. + // Without the short-circuit, we've seen that threads keep blocking each other + // because they all try to acquire Salsa's `CallableType` lock that ensures each type + // is only interned once. The lock is so heavily congested because there are only + // very few dynamic types, in which case Salsa's sharding the locks by value + // doesn't help much. + // See . + if matches!(ty, Type::Dynamic(_)) { + return Truthiness::AlwaysFalse.negate_if(!predicate.is_positive); + } + let overloads_iterator = if let Some(Type::Callable(callable)) = ty.into_callable(db) { callable.signatures(db).overloads.iter()