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.
- tpy_lang-0.3.0.dev0.dist-info/METADATA +151 -0
- tpy_lang-0.3.0.dev0.dist-info/RECORD +333 -0
- tpy_lang-0.3.0.dev0.dist-info/WHEEL +4 -0
- tpy_lang-0.3.0.dev0.dist-info/entry_points.txt +3 -0
- tpyc/__init__.py +104 -0
- tpyc/__main__.py +6 -0
- tpyc/_buildinfo.py +1 -0
- tpyc/_data/docs/LANGUAGE_FEATURES.md +6278 -0
- tpyc/_data/docs/STDLIB_ROADMAP.md +1258 -0
- tpyc/_data/docs/TPY_FOR_AGENTS.md +556 -0
- tpyc/_data/lib/tpy/_bindings/__init__.py +6 -0
- tpyc/_data/lib/tpy/_bindings/pcre2.py +173 -0
- tpyc/_data/lib/tpy/_bindings/posix_socket.py +161 -0
- tpyc/_data/lib/tpy/_functools_macros.py +80 -0
- tpyc/_data/lib/tpy/_macro_helpers.py +161 -0
- tpyc/_data/lib/tpy/argparse.py +2062 -0
- tpyc/_data/lib/tpy/asyncio/__init__.py +744 -0
- tpyc/_data/lib/tpy/asyncio/_executor.py +515 -0
- tpyc/_data/lib/tpy/base64.py +410 -0
- tpyc/_data/lib/tpy/bisect.py +39 -0
- tpyc/_data/lib/tpy/builtins.py +38 -0
- tpyc/_data/lib/tpy/dataclasses.py +354 -0
- tpyc/_data/lib/tpy/enum.py +23 -0
- tpyc/_data/lib/tpy/functools.py +33 -0
- tpyc/_data/lib/tpy/hashlib.py +206 -0
- tpyc/_data/lib/tpy/heapq.py +118 -0
- tpyc/_data/lib/tpy/io.py +395 -0
- tpyc/_data/lib/tpy/json.py +221 -0
- tpyc/_data/lib/tpy/math.py +406 -0
- tpyc/_data/lib/tpy/random.py +597 -0
- tpyc/_data/lib/tpy/re.py +467 -0
- tpyc/_data/lib/tpy/socket.py +379 -0
- tpyc/_data/lib/tpy/struct.py +178 -0
- tpyc/_data/lib/tpy/sys.py +40 -0
- tpyc/_data/lib/tpy/time.py +39 -0
- tpyc/_data/lib/tpy/tpy/__init__.py +78 -0
- tpyc/_data/lib/tpy/tpy/_bootstrap/__init__.py +10 -0
- tpyc/_data/lib/tpy/tpy/_bootstrap/_decorators.py +37 -0
- tpyc/_data/lib/tpy/tpy/_bootstrap/_extern.py +64 -0
- tpyc/_data/lib/tpy/tpy/_builtins/__init__.py +11 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_bytes.py +378 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_dict.py +151 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_exceptions.py +125 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_funcs.py +681 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_io.py +97 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_list.py +127 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_range.py +52 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_set.py +139 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_super.py +11 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_types.py +661 -0
- tpyc/_data/lib/tpy/tpy/_core/__init__.py +23 -0
- tpyc/_data/lib/tpy/tpy/_core/_bytes_view.py +129 -0
- tpyc/_data/lib/tpy/tpy/_core/_containers.py +137 -0
- tpyc/_data/lib/tpy/tpy/_core/_functions.py +40 -0
- tpyc/_data/lib/tpy/tpy/_core/_types.py +2061 -0
- tpyc/_data/lib/tpy/tpy/_typing/__init__.py +77 -0
- tpyc/_data/lib/tpy/tpy/_version.py +29 -0
- tpyc/_data/lib/tpy/tpy/bits.py +28 -0
- tpyc/_data/lib/tpy/tpy/coro/__init__.py +127 -0
- tpyc/_data/lib/tpy/tpy/extern.py +8 -0
- tpyc/_data/lib/tpy/tpy/mem.py +49 -0
- tpyc/_data/lib/tpy/tpy/unsafe.py +195 -0
- tpyc/_data/lib/tpy/tpy/version.py +21 -0
- tpyc/_data/lib/tpy/typing.py +13 -0
- tpyc/_data/runtime/cpp/include/tpy/any.hpp +461 -0
- tpyc/_data/runtime/cpp/include/tpy/as_ostream.hpp +117 -0
- tpyc/_data/runtime/cpp/include/tpy/async.hpp +76 -0
- tpyc/_data/runtime/cpp/include/tpy/bigint.hpp +1343 -0
- tpyc/_data/runtime/cpp/include/tpy/builtins.hpp +400 -0
- tpyc/_data/runtime/cpp/include/tpy/bytes_ops.hpp +469 -0
- tpyc/_data/runtime/cpp/include/tpy/container_ops.hpp +487 -0
- tpyc/_data/runtime/cpp/include/tpy/copy_iter.hpp +82 -0
- tpyc/_data/runtime/cpp/include/tpy/core.hpp +558 -0
- tpyc/_data/runtime/cpp/include/tpy/dict_ops.hpp +289 -0
- tpyc/_data/runtime/cpp/include/tpy/dunder.hpp +750 -0
- tpyc/_data/runtime/cpp/include/tpy/dynamic.hpp +44 -0
- tpyc/_data/runtime/cpp/include/tpy/enum.hpp +40 -0
- tpyc/_data/runtime/cpp/include/tpy/file.hpp +245 -0
- tpyc/_data/runtime/cpp/include/tpy/fixed_int.hpp +317 -0
- tpyc/_data/runtime/cpp/include/tpy/format.hpp +954 -0
- tpyc/_data/runtime/cpp/include/tpy/frame_slot.hpp +120 -0
- tpyc/_data/runtime/cpp/include/tpy/generator.hpp +47 -0
- tpyc/_data/runtime/cpp/include/tpy/iterable_ops.hpp +122 -0
- tpyc/_data/runtime/cpp/include/tpy/itertools.hpp +749 -0
- tpyc/_data/runtime/cpp/include/tpy/next_iter.hpp +82 -0
- tpyc/_data/runtime/cpp/include/tpy/ordered_map.hpp +518 -0
- tpyc/_data/runtime/cpp/include/tpy/ordered_set.hpp +337 -0
- tpyc/_data/runtime/cpp/include/tpy/own_iter.hpp +54 -0
- tpyc/_data/runtime/cpp/include/tpy/pascal_graph_sdl.hpp +192 -0
- tpyc/_data/runtime/cpp/include/tpy/printing.hpp +302 -0
- tpyc/_data/runtime/cpp/include/tpy/protocols.hpp +61 -0
- tpyc/_data/runtime/cpp/include/tpy/range.hpp +115 -0
- tpyc/_data/runtime/cpp/include/tpy/ranges.hpp +212 -0
- tpyc/_data/runtime/cpp/include/tpy/set_ops.hpp +265 -0
- tpyc/_data/runtime/cpp/include/tpy/slice.hpp +47 -0
- tpyc/_data/runtime/cpp/include/tpy/span_iter.hpp +42 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/math.hpp +41 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/pcre2_h.hpp +96 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/random.hpp +25 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/socket_h.hpp +145 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/time.hpp +62 -0
- tpyc/_data/runtime/cpp/include/tpy/system.hpp +121 -0
- tpyc/_data/runtime/cpp/include/tpy/throwable.hpp +55 -0
- tpyc/_data/runtime/cpp/include/tpy/tpy.hpp +156 -0
- tpyc/_data/runtime/cpp/include/tpy/type_name.hpp +77 -0
- tpyc/_data/runtime/cpp/include/tpy/type_traits.hpp +240 -0
- tpyc/_data/runtime/cpp/include/tpy/uninit_array_storage.hpp +250 -0
- tpyc/_data/runtime/cpp/include/tpy/uninit_heap_storage.hpp +277 -0
- tpyc/_data/runtime/cpp/include/tpy/varargs.hpp +174 -0
- tpyc/_data/runtime/cpp/include/tpy/variant_ref.hpp +118 -0
- tpyc/_data/runtime/cpp/src/stdlib/socket_impl.cpp +104 -0
- tpyc/_data/runtime/cpp/third_party/README.md +58 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/AUTHORS +36 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/CMakeLists.txt +1233 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/COPYING +5 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/ChangeLog +3097 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/HACKING +853 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/INSTALL +368 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/LICENCE +94 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/NEWS +492 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/NON-AUTOTOOLS-BUILD +430 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/README +956 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/COPYING-CMAKE-SCRIPTS +22 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindEditline.cmake +16 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindPackageHandleStandardArgs.cmake +58 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindReadline.cmake +29 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/pcre2-config-version.cmake.in +15 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/pcre2-config.cmake.in +148 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/config-cmake.h.in +56 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-16.pc.in +13 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-32.pc.in +13 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-8.pc.in +13 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-posix.pc.in +13 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/pcre2-config.in +121 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h +483 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h.generic +483 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h.in +460 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h +1010 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h.generic +1010 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h.in +1010 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_auto_possess.c +1371 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chartables.c +196 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chartables.c.dist +196 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chkdint.c +96 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_compile.c +11001 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_config.c +252 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_context.c +510 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_convert.c +1189 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_dfa_match.c +4119 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_dftables.c +297 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_error.c +345 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_extuni.c +162 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_find_bracket.c +219 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_fuzzsupport.c +792 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_internal.h +2084 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_intmodedep.h +940 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_compile.c +14972 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_match.c +200 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_misc.c +234 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_neon_inc.h +354 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_simd_inc.h +2355 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_test.c +2528 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_maketables.c +165 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_match.c +7777 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_match_data.c +185 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_newline.c +243 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ord2utf.c +120 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_pattern_info.c +432 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_printint.c +886 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_script_run.c +344 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_serialize.c +286 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_string_utils.c +237 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_study.c +1915 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_substitute.c +1009 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_substring.c +550 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_tables.c +234 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucd.c +5460 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucp.h +396 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucptables.c +1533 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_valid_utf.c +398 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_xclass.c +308 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2demo.c +497 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2grep.c +4606 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix.c +425 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix.h +187 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix_test.c +209 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2test.c +9708 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorApple.c +137 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorCore.c +327 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorFreeBSD.c +89 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorPosix.c +62 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorWindows.c +40 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitProtExecAllocatorNetBSD.c +72 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitProtExecAllocatorPosix.c +172 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitWXExecAllocatorPosix.c +141 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitWXExecAllocatorWindows.c +102 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfig.h +142 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfigCPU.h +188 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfigInternal.h +907 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitLir.c +3561 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitLir.h +2466 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_32.c +4636 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_64.c +3491 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_T2_32.c +4302 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeLOONGARCH_64.c +3765 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_32.c +472 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_64.c +387 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_common.c +4259 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_32.c +485 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_64.c +719 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_common.c +3161 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_32.c +142 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_64.c +222 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_common.c +3121 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeS390X.c +4526 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_32.c +1685 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_64.c +1398 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_common.c +5001 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitSerialize.c +516 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitUtils.c +344 -0
- tpyc/_data/runtime/cpp/third_party/pcre2.sources.txt +54 -0
- tpyc/_data/runtime/cpp/third_party/pcre2.vendor.json +7 -0
- tpyc/build/__init__.py +7 -0
- tpyc/build/pcre2.py +122 -0
- tpyc/build/third_party.py +413 -0
- tpyc/cli.py +822 -0
- tpyc/codegen_cpp/__init__.py +18 -0
- tpyc/codegen_cpp/builtins.py +484 -0
- tpyc/codegen_cpp/context.py +2064 -0
- tpyc/codegen_cpp/expressions.py +5940 -0
- tpyc/codegen_cpp/functions.py +1913 -0
- tpyc/codegen_cpp/gen_async.py +3258 -0
- tpyc/codegen_cpp/gen_generators.py +657 -0
- tpyc/codegen_cpp/generator.py +2258 -0
- tpyc/codegen_cpp/match.py +1997 -0
- tpyc/codegen_cpp/param_const.py +172 -0
- tpyc/codegen_cpp/protocols.py +907 -0
- tpyc/codegen_cpp/records.py +1654 -0
- tpyc/codegen_cpp/resumable_cfg.py +1651 -0
- tpyc/codegen_cpp/statements.py +4963 -0
- tpyc/codegen_cpp/string_dispatch.py +76 -0
- tpyc/codegen_cpp/test_context.py +46 -0
- tpyc/codegen_cpp/test_param_const.py +113 -0
- tpyc/codegen_cpp/test_resumable_cfg.py +182 -0
- tpyc/codegen_cpp/type_resolution.py +53 -0
- tpyc/codegen_cpp/types.py +436 -0
- tpyc/codegen_cpp/variant_access.py +135 -0
- tpyc/coercions.py +749 -0
- tpyc/compilation_context.py +57 -0
- tpyc/compiler.py +3945 -0
- tpyc/cycle_detection.py +358 -0
- tpyc/diagnostics.py +135 -0
- tpyc/dump_types.py +353 -0
- tpyc/frontend_diagnostics.py +47 -0
- tpyc/frontend_ir/__init__.py +140 -0
- tpyc/frontend_ir/lower.py +1098 -0
- tpyc/frontend_ir/nodes.py +718 -0
- tpyc/frontend_ir/resolver_adapter.py +151 -0
- tpyc/frontend_plugin.py +209 -0
- tpyc/install_docs.py +81 -0
- tpyc/liveness.py +756 -0
- tpyc/macro_api.py +1724 -0
- tpyc/macro_loader.py +497 -0
- tpyc/module_names.py +64 -0
- tpyc/modules/__init__.py +31 -0
- tpyc/modules/defs.py +89 -0
- tpyc/modules/registry.py +36 -0
- tpyc/modules/resolver.py +192 -0
- tpyc/modules/type_resolution.py +629 -0
- tpyc/namespace.py +172 -0
- tpyc/parse/__init__.py +84 -0
- tpyc/parse/imports.py +490 -0
- tpyc/parse/nodes.py +1732 -0
- tpyc/parse/parser.py +4043 -0
- tpyc/parse/resolve_refs.py +466 -0
- tpyc/parse/type_resolver.py +1060 -0
- tpyc/prescan.py +254 -0
- tpyc/qnames.py +149 -0
- tpyc/repl.py +529 -0
- tpyc/repl_backends.py +848 -0
- tpyc/sema/__init__.py +21 -0
- tpyc/sema/analyzer.py +3625 -0
- tpyc/sema/bound_check.py +72 -0
- tpyc/sema/builder_trace.py +684 -0
- tpyc/sema/calls.py +5406 -0
- tpyc/sema/compatibility.py +2107 -0
- tpyc/sema/context.py +1243 -0
- tpyc/sema/expressions.py +3737 -0
- tpyc/sema/flow_facts.py +199 -0
- tpyc/sema/init_tracker.py +150 -0
- tpyc/sema/list_literals.py +69 -0
- tpyc/sema/literal_utils.py +27 -0
- tpyc/sema/local_deduction.py +1088 -0
- tpyc/sema/macros.py +179 -0
- tpyc/sema/match.py +1177 -0
- tpyc/sema/method_expansion.py +347 -0
- tpyc/sema/methods.py +2197 -0
- tpyc/sema/mutation_propagation.py +268 -0
- tpyc/sema/narrowing.py +857 -0
- tpyc/sema/numeric_lattice.py +160 -0
- tpyc/sema/operators.py +402 -0
- tpyc/sema/overloads.py +841 -0
- tpyc/sema/protocols.py +1209 -0
- tpyc/sema/reach_analysis.py +202 -0
- tpyc/sema/registration.py +3156 -0
- tpyc/sema/scope_tracker.py +193 -0
- tpyc/sema/statements.py +4426 -0
- tpyc/sema/type_ops.py +1879 -0
- tpyc/sema/value_range.py +181 -0
- tpyc/symbol_binding.py +259 -0
- tpyc/test_c3_mro.py +208 -0
- tpyc/test_cli_argv.py +52 -0
- tpyc/test_compiler.py +559 -0
- tpyc/test_contains_type_param.py +101 -0
- tpyc/test_cycle_detection.py +221 -0
- tpyc/test_dump_types.py +225 -0
- tpyc/test_install_docs.py +65 -0
- tpyc/test_local_cpp_form.py +135 -0
- tpyc/test_macro_loader.py +76 -0
- tpyc/test_method_expansion.py +254 -0
- tpyc/test_nominal_identity.py +182 -0
- tpyc/test_overloads.py +410 -0
- tpyc/test_parse.py +303 -0
- tpyc/test_parse_type_ref.py +506 -0
- tpyc/test_parse_version_info.py +58 -0
- tpyc/test_reach_analysis.py +72 -0
- tpyc/test_ref_type.py +216 -0
- tpyc/test_send_sync_substitution.py +276 -0
- tpyc/test_tuple_mutation_propagation.py +206 -0
- tpyc/test_type_def_registry.py +1729 -0
- tpyc/test_union_types.py +195 -0
- tpyc/type_def_registry.py +975 -0
- 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
|
+
|