Skip to content

Conversation

dcreager
Copy link
Member

We now correctly exclude legacy typevars from enclosing scopes when constructing the generic context for a generic function.

more detail:

A function is generic if it refers to legacy typevars in its signature:

from typing import TypeVar

T = TypeVar("T")

def f(t: T) -> T:
    return t

Generic functions are allowed to appear inside of other generic contexts. When they do, they can refer to the typevars of those enclosing generic contexts, and that should not rebind the typevar:

from typing import TypeVar, Generic

T = TypeVar("T")
U = TypeVar("U")

class C(Generic[T]):
    @staticmethod
    def method(t: T, u: U) -> None: ...

# revealed: def method(t: int, u: U) -> None
reveal_type(C[int].method)

This substitution was already being performed correctly, but we were also still including the enclosing legacy typevars in the method's own generic context, which can be seen via ty_extensions.generic_context (which has been updated to work on generic functions and methods):

from ty_extensions import generic_context

# before: tuple[T, U]
# after: tuple[U]
reveal_type(generic_context(C[int].method))

Copy link
Contributor

github-actions bot commented Jul 25, 2025

mypy_primer results

Changes were detected when running on open source projects
psycopg (https://github.com/psycopg/psycopg)
- psycopg/psycopg/_py_transformer.py:329:16: error[invalid-return-type] Return type does not match returned value: expected `Row`, found `tuple[Any, ...]`
- psycopg_pool/psycopg_pool/pool.py:263:16: error[invalid-return-type] Return type does not match returned value: expected `CT`, found `(Unknown & ~AlwaysFalsy) | Connection[@Todo(Support for `typing.TypeAlias`)]`
- Found 658 diagnostics
+ Found 656 diagnostics

zulip (https://github.com/zulip/zulip)
- zerver/tests/test_auth_backends.py:8035:26: error[invalid-argument-type] Argument to bound method `set` is incorrect: Argument type `Unknown | int` does not satisfy upper bound of type variable `Self`
- Found 7421 diagnostics
+ Found 7420 diagnostics
No memory usage changes detected ✅

@MichaReiser MichaReiser added the ty Multi-file analysis & type inference label Jul 25, 2025
Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

Comment on lines 391 to 394
db: &'db dyn Db,
module: &ParsedModuleRef,
index: &SemanticIndex<'db>,
scope: FileScopeId,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to above, I'd suggest instead of taking module or index we might just take a File here.

Or take only a ScopeId, which encompasses a File and a FileScopeId.

I guess the one argument in favor of taking module and index is that it kind of makes it explicit that this method has a dependency on the AST of the module containing scope. But I'm not sure how obvious this is; it's not the way we've typically handled this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

I guess the one argument in favor of taking module and index is that it kind of makes it explicit that this method has a dependency on the AST of the module containing scope.

That wasn't my intent, and I think it's an open question how we want to model that better more generally, so I'm going with the simpler function signatures that you've suggested

@@ -385,6 +385,47 @@ pub(crate) fn nearest_enclosing_class<'db>(
})
}

/// Returns in iterator of any generic context introduced by the given scope or any enclosing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

infer.rs is already massive with TypeInferenceBuilder and the various *Inference structs. Is it the best place for these new free functions? Seems like they kind of would belong in generics.rs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved. That also let me make them file-local instead of pub(crate)

dcreager and others added 4 commits July 25, 2025 16:30
* main:
  [ty] Added support for "document symbols" and "workspace symbols" (#19521)
  Add `TextEmitter::with_color` and disable colors in `unreadable_files` test (#19562)
  [ty] Added support for document highlights in playground. (#19540)
  [`refurb`] Ignore decorated functions for `FURB118` (#19339)
  [ty] Add workflow to comment diagnostic diff for conformance tests (#19556)
@dcreager dcreager merged commit e867830 into main Jul 25, 2025
37 checks passed
@dcreager dcreager deleted the dcreager/containing-legacy-typevar branch July 25, 2025 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ty Multi-file analysis & type inference
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants