hackity hack

This commit is contained in:
Douglas Creager
2025-12-02 18:24:15 -05:00
parent 957304ec15
commit 7bbf839325
3 changed files with 27 additions and 10 deletions

View File

@@ -329,19 +329,11 @@ from typing_extensions import overload
def f(x: str): ...
@overload
def f(x: int): ...
# TODO: no error
# (This is caused by a salsa cycle and how we union type inference results to avoid oscillations
# during cycle handling.)
# error: [invalid-overload] "Overloaded function `f` requires at least two overloads"
def f(x):
print(x)
f(1)
# TODO: no invalid-argument-type error
# TODO: error: [deprecated] "strings are no longer supported"
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["hello"]`"
f("hello")
f("hello") # TODO: error: [deprecated] "strings are no longer supported"
```
If the actual impl is deprecated, the deprecation always fires.

View File

@@ -907,6 +907,8 @@ impl<'db> Type<'db> {
previous: Self,
cycle: &salsa::Cycle,
) -> Self {
// Note: other parts of this crate assume that this union will be ordered with the types
// from later cycle iterations appearing first.
UnionType::from_elements_cycle_recovery(db, [self, previous])
.recursive_type_normalized(db, cycle)
}

View File

@@ -372,12 +372,35 @@ impl<'db> OverloadLiteral<'db> {
.name
.scoped_use_id(db, scope);
let Place::Defined(Type::FunctionLiteral(previous_type), _, Definedness::AlwaysDefined) =
let Place::Defined(previous_type, _, Definedness::AlwaysDefined) =
place_from_bindings(db, use_def.bindings_at_use(use_id))
else {
return None;
};
// TODO: When we encounter a salsa cycle during type inference, we currently union together
// the inferred types from each cycle iteration. That means that in certain cases
// (especially involving decorators), we can end up with a union of FunctionLiterals for
// each overload, instead of a single bare FunctionLiteral. If we do see a union
// (containing _only_ function literals), pull out the first function literal and use it as
// the type of the overload. Note that this depends on how Type::cycle_normalized orders
// things so that later cycle iterations appear first in the union.
let previous_type = match previous_type {
Type::FunctionLiteral(function) => function,
Type::Union(union_type)
if union_type
.elements(db)
.iter()
.all(|element| element.is_function_literal()) =>
{
// SAFETY: We just checked this
union_type.elements(db)[0]
.as_function_literal()
.expect("type should be a function literal")
}
_ => return None,
};
let previous_literal = previous_type.literal(db);
let previous_overload = previous_literal.last_definition(db);
if !previous_overload.is_overload(db) {