diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index b1f2ed1a1a65..d49c0e580c91 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -24,7 +24,7 @@ TypeInfo, is_class_var, ) -from mypy.types import ENUM_REMOVED_PROPS, Instance, get_proper_type +from mypy.types import ENUM_REMOVED_PROPS, Instance, UnboundType, get_proper_type from mypyc.ir.class_ir import ClassIR, NonExtClassInfo from mypyc.ir.func_ir import FuncDecl, FuncSignature from mypyc.ir.ops import ( @@ -556,6 +556,7 @@ def add_non_ext_class_attr_ann( get_type_info: Callable[[AssignmentStmt], TypeInfo | None] | None = None, ) -> None: """Add a class attribute to __annotations__ of a non-extension class.""" + # FIXME: try to better preserve the special forms and type parameters of generics. typ: Value | None = None if get_type_info is not None: type_info = get_type_info(stmt) @@ -565,7 +566,17 @@ def add_non_ext_class_attr_ann( if typ is None: # FIXME: if get_type_info is not provided, don't fall back to stmt.type? ann_type = get_proper_type(stmt.type) - if isinstance(ann_type, Instance): + if ( + isinstance(stmt.unanalyzed_type, UnboundType) + and stmt.unanalyzed_type.original_str_expr is not None + ): + # Annotation is a forward reference, so don't attempt to load the actual + # type and load the string instead. + # + # TODO: is it possible to determine whether a non-string annotation is + # actually a forward reference due to the __annotations__ future? + typ = builder.load_str(stmt.unanalyzed_type.original_str_expr) + elif isinstance(ann_type, Instance): typ = load_type(builder, ann_type.type, stmt.line) else: typ = builder.add(LoadAddress(type_object_op.type, type_object_op.src, stmt.line)) diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test index 26b039320844..f6c92b9c720f 100644 --- a/mypyc/test-data/run-tuples.test +++ b/mypyc/test-data/run-tuples.test @@ -99,8 +99,6 @@ assert f(Sub(3, 2)) == 3 [case testNamedTupleClassSyntax] from typing import Dict, List, NamedTuple, Optional, Tuple, Union -class ClassIR: pass - class FuncIR: pass StealsDescription = Union[bool, List[bool]] @@ -119,8 +117,12 @@ class Record(NamedTuple): ordering: Optional[List[int]] extra_int_constants: List[Tuple[int]] +# Make sure mypyc loads the annotation string for this forward reference. +# Ref: https://github.com/mypyc/mypyc/issues/938 +class ClassIR: pass + [file driver.py] -from typing import Optional +from typing import ForwardRef, Optional from native import ClassIR, FuncIR, Record assert Record.__annotations__ == { @@ -129,7 +131,7 @@ assert Record.__annotations__ == { 'is_borrowed': bool, 'hash': str, 'python_path': tuple, - 'type': ClassIR, + 'type': ForwardRef('ClassIR'), 'method': FuncIR, 'shadow_method': type, 'classes': dict,