Implement preliminary PEP 695 support - #5371
Merged
Merged
Conversation
I also tried to fix by annotating `positive_lookahead` with `@specialize.argtype(2)`, but RPython still complained about mixing numbers and strings, probably because they're wrapped in a tuple (*args).
codegen.py: - Extract _visit_type_params_and_collect_names helper method - Remove duplicate _load_pypy_typing_attr in TypeParamBlockCodeGenerator - Replace silent fallback with AssertionError for missing _type_params_node symtable.py: - Extract _visit_function helper to deduplicate FunctionDef/AsyncFunctionDef - Replace _visit_type_params with visit_sequence apptest_pep695.py: - Remove redundant docstrings - Use 1/0 and raises(ZeroDivisionError) for lazy evaluation tests - Consolidate syntax error tests into single test - Use global raises instead of importing pytest - Add test_lazy_constraints_evaluation - Add assertions to test_type_alias_type_subscript
- Import TypeVar, ParamSpec, TypeVarTuple, TypeAliasType, Generic from _pypy_typing into typing.py (removes duplicate implementations) - Add _make_union helper for TypeVar.__or__ forward reference support - Add _generic_class_getitem and _generic_init_subclass helpers - Move Generic class to _pypy_typing.py - Refactor _pypy_typing.py with _BoundVarianceMixin and _LazyEvaluator - Add missing methods: __mro_entries__, __typing_subst__, __typing_prepare_subst__, __parameters__ - Extract _emit_type_alias_type_call helper in codegen.py
Implement CPython's __classdict__ mechanism to allow type parameter bounds and type alias values to access names defined in enclosing class scopes. Key changes: - symtable.py: Track __classdict__ cell in class scopes and mark annotation scopes nested in classes as needing classdict access - codegen.py: Initialize __classdict__ with LOAD_LOCALS at class body start; create AnnotationScopeCodeGenerator base class that uses LOAD_FROM_DICT_OR_* opcodes to look up names from class dict first - assemble.py: Add stack effects for LOAD_LOCALS, LOAD_FROM_DICT_OR_GLOBALS, and LOAD_FROM_DICT_OR_DEREF - pyopcode.py: Fix oefmt_name_error call in LOAD_FROM_DICT_OR_GLOBALS Behavior matches CPython 3.12: - Class namespace is accessible from annotation scopes directly nested in class - Functions break the chain (annotation scopes inside functions cannot access enclosing class namespaces) - Class-level names shadow both globals and type parameters when names conflict - Each class has its own __classdict__; nested classes see only their immediate enclosing class
- Add _push_annotation_scope helper for creating annotation scopes with classdict handling - Add _enter_typeparam_scope helper for type parameter scope creation - Replace is_annotation_scope attribute with isinstance checks - Apply helpers in _visit_function, visit_ClassDef, visit_TypeAlias, and visit_TypeVar
- Add SYM_TYPE_PARAM flag to identify type parameter definitions - Detect and error on duplicate type parameters (e.g., def f[T, T]()) - Track type parameters through scope analysis via typeparams dict - Error on nonlocal binding of type parameters
- Disallow lambda and comprehensions in annotation scopes within class scope (they would break the __classdict__ lookup mechanism) - Fix class scope name resolution: class-bound names now resolve to globals (via LOAD_FROM_DICT_OR_GLOBALS) instead of via closure, so runtime checks __classdict__ then falls back to globals rather than the enclosing function's closure - Add test for conditionally-bound class names with enclosing function
…_scope - Make needs_classdict a property derived from class_entry - Simplify _find_enclosing_class_scope to use class_entry propagation
Group related test scenarios into fewer, more focused test functions.
Also fix a bug where TypeVar constraints could not contained starred expressions.
Annotation scopes should be "invisible" in qualnames per PEP 695. The fix checks for AnnotationScope before FunctionScope in sub_scope().
Function defaults in generic functions were being evaluated inside the
type params scope, causing them to incorrectly see type parameters.
For example:
T = 1
def f[T](x=T): return x
f() # Should return 1, was failing
Now defaults are compiled in the outer scope before entering the type
params wrapper, then passed as arguments (.defaults, .kwdefaults) to
the wrapper which loads them via LOAD_FAST.
Generic classes defined with PEP 695 syntax now automatically inherit from typing.Generic, matching CPython behavior: class C[T]: pass # C.__bases__ == (Generic,) # C.__orig_bases__ == (Generic[T],) Implementation follows CPython's pattern: - Register .generic_base in type param scope (symtable) - Create Generic[T, ...] by subscripting Generic with type params - Append synthetic .generic_base Name node to bases list
Add check for AnnotationScope before FunctionScope in visit_NamedExpr so that assignment expressions within comprehensions inside annotation scopes (e.g., type parameter bounds) raise a SyntaxError.
Explicit `nonlocal` declarations should always add the name to free_vars for closure propagation, even inside classes.
cfbolz
reviewed
Feb 5, 2026
Member
|
thanks a lot @BarrensZeppelin! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains a first-pass implementation of PEP 695 support (type parameter syntax).
Related to #5273.
For the implementation details and design decisions I used
as references.
This was quite a large task, so I got some help from LLMs. As a result, parts of the implementation are more verbose than they need to be. We can trim it down if necessary.
Known issues:
_pypy_typingmodule is a hack. To match CPython's behaviour it must probably be rewritten in RPython.__qualname__of functions created for annotation scopes are incorrect. I'd rather fix this once the stdlib tests are merged.