[ty] don't mark entire type-alias scopes as Deferred (#20086)

## Summary

This has been here for awhile (since our initial PEP 695 type alias
support) but isn't really correct. The right-hand-side of a PEP 695 type
alias is a distinct scope, and we don't mark it as an "eager" nested
scope, so it automatically gets "deferred" resolution of names from
outer scopes (just like a nested function). Thus it's
redundant/unnecessary for us to use `DeferredExpressionState::Deferred`
for resolving that RHS expression -- that's for deferring resolution of
individual names within a scope. Using it here causes us to wrongly
ignore applicable outer-scope narrowing.

## Test Plan

Added mdtest that failed before this PR (the second snippet -- the first
snippet always passed.)
This commit is contained in:
Carl Meyer 2025-08-25 11:32:18 -07:00 committed by GitHub
parent ba47010150
commit 33c5f6f4f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 27 additions and 1 deletions

View file

@ -75,6 +75,32 @@ def f(x: T):
reveal_type(b) # revealed: str
```
## Scoping
PEP 695 type aliases delay runtime evaluation of their right-hand side, so they are a lazy (not
eager) nested scope.
```py
type Alias = Foo | str
def f(x: Alias):
reveal_type(x) # revealed: Foo | str
class Foo:
pass
```
But narrowing of names used in the type alias is still respected:
```py
def _(flag: bool):
t = int if flag else None
if t is not None:
type Alias = t | str
def f(x: Alias):
reveal_type(x) # revealed: int | str
```
## Generic type aliases
```py

View file

@ -2344,7 +2344,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}
fn infer_type_alias(&mut self, type_alias: &ast::StmtTypeAlias) {
self.infer_annotation_expression(&type_alias.value, DeferredExpressionState::Deferred);
self.infer_annotation_expression(&type_alias.value, DeferredExpressionState::None);
}
/// If the current scope is a method inside an enclosing class,