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
tpyc/sema/narrowing.py ADDED
@@ -0,0 +1,857 @@
1
+ """
2
+ TurboPython Narrowing Tracker
3
+
4
+ Centralizes type narrowing (Optional + Union) and fact invalidation on writes.
5
+ """
6
+
7
+ from __future__ import annotations
8
+ from typing import TYPE_CHECKING
9
+
10
+ from ..typesys import (
11
+ TpyType, OptionalType, NoneType, VoidType, PtrType, OwnType, NominalType, AliasRef,
12
+ TypeParamRef, IntLiteralType, AnyType,
13
+ ReadonlyType, UnionType, unwrap_readonly, unwrap_qualifiers, unwrap_ref_type, make_union, union_none_narrow,
14
+ is_protocol_type, is_dynamic_dispatch_inner, polymorphic_source_inner,
15
+ LiteralType, LiteralValue, is_any_int_type,
16
+
17
+ )
18
+ from ..parse import (
19
+ TpyExpr, TpyName, TpyBinOp, TpyUnaryOp, TpyFieldAccess,
20
+ TpySubscript, TpyNoneLiteral, TpyCall, TpyMethodCall,
21
+ TpyIntLiteral, TpyCoerce, TpyNamedExpr,
22
+ )
23
+ from .literal_utils import literal_value_from_expr
24
+ from .value_range import ValueRange
25
+ from ..prescan import match_is_none, _expr_to_narrowing_key, deref_view_key
26
+ from ..namespace import BindingKind
27
+ from ..diagnostics import OPTIONAL_VALUE_TRUTHINESS_WARNING
28
+ from ..type_def_registry import protocol_info_of
29
+
30
+ if TYPE_CHECKING:
31
+ from .context import SemanticContext
32
+ from .type_ops import TypeOperations
33
+ from .protocols import ProtocolChecker
34
+
35
+
36
+ class NarrowingTracker:
37
+ """Centralized type narrowing flow analysis.
38
+
39
+ Operates on ctx.func.narrowed_types without owning it.
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ ctx: SemanticContext,
45
+ type_ops: TypeOperations,
46
+ protocols: ProtocolChecker,
47
+ ):
48
+ self.ctx = ctx
49
+ self.type_ops = type_ops
50
+ self.protocols = protocols
51
+
52
+ # -- Name-based narrowing -------------------------------------------
53
+
54
+ def narrow_name_type(self, name: str, typ: TpyType) -> TpyType:
55
+ """Narrow Optional/Union name type using flow facts."""
56
+ return self.ctx.func.narrowed_types.get(name, typ)
57
+
58
+ def declared_type_for_name(self, name: str) -> TpyType | None:
59
+ """Get a variable's declared type (without applying flow narrowing).
60
+
61
+ Strips Ref -- declared types reflect the user's annotation, not
62
+ the internal reference-provenance wrapper.
63
+ """
64
+ if self.ctx.func.current_ns:
65
+ binding = self.ctx.func.current_ns.lookup(name)
66
+ if binding and binding.kind == BindingKind.VARIABLE:
67
+ return unwrap_ref_type(binding.type)
68
+ typ = self.ctx.func.current_scope.lookup(name)
69
+ return unwrap_ref_type(typ) if typ is not None else None
70
+
71
+ def _resolve_field_path_type(self, key: str) -> TpyType | None:
72
+ """Resolve the declared type for a dotted field path like 'obj.field' or 'obj.a.b'."""
73
+ parts = key.split(".")
74
+ if len(parts) < 2:
75
+ return None
76
+ expr: TpyExpr = TpyName(parts[0])
77
+ for field in parts[1:]:
78
+ expr = TpyFieldAccess(expr, field)
79
+ return self.declared_type_for_expr(expr)
80
+
81
+ def effective_union_type(self, name: str) -> TpyType | None:
82
+ """Get effective type for isinstance/narrowing, falling through assignment narrowing.
83
+
84
+ If the variable is narrowed to a concrete (non-union) type by assignment
85
+ narrowing, returns the declared union type instead, since isinstance and
86
+ condition_type_facts need the full union to compute branch facts.
87
+ """
88
+ effective = self.ctx.func.narrowed_types.get(name)
89
+ if effective is None:
90
+ effective = self.declared_type_for_name(name)
91
+ # Strip Own[T] -- narrowing operates on the underlying type
92
+ if isinstance(effective, OwnType):
93
+ effective = effective.wrapped
94
+ if not isinstance(effective, UnionType):
95
+ declared = self.declared_type_for_name(name)
96
+ if declared is not None:
97
+ inner = unwrap_readonly(declared)
98
+ if isinstance(inner, OwnType):
99
+ inner = inner.wrapped
100
+ if isinstance(inner, UnionType):
101
+ return inner
102
+ # Expand recursive union alias placeholder to underlying UnionType
103
+ # (e.g. after Optional narrowing: Tree | None -> Tree -> int | list[Tree]).
104
+ # `AliasRef` is the parser-emitted self-reference inside a
105
+ # recursive alias body; expand by looking the alias up.
106
+ if isinstance(effective, AliasRef):
107
+ alias = self.ctx.registry.get_type_alias(effective.name)
108
+ if alias is not None:
109
+ return alias
110
+ return effective
111
+
112
+ # -- Declared type resolution for expressions ------------------------
113
+
114
+ def declared_type_for_expr(self, expr: TpyExpr) -> TpyType | None:
115
+ """Get declared type for identity-capable expressions without flow narrowing."""
116
+ if isinstance(expr, TpyName):
117
+ return self.declared_type_for_name(expr.name)
118
+ if isinstance(expr, TpyFieldAccess):
119
+ obj_type = self.declared_type_for_expr(expr.obj)
120
+ if obj_type is None:
121
+ return None
122
+ actual_type = unwrap_readonly(unwrap_ref_type(obj_type))
123
+ if isinstance(actual_type, PtrType):
124
+ actual_type = actual_type.pointee
125
+ elif isinstance(actual_type, OwnType):
126
+ actual_type = actual_type.wrapped
127
+ elif isinstance(actual_type, OptionalType):
128
+ if actual_type.inner.is_value_type():
129
+ return None
130
+ actual_type = actual_type.inner
131
+
132
+ if isinstance(actual_type, NominalType) and actual_type.is_record:
133
+ record = self.ctx.registry.get_record_for_type(actual_type)
134
+ if not record:
135
+ return None
136
+ type_subst = self.type_ops.build_type_substitution(actual_type)
137
+ field_info = self.protocols.lookup_record_field(record, expr.field)
138
+ if field_info is None:
139
+ return None
140
+ field_type = field_info.type
141
+ if type_subst:
142
+ field_type = self.type_ops.substitute_type_params(field_type, type_subst)
143
+ return field_type
144
+
145
+ if isinstance(actual_type, TypeParamRef):
146
+ bound = self.type_ops.get_type_param_bound(actual_type.name)
147
+ if bound is not None and is_protocol_type(bound):
148
+ protocol_info = protocol_info_of(bound)
149
+ if protocol_info:
150
+ for field_name, field_type in protocol_info.fields or []:
151
+ if field_name == expr.field:
152
+ type_subst: dict[str, TpyType] = {"Self": actual_type}
153
+ if protocol_info.type_params and bound.type_args:
154
+ type_subst.update(dict(zip(protocol_info.type_params, bound.type_args)))
155
+ return self.type_ops.substitute_types(field_type, type_subst)
156
+ return None
157
+ if isinstance(expr, TpySubscript):
158
+ obj_type = self.declared_type_for_expr(expr.obj)
159
+ if obj_type is None:
160
+ return None
161
+ actual_type = unwrap_readonly(obj_type)
162
+ if isinstance(actual_type, OptionalType):
163
+ if actual_type.inner.is_value_type():
164
+ return None
165
+ actual_type = actual_type.inner
166
+ elem_type = actual_type.get_element_type()
167
+ if elem_type is not None:
168
+ return elem_type
169
+ if is_protocol_type(actual_type):
170
+ return self._get_protocol_getitem_type(actual_type)
171
+ if isinstance(actual_type, NominalType) and actual_type.is_record:
172
+ return self._get_record_getitem_type(actual_type)
173
+ return None
174
+
175
+ def _get_protocol_getitem_type(self, protocol: NominalType) -> TpyType | None:
176
+ """Get __getitem__ return type for a protocol (returns None on failure)."""
177
+ protocol_info = protocol_info_of(protocol)
178
+ if protocol_info is None:
179
+ return None
180
+ type_subst: dict[str, TpyType] = {}
181
+ if protocol_info.type_params and protocol.type_args:
182
+ type_subst = dict(zip(protocol_info.type_params, protocol.type_args))
183
+ for method_sig in protocol_info.methods:
184
+ if method_sig.name == "__getitem__":
185
+ if type_subst:
186
+ return self.type_ops.substitute_types(method_sig.return_type, type_subst)
187
+ return method_sig.return_type
188
+ return None
189
+
190
+ def _get_record_getitem_type(self, record_type: NominalType) -> TpyType | None:
191
+ """Get __getitem__ return type for a record (returns None on failure)."""
192
+ record = self.ctx.registry.get_record_for_type(record_type)
193
+ if record is None:
194
+ return None
195
+ getitem = self.protocols.lookup_record_method(record, "__getitem__")
196
+ if getitem is None:
197
+ return None
198
+ type_subst = self.type_ops.build_type_substitution(record_type)
199
+ if type_subst:
200
+ return self.type_ops.substitute_type_params(getitem.return_type, type_subst)
201
+ return getitem.return_type
202
+
203
+ # -- Condition fact extraction --------------------------------------
204
+
205
+ @staticmethod
206
+ def _is_optional_type(typ: TpyType | None) -> bool:
207
+ """Check if type is Optional (possibly wrapped in ReadonlyType)."""
208
+ if typ is None:
209
+ return False
210
+ if isinstance(typ, ReadonlyType):
211
+ typ = typ.wrapped
212
+ return isinstance(typ, OptionalType)
213
+
214
+ @staticmethod
215
+ def _optional_inner_type(typ: TpyType) -> TpyType:
216
+ """Extract inner type from Optional, preserving ReadonlyType/OwnType wrappers."""
217
+ if isinstance(typ, ReadonlyType):
218
+ return ReadonlyType(typ.wrapped.inner)
219
+ if isinstance(typ, OwnType):
220
+ inner = typ.wrapped
221
+ if isinstance(inner, OptionalType):
222
+ return inner.inner
223
+ return inner
224
+ return typ.inner
225
+
226
+ def _effective_type_for_key(self, key: str) -> TpyType | None:
227
+ """Get the effective type for a narrowing key (name or dotted path)."""
228
+ effective = self.ctx.func.narrowed_types.get(key)
229
+ if effective is not None:
230
+ return effective
231
+ if "." in key:
232
+ return self._resolve_field_path_type(key)
233
+ result = self.declared_type_for_name(key)
234
+ # Strip Own[T] -- narrowing operates on the underlying type
235
+ if isinstance(result, OwnType):
236
+ result = result.wrapped
237
+ return result
238
+
239
+ # -- Type narrowing (isinstance, is None, truthiness) ---------------
240
+
241
+ def _isinstance_facts(
242
+ self, expr: TpyExpr,
243
+ ) -> tuple[dict[str, TpyType], dict[str, TpyType]]:
244
+ """Extract (true_facts, false_facts) for type narrowing.
245
+
246
+ Handles isinstance checks (union), is/is not None (union + optional),
247
+ and truthiness (optional).
248
+ """
249
+ if isinstance(expr, TpyCall) and expr.isinstance_var is not None and expr.isinstance_type is not None:
250
+ name = expr.isinstance_var
251
+ check_type = expr.isinstance_type
252
+ # Deref-view narrowing (owning wrapper source): narrow the wrapper's
253
+ # polymorphic payload under a distinct key, leaving the wrapper
254
+ # variable's own type intact. The false branch can't narrow the
255
+ # payload (it might be any other subclass).
256
+ if expr.isinstance_deref_depth > 0:
257
+ return {deref_view_key(name): check_type}, {}
258
+ effective = self.effective_union_type(name)
259
+ # Any narrowing (D15): non-consuming -- the true branch sees the
260
+ # variable as `check_type` (a borrow into the cell); the false
261
+ # branch keeps the original Any (no narrowing). The outer cell
262
+ # remains alive in both branches.
263
+ if isinstance(effective, AnyType):
264
+ return {name: check_type}, {}
265
+ if isinstance(effective, UnionType):
266
+ # Tuple form isinstance(x, (A, B)) packs check types as a union.
267
+ check_members = (tuple(check_type.members)
268
+ if isinstance(check_type, UnionType)
269
+ else (check_type,))
270
+ remaining = [m for m in effective.members if m not in check_members]
271
+ false_type = (make_union(*remaining) if remaining else check_type)
272
+ return {name: check_type}, {name: false_type}
273
+ # Optional[Protocol] isinstance narrows to the inner protocol type.
274
+ # Exception: a @dynamic protocol inner with a concrete-class check
275
+ # type is a runtime subclass dispatch -- fall through to the
276
+ # polymorphic branch below so it narrows to the subclass (Dog),
277
+ # not the bare protocol (Pet).
278
+ if self._is_optional_type(effective):
279
+ inner = self._optional_inner_type(effective)
280
+ if is_protocol_type(inner) and not (
281
+ is_dynamic_dispatch_inner(inner, self.ctx.registry)
282
+ and isinstance(check_type, NominalType)
283
+ and not is_protocol_type(check_type)):
284
+ return {name: inner}, {}
285
+ # Optional[Polymorphic class] or post-`is not None`-narrowed
286
+ # polymorphic class: isinstance(x, Subclass) narrows to Subclass.
287
+ # Codegen emits a dynamic_cast'd reference at branch entry and
288
+ # routes downstream reads through it (cast-and-cache).
289
+ if (polymorphic_source_inner(effective, self.ctx.registry) is not None
290
+ and isinstance(check_type, NominalType)):
291
+ return {name: check_type}, {}
292
+ # Bare-protocol isinstance: narrowing to a *child* protocol
293
+ # (e.g. Iterable[T] -> NativeIterable[T]) lets downstream codegen
294
+ # (for-loop dispatch, `in` operator) see the refined protocol via
295
+ # protocol_narrowings. Only narrow when the check protocol
296
+ # inherits from the declared one -- otherwise the source's
297
+ # interface is lost (e.g. Sequence[T] -> Hashable would drop
298
+ # __len__ from sema's view, even though both constraints hold
299
+ # in the C++ `if constexpr` branch).
300
+ if (expr.isinstance_is_protocol
301
+ and is_protocol_type(effective) and is_protocol_type(check_type)
302
+ and self.protocols.protocol_inherits_from(
303
+ check_type.name, effective.name)):
304
+ return {name: check_type}, {}
305
+
306
+ # is None / is not None on union or optional types
307
+ match = match_is_none(expr)
308
+ if match is not None:
309
+ key, is_not_none = match
310
+ effective = self._effective_type_for_key(key)
311
+ if isinstance(effective, UnionType) and effective.has_none_member():
312
+ non_none_type, none_type = union_none_narrow(effective)
313
+ if is_not_none:
314
+ return {key: non_none_type}, {key: none_type}
315
+ else:
316
+ return {key: none_type}, {key: non_none_type}
317
+ if self._is_optional_type(effective):
318
+ inner_type = self._optional_inner_type(effective)
319
+ if is_not_none:
320
+ return {key: inner_type}, {}
321
+ else:
322
+ return {}, {key: inner_type}
323
+
324
+ # Truthiness on Optional: `if x:` / `if obj.field:` / `if (x := get()):` narrows
325
+ if isinstance(expr, (TpyName, TpyFieldAccess, TpyNamedExpr)):
326
+ key = _expr_to_narrowing_key(expr)
327
+ if key is not None:
328
+ effective = self._effective_type_for_key(key)
329
+ if self._is_optional_type(effective):
330
+ inner_type = self._optional_inner_type(effective)
331
+ return {key: inner_type}, {}
332
+
333
+ # Equality narrowing on Literal types: `if mode == "rb":`
334
+ if isinstance(expr, TpyBinOp) and expr.op in ("==", "!="):
335
+ facts = self._literal_equality_facts(expr)
336
+ if facts is not None:
337
+ return facts
338
+
339
+ if isinstance(expr, TpyUnaryOp) and expr.op == "!":
340
+ true_facts, false_facts = self._isinstance_facts(expr.operand)
341
+ return false_facts, true_facts
342
+
343
+ if isinstance(expr, TpyBinOp):
344
+ if expr.op == "&&":
345
+ left_true, left_false = self._isinstance_facts(expr.left)
346
+ right_true, right_false = self._isinstance_facts(expr.right)
347
+ merged_true = self._merge_facts_both_hold(left_true, right_true)
348
+ merged_false = {k: v for k, v in left_false.items()
349
+ if k in right_false and right_false[k] == v}
350
+ return merged_true, merged_false
351
+ if expr.op == "||":
352
+ left_true, left_false = self._isinstance_facts(expr.left)
353
+ right_true, right_false = self._isinstance_facts(expr.right)
354
+ merged_true = self._merge_facts_either_holds(left_true, right_true)
355
+ merged_false = self._merge_facts_both_hold(left_false, right_false)
356
+ return merged_true, merged_false
357
+
358
+ return {}, {}
359
+
360
+ @staticmethod
361
+ def _merge_facts_either_holds(
362
+ left: dict[str, TpyType], right: dict[str, TpyType],
363
+ ) -> dict[str, TpyType]:
364
+ """Merge facts where at least one holds (||-true, &&-false).
365
+
366
+ For same key with LiteralType on both sides, union the value sets.
367
+ For same key with non-Literal types, keep only if they agree exactly.
368
+ Keys present in only one side are dropped (can't assume which held).
369
+ """
370
+ merged = {}
371
+ for k, lv in left.items():
372
+ if k not in right:
373
+ continue
374
+ rv = right[k]
375
+ if (isinstance(lv, LiteralType) and isinstance(rv, LiteralType)
376
+ and lv.base_type == rv.base_type):
377
+ seen = set(lv.values)
378
+ combined = list(lv.values) + [v for v in rv.values if v not in seen]
379
+ merged[k] = LiteralType(base_type=lv.base_type, values=tuple(combined))
380
+ elif lv == rv:
381
+ merged[k] = lv
382
+ return merged
383
+
384
+ @staticmethod
385
+ def _merge_facts_both_hold(
386
+ left: dict[str, TpyType], right: dict[str, TpyType],
387
+ ) -> dict[str, TpyType]:
388
+ """Merge facts where both must hold (&&-true, ||-false).
389
+
390
+ For distinct keys, include both. For same key with LiteralType on
391
+ both sides, intersect the value sets. For non-Literal same-key
392
+ conflicts, right overrides left (preserves pre-existing behavior
393
+ where isinstance narrows further after is-not-None).
394
+ """
395
+ merged = dict(left)
396
+ for k, rv in right.items():
397
+ if k not in merged:
398
+ merged[k] = rv
399
+ continue
400
+ lv = merged[k]
401
+ if (isinstance(lv, LiteralType) and isinstance(rv, LiteralType)
402
+ and lv.base_type == rv.base_type):
403
+ common = tuple(v for v in lv.values if v in rv.values)
404
+ if common:
405
+ merged[k] = LiteralType(base_type=lv.base_type, values=common)
406
+ else:
407
+ merged[k] = lv.base_type
408
+ else:
409
+ merged[k] = rv
410
+ return merged
411
+
412
+ def _literal_equality_facts(
413
+ self, expr: TpyBinOp,
414
+ ) -> tuple[dict[str, TpyType], dict[str, TpyType]] | None:
415
+ """Extract narrowing facts for `x == lit` / `lit == x` on LiteralType vars."""
416
+ key: str | None = None
417
+ lit_val: LiteralValue | None = None
418
+ # Try both directions: `x == "rb"` and `"rb" == x`
419
+ for var_side, lit_side in [(expr.left, expr.right), (expr.right, expr.left)]:
420
+ k = _expr_to_narrowing_key(var_side)
421
+ if k is None:
422
+ continue
423
+ lv = literal_value_from_expr(lit_side)
424
+ if lv is not None:
425
+ key, lit_val = k, lv
426
+ break
427
+ if key is None or lit_val is None:
428
+ return None
429
+ effective = self._effective_type_for_key(key)
430
+ if not isinstance(effective, LiteralType):
431
+ return None
432
+ if lit_val not in effective.values:
433
+ return None
434
+ true_type = LiteralType(base_type=effective.base_type, values=(lit_val,))
435
+ remaining = tuple(v for v in effective.values if v != lit_val)
436
+ false_type = (LiteralType(base_type=effective.base_type, values=remaining)
437
+ if remaining else effective.base_type)
438
+ if expr.op == "==":
439
+ return {key: true_type}, {key: false_type}
440
+ else:
441
+ return {key: false_type}, {key: true_type}
442
+
443
+ def condition_type_facts(
444
+ self, condition: TpyExpr,
445
+ ) -> tuple[dict[str, TpyType], dict[str, TpyType]]:
446
+ """Get (true_facts, false_facts) for type narrowing (isinstance, is None, truthiness)."""
447
+ return self._isinstance_facts(condition)
448
+
449
+ def condition_ptr_null_facts(
450
+ self, condition: TpyExpr,
451
+ ) -> tuple[set[str], set[str]]:
452
+ """Get (true_nonnull, false_nonnull) for Ptr is/is not None checks.
453
+
454
+ Returns names proven non-null in the true branch and names proven
455
+ non-null in the false branch, respectively. When `p is not None`
456
+ and p is a Ptr[T], returns ({p}, {}) -- p is non-null when the
457
+ condition is true. When `p is None`, returns ({}, {p}) -- p is
458
+ non-null when the condition is false.
459
+ """
460
+ return self._ptr_null_facts(condition)
461
+
462
+ def _ptr_null_facts(
463
+ self, expr: TpyExpr,
464
+ ) -> tuple[set[str], set[str]]:
465
+ match = match_is_none(expr)
466
+ if match is not None:
467
+ key, is_not_none = match
468
+ if "." in key:
469
+ declared = self._resolve_field_path_type(key)
470
+ else:
471
+ declared = self.declared_type_for_name(key)
472
+ if isinstance(declared, PtrType):
473
+ if is_not_none:
474
+ return {key}, set()
475
+ else:
476
+ return set(), {key}
477
+
478
+ if isinstance(expr, TpyUnaryOp) and expr.op == "!":
479
+ non_null, null = self._ptr_null_facts(expr.operand)
480
+ return null, non_null
481
+
482
+ if isinstance(expr, TpyBinOp):
483
+ if expr.op == "&&":
484
+ l_nn, l_n = self._ptr_null_facts(expr.left)
485
+ r_nn, r_n = self._ptr_null_facts(expr.right)
486
+ return l_nn | r_nn, l_n & r_n
487
+ if expr.op == "||":
488
+ l_nn, l_n = self._ptr_null_facts(expr.left)
489
+ r_nn, r_n = self._ptr_null_facts(expr.right)
490
+ return l_nn & r_nn, l_n | r_n
491
+
492
+ return set(), set()
493
+
494
+ # -- Integer range facts from conditions ----------------------------
495
+
496
+ def condition_range_facts(
497
+ self, condition: TpyExpr,
498
+ ) -> tuple[dict[str, ValueRange], dict[str, ValueRange]]:
499
+ """Get (true_facts, false_facts) for integer range narrowing.
500
+
501
+ Handles comparisons (!=, ==, <, <=, >, >=), including symbolic
502
+ len() comparisons, and logical composition (and/or/not).
503
+ """
504
+ return self._range_facts(condition)
505
+
506
+ def _range_facts(
507
+ self, expr: TpyExpr,
508
+ ) -> tuple[dict[str, ValueRange], dict[str, ValueRange]]:
509
+ if isinstance(expr, TpyUnaryOp) and expr.op == "!":
510
+ true_facts, false_facts = self._range_facts(expr.operand)
511
+ return false_facts, true_facts
512
+
513
+ if isinstance(expr, TpyBinOp):
514
+ if expr.op == "&&":
515
+ l_true, l_false = self._range_facts(expr.left)
516
+ r_true, r_false = self._range_facts(expr.right)
517
+ merged_true = _intersect_range_dicts(l_true, r_true)
518
+ merged_false = _merge_range_dicts(l_false, r_false)
519
+ return merged_true, merged_false
520
+ if expr.op == "||":
521
+ l_true, l_false = self._range_facts(expr.left)
522
+ r_true, r_false = self._range_facts(expr.right)
523
+ merged_true = _merge_range_dicts(l_true, r_true)
524
+ merged_false = _intersect_range_dicts(l_false, r_false)
525
+ return merged_true, merged_false
526
+
527
+ return self._comparison_range_facts(expr)
528
+
529
+ return {}, {}
530
+
531
+ def _is_integer_typed(self, expr: TpyExpr) -> bool:
532
+ """Check if an expression has an integer type (fixed-width or BigInt)."""
533
+ typ = self.ctx.get_expr_type(expr)
534
+ if typ is None:
535
+ return False
536
+ return is_any_int_type(typ)
537
+
538
+ def _comparison_range_facts(
539
+ self, expr: TpyBinOp,
540
+ ) -> tuple[dict[str, ValueRange], dict[str, ValueRange]]:
541
+ """Extract range facts from a single comparison (==, !=, <, <=, >, >=)."""
542
+ op = expr.op
543
+ if op not in ("==", "!=", "<", "<=", ">", ">="):
544
+ return {}, {}
545
+
546
+ left, right = expr.left, expr.right
547
+
548
+ # Normalize: put the variable name on the left side
549
+ name: str | None = None
550
+ other: TpyExpr | None = None
551
+
552
+ if isinstance(left, TpyName) and self._is_integer_typed(left):
553
+ name = left.name
554
+ other = right
555
+ elif isinstance(right, TpyName) and self._is_integer_typed(right):
556
+ name = right.name
557
+ other = left
558
+ # Flip the operator: x < 5 becomes 5 > x
559
+ op = {"<": ">", "<=": ">=", ">": "<", ">=": "<=",
560
+ "==": "==", "!=": "!="}[op]
561
+
562
+ if name is None or other is None:
563
+ return {}, {}
564
+
565
+ # Determine what 'other' is: literal, len() call, or unknown
566
+ literal_val = self._extract_int_literal(other)
567
+ len_of = self._extract_len_of(other)
568
+
569
+ existing = self.ctx.func.value_ranges.get(name, ValueRange())
570
+
571
+ if op == "!=":
572
+ if literal_val == 0:
573
+ return {name: existing.with_non_zero()}, {}
574
+ return {}, {}
575
+
576
+ if op == "==":
577
+ if literal_val is not None:
578
+ false_facts: dict[str, ValueRange] = {}
579
+ if literal_val == 0:
580
+ false_facts = {name: existing.with_non_zero()}
581
+ return {name: ValueRange.from_literal(literal_val)}, false_facts
582
+ return {}, {}
583
+
584
+ if op == ">":
585
+ if literal_val is not None:
586
+ nz = literal_val >= 0
587
+ r = existing.with_lo(literal_val + 1)
588
+ if nz:
589
+ r = r.with_non_zero()
590
+ return {name: r}, {name: existing.with_hi(literal_val)}
591
+ return {}, {}
592
+
593
+ if op == ">=":
594
+ if literal_val is not None:
595
+ nz = literal_val > 0
596
+ r = existing.with_lo(literal_val)
597
+ if nz:
598
+ r = r.with_non_zero()
599
+ return {name: r}, {name: existing.with_hi(literal_val - 1)}
600
+ return {}, {}
601
+
602
+ if op == "<":
603
+ if literal_val is not None:
604
+ return (
605
+ {name: existing.with_hi(literal_val - 1)},
606
+ {name: existing.with_lo(literal_val)},
607
+ )
608
+ if len_of is not None:
609
+ return (
610
+ {name: existing.with_hi_len_of(len_of)},
611
+ {},
612
+ )
613
+ return {}, {}
614
+
615
+ if op == "<=":
616
+ if literal_val is not None:
617
+ return (
618
+ {name: existing.with_hi(literal_val)},
619
+ {name: existing.with_lo(literal_val + 1)},
620
+ )
621
+ return {}, {}
622
+
623
+ return {}, {}
624
+
625
+ def _extract_int_literal(self, expr: TpyExpr) -> int | None:
626
+ """Extract a concrete integer value from an expression."""
627
+ if isinstance(expr, TpyIntLiteral):
628
+ return expr.value
629
+ return None
630
+
631
+ def _extract_len_of(self, expr: TpyExpr) -> str | None:
632
+ """Extract container name from len(container) call."""
633
+ if (isinstance(expr, TpyCall) and expr.func_name == "len"
634
+ and len(expr.args) == 1 and isinstance(expr.args[0], TpyName)):
635
+ return expr.args[0].name
636
+ return None
637
+
638
+ # -- Truthiness warnings -------------------------------------------
639
+
640
+ def _truthy_names(self, expr: TpyExpr) -> set[str]:
641
+ """Collect Optional variable/field keys used in truthiness contexts."""
642
+ if isinstance(expr, TpyName):
643
+ declared = self.declared_type_for_name(expr.name)
644
+ if self._is_optional_type(declared):
645
+ return {expr.name}
646
+ return set()
647
+ if isinstance(expr, TpyFieldAccess):
648
+ key = _expr_to_narrowing_key(expr)
649
+ if key is not None:
650
+ declared = self._resolve_field_path_type(key)
651
+ if self._is_optional_type(declared):
652
+ return {key}
653
+ return set()
654
+ if isinstance(expr, TpyUnaryOp) and expr.op == "!":
655
+ return self._truthy_names(expr.operand)
656
+ if isinstance(expr, TpyBinOp) and expr.op in ("&&", "||"):
657
+ return self._truthy_names(expr.left) | self._truthy_names(expr.right)
658
+ return set()
659
+
660
+ def condition_truthy_value_optional_names(self, condition: TpyExpr) -> set[str]:
661
+ """Get value-optionals used via truthiness in a condition."""
662
+ keys = self._truthy_names(condition)
663
+ result: set[str] = set()
664
+ for key in keys:
665
+ if "." in key:
666
+ declared = self._resolve_field_path_type(key)
667
+ else:
668
+ declared = self.declared_type_for_name(key)
669
+ inner = unwrap_readonly(declared) if declared else None
670
+ if isinstance(inner, OptionalType) and inner.inner.is_value_type():
671
+ result.add(key)
672
+ return result
673
+
674
+ def warn_truthy_value_optionals(self, condition: TpyExpr) -> None:
675
+ """Warn when truthiness narrows optional value-typed variables."""
676
+ names = self.condition_truthy_value_optional_names(condition)
677
+ for name in sorted(names):
678
+ self.ctx.warning(
679
+ f"{OPTIONAL_VALUE_TRUTHINESS_WARNING} (variable '{name}')",
680
+ condition,
681
+ )
682
+
683
+ # -- Fact invalidation on writes/calls -----------------------------
684
+
685
+ def update_after_write(
686
+ self,
687
+ name: str,
688
+ target_type: TpyType,
689
+ rhs_type: TpyType | None = None,
690
+ rhs_expr: TpyExpr | None = None,
691
+ ) -> None:
692
+ """Update flow facts after assigning/writing a variable."""
693
+ self.ctx.func.narrowed_types.pop(name, None)
694
+ # A deref-view narrowing (isinstance through an owning wrapper) is keyed
695
+ # apart from the variable's own narrowing, so the pop above misses it;
696
+ # clear it too or a reassigned wrapper keeps the stale subclass cast.
697
+ self.ctx.func.narrowed_types.pop(deref_view_key(name), None)
698
+ # Invalidate field narrowing facts rooted at this variable
699
+ self._invalidate_field_facts(name)
700
+ # Invalidate integer range facts for this variable
701
+ self.ctx.func.value_ranges.pop(name, None)
702
+ # Invalidate symbolic bounds referencing this variable's length
703
+ self._invalidate_len_ranges(name)
704
+ # Set range fact for integer literal assignments (e.g. i = 0, i: Int32 = 0).
705
+ # Unwrap one level of TpyCoerce (typed annotations wrap the literal in a coerce node).
706
+ rhs_inner = rhs_expr.expr if isinstance(rhs_expr, TpyCoerce) else rhs_expr
707
+ if isinstance(rhs_inner, TpyIntLiteral):
708
+ self.ctx.func.value_ranges[name] = ValueRange.from_literal(rhs_inner.value)
709
+ # For Optional targets, re-narrow if RHS is provably non-None
710
+ inner_target = unwrap_readonly(target_type)
711
+ if isinstance(inner_target, OwnType):
712
+ inner_target = inner_target.wrapped
713
+ if not isinstance(inner_target, OptionalType):
714
+ return
715
+ if rhs_type is None:
716
+ return
717
+ # Peel readonly/own/ref wrappers on the RHS to mirror the LHS unwrap
718
+ # above -- a readonly Optional field read produces an Optional under
719
+ # a wrapper, and that's still possibly-None.
720
+ rhs_unwrapped = unwrap_qualifiers(rhs_type)
721
+ if isinstance(rhs_unwrapped, (NoneType, OptionalType)) or isinstance(rhs_expr, TpyNoneLiteral):
722
+ return
723
+ self.ctx.func.narrowed_types[name] = self._optional_inner_type(target_type)
724
+
725
+ def _invalidate_field_facts(self, name: str) -> None:
726
+ """Remove all field narrowing facts rooted at the given variable name."""
727
+ prefix = name + "."
728
+ stale = [k for k in self.ctx.func.narrowed_types if k.startswith(prefix)]
729
+ for k in stale:
730
+ del self.ctx.func.narrowed_types[k]
731
+ stale_ptr = [k for k in self.ctx.func.non_null_ptr_vars if k.startswith(prefix)]
732
+ for k in stale_ptr:
733
+ self.ctx.func.non_null_ptr_vars.discard(k)
734
+
735
+ def invalidate_for_field_write(self, target: TpyExpr) -> None:
736
+ """Invalidate narrowing facts for sub-paths when a field is written.
737
+
738
+ When `obj.inner = ...` is written, clears Optional narrowing facts for
739
+ deeper paths like `obj.inner.value` (the new object may have different
740
+ field values). Does NOT clear the Optional fact for the written field
741
+ itself -- that is managed by the enclosing `is not None` guard.
742
+ Ptr non-null facts ARE cleared for the written key itself, since a
743
+ field write always introduces a potentially-null pointer value.
744
+ """
745
+ key = _expr_to_narrowing_key(target)
746
+ if key is None or "." not in key:
747
+ return
748
+ prefix = key + "."
749
+ stale = [k for k in self.ctx.func.narrowed_types if k.startswith(prefix)]
750
+ for k in stale:
751
+ del self.ctx.func.narrowed_types[k]
752
+ self.ctx.func.non_null_ptr_vars.discard(key)
753
+ stale_ptr = [k for k in self.ctx.func.non_null_ptr_vars if k.startswith(prefix)]
754
+ for k in stale_ptr:
755
+ self.ctx.func.non_null_ptr_vars.discard(k)
756
+
757
+ def _invalidate_len_ranges(self, name: str) -> None:
758
+ """Remove range facts whose symbolic bound references len(name)."""
759
+ stale = [k for k, v in self.ctx.func.value_ranges.items()
760
+ if v.hi_len_of == name]
761
+ for k in stale:
762
+ del self.ctx.func.value_ranges[k]
763
+
764
+ def invalidate_field_facts_for_call(self, call: TpyCall) -> None:
765
+ """Invalidate field narrowing facts for name arguments passed by mutable reference.
766
+
767
+ When a non-value-type object is passed to a function, the callee receives
768
+ a mutable reference and may modify any field, so field narrowing facts
769
+ for that object are no longer reliable.
770
+ """
771
+ for arg in call.args:
772
+ if not isinstance(arg, TpyName):
773
+ continue
774
+ arg_type = self.ctx.get_expr_type(arg)
775
+ if arg_type is None:
776
+ continue
777
+ inner = unwrap_readonly(arg_type)
778
+ if inner.is_value_type():
779
+ continue
780
+ self._invalidate_field_facts(arg.name)
781
+ self._invalidate_len_ranges(arg.name)
782
+
783
+ def invalidate_field_facts_for_method_call(self, call: TpyMethodCall) -> None:
784
+ """Invalidate field narrowing facts after a method call.
785
+
786
+ The receiver object is passed as mutable self, so any field could be mutated.
787
+ Also invalidates symbolic len-based range facts for the receiver (the method
788
+ may change the container's length, e.g. pop/clear/insert).
789
+ Also invalidates for any non-value-type arguments.
790
+ """
791
+ if not call.is_static_call:
792
+ if isinstance(call.obj, TpyName):
793
+ self._invalidate_field_facts(call.obj.name)
794
+ self._invalidate_len_ranges(call.obj.name)
795
+ else:
796
+ obj_key = _expr_to_narrowing_key(call.obj)
797
+ if obj_key is not None:
798
+ self._invalidate_field_facts(obj_key)
799
+ for arg in call.args:
800
+ if not isinstance(arg, TpyName):
801
+ continue
802
+ arg_type = self.ctx.get_expr_type(arg)
803
+ if arg_type is None:
804
+ continue
805
+ inner = unwrap_readonly(arg_type)
806
+ if inner.is_value_type():
807
+ continue
808
+ self._invalidate_field_facts(arg.name)
809
+
810
+
811
+ # -- Module-level helpers for range fact dict operations ------------------
812
+
813
+ def deref_view_narrowed(ctx, obj, deref_target: TpyType) -> 'NominalType | None':
814
+ """Deref-view narrowed subclass for receiver `obj` when the peeled payload
815
+ `deref_target` is a @dynamic-dispatch inner narrowed by an active
816
+ `if isinstance(<obj>, Sub):`. Shared by the method-call (methods.py) and
817
+ field-access (expressions.py) deref-chain resolvers."""
818
+ inner = unwrap_readonly(deref_target)
819
+ if not (isinstance(inner, NominalType)
820
+ and is_dynamic_dispatch_inner(inner, ctx.registry)):
821
+ return None
822
+ key = _expr_to_narrowing_key(obj)
823
+ if key is None:
824
+ return None
825
+ narrowed = ctx.func.narrowed_types.get(deref_view_key(key))
826
+ return narrowed if isinstance(narrowed, NominalType) else None
827
+
828
+
829
+ def _intersect_range_dicts(
830
+ a: dict[str, ValueRange], b: dict[str, ValueRange],
831
+ ) -> dict[str, ValueRange]:
832
+ """Combine two range fact dicts (and-composition): tighten overlapping keys."""
833
+ if not a:
834
+ return b
835
+ if not b:
836
+ return a
837
+ result = dict(a)
838
+ for k, v in b.items():
839
+ if k in result:
840
+ result[k] = ValueRange.intersect(result[k], v)
841
+ else:
842
+ result[k] = v
843
+ return result
844
+
845
+
846
+ def _merge_range_dicts(
847
+ a: dict[str, ValueRange], b: dict[str, ValueRange],
848
+ ) -> dict[str, ValueRange]:
849
+ """Combine two range fact dicts (or-composition): widen overlapping keys, drop unique."""
850
+ if not a or not b:
851
+ return {}
852
+ result = {}
853
+ for k, v in a.items():
854
+ if k in b:
855
+ result[k] = ValueRange.merge(v, b[k])
856
+ return result
857
+