tpy-lang 0.3.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. tpy_lang-0.3.0.dev0.dist-info/METADATA +151 -0
  2. tpy_lang-0.3.0.dev0.dist-info/RECORD +333 -0
  3. tpy_lang-0.3.0.dev0.dist-info/WHEEL +4 -0
  4. tpy_lang-0.3.0.dev0.dist-info/entry_points.txt +3 -0
  5. tpyc/__init__.py +104 -0
  6. tpyc/__main__.py +6 -0
  7. tpyc/_buildinfo.py +1 -0
  8. tpyc/_data/docs/LANGUAGE_FEATURES.md +6278 -0
  9. tpyc/_data/docs/STDLIB_ROADMAP.md +1258 -0
  10. tpyc/_data/docs/TPY_FOR_AGENTS.md +556 -0
  11. tpyc/_data/lib/tpy/_bindings/__init__.py +6 -0
  12. tpyc/_data/lib/tpy/_bindings/pcre2.py +173 -0
  13. tpyc/_data/lib/tpy/_bindings/posix_socket.py +161 -0
  14. tpyc/_data/lib/tpy/_functools_macros.py +80 -0
  15. tpyc/_data/lib/tpy/_macro_helpers.py +161 -0
  16. tpyc/_data/lib/tpy/argparse.py +2062 -0
  17. tpyc/_data/lib/tpy/asyncio/__init__.py +744 -0
  18. tpyc/_data/lib/tpy/asyncio/_executor.py +515 -0
  19. tpyc/_data/lib/tpy/base64.py +410 -0
  20. tpyc/_data/lib/tpy/bisect.py +39 -0
  21. tpyc/_data/lib/tpy/builtins.py +38 -0
  22. tpyc/_data/lib/tpy/dataclasses.py +354 -0
  23. tpyc/_data/lib/tpy/enum.py +23 -0
  24. tpyc/_data/lib/tpy/functools.py +33 -0
  25. tpyc/_data/lib/tpy/hashlib.py +206 -0
  26. tpyc/_data/lib/tpy/heapq.py +118 -0
  27. tpyc/_data/lib/tpy/io.py +395 -0
  28. tpyc/_data/lib/tpy/json.py +221 -0
  29. tpyc/_data/lib/tpy/math.py +406 -0
  30. tpyc/_data/lib/tpy/random.py +597 -0
  31. tpyc/_data/lib/tpy/re.py +467 -0
  32. tpyc/_data/lib/tpy/socket.py +379 -0
  33. tpyc/_data/lib/tpy/struct.py +178 -0
  34. tpyc/_data/lib/tpy/sys.py +40 -0
  35. tpyc/_data/lib/tpy/time.py +39 -0
  36. tpyc/_data/lib/tpy/tpy/__init__.py +78 -0
  37. tpyc/_data/lib/tpy/tpy/_bootstrap/__init__.py +10 -0
  38. tpyc/_data/lib/tpy/tpy/_bootstrap/_decorators.py +37 -0
  39. tpyc/_data/lib/tpy/tpy/_bootstrap/_extern.py +64 -0
  40. tpyc/_data/lib/tpy/tpy/_builtins/__init__.py +11 -0
  41. tpyc/_data/lib/tpy/tpy/_builtins/_bytes.py +378 -0
  42. tpyc/_data/lib/tpy/tpy/_builtins/_dict.py +151 -0
  43. tpyc/_data/lib/tpy/tpy/_builtins/_exceptions.py +125 -0
  44. tpyc/_data/lib/tpy/tpy/_builtins/_funcs.py +681 -0
  45. tpyc/_data/lib/tpy/tpy/_builtins/_io.py +97 -0
  46. tpyc/_data/lib/tpy/tpy/_builtins/_list.py +127 -0
  47. tpyc/_data/lib/tpy/tpy/_builtins/_range.py +52 -0
  48. tpyc/_data/lib/tpy/tpy/_builtins/_set.py +139 -0
  49. tpyc/_data/lib/tpy/tpy/_builtins/_super.py +11 -0
  50. tpyc/_data/lib/tpy/tpy/_builtins/_types.py +661 -0
  51. tpyc/_data/lib/tpy/tpy/_core/__init__.py +23 -0
  52. tpyc/_data/lib/tpy/tpy/_core/_bytes_view.py +129 -0
  53. tpyc/_data/lib/tpy/tpy/_core/_containers.py +137 -0
  54. tpyc/_data/lib/tpy/tpy/_core/_functions.py +40 -0
  55. tpyc/_data/lib/tpy/tpy/_core/_types.py +2061 -0
  56. tpyc/_data/lib/tpy/tpy/_typing/__init__.py +77 -0
  57. tpyc/_data/lib/tpy/tpy/_version.py +29 -0
  58. tpyc/_data/lib/tpy/tpy/bits.py +28 -0
  59. tpyc/_data/lib/tpy/tpy/coro/__init__.py +127 -0
  60. tpyc/_data/lib/tpy/tpy/extern.py +8 -0
  61. tpyc/_data/lib/tpy/tpy/mem.py +49 -0
  62. tpyc/_data/lib/tpy/tpy/unsafe.py +195 -0
  63. tpyc/_data/lib/tpy/tpy/version.py +21 -0
  64. tpyc/_data/lib/tpy/typing.py +13 -0
  65. tpyc/_data/runtime/cpp/include/tpy/any.hpp +461 -0
  66. tpyc/_data/runtime/cpp/include/tpy/as_ostream.hpp +117 -0
  67. tpyc/_data/runtime/cpp/include/tpy/async.hpp +76 -0
  68. tpyc/_data/runtime/cpp/include/tpy/bigint.hpp +1343 -0
  69. tpyc/_data/runtime/cpp/include/tpy/builtins.hpp +400 -0
  70. tpyc/_data/runtime/cpp/include/tpy/bytes_ops.hpp +469 -0
  71. tpyc/_data/runtime/cpp/include/tpy/container_ops.hpp +487 -0
  72. tpyc/_data/runtime/cpp/include/tpy/copy_iter.hpp +82 -0
  73. tpyc/_data/runtime/cpp/include/tpy/core.hpp +558 -0
  74. tpyc/_data/runtime/cpp/include/tpy/dict_ops.hpp +289 -0
  75. tpyc/_data/runtime/cpp/include/tpy/dunder.hpp +750 -0
  76. tpyc/_data/runtime/cpp/include/tpy/dynamic.hpp +44 -0
  77. tpyc/_data/runtime/cpp/include/tpy/enum.hpp +40 -0
  78. tpyc/_data/runtime/cpp/include/tpy/file.hpp +245 -0
  79. tpyc/_data/runtime/cpp/include/tpy/fixed_int.hpp +317 -0
  80. tpyc/_data/runtime/cpp/include/tpy/format.hpp +954 -0
  81. tpyc/_data/runtime/cpp/include/tpy/frame_slot.hpp +120 -0
  82. tpyc/_data/runtime/cpp/include/tpy/generator.hpp +47 -0
  83. tpyc/_data/runtime/cpp/include/tpy/iterable_ops.hpp +122 -0
  84. tpyc/_data/runtime/cpp/include/tpy/itertools.hpp +749 -0
  85. tpyc/_data/runtime/cpp/include/tpy/next_iter.hpp +82 -0
  86. tpyc/_data/runtime/cpp/include/tpy/ordered_map.hpp +518 -0
  87. tpyc/_data/runtime/cpp/include/tpy/ordered_set.hpp +337 -0
  88. tpyc/_data/runtime/cpp/include/tpy/own_iter.hpp +54 -0
  89. tpyc/_data/runtime/cpp/include/tpy/pascal_graph_sdl.hpp +192 -0
  90. tpyc/_data/runtime/cpp/include/tpy/printing.hpp +302 -0
  91. tpyc/_data/runtime/cpp/include/tpy/protocols.hpp +61 -0
  92. tpyc/_data/runtime/cpp/include/tpy/range.hpp +115 -0
  93. tpyc/_data/runtime/cpp/include/tpy/ranges.hpp +212 -0
  94. tpyc/_data/runtime/cpp/include/tpy/set_ops.hpp +265 -0
  95. tpyc/_data/runtime/cpp/include/tpy/slice.hpp +47 -0
  96. tpyc/_data/runtime/cpp/include/tpy/span_iter.hpp +42 -0
  97. tpyc/_data/runtime/cpp/include/tpy/stdlib/math.hpp +41 -0
  98. tpyc/_data/runtime/cpp/include/tpy/stdlib/pcre2_h.hpp +96 -0
  99. tpyc/_data/runtime/cpp/include/tpy/stdlib/random.hpp +25 -0
  100. tpyc/_data/runtime/cpp/include/tpy/stdlib/socket_h.hpp +145 -0
  101. tpyc/_data/runtime/cpp/include/tpy/stdlib/time.hpp +62 -0
  102. tpyc/_data/runtime/cpp/include/tpy/system.hpp +121 -0
  103. tpyc/_data/runtime/cpp/include/tpy/throwable.hpp +55 -0
  104. tpyc/_data/runtime/cpp/include/tpy/tpy.hpp +156 -0
  105. tpyc/_data/runtime/cpp/include/tpy/type_name.hpp +77 -0
  106. tpyc/_data/runtime/cpp/include/tpy/type_traits.hpp +240 -0
  107. tpyc/_data/runtime/cpp/include/tpy/uninit_array_storage.hpp +250 -0
  108. tpyc/_data/runtime/cpp/include/tpy/uninit_heap_storage.hpp +277 -0
  109. tpyc/_data/runtime/cpp/include/tpy/varargs.hpp +174 -0
  110. tpyc/_data/runtime/cpp/include/tpy/variant_ref.hpp +118 -0
  111. tpyc/_data/runtime/cpp/src/stdlib/socket_impl.cpp +104 -0
  112. tpyc/_data/runtime/cpp/third_party/README.md +58 -0
  113. tpyc/_data/runtime/cpp/third_party/pcre2/AUTHORS +36 -0
  114. tpyc/_data/runtime/cpp/third_party/pcre2/CMakeLists.txt +1233 -0
  115. tpyc/_data/runtime/cpp/third_party/pcre2/COPYING +5 -0
  116. tpyc/_data/runtime/cpp/third_party/pcre2/ChangeLog +3097 -0
  117. tpyc/_data/runtime/cpp/third_party/pcre2/HACKING +853 -0
  118. tpyc/_data/runtime/cpp/third_party/pcre2/INSTALL +368 -0
  119. tpyc/_data/runtime/cpp/third_party/pcre2/LICENCE +94 -0
  120. tpyc/_data/runtime/cpp/third_party/pcre2/NEWS +492 -0
  121. tpyc/_data/runtime/cpp/third_party/pcre2/NON-AUTOTOOLS-BUILD +430 -0
  122. tpyc/_data/runtime/cpp/third_party/pcre2/README +956 -0
  123. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/COPYING-CMAKE-SCRIPTS +22 -0
  124. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindEditline.cmake +16 -0
  125. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindPackageHandleStandardArgs.cmake +58 -0
  126. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindReadline.cmake +29 -0
  127. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/pcre2-config-version.cmake.in +15 -0
  128. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/pcre2-config.cmake.in +148 -0
  129. tpyc/_data/runtime/cpp/third_party/pcre2/config-cmake.h.in +56 -0
  130. tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-16.pc.in +13 -0
  131. tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-32.pc.in +13 -0
  132. tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-8.pc.in +13 -0
  133. tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-posix.pc.in +13 -0
  134. tpyc/_data/runtime/cpp/third_party/pcre2/pcre2-config.in +121 -0
  135. tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h +483 -0
  136. tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h.generic +483 -0
  137. tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h.in +460 -0
  138. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h +1010 -0
  139. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h.generic +1010 -0
  140. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h.in +1010 -0
  141. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_auto_possess.c +1371 -0
  142. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chartables.c +196 -0
  143. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chartables.c.dist +196 -0
  144. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chkdint.c +96 -0
  145. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_compile.c +11001 -0
  146. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_config.c +252 -0
  147. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_context.c +510 -0
  148. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_convert.c +1189 -0
  149. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_dfa_match.c +4119 -0
  150. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_dftables.c +297 -0
  151. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_error.c +345 -0
  152. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_extuni.c +162 -0
  153. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_find_bracket.c +219 -0
  154. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_fuzzsupport.c +792 -0
  155. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_internal.h +2084 -0
  156. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_intmodedep.h +940 -0
  157. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_compile.c +14972 -0
  158. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_match.c +200 -0
  159. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_misc.c +234 -0
  160. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_neon_inc.h +354 -0
  161. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_simd_inc.h +2355 -0
  162. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_test.c +2528 -0
  163. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_maketables.c +165 -0
  164. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_match.c +7777 -0
  165. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_match_data.c +185 -0
  166. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_newline.c +243 -0
  167. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ord2utf.c +120 -0
  168. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_pattern_info.c +432 -0
  169. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_printint.c +886 -0
  170. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_script_run.c +344 -0
  171. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_serialize.c +286 -0
  172. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_string_utils.c +237 -0
  173. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_study.c +1915 -0
  174. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_substitute.c +1009 -0
  175. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_substring.c +550 -0
  176. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_tables.c +234 -0
  177. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucd.c +5460 -0
  178. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucp.h +396 -0
  179. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucptables.c +1533 -0
  180. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_valid_utf.c +398 -0
  181. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_xclass.c +308 -0
  182. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2demo.c +497 -0
  183. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2grep.c +4606 -0
  184. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix.c +425 -0
  185. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix.h +187 -0
  186. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix_test.c +209 -0
  187. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2test.c +9708 -0
  188. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorApple.c +137 -0
  189. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorCore.c +327 -0
  190. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorFreeBSD.c +89 -0
  191. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorPosix.c +62 -0
  192. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorWindows.c +40 -0
  193. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitProtExecAllocatorNetBSD.c +72 -0
  194. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitProtExecAllocatorPosix.c +172 -0
  195. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitWXExecAllocatorPosix.c +141 -0
  196. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitWXExecAllocatorWindows.c +102 -0
  197. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfig.h +142 -0
  198. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfigCPU.h +188 -0
  199. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfigInternal.h +907 -0
  200. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitLir.c +3561 -0
  201. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitLir.h +2466 -0
  202. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_32.c +4636 -0
  203. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_64.c +3491 -0
  204. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_T2_32.c +4302 -0
  205. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeLOONGARCH_64.c +3765 -0
  206. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_32.c +472 -0
  207. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_64.c +387 -0
  208. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_common.c +4259 -0
  209. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_32.c +485 -0
  210. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_64.c +719 -0
  211. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_common.c +3161 -0
  212. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_32.c +142 -0
  213. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_64.c +222 -0
  214. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_common.c +3121 -0
  215. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeS390X.c +4526 -0
  216. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_32.c +1685 -0
  217. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_64.c +1398 -0
  218. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_common.c +5001 -0
  219. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitSerialize.c +516 -0
  220. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitUtils.c +344 -0
  221. tpyc/_data/runtime/cpp/third_party/pcre2.sources.txt +54 -0
  222. tpyc/_data/runtime/cpp/third_party/pcre2.vendor.json +7 -0
  223. tpyc/build/__init__.py +7 -0
  224. tpyc/build/pcre2.py +122 -0
  225. tpyc/build/third_party.py +413 -0
  226. tpyc/cli.py +822 -0
  227. tpyc/codegen_cpp/__init__.py +18 -0
  228. tpyc/codegen_cpp/builtins.py +484 -0
  229. tpyc/codegen_cpp/context.py +2064 -0
  230. tpyc/codegen_cpp/expressions.py +5940 -0
  231. tpyc/codegen_cpp/functions.py +1913 -0
  232. tpyc/codegen_cpp/gen_async.py +3258 -0
  233. tpyc/codegen_cpp/gen_generators.py +657 -0
  234. tpyc/codegen_cpp/generator.py +2258 -0
  235. tpyc/codegen_cpp/match.py +1997 -0
  236. tpyc/codegen_cpp/param_const.py +172 -0
  237. tpyc/codegen_cpp/protocols.py +907 -0
  238. tpyc/codegen_cpp/records.py +1654 -0
  239. tpyc/codegen_cpp/resumable_cfg.py +1651 -0
  240. tpyc/codegen_cpp/statements.py +4963 -0
  241. tpyc/codegen_cpp/string_dispatch.py +76 -0
  242. tpyc/codegen_cpp/test_context.py +46 -0
  243. tpyc/codegen_cpp/test_param_const.py +113 -0
  244. tpyc/codegen_cpp/test_resumable_cfg.py +182 -0
  245. tpyc/codegen_cpp/type_resolution.py +53 -0
  246. tpyc/codegen_cpp/types.py +436 -0
  247. tpyc/codegen_cpp/variant_access.py +135 -0
  248. tpyc/coercions.py +749 -0
  249. tpyc/compilation_context.py +57 -0
  250. tpyc/compiler.py +3945 -0
  251. tpyc/cycle_detection.py +358 -0
  252. tpyc/diagnostics.py +135 -0
  253. tpyc/dump_types.py +353 -0
  254. tpyc/frontend_diagnostics.py +47 -0
  255. tpyc/frontend_ir/__init__.py +140 -0
  256. tpyc/frontend_ir/lower.py +1098 -0
  257. tpyc/frontend_ir/nodes.py +718 -0
  258. tpyc/frontend_ir/resolver_adapter.py +151 -0
  259. tpyc/frontend_plugin.py +209 -0
  260. tpyc/install_docs.py +81 -0
  261. tpyc/liveness.py +756 -0
  262. tpyc/macro_api.py +1724 -0
  263. tpyc/macro_loader.py +497 -0
  264. tpyc/module_names.py +64 -0
  265. tpyc/modules/__init__.py +31 -0
  266. tpyc/modules/defs.py +89 -0
  267. tpyc/modules/registry.py +36 -0
  268. tpyc/modules/resolver.py +192 -0
  269. tpyc/modules/type_resolution.py +629 -0
  270. tpyc/namespace.py +172 -0
  271. tpyc/parse/__init__.py +84 -0
  272. tpyc/parse/imports.py +490 -0
  273. tpyc/parse/nodes.py +1732 -0
  274. tpyc/parse/parser.py +4043 -0
  275. tpyc/parse/resolve_refs.py +466 -0
  276. tpyc/parse/type_resolver.py +1060 -0
  277. tpyc/prescan.py +254 -0
  278. tpyc/qnames.py +149 -0
  279. tpyc/repl.py +529 -0
  280. tpyc/repl_backends.py +848 -0
  281. tpyc/sema/__init__.py +21 -0
  282. tpyc/sema/analyzer.py +3625 -0
  283. tpyc/sema/bound_check.py +72 -0
  284. tpyc/sema/builder_trace.py +684 -0
  285. tpyc/sema/calls.py +5406 -0
  286. tpyc/sema/compatibility.py +2107 -0
  287. tpyc/sema/context.py +1243 -0
  288. tpyc/sema/expressions.py +3737 -0
  289. tpyc/sema/flow_facts.py +199 -0
  290. tpyc/sema/init_tracker.py +150 -0
  291. tpyc/sema/list_literals.py +69 -0
  292. tpyc/sema/literal_utils.py +27 -0
  293. tpyc/sema/local_deduction.py +1088 -0
  294. tpyc/sema/macros.py +179 -0
  295. tpyc/sema/match.py +1177 -0
  296. tpyc/sema/method_expansion.py +347 -0
  297. tpyc/sema/methods.py +2197 -0
  298. tpyc/sema/mutation_propagation.py +268 -0
  299. tpyc/sema/narrowing.py +857 -0
  300. tpyc/sema/numeric_lattice.py +160 -0
  301. tpyc/sema/operators.py +402 -0
  302. tpyc/sema/overloads.py +841 -0
  303. tpyc/sema/protocols.py +1209 -0
  304. tpyc/sema/reach_analysis.py +202 -0
  305. tpyc/sema/registration.py +3156 -0
  306. tpyc/sema/scope_tracker.py +193 -0
  307. tpyc/sema/statements.py +4426 -0
  308. tpyc/sema/type_ops.py +1879 -0
  309. tpyc/sema/value_range.py +181 -0
  310. tpyc/symbol_binding.py +259 -0
  311. tpyc/test_c3_mro.py +208 -0
  312. tpyc/test_cli_argv.py +52 -0
  313. tpyc/test_compiler.py +559 -0
  314. tpyc/test_contains_type_param.py +101 -0
  315. tpyc/test_cycle_detection.py +221 -0
  316. tpyc/test_dump_types.py +225 -0
  317. tpyc/test_install_docs.py +65 -0
  318. tpyc/test_local_cpp_form.py +135 -0
  319. tpyc/test_macro_loader.py +76 -0
  320. tpyc/test_method_expansion.py +254 -0
  321. tpyc/test_nominal_identity.py +182 -0
  322. tpyc/test_overloads.py +410 -0
  323. tpyc/test_parse.py +303 -0
  324. tpyc/test_parse_type_ref.py +506 -0
  325. tpyc/test_parse_version_info.py +58 -0
  326. tpyc/test_reach_analysis.py +72 -0
  327. tpyc/test_ref_type.py +216 -0
  328. tpyc/test_send_sync_substitution.py +276 -0
  329. tpyc/test_tuple_mutation_propagation.py +206 -0
  330. tpyc/test_type_def_registry.py +1729 -0
  331. tpyc/test_union_types.py +195 -0
  332. tpyc/type_def_registry.py +975 -0
  333. tpyc/typesys.py +5104 -0
@@ -0,0 +1,657 @@
1
+ """Code generation for generator functions (yield -> state machine structs)."""
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import dataclass
5
+ from typing import Callable, TYPE_CHECKING
6
+
7
+ from ..parse.nodes import (
8
+ TpyFunction, TpyYield, TpyStmt, TpyWhile, TpyForEach, TpyReturn, TpyVarDecl,
9
+ TpyCall, TpyName, TpyExpr, TpyTupleUnpack,
10
+ )
11
+ from ..typesys import IntLiteralType, OptionalType, ReadonlyType, TypeParamRef, TupleType, is_protocol_type, unwrap_readonly, unwrap_ref_type
12
+ from tpyc import modules as builtin_modules
13
+ from .context import INDENT, escape_cpp_name
14
+
15
+
16
+ @dataclass
17
+ class GeneratorForInfo:
18
+ """Pre-scanned info about a for-loop containing yields in a state machine generator."""
19
+ uid: int
20
+ strategy: str # "range" | "begin_end" | "next" | "iter_next"
21
+ fields: list[tuple[str, str]] # (field_name, cpp_type_string) for struct fields
22
+ # Loop variable name when the iter source yields stable lvalue references
23
+ # (begin_end over a NativeIterable container of non-value elements). The
24
+ # variable's frame slot is emitted as `T*` (pointer-form), not
25
+ # `std::optional<T>` (value-copy), so `for it in items: ... prev = it`
26
+ # preserves CPython aliasing semantics across yield/resume instead of
27
+ # copying the element into the frame. None when copy-storage is used.
28
+ pointer_form_loop_var: str | None = None
29
+ # For a tuple-unpack loop (`for a, b in items:`) whose element tuple
30
+ # contains non-value, non-readonly members, the names of the unpack
31
+ # targets bound to those members. They are stored as `T*` (alias into
32
+ # the live container element via the pointer-form `__for_tup`), so
33
+ # mutating an unpacked record propagates to the source -- matching both
34
+ # CPython and the plain pointer-form loop var above. Value/readonly
35
+ # members are NOT listed (they stay value-copy). Empty for non-unpack
36
+ # loops and all-value tuples.
37
+ pointer_form_unpack_targets: frozenset[str] = frozenset()
38
+
39
+
40
+ def _collect_yield_stmts(stmts: list[TpyStmt]) -> list[TpyYield]:
41
+ """Collect all TpyYield statements from a body (recursive, no nested defs)."""
42
+ result: list[TpyYield] = []
43
+ for stmt in stmts:
44
+ if isinstance(stmt, TpyYield):
45
+ result.append(stmt)
46
+ for body in stmt.sub_bodies():
47
+ result.extend(_collect_yield_stmts(body))
48
+ return result
49
+
50
+
51
+ def _contains_return(stmts: list[TpyStmt]) -> bool:
52
+ """Check if any TpyReturn exists in the statement list (recursive)."""
53
+ for stmt in stmts:
54
+ if isinstance(stmt, TpyReturn):
55
+ return True
56
+ for body in stmt.sub_bodies():
57
+ if _contains_return(body):
58
+ return True
59
+ return False
60
+
61
+
62
+ if TYPE_CHECKING:
63
+ from io import TextIO
64
+ from .context import CodeGenContext
65
+ from .types import TypeMapper
66
+ from .expressions import ExpressionGenerator
67
+ from .statements import StatementGenerator
68
+ from .functions import FunctionGenerator
69
+
70
+
71
+ class GeneratorCodegen:
72
+ """Generates C++ code for generator functions as state machine structs."""
73
+
74
+ def __init__(
75
+ self,
76
+ ctx: CodeGenContext,
77
+ types: TypeMapper,
78
+ expressions: ExpressionGenerator,
79
+ statements: StatementGenerator,
80
+ functions: FunctionGenerator,
81
+ ):
82
+ self.ctx = ctx
83
+ self.types = types
84
+ self.expressions = expressions
85
+ self.statements = statements
86
+ self.functions = functions
87
+
88
+ def _gen_template_header(self, func: TpyFunction, indent: str = "") -> str:
89
+ """Generate template header for a generic generator function.
90
+
91
+ Returns empty string if the function is not generic and has no protocol params.
92
+ """
93
+ proto_params = self.functions.protocols.get_all_protocol_params(func.params)
94
+ return self.functions._gen_template_header_with_fn(func, proto_params, indent=indent)
95
+
96
+ def _gen_params(self, func: TpyFunction, *, emit_defaults: bool = False) -> str:
97
+ """Generate parameter list for a generator function, handling protocol params."""
98
+ proto_params = self.functions.protocols.get_all_protocol_params(func.params)
99
+ has_dynamic = self.functions._has_dynamic_protocol_params(func.params)
100
+ if proto_params or has_dynamic:
101
+ return self.functions.gen_params_with_protocols(
102
+ func.params, func.type_params, emit_defaults=emit_defaults)
103
+ return self.functions.gen_params(func.params, func.type_params,
104
+ emit_defaults=emit_defaults, func=func)
105
+
106
+ @staticmethod
107
+ def gen_struct_name(func: TpyFunction, record_name: str | None = None) -> str:
108
+ """Compute the generator struct name."""
109
+ if record_name:
110
+ return f"__gen_{record_name}_{func.name}"
111
+ return f"__gen_{func.name}"
112
+
113
+ def is_simple_generator(self, func: TpyFunction) -> bool:
114
+ """Check if a generator can use the lightweight lambda/wrapper path.
115
+
116
+ Simple generators have exactly one yield inside a single while-loop or
117
+ for-loop, with no early returns or nested control flow around the yield.
118
+ """
119
+ yields = _collect_yield_stmts(func.body)
120
+ if len(yields) != 1:
121
+ return False
122
+ if _contains_return(func.body):
123
+ return False
124
+ if not func.body:
125
+ return False
126
+ last = func.body[-1]
127
+ if isinstance(last, TpyWhile) and not last.orelse:
128
+ loop_body = last.body
129
+ elif isinstance(last, TpyForEach) and not last.orelse and not last.is_tuple_unpack:
130
+ loop_body = last.body
131
+ else:
132
+ return False
133
+ # The single yield must be a direct child of the loop body
134
+ for stmt in loop_body:
135
+ if isinstance(stmt, TpyYield):
136
+ return True
137
+ return False
138
+
139
+ def gen_simple_generator_inline(self, out: TextIO, func: TpyFunction,
140
+ record_name: str | None = None) -> None:
141
+ """Generate a simple generator as an inline function using make_generator + lambda."""
142
+ last = func.body[-1]
143
+ if isinstance(last, TpyForEach):
144
+ self._gen_simple_for_generator(out, func, record_name=record_name)
145
+ else:
146
+ self._gen_simple_while_generator(out, func, record_name=record_name)
147
+
148
+ def _gen_simple_while_generator(self, out: TextIO, func: TpyFunction,
149
+ record_name: str | None = None) -> None:
150
+ """Lambda codegen for: [init] while(cond): ... yield expr ..."""
151
+ elem_type = func.generator_yield_type
152
+ assert elem_type is not None
153
+ cpp_elem = self.types.type_to_cpp(elem_type)
154
+ cpp_iter_slot = self._iter_slot_for_yield(elem_type, cpp_elem)
155
+
156
+ while_stmt = func.body[-1]
157
+ assert isinstance(while_stmt, TpyWhile)
158
+ init_stmts = func.body[:-1]
159
+ yield_stmt, pre_yield, post_yield = self._split_at_yield(while_stmt.body)
160
+ ref_yield = not post_yield and isinstance(elem_type, TypeParamRef)
161
+ val_binding = "auto&&" if ref_yield else "auto"
162
+
163
+ ind1 = INDENT
164
+ ind2 = INDENT * 2
165
+ ind3 = INDENT * 3
166
+ ind4 = INDENT * 4
167
+
168
+ extra = 1 if record_name else 0
169
+
170
+ if record_name:
171
+ const_suffix = " const" if func.is_readonly else ""
172
+ out.write(f"\n")
173
+ self.ctx.emit_source_comment(out, func.loc, indent=INDENT)
174
+ tpl_header = self._gen_template_header(func, indent=INDENT)
175
+ if tpl_header:
176
+ out.write(tpl_header)
177
+ params = self._gen_params(func, emit_defaults=True)
178
+ out.write(f"{ind1}auto {func.name}({params}){const_suffix} {{\n")
179
+ else:
180
+ tpl_header = self._gen_template_header(func)
181
+ if tpl_header:
182
+ out.write(tpl_header)
183
+ params = self._gen_params(func, emit_defaults=True)
184
+ out.write(f"inline auto {escape_cpp_name(func.name)}({params}) {{\n")
185
+
186
+ # Generate init stmts and set up codegen scope
187
+ old_self_ref = self.ctx.generator_self_ref
188
+ if record_name:
189
+ self.ctx.generator_self_ref = "(*this)"
190
+ self._setup_body_scope(out, func, init_stmts,
191
+ indent_level=1 + extra)
192
+
193
+ captures = self._build_capture_list(func.params, init_stmts)
194
+ if record_name:
195
+ self_capture = "this"
196
+ captures = f"{self_capture}, {captures}" if captures else self_capture
197
+ out.write(f"{INDENT * (1 + extra)}return ::tpy::make_generator<{cpp_iter_slot}>(\n")
198
+ out.write(f"{INDENT * (2 + extra)}[{captures}]() mutable -> std::optional<{cpp_iter_slot}> {{\n")
199
+
200
+ old_indent = self.ctx.indent_level
201
+ self.ctx.indent_level = 3 + extra
202
+ cond_code = self.expressions.gen_expr(while_stmt.condition)
203
+ out.write(f"{INDENT * (3 + extra)}while ({cond_code}) {{\n")
204
+ self.ctx.indent_level = 4 + extra
205
+
206
+ for stmt in pre_yield:
207
+ self.statements.gen_stmt(out, stmt)
208
+ yield_expr = self.statements.gen_yield_value(yield_stmt)
209
+ out.write(f"{INDENT * (4 + extra)}{val_binding} __val = {yield_expr};\n")
210
+ for stmt in post_yield:
211
+ self.statements.gen_stmt(out, stmt)
212
+
213
+ out.write(f"{INDENT * (4 + extra)}return std::optional<{cpp_iter_slot}>(__val);\n")
214
+ out.write(f"{INDENT * (3 + extra)}}}\n")
215
+ out.write(f"{INDENT * (3 + extra)}return std::nullopt;\n")
216
+ out.write(f"{INDENT * (2 + extra)}}}\n")
217
+ out.write(f"{INDENT * (1 + extra)});\n")
218
+ out.write(f"{INDENT if record_name else ''}}}\n")
219
+ self.ctx.indent_level = old_indent
220
+ self.ctx.generator_self_ref = old_self_ref
221
+
222
+ def _gen_simple_for_generator(self, out: TextIO, func: TpyFunction,
223
+ record_name: str | None = None) -> None:
224
+ """Lambda codegen for: [init] for x in iterable: ... yield expr ..."""
225
+ from .context import is_lvalue_iterable
226
+
227
+ elem_type = func.generator_yield_type
228
+ assert elem_type is not None
229
+ cpp_elem = self.types.type_to_cpp(elem_type)
230
+ cpp_iter_slot = self._iter_slot_for_yield(elem_type, cpp_elem)
231
+
232
+ for_stmt = func.body[-1]
233
+ assert isinstance(for_stmt, TpyForEach)
234
+ init_stmts = func.body[:-1]
235
+ yield_stmt, pre_yield, post_yield = self._split_at_yield(for_stmt.body)
236
+
237
+ # auto&&: preserve references for generic generators (T may be val_or_ref).
238
+ # Only safe when no post_yield code can modify the referenced value.
239
+ ref_yield = not post_yield and isinstance(elem_type, TypeParamRef)
240
+ val_binding = "auto&&" if ref_yield else "auto"
241
+
242
+ ind1 = INDENT
243
+ ind2 = INDENT * 2
244
+ ind3 = INDENT * 3
245
+ ind4 = INDENT * 4
246
+
247
+ extra = 1 if record_name else 0
248
+ if record_name:
249
+ const_suffix = " const" if func.is_readonly else ""
250
+ out.write(f"\n")
251
+ self.ctx.emit_source_comment(out, func.loc, indent=INDENT)
252
+ tpl_header = self._gen_template_header(func, indent=INDENT)
253
+ if tpl_header:
254
+ out.write(tpl_header)
255
+ params = self._gen_params(func, emit_defaults=True)
256
+ out.write(f"{ind1}auto {func.name}({params}){const_suffix} {{\n")
257
+ else:
258
+ tpl_header = self._gen_template_header(func)
259
+ if tpl_header:
260
+ out.write(tpl_header)
261
+ params = self._gen_params(func, emit_defaults=True)
262
+ out.write(f"inline auto {escape_cpp_name(func.name)}({params}) {{\n")
263
+
264
+ old_self_ref = self.ctx.generator_self_ref
265
+ if record_name:
266
+ self.ctx.generator_self_ref = "(*this)"
267
+ self._setup_body_scope(out, func, init_stmts,
268
+ indent_level=1 + extra)
269
+
270
+ old_indent = self.ctx.indent_level
271
+ self.ctx.indent_level = 2 + extra
272
+
273
+ # Determine iteration strategy
274
+ is_range = isinstance(for_stmt.iterable, TpyCall) and for_stmt.iterable.func_name == "range"
275
+ iter_elem = for_stmt.elem_type
276
+ if iter_elem and isinstance(iter_elem, IntLiteralType):
277
+ iter_elem = self.ctx.analyzer.ctx.default_int_type
278
+ cpp_iter_elem = self.types.type_to_cpp(iter_elem) if iter_elem else "auto"
279
+ cpp_var = escape_cpp_name(for_stmt.var)
280
+
281
+ # Helper: prepend self capture for method generators
282
+ def _add_self_capture(captures: str) -> str:
283
+ if record_name:
284
+ return f"this, {captures}" if captures else "this"
285
+ return captures
286
+
287
+ # Indentation helpers adjusted for method nesting
288
+ I = lambda n: INDENT * (n + extra)
289
+
290
+ if is_range and len(for_stmt.iterable.args) <= 2:
291
+ # range(n) or range(start, stop): counter in lambda captures
292
+ range_call = for_stmt.iterable
293
+ nargs = len(range_call.args)
294
+ if nargs == 1:
295
+ stop_code = self.expressions.gen_expr(range_call.args[0])
296
+ extra_captures = f"__i = {cpp_iter_elem}(0), __stop = static_cast<{cpp_iter_elem}>({stop_code})"
297
+ else:
298
+ start_code = self.expressions.gen_expr(range_call.args[0])
299
+ stop_code = self.expressions.gen_expr(range_call.args[1])
300
+ extra_captures = f"__i = static_cast<{cpp_iter_elem}>({start_code}), __stop = static_cast<{cpp_iter_elem}>({stop_code})"
301
+
302
+ base_captures = self._build_capture_list(func.params, init_stmts)
303
+ all_captures = f"{base_captures}, {extra_captures}" if base_captures else extra_captures
304
+ all_captures = _add_self_capture(all_captures)
305
+
306
+ out.write(f"{I(1)}return ::tpy::make_generator<{cpp_iter_slot}>(\n")
307
+ out.write(f"{I(2)}[{all_captures}]() mutable -> std::optional<{cpp_iter_slot}> {{\n")
308
+ out.write(f"{I(3)}while (__i < __stop) {{\n")
309
+
310
+ self.ctx.indent_level = 4 + extra
311
+ out.write(f"{I(4)}{cpp_iter_elem} {cpp_var} = __i++;\n")
312
+ for stmt in pre_yield:
313
+ self.statements.gen_stmt(out, stmt)
314
+ yield_expr = self.statements.gen_yield_value(yield_stmt)
315
+ out.write(f"{I(4)}{val_binding} __val = {yield_expr};\n")
316
+ for stmt in post_yield:
317
+ self.statements.gen_stmt(out, stmt)
318
+ out.write(f"{I(4)}return std::optional<{cpp_iter_slot}>(__val);\n")
319
+ out.write(f"{I(3)}}}\n")
320
+ out.write(f"{I(3)}return std::nullopt;\n")
321
+ out.write(f"{I(2)}}}\n")
322
+ out.write(f"{I(1)});\n")
323
+ out.write(f"{I(0)}}}\n")
324
+ elif self._is_direct_iterator(for_stmt):
325
+ # Iterator[T] protocol: the param IS the iterator, call __next__() directly.
326
+ # No __iter__() call needed -- avoids copying move-only iterators.
327
+ iterable_code = self.expressions.gen_expr(for_stmt.iterable)
328
+ base_captures = self._build_capture_list(func.params, init_stmts)
329
+ all_captures = _add_self_capture(base_captures)
330
+
331
+ out.write(f"{I(1)}return ::tpy::make_generator<{cpp_iter_slot}>(\n")
332
+ out.write(f"{I(2)}[{all_captures}]() mutable -> std::optional<{cpp_iter_slot}> {{\n")
333
+ out.write(f"{I(3)}auto __r = ({iterable_code}).__next__();\n")
334
+ out.write(f"{I(3)}if (!__r.has_value()) return std::nullopt;\n")
335
+
336
+ self._gen_simple_for_yield_body(
337
+ out, for_stmt, pre_yield, post_yield, yield_stmt,
338
+ iter_elem, cpp_iter_elem, cpp_var, cpp_iter_slot, val_binding, I, extra)
339
+ elif self._is_builtin_native_iterable(for_stmt):
340
+ # Built-in NativeIterable (list, dict, set, Span, etc.):
341
+ # begin/end peephole for efficiency.
342
+ iterable_code = self.expressions.gen_expr(for_stmt.iterable)
343
+
344
+ base_captures = self._build_capture_list(func.params, init_stmts)
345
+ iter_type = f"decltype(({iterable_code}).begin())"
346
+ beg_capture = f"__beg = {iter_type}()"
347
+ end_capture = f"__end = {iter_type}()"
348
+ init_flag = "__init = false"
349
+ parts = [p for p in [base_captures, beg_capture, end_capture, init_flag] if p]
350
+ all_captures = ", ".join(parts)
351
+ all_captures = _add_self_capture(all_captures)
352
+
353
+ out.write(f"{I(1)}return ::tpy::make_generator<{cpp_iter_slot}>(\n")
354
+ out.write(f"{I(2)}[{all_captures}]() mutable -> std::optional<{cpp_iter_slot}> {{\n")
355
+ out.write(f"{I(3)}if (!__init) {{ __beg = ({iterable_code}).begin(); __end = ({iterable_code}).end(); __init = true; }}\n")
356
+ out.write(f"{I(3)}if (__beg != __end) {{\n")
357
+
358
+ self.ctx.indent_level = 4 + extra
359
+ if iter_elem and iter_elem.is_value_type():
360
+ out.write(f"{I(4)}{cpp_iter_elem} {cpp_var} = *__beg++;\n")
361
+ else:
362
+ out.write(f"{I(4)}auto&& {cpp_var} = *__beg++;\n")
363
+ if (isinstance(iter_elem, TupleType)
364
+ and iter_elem.has_pointer_repr_optional_element()):
365
+ self.ctx.storage_form_tuple_locals.add(for_stmt.var)
366
+
367
+ for stmt in pre_yield:
368
+ self.statements.gen_stmt(out, stmt)
369
+ yield_expr = self.statements.gen_yield_value(yield_stmt)
370
+ out.write(f"{I(4)}{val_binding} __val = {yield_expr};\n")
371
+ for stmt in post_yield:
372
+ self.statements.gen_stmt(out, stmt)
373
+ out.write(f"{I(4)}return std::optional<{cpp_iter_slot}>(__val);\n")
374
+ out.write(f"{I(3)}}}\n")
375
+ out.write(f"{I(3)}return std::nullopt;\n")
376
+ out.write(f"{I(2)}}}\n")
377
+ out.write(f"{I(1)});\n")
378
+ out.write(f"{I(0)}}}\n")
379
+ else:
380
+ # Universal default: ::tpy::__iter__() + __next__() loop.
381
+ # Handles Iterable[T] protocol, NativeIterable[T] protocol,
382
+ # user types with __iter__(), error_return __next__ iterators.
383
+ iterable_code = self.expressions.gen_expr(for_stmt.iterable)
384
+ base_captures = self._build_capture_list(func.params, init_stmts)
385
+
386
+ iter_type = f"std::decay_t<decltype(::tpy::__iter__({iterable_code}))>"
387
+ iter_capture = f"__iter = std::optional<{iter_type}>()"
388
+ parts = [p for p in [base_captures, iter_capture] if p]
389
+ all_captures = ", ".join(parts)
390
+ all_captures = _add_self_capture(all_captures)
391
+
392
+ out.write(f"{I(1)}return ::tpy::make_generator<{cpp_iter_slot}>(\n")
393
+ out.write(f"{I(2)}[{all_captures}]() mutable -> std::optional<{cpp_iter_slot}> {{\n")
394
+ out.write(f"{I(3)}if (!__iter) {{ __iter.emplace(::tpy::__iter__({iterable_code})); }}\n")
395
+ out.write(f"{I(3)}auto __r = (*__iter).__next__();\n")
396
+ out.write(f"{I(3)}if (!__r.has_value()) return std::nullopt;\n")
397
+
398
+ self._gen_simple_for_yield_body(
399
+ out, for_stmt, pre_yield, post_yield, yield_stmt,
400
+ iter_elem, cpp_iter_elem, cpp_var, cpp_iter_slot, val_binding, I, extra)
401
+
402
+ self.ctx.indent_level = old_indent
403
+ self.ctx.generator_self_ref = old_self_ref
404
+
405
+ def _resolve_iterable_type(self, for_stmt: TpyForEach):
406
+ """Resolve the iterable's type, following TypeParamRef bounds."""
407
+ resolved = self.types.get_resolved_type(for_stmt.iterable)
408
+ if isinstance(resolved, TypeParamRef):
409
+ bound = self.ctx.current_type_param_bounds.get(resolved.name)
410
+ if bound is not None and is_protocol_type(bound):
411
+ return bound
412
+ return resolved
413
+
414
+ def _is_direct_iterator(self, for_stmt: TpyForEach) -> bool:
415
+ """Check if the iterable IS an iterator (has __next__() directly).
416
+
417
+ True for Iterator[T] protocol params. These should not go through
418
+ __iter__() to avoid copying move-only iterators.
419
+ """
420
+ resolved = self._resolve_iterable_type(for_stmt)
421
+ return (is_protocol_type(resolved)
422
+ and resolved.qualified_name() == "typing.Iterator")
423
+
424
+ def _is_builtin_native_iterable(self, for_stmt: TpyForEach) -> bool:
425
+ """Check if the iterable is a built-in NativeIterable (list, dict, etc.)."""
426
+ resolved = self._resolve_iterable_type(for_stmt)
427
+ record = self.ctx.analyzer.registry.get_record_for_type(resolved)
428
+ return (record is not None and record.is_native
429
+ and builtin_modules.is_native_iterable(resolved, registry=self.ctx.analyzer.registry))
430
+
431
+ def _gen_simple_for_yield_body(
432
+ self, out: 'TextIO', for_stmt: TpyForEach,
433
+ pre_yield: list[TpyStmt], post_yield: list[TpyStmt], yield_stmt: TpyYield,
434
+ iter_elem: 'TpyType | None', cpp_iter_elem: str, cpp_var: str,
435
+ cpp_iter_slot: str, val_binding: str, I: 'Callable[[int], str]', extra: int,
436
+ ) -> None:
437
+ """Emit the shared yield body for __iter__+__next__ simple generator branches."""
438
+ self.ctx.indent_level = 3 + extra
439
+ out.write(f"{I(3)}{{\n")
440
+ self.ctx.indent_level = 4 + extra
441
+ # tuple[T | None, ...] has two distinct C++ shapes (storage vs borrow);
442
+ # the inner iterator's __next__() returns borrow form for generators and
443
+ # storage form for NativeIterables. `auto&&` binds to either without the
444
+ # codegen having to know which.
445
+ iter_elem_unwrapped = unwrap_ref_type(iter_elem) if iter_elem else None
446
+ is_pointer_repr_tuple = (isinstance(iter_elem_unwrapped, TupleType)
447
+ and iter_elem_unwrapped.has_pointer_repr_optional_element())
448
+ if iter_elem and iter_elem.is_value_type() and not is_pointer_repr_tuple:
449
+ out.write(f"{I(4)}{cpp_iter_elem} {cpp_var} = ::tpy::unwrap_ref(*__r);\n")
450
+ else:
451
+ out.write(f"{I(4)}auto&& {cpp_var} = ::tpy::unwrap_ref(*__r);\n")
452
+
453
+ for stmt in pre_yield:
454
+ self.statements.gen_stmt(out, stmt)
455
+ yield_expr = self.statements.gen_yield_value(yield_stmt)
456
+ out.write(f"{I(4)}{val_binding} __val = {yield_expr};\n")
457
+ for stmt in post_yield:
458
+ self.statements.gen_stmt(out, stmt)
459
+ out.write(f"{I(4)}return std::optional<{cpp_iter_slot}>(__val);\n")
460
+ out.write(f"{I(3)}}}\n")
461
+ out.write(f"{I(2)}}}\n")
462
+ out.write(f"{I(1)});\n")
463
+ out.write(f"{I(0)}}}\n")
464
+
465
+ @staticmethod
466
+ def _iter_slot_for_yield(elem_type: 'TpyType', cpp_elem: str) -> str:
467
+ """Pick the make_generator iterator slot type for a simple-generator yield.
468
+
469
+ Iterator yields hand out references like function returns (CPython
470
+ semantics), so borrow form is the default for tuple yields:
471
+ `tuple[Int32, Point]` -> `std::tuple<int32_t, Point&>`,
472
+ `tuple[P | None, P | None]` -> `std::tuple<P*, P*>`. The outer tuple
473
+ is a value type that std::optional can hold, with reference-bearing
474
+ inner elements preserved through the yield boundary.
475
+
476
+ Non-tuple yields stay value form because `to_cpp_return()` on a bare
477
+ non-value type expands to `T&` directly, and std::optional<T&> is
478
+ ill-formed pre-C++26.
479
+
480
+ A `readonly[tuple[...]]` yield must peel the ReadonlyType wrapper
481
+ before the tuple check (`unwrap_ref_type` only strips RefType), else
482
+ the slot collapses to value form and copies the elements;
483
+ `to_cpp_return()` on the readonly tuple still yields the const-borrow
484
+ form (`std::tuple<const T&, ...>`).
485
+ """
486
+ unwrapped = unwrap_ref_type(unwrap_readonly(elem_type))
487
+ if not isinstance(unwrapped, TupleType):
488
+ return cpp_elem
489
+ return elem_type.to_cpp_return()
490
+
491
+ @staticmethod
492
+ def _split_at_yield(body: list[TpyStmt]) -> tuple[TpyYield, list[TpyStmt], list[TpyStmt]]:
493
+ """Split a loop body at the yield statement. Returns (yield, pre, post)."""
494
+ for i, stmt in enumerate(body):
495
+ if isinstance(stmt, TpyYield):
496
+ return stmt, body[:i], body[i + 1:]
497
+ raise AssertionError("no yield found in body")
498
+
499
+ def _analyze_for_strategy(self, stmt: TpyForEach, uid: int) -> GeneratorForInfo | None:
500
+ """Determine the iteration strategy and struct fields for a for-loop with yield."""
501
+ from tpyc.modules import get_error_return_next_element_type
502
+
503
+ elem_type = stmt.elem_type
504
+ if elem_type and isinstance(elem_type, IntLiteralType):
505
+ elem_type = self.ctx.analyzer.ctx.default_int_type
506
+ elem_cpp = self.types.type_to_cpp(elem_type) if elem_type else "int32_t"
507
+
508
+ # Range counter optimization
509
+ if isinstance(stmt.iterable, TpyCall) and stmt.iterable.func_name == "range":
510
+ fields: list[tuple[str, str]] = [
511
+ (f"__for_i_{uid}", elem_cpp),
512
+ (f"__for_stop_{uid}", elem_cpp),
513
+ ]
514
+ nargs = len(stmt.iterable.args)
515
+ if nargs == 3:
516
+ fields.append((f"__for_step_{uid}", elem_cpp))
517
+ return GeneratorForInfo(uid=uid, strategy="range", fields=fields)
518
+
519
+ iterable_type = self.types.get_resolved_type(stmt.iterable)
520
+
521
+ # Iterator[T] protocol -- iterable already has __next__()
522
+ if is_protocol_type(iterable_type) and iterable_type.qualified_name() == "typing.Iterator":
523
+ result_type = f"std::expected<{elem_cpp}, ::tpy::StopIteration>"
524
+ fields: list[tuple[str, str]] = [(f"__for_r_{uid}", result_type)]
525
+ return GeneratorForInfo(uid=uid, strategy="next", fields=fields)
526
+
527
+ # error_return __next__ types (user iterators)
528
+ er_elem = get_error_return_next_element_type(
529
+ iterable_type, registry=self.ctx.analyzer.registry)
530
+ if er_elem is not None:
531
+ iter_cpp = self.types.type_to_cpp(iterable_type)
532
+ result_type = f"std::expected<{elem_cpp}, ::tpy::StopIteration>"
533
+ fields = []
534
+ if not self._is_named_generator_field(stmt.iterable):
535
+ fields.append((f"__for_src_{uid}", iter_cpp))
536
+ fields.append((f"__for_r_{uid}", result_type))
537
+ return GeneratorForInfo(uid=uid, strategy="next", fields=fields)
538
+
539
+ # Built-in NativeIterable containers (list, dict, set, Array, Span, str) -- begin/end
540
+ record = self.ctx.analyzer.registry.get_record_for_type(iterable_type)
541
+ is_builtin_ni = (record is not None and record.is_native
542
+ and builtin_modules.is_native_iterable(iterable_type, registry=self.ctx.analyzer.registry))
543
+ native_elem = builtin_modules.get_iterable_element_type(iterable_type, registry=self.ctx.analyzer.registry) if is_builtin_ni else None
544
+ if native_elem is not None:
545
+ container_cpp = self.types.type_to_cpp(iterable_type)
546
+ iter_type = f"decltype(std::declval<{container_cpp}&>().begin())"
547
+ fields = [
548
+ (f"__for_it_{uid}", iter_type),
549
+ (f"__for_end_{uid}", iter_type),
550
+ ]
551
+ # If iterable is not a named variable (param or local), need a source field
552
+ if not self._is_named_generator_field(stmt.iterable):
553
+ fields.insert(0, (f"__for_src_{uid}", container_cpp))
554
+ # Non-value element type with stable lvalue source: the loop var
555
+ # becomes T* (aliasing the container element) instead of
556
+ # std::optional<T> (value-copy). Preserves CPython aliasing
557
+ # semantics across yield/resume.
558
+ # ReadonlyType elements need a `const T*` slot (not plain `T*`);
559
+ # the current emission path only emits the latter, so readonly
560
+ # elements fall back to value-storage to avoid a
561
+ # const-correctness violation when taking `&(*iter)`.
562
+ elem_for_form = unwrap_ref_type(native_elem) if native_elem else None
563
+ pointer_form_targets: frozenset[str] = frozenset()
564
+ if (stmt.is_tuple_unpack and isinstance(elem_for_form, TupleType)
565
+ and stmt.body and isinstance(stmt.body[0], TpyTupleUnpack)):
566
+ # Tuple-unpack over a stable lvalue container: alias the
567
+ # non-value, non-readonly members so mutating an unpacked
568
+ # record propagates to the source element (CPython semantics,
569
+ # matching the plain pointer-form loop var). `__for_tup`
570
+ # becomes T* and the aliased targets become T* too; value /
571
+ # readonly members stay value-copy.
572
+ unpack = stmt.body[0]
573
+ # Optional members are excluded: they go through the existing
574
+ # pointer-repr-Optional path (`T* = nullptr`, optional_to_ptr)
575
+ # in the struct emit and `_gen_tuple_unpack`. Only plain
576
+ # reference members alias via `&std::get<i>(...)`.
577
+ aliased = {
578
+ tname
579
+ for tname, etype in zip(
580
+ unpack.targets, elem_for_form.element_types)
581
+ if tname is not None
582
+ and not unwrap_ref_type(etype).is_value_type()
583
+ and not isinstance(
584
+ unwrap_ref_type(etype), (ReadonlyType, OptionalType))
585
+ }
586
+ pointer_form_var = stmt.var if aliased else None
587
+ pointer_form_targets = frozenset(aliased)
588
+ else:
589
+ pointer_form_var = (
590
+ stmt.var
591
+ if (elem_for_form is not None
592
+ and not elem_for_form.is_value_type()
593
+ and not isinstance(elem_for_form, ReadonlyType))
594
+ else None
595
+ )
596
+ return GeneratorForInfo(
597
+ uid=uid, strategy="begin_end", fields=fields,
598
+ pointer_form_loop_var=pointer_form_var,
599
+ pointer_form_unpack_targets=pointer_form_targets,
600
+ )
601
+
602
+ # Universal default: ::tpy::__iter__() + __next__() loop.
603
+ # Handles Iterable[T]/NativeIterable[T] protocol params, user types
604
+ # with __iter__(), and any remaining iterable types.
605
+ src_cpp = self.types.type_to_cpp(iterable_type)
606
+ iter_field_type = f"std::decay_t<decltype(::tpy::__iter__(std::declval<{src_cpp}&>()))>"
607
+ result_field_type = f"decltype(std::declval<{iter_field_type}&>().__next__())"
608
+ fields = [
609
+ (f"__for_itr_{uid}", iter_field_type),
610
+ (f"__for_r_{uid}", result_field_type),
611
+ ]
612
+ return GeneratorForInfo(uid=uid, strategy="iter_next", fields=fields)
613
+
614
+ def _is_named_generator_field(self, expr: TpyExpr) -> bool:
615
+ """Check if expression is a named variable that's stable across yields.
616
+
617
+ Returns True for named variables (params, locals, globals) since they
618
+ persist across __next__() calls. Returns False for expressions (calls,
619
+ constructors) that would need to be stored in a synthetic field.
620
+ """
621
+ return isinstance(expr, TpyName)
622
+
623
+ def _setup_body_scope(self, out: TextIO, func: TpyFunction, init_stmts: list[TpyStmt],
624
+ indent_level: int = 1) -> None:
625
+ """Generate init stmts and set up codegen scope."""
626
+ from ..namespace import Namespace
627
+ local_ns = Namespace(parent=self.ctx.analyzer.global_ns)
628
+ for pname, ptype in func.params:
629
+ local_ns.bind_variable(pname, ptype)
630
+ self.statements.gen_body(
631
+ out, init_stmts, func.params, func.generator_yield_type,
632
+ func, local_ns, indent_level=indent_level, is_method=False,
633
+ )
634
+
635
+ @staticmethod
636
+ def _build_capture_list(params: list, init_stmts: list[TpyStmt],
637
+ exclude: set[str] | None = None) -> str:
638
+ """Build lambda capture list from params + init-stmt locals.
639
+
640
+ Non-value types are captured by reference (&name) to preserve
641
+ Python's reference semantics. Names in `exclude` are skipped
642
+ (e.g. the iterable param when it's copied into __src).
643
+ """
644
+ captures = []
645
+ for pname, ptype in params:
646
+ if exclude and pname in exclude:
647
+ continue
648
+ cpp_name = escape_cpp_name(pname)
649
+ if not ptype.is_value_type():
650
+ captures.append(f"&{cpp_name}")
651
+ else:
652
+ captures.append(cpp_name)
653
+ for stmt in init_stmts:
654
+ if isinstance(stmt, TpyVarDecl):
655
+ captures.append(escape_cpp_name(stmt.name))
656
+ return ", ".join(captures)
657
+