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
tpyc/sema/value_range.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ValueRange -- tracks provable integer value constraints.
|
|
3
|
+
|
|
4
|
+
Represents a [lo, hi] interval with optional non_zero flag and symbolic
|
|
5
|
+
upper bound (hi_len_of). Used for bounds check elision and division-by-zero
|
|
6
|
+
check elision.
|
|
7
|
+
|
|
8
|
+
Frozen dataclass so instances can be stored in FlowFacts frozensets.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
|
|
15
|
+
# INT32 bounds (loop indices and len() return Int32)
|
|
16
|
+
_INT32_MAX = 2**31 - 1
|
|
17
|
+
_INT32_MIN = -(2**31)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True, slots=True)
|
|
21
|
+
class ValueRange:
|
|
22
|
+
"""Proven integer value constraints for a variable.
|
|
23
|
+
|
|
24
|
+
lo/hi: concrete bounds (None = unbounded in that direction).
|
|
25
|
+
non_zero: True if the value is provably != 0.
|
|
26
|
+
hi_len_of: when set, hi is symbolically len(var_name) - 1.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
lo: int | None = None
|
|
30
|
+
hi: int | None = None
|
|
31
|
+
non_zero: bool = False
|
|
32
|
+
hi_len_of: str | None = None
|
|
33
|
+
|
|
34
|
+
# -- Factories --
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def from_literal(value: int) -> ValueRange:
|
|
38
|
+
return ValueRange(lo=value, hi=value, non_zero=(value != 0))
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def from_len() -> ValueRange:
|
|
42
|
+
"""Result of len() -- always non-negative."""
|
|
43
|
+
return ValueRange(lo=0, hi=_INT32_MAX)
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def non_negative() -> ValueRange:
|
|
47
|
+
return ValueRange(lo=0)
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def only_non_zero() -> ValueRange:
|
|
51
|
+
return ValueRange(non_zero=True)
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def for_range_index(stop_len_of: str | None = None,
|
|
55
|
+
stop_literal: int | None = None) -> ValueRange:
|
|
56
|
+
"""Range fact for a for-loop variable iterating over range().
|
|
57
|
+
|
|
58
|
+
stop_len_of: container name when stop is len(container).
|
|
59
|
+
stop_literal: concrete stop value when known.
|
|
60
|
+
"""
|
|
61
|
+
if stop_len_of is not None:
|
|
62
|
+
return ValueRange(lo=0, hi_len_of=stop_len_of)
|
|
63
|
+
if stop_literal is not None:
|
|
64
|
+
if stop_literal <= 0:
|
|
65
|
+
# Empty range -- loop body never executes, but if it did
|
|
66
|
+
# the variable would have no valid value. Use [0, -1].
|
|
67
|
+
return ValueRange(lo=0, hi=-1)
|
|
68
|
+
return ValueRange(lo=0, hi=stop_literal - 1)
|
|
69
|
+
return ValueRange(lo=0)
|
|
70
|
+
|
|
71
|
+
# -- Queries --
|
|
72
|
+
|
|
73
|
+
def is_non_negative(self) -> bool:
|
|
74
|
+
return self.lo is not None and self.lo >= 0
|
|
75
|
+
|
|
76
|
+
def is_bounded_by_len(self, container: str) -> bool:
|
|
77
|
+
"""True if hi is symbolically <= len(container) - 1."""
|
|
78
|
+
return self.hi_len_of is not None and self.hi_len_of == container
|
|
79
|
+
|
|
80
|
+
# -- Combinators --
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def merge(a: ValueRange, b: ValueRange) -> ValueRange:
|
|
84
|
+
"""Branch join (union): widens to cover both possibilities."""
|
|
85
|
+
if a.lo is None or b.lo is None:
|
|
86
|
+
lo = None
|
|
87
|
+
else:
|
|
88
|
+
lo = min(a.lo, b.lo)
|
|
89
|
+
|
|
90
|
+
# Symbolic bounds only preserved when both agree
|
|
91
|
+
if a.hi_len_of is not None and a.hi_len_of == b.hi_len_of:
|
|
92
|
+
return ValueRange(
|
|
93
|
+
lo=lo,
|
|
94
|
+
hi_len_of=a.hi_len_of,
|
|
95
|
+
non_zero=a.non_zero and b.non_zero,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if a.hi is None or b.hi is None:
|
|
99
|
+
hi = None
|
|
100
|
+
else:
|
|
101
|
+
hi = max(a.hi, b.hi)
|
|
102
|
+
|
|
103
|
+
return ValueRange(
|
|
104
|
+
lo=lo,
|
|
105
|
+
hi=hi,
|
|
106
|
+
non_zero=a.non_zero and b.non_zero,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def intersect(a: ValueRange, b: ValueRange) -> ValueRange:
|
|
111
|
+
"""And-composition: tightens to what both prove."""
|
|
112
|
+
if a.lo is None:
|
|
113
|
+
lo = b.lo
|
|
114
|
+
elif b.lo is None:
|
|
115
|
+
lo = a.lo
|
|
116
|
+
else:
|
|
117
|
+
lo = max(a.lo, b.lo)
|
|
118
|
+
|
|
119
|
+
# Keep symbolic bound only when both sides agree (or one is absent)
|
|
120
|
+
if a.hi_len_of is not None and b.hi_len_of is not None:
|
|
121
|
+
hi_len_of = a.hi_len_of if a.hi_len_of == b.hi_len_of else None
|
|
122
|
+
else:
|
|
123
|
+
hi_len_of = a.hi_len_of or b.hi_len_of
|
|
124
|
+
|
|
125
|
+
if hi_len_of is not None:
|
|
126
|
+
return ValueRange(
|
|
127
|
+
lo=lo,
|
|
128
|
+
hi_len_of=hi_len_of,
|
|
129
|
+
non_zero=a.non_zero or b.non_zero,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if a.hi is None:
|
|
133
|
+
hi = b.hi
|
|
134
|
+
elif b.hi is None:
|
|
135
|
+
hi = a.hi
|
|
136
|
+
else:
|
|
137
|
+
hi = min(a.hi, b.hi)
|
|
138
|
+
|
|
139
|
+
return ValueRange(
|
|
140
|
+
lo=lo,
|
|
141
|
+
hi=hi,
|
|
142
|
+
non_zero=a.non_zero or b.non_zero,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def with_non_zero(self, nz: bool = True) -> ValueRange:
|
|
146
|
+
"""Return a copy with non_zero set."""
|
|
147
|
+
if self.non_zero == nz:
|
|
148
|
+
return self
|
|
149
|
+
return ValueRange(
|
|
150
|
+
lo=self.lo, hi=self.hi,
|
|
151
|
+
non_zero=nz, hi_len_of=self.hi_len_of,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
def with_lo(self, lo: int) -> ValueRange:
|
|
155
|
+
"""Return a copy with lo tightened (only if stricter)."""
|
|
156
|
+
current_lo = self.lo
|
|
157
|
+
if current_lo is not None and current_lo >= lo:
|
|
158
|
+
return self
|
|
159
|
+
nz = self.non_zero or lo > 0
|
|
160
|
+
return ValueRange(
|
|
161
|
+
lo=lo, hi=self.hi,
|
|
162
|
+
non_zero=nz, hi_len_of=self.hi_len_of,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
def with_hi(self, hi: int) -> ValueRange:
|
|
166
|
+
"""Return a copy with hi tightened (only if stricter)."""
|
|
167
|
+
current_hi = self.hi
|
|
168
|
+
if current_hi is not None and current_hi <= hi:
|
|
169
|
+
return self
|
|
170
|
+
nz = self.non_zero or (self.lo is not None and self.lo > 0)
|
|
171
|
+
return ValueRange(
|
|
172
|
+
lo=self.lo, hi=hi,
|
|
173
|
+
non_zero=nz, hi_len_of=None,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def with_hi_len_of(self, container: str) -> ValueRange:
|
|
177
|
+
"""Return a copy with symbolic upper bound."""
|
|
178
|
+
return ValueRange(
|
|
179
|
+
lo=self.lo, hi=None,
|
|
180
|
+
non_zero=self.non_zero, hi_len_of=container,
|
|
181
|
+
)
|
tpyc/symbol_binding.py
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"""Per-module attribute table.
|
|
2
|
+
|
|
3
|
+
A single `dict[str, BindingCell]` per module, indexed by short local
|
|
4
|
+
name. Each cell holds a `SymbolBinding` describing what the name
|
|
5
|
+
refers to: kind (function / record / protocol / ...), the canonical
|
|
6
|
+
Info object, and the (defining_module, canonical_name) pair the
|
|
7
|
+
codegen `qualify_imported()` helper turns into a C++ qname.
|
|
8
|
+
|
|
9
|
+
Cells are mutable indirections so peer references captured during
|
|
10
|
+
pre-population stay live as the binding's payload Info object is
|
|
11
|
+
filled in by the registration paths. The binding itself is frozen --
|
|
12
|
+
identity refinement, when needed, happens by rebinding the cell's
|
|
13
|
+
`.binding` slot.
|
|
14
|
+
|
|
15
|
+
See docs/MUTUAL_IMPORTS_DESIGN.md "Universal re-export via per-module
|
|
16
|
+
attribute table" for the full design.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from enum import Enum
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SymbolKind(Enum):
|
|
26
|
+
FUNCTION = "function"
|
|
27
|
+
RECORD = "record"
|
|
28
|
+
PROTOCOL_STATIC = "protocol_static"
|
|
29
|
+
PROTOCOL_DYNAMIC = "protocol_dynamic"
|
|
30
|
+
ENUM = "enum"
|
|
31
|
+
VARIABLE = "variable"
|
|
32
|
+
TYPE_ALIAS = "type_alias"
|
|
33
|
+
MODULE = "module"
|
|
34
|
+
SUBMODULE = "submodule"
|
|
35
|
+
CLASS_MACRO = "class_macro"
|
|
36
|
+
CALL_MACRO = "call_macro"
|
|
37
|
+
BUILDER_MACRO = "builder_macro"
|
|
38
|
+
PARSER_KEYWORD = "parser_keyword"
|
|
39
|
+
OPAQUE = "opaque"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass(frozen=True)
|
|
43
|
+
class SymbolBinding:
|
|
44
|
+
"""One name in a module's attribute table.
|
|
45
|
+
|
|
46
|
+
`info` holds the canonical Info object for the binding's kind:
|
|
47
|
+
FUNCTION -> list[FunctionInfo]
|
|
48
|
+
RECORD -> RecordInfo
|
|
49
|
+
PROTOCOL_* -> ProtocolInfo
|
|
50
|
+
ENUM -> NominalType (enum-kind)
|
|
51
|
+
VARIABLE -> TpyType (var's declared type)
|
|
52
|
+
TYPE_ALIAS -> TpyType (alias body)
|
|
53
|
+
MODULE / SUBMODULE -> ModuleInfo (or None for special modules)
|
|
54
|
+
CLASS_MACRO / ... -> macro registration object
|
|
55
|
+
PARSER_KEYWORD -> None
|
|
56
|
+
OPAQUE -> None
|
|
57
|
+
|
|
58
|
+
`defining_module` is None when the symbol is locally defined in the
|
|
59
|
+
module that owns this binding. For imports/re-exports it's the
|
|
60
|
+
*ultimate* defining module (chain-flattened): records use
|
|
61
|
+
`record_info.defining_module`, enums use `EnumInfo.module_name`,
|
|
62
|
+
functions use `FunctionInfo.originating_module`, variables use
|
|
63
|
+
the second pre-pop pass's chain-walk.
|
|
64
|
+
|
|
65
|
+
`canonical_name` is the symbol's name in the defining module
|
|
66
|
+
(== local_name when no aliasing).
|
|
67
|
+
"""
|
|
68
|
+
local_name: str
|
|
69
|
+
kind: SymbolKind
|
|
70
|
+
info: object
|
|
71
|
+
defining_module: str | None
|
|
72
|
+
canonical_name: str
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class BindingCell:
|
|
77
|
+
"""Mutable indirection wrapping a `SymbolBinding`.
|
|
78
|
+
|
|
79
|
+
Cells are the stable references peers capture during
|
|
80
|
+
`bind_imports`; the cell's `.binding` slot may be rebound when
|
|
81
|
+
the same name is re-installed (e.g. a local definition shadowing
|
|
82
|
+
an earlier import in source order, or a registration path
|
|
83
|
+
refining a pre-pop'd skeleton). The binding objects themselves
|
|
84
|
+
stay frozen.
|
|
85
|
+
"""
|
|
86
|
+
binding: SymbolBinding
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def make_binding(
|
|
90
|
+
local_name: str,
|
|
91
|
+
kind: SymbolKind,
|
|
92
|
+
info: object,
|
|
93
|
+
*,
|
|
94
|
+
defining_module: str | None = None,
|
|
95
|
+
canonical_name: str | None = None,
|
|
96
|
+
) -> SymbolBinding:
|
|
97
|
+
return SymbolBinding(
|
|
98
|
+
local_name=local_name,
|
|
99
|
+
kind=kind,
|
|
100
|
+
info=info,
|
|
101
|
+
defining_module=defining_module,
|
|
102
|
+
canonical_name=canonical_name if canonical_name is not None else local_name,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def install_binding(
|
|
107
|
+
table: 'dict[str, BindingCell] | None',
|
|
108
|
+
local_name: str,
|
|
109
|
+
kind: SymbolKind,
|
|
110
|
+
info: object,
|
|
111
|
+
*,
|
|
112
|
+
defining_module: str | None = None,
|
|
113
|
+
canonical_name: str | None = None,
|
|
114
|
+
) -> None:
|
|
115
|
+
"""Install or replace an entry in the attribute table.
|
|
116
|
+
|
|
117
|
+
No-op when `table` is None (some analyzer constructions don't have
|
|
118
|
+
one -- e.g. ad-hoc tests / REPL).
|
|
119
|
+
|
|
120
|
+
Cells are mutated in place when they already exist, preserving the
|
|
121
|
+
cell identity peer modules may have captured during `bind_imports`.
|
|
122
|
+
"""
|
|
123
|
+
if table is None:
|
|
124
|
+
return
|
|
125
|
+
binding = make_binding(
|
|
126
|
+
local_name, kind, info,
|
|
127
|
+
defining_module=defining_module,
|
|
128
|
+
canonical_name=canonical_name,
|
|
129
|
+
)
|
|
130
|
+
cell = table.get(local_name)
|
|
131
|
+
if cell is None:
|
|
132
|
+
table[local_name] = BindingCell(binding=binding)
|
|
133
|
+
else:
|
|
134
|
+
cell.binding = binding
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def protocol_kind_for(is_dynamic: bool) -> SymbolKind:
|
|
138
|
+
return SymbolKind.PROTOCOL_DYNAMIC if is_dynamic else SymbolKind.PROTOCOL_STATIC
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def qualify_imported(binding: SymbolBinding, current_module: str) -> tuple[str, str]:
|
|
142
|
+
"""Return the (module, canonical_name) pair to use when emitting a
|
|
143
|
+
use-site reference to `binding` from inside `current_module`.
|
|
144
|
+
|
|
145
|
+
The result is the input to `qualified_cpp_name(...)`; callers that
|
|
146
|
+
want the rendered C++ string take that extra step so this helper
|
|
147
|
+
stays free of the codegen-context import.
|
|
148
|
+
"""
|
|
149
|
+
if binding.defining_module is None:
|
|
150
|
+
return (current_module, binding.canonical_name)
|
|
151
|
+
return (binding.defining_module, binding.canonical_name)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def lookup_qualified(
|
|
155
|
+
table: 'dict[str, BindingCell] | None', name: str, current_module: str,
|
|
156
|
+
) -> 'tuple[str, str] | None':
|
|
157
|
+
"""Convenience for the common codegen pattern: fetch the cell from
|
|
158
|
+
`table` and pass its binding through `qualify_imported`. Returns
|
|
159
|
+
None when the table is missing or the name isn't bound, leaving
|
|
160
|
+
the caller's fallback path in charge.
|
|
161
|
+
"""
|
|
162
|
+
if table is None:
|
|
163
|
+
return None
|
|
164
|
+
cell = table.get(name)
|
|
165
|
+
if cell is None:
|
|
166
|
+
return None
|
|
167
|
+
return qualify_imported(cell.binding, current_module)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def lookup_imported(
|
|
171
|
+
table: 'dict[str, BindingCell] | None', name: str, *kinds: SymbolKind,
|
|
172
|
+
) -> 'tuple[str, str] | None':
|
|
173
|
+
"""Return `(defining_module, canonical_name)` when `name` is bound as
|
|
174
|
+
an *imported* symbol of one of `kinds` -- i.e. the cell exists, kind
|
|
175
|
+
matches, and the binding has a non-None `defining_module`. Returns
|
|
176
|
+
None for local definitions, kind mismatches, missing entries, and a
|
|
177
|
+
None table.
|
|
178
|
+
"""
|
|
179
|
+
if table is None:
|
|
180
|
+
return None
|
|
181
|
+
cell = table.get(name)
|
|
182
|
+
if cell is None:
|
|
183
|
+
return None
|
|
184
|
+
bd = cell.binding
|
|
185
|
+
if bd.defining_module is None:
|
|
186
|
+
return None
|
|
187
|
+
if kinds and bd.kind not in kinds:
|
|
188
|
+
return None
|
|
189
|
+
return (bd.defining_module, bd.canonical_name)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def resolve_definer(
|
|
193
|
+
registry, module_name: str, name: str, *kinds: SymbolKind,
|
|
194
|
+
) -> tuple[str, str]:
|
|
195
|
+
"""Return the (definer_module, definer_name) for `name` as bound in
|
|
196
|
+
`module_name`'s attribute table, or `(module_name, name)` if the
|
|
197
|
+
cell is missing / locally-defined / kind-mismatched.
|
|
198
|
+
|
|
199
|
+
`registry` only needs `get_module(name) -> object | None`; the
|
|
200
|
+
returned object only needs a `module_attributes` attribute that's
|
|
201
|
+
a `dict | None`. Works equally with the sema `TypeRegistry` and the
|
|
202
|
+
`Compiler`'s `modules` dict via a thin adapter.
|
|
203
|
+
"""
|
|
204
|
+
mi = registry.get_module(module_name)
|
|
205
|
+
if mi is None:
|
|
206
|
+
return (module_name, name)
|
|
207
|
+
table = getattr(mi, "module_attributes", None)
|
|
208
|
+
imp = lookup_imported(table, name, *kinds)
|
|
209
|
+
if imp is None:
|
|
210
|
+
return (module_name, name)
|
|
211
|
+
return imp
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def walk_attribute_chain(
|
|
215
|
+
registry, module_name: str, name: str,
|
|
216
|
+
accept, # Callable[[SymbolBinding], bool]
|
|
217
|
+
) -> 'tuple[str, str, SymbolBinding] | None':
|
|
218
|
+
"""Walk the `module_attributes` chain across `registry`'s modules
|
|
219
|
+
until `accept(binding)` returns True or the chain dead-ends.
|
|
220
|
+
|
|
221
|
+
Returns `(defining_module_or_current, canonical_name, binding)` for
|
|
222
|
+
the accepted cell, or None when no hop matches. `registry` only
|
|
223
|
+
needs `get_module(name) -> ModuleInfo | None` -- works equally with
|
|
224
|
+
`TypeRegistry` (sema) and `Compiler` (with a wrapper).
|
|
225
|
+
"""
|
|
226
|
+
visited: set[tuple[str, str]] = set()
|
|
227
|
+
cur_mod, cur_name = module_name, name
|
|
228
|
+
while True:
|
|
229
|
+
key = (cur_mod, cur_name)
|
|
230
|
+
if key in visited:
|
|
231
|
+
return None
|
|
232
|
+
visited.add(key)
|
|
233
|
+
mi = registry.get_module(cur_mod)
|
|
234
|
+
if mi is None or mi.module_attributes is None:
|
|
235
|
+
return None
|
|
236
|
+
cell = mi.module_attributes.get(cur_name)
|
|
237
|
+
if cell is None:
|
|
238
|
+
return None
|
|
239
|
+
bd = cell.binding
|
|
240
|
+
if accept(bd):
|
|
241
|
+
return (bd.defining_module or cur_mod, bd.canonical_name, bd)
|
|
242
|
+
if bd.defining_module is None:
|
|
243
|
+
return None
|
|
244
|
+
cur_mod, cur_name = bd.defining_module, bd.canonical_name
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
_MACRO_KINDS = frozenset({
|
|
248
|
+
SymbolKind.CLASS_MACRO, SymbolKind.CALL_MACRO, SymbolKind.BUILDER_MACRO,
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def is_macro_kind(binding: SymbolBinding) -> bool:
|
|
253
|
+
return binding.kind in _MACRO_KINDS
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def is_kind(*kinds: SymbolKind):
|
|
257
|
+
"""Return an `accept` predicate matching any of `kinds`."""
|
|
258
|
+
s = frozenset(kinds)
|
|
259
|
+
return lambda bd: bd.kind in s
|
tpyc/test_c3_mro.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""C3 linearization and diamond detection invariants (D22 prep).
|
|
2
|
+
|
|
3
|
+
The multi-base path is still gated in sema, so these tests exercise
|
|
4
|
+
`c3_linearize`, `TypeRegistry.compute_mro_ancestors`, and
|
|
5
|
+
`TypeRegistry.detect_diamond` directly. Once D22 lifts the gate, the
|
|
6
|
+
algorithms these tests pin down start running in production.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from tpyc.typesys import (
|
|
13
|
+
NominalType, RecordInfo, TypeRegistry,
|
|
14
|
+
c3_linearize, C3LinearizationError,
|
|
15
|
+
same_nominal_symbol_loose,
|
|
16
|
+
)
|
|
17
|
+
from tpyc.type_def_registry import (
|
|
18
|
+
attach_dynamic_type_def, TypeCategory, clear_dynamic_type_defs,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture(autouse=True)
|
|
23
|
+
def _cleanup():
|
|
24
|
+
yield
|
|
25
|
+
clear_dynamic_type_defs()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _nom(name: str) -> NominalType:
|
|
29
|
+
"""NominalType with qname set so is_user_record resolves via the attached TypeDef."""
|
|
30
|
+
return NominalType(name, _module_qname=f"m.{name}")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _record(name: str, parents: list[NominalType], mro_ancestors: list[NominalType]) -> RecordInfo:
|
|
34
|
+
info = RecordInfo(
|
|
35
|
+
name=name, fields=[], module="m",
|
|
36
|
+
parents=list(parents),
|
|
37
|
+
mro_ancestors=list(mro_ancestors),
|
|
38
|
+
)
|
|
39
|
+
attach_dynamic_type_def(
|
|
40
|
+
info.qualified_name(),
|
|
41
|
+
TypeCategory.RECORD,
|
|
42
|
+
record=info,
|
|
43
|
+
is_value_type=False,
|
|
44
|
+
)
|
|
45
|
+
return info
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _type_names(types: list) -> list[str]:
|
|
49
|
+
return [t.name for t in types]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# ---------------------------------------------------------------------------
|
|
53
|
+
# c3_linearize: pure algorithm
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
class TestC3Linearize:
|
|
57
|
+
def test_empty_bases(self) -> None:
|
|
58
|
+
assert c3_linearize([]) == []
|
|
59
|
+
|
|
60
|
+
def test_single_base_no_ancestors(self) -> None:
|
|
61
|
+
"""class Child(Root): -> [Root]."""
|
|
62
|
+
root = _nom("Root")
|
|
63
|
+
assert _type_names(c3_linearize([(root, [])])) == ["Root"]
|
|
64
|
+
|
|
65
|
+
def test_single_base_with_chain(self) -> None:
|
|
66
|
+
"""A <- B <- C. class D(C): -> [C, B, A]."""
|
|
67
|
+
a, b, c = _nom("A"), _nom("B"), _nom("C")
|
|
68
|
+
# C's own ancestry: [B, A]
|
|
69
|
+
result = c3_linearize([(c, [b, a])])
|
|
70
|
+
assert _type_names(result) == ["C", "B", "A"]
|
|
71
|
+
|
|
72
|
+
def test_non_diamond_two_parents(self) -> None:
|
|
73
|
+
"""class D(B, C): with B(A), C: -> [B, A, C]."""
|
|
74
|
+
a, b, c = _nom("A"), _nom("B"), _nom("C")
|
|
75
|
+
result = c3_linearize([(b, [a]), (c, [])])
|
|
76
|
+
assert _type_names(result) == ["B", "A", "C"]
|
|
77
|
+
|
|
78
|
+
def test_canonical_diamond_c3(self) -> None:
|
|
79
|
+
"""Canonical C3: O <- A, O <- B, A <- X(A, B), result should include A then B.
|
|
80
|
+
|
|
81
|
+
This is a 'diamond-free' compose since A and B don't share an ancestor in
|
|
82
|
+
this reduced form. Here: class D(B, C) where B(A), C(A) -- A is shared.
|
|
83
|
+
C3 linearizes this as [B, C, A], but our detect_diamond rejects it.
|
|
84
|
+
The linearization algorithm itself is permissive.
|
|
85
|
+
"""
|
|
86
|
+
a, b, c = _nom("A"), _nom("B"), _nom("C")
|
|
87
|
+
# B.mro_ancestors = [A], C.mro_ancestors = [A]
|
|
88
|
+
result = c3_linearize([(b, [a]), (c, [a])])
|
|
89
|
+
assert _type_names(result) == ["B", "C", "A"]
|
|
90
|
+
|
|
91
|
+
def test_inconsistent_hierarchy_raises(self) -> None:
|
|
92
|
+
"""class X(A, B) where A's MRO says [A, P, Q] and B's says [B, Q, P] -- no valid linearization."""
|
|
93
|
+
a, b, p, q = _nom("A"), _nom("B"), _nom("P"), _nom("Q")
|
|
94
|
+
with pytest.raises(C3LinearizationError):
|
|
95
|
+
c3_linearize([(a, [p, q]), (b, [q, p])])
|
|
96
|
+
|
|
97
|
+
def test_duplicate_direct_bases_raises(self) -> None:
|
|
98
|
+
"""class D(B, B) -- same base listed twice is inconsistent."""
|
|
99
|
+
b = _nom("B")
|
|
100
|
+
with pytest.raises(C3LinearizationError):
|
|
101
|
+
c3_linearize([(b, []), (b, [])])
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ---------------------------------------------------------------------------
|
|
105
|
+
# TypeRegistry.compute_mro_ancestors + detect_diamond
|
|
106
|
+
# ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
class TestRegistryMRO:
|
|
109
|
+
def test_leaf_record_mro_empty(self) -> None:
|
|
110
|
+
reg = TypeRegistry()
|
|
111
|
+
a = _record("A", parents=[], mro_ancestors=[])
|
|
112
|
+
reg.register_record(a)
|
|
113
|
+
assert reg.compute_mro_ancestors(a) == []
|
|
114
|
+
|
|
115
|
+
def test_single_parent_linear(self) -> None:
|
|
116
|
+
reg = TypeRegistry()
|
|
117
|
+
a = _record("A", parents=[], mro_ancestors=[])
|
|
118
|
+
reg.register_record(a)
|
|
119
|
+
a_ref = _nom("A")
|
|
120
|
+
b = _record("B", parents=[a_ref], mro_ancestors=[a_ref])
|
|
121
|
+
reg.register_record(b)
|
|
122
|
+
b_ref = _nom("B")
|
|
123
|
+
c = _record("C", parents=[b_ref], mro_ancestors=[])
|
|
124
|
+
reg.register_record(c)
|
|
125
|
+
result = reg.compute_mro_ancestors(c)
|
|
126
|
+
assert _type_names(result) == ["B", "A"]
|
|
127
|
+
|
|
128
|
+
def test_multi_parent_no_diamond(self) -> None:
|
|
129
|
+
reg = TypeRegistry()
|
|
130
|
+
a = _record("A", parents=[], mro_ancestors=[])
|
|
131
|
+
reg.register_record(a)
|
|
132
|
+
b = _record("B", parents=[], mro_ancestors=[])
|
|
133
|
+
reg.register_record(b)
|
|
134
|
+
a_ref, b_ref = _nom("A"), _nom("B")
|
|
135
|
+
d = _record("D", parents=[a_ref, b_ref], mro_ancestors=[])
|
|
136
|
+
reg.register_record(d)
|
|
137
|
+
result = reg.compute_mro_ancestors(d)
|
|
138
|
+
assert _type_names(result) == ["A", "B"]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class TestDiamondDetection:
|
|
142
|
+
def test_single_parent_no_diamond(self) -> None:
|
|
143
|
+
reg = TypeRegistry()
|
|
144
|
+
a = _record("A", parents=[], mro_ancestors=[])
|
|
145
|
+
reg.register_record(a)
|
|
146
|
+
a_ref = _nom("A")
|
|
147
|
+
b = _record("B", parents=[a_ref], mro_ancestors=[a_ref])
|
|
148
|
+
reg.register_record(b)
|
|
149
|
+
assert reg.detect_diamond(b) is None
|
|
150
|
+
|
|
151
|
+
def test_unrelated_multi_parent_no_diamond(self) -> None:
|
|
152
|
+
reg = TypeRegistry()
|
|
153
|
+
a = _record("A", parents=[], mro_ancestors=[])
|
|
154
|
+
b = _record("B", parents=[], mro_ancestors=[])
|
|
155
|
+
reg.register_record(a)
|
|
156
|
+
reg.register_record(b)
|
|
157
|
+
d = _record("D", parents=[_nom("A"), _nom("B")], mro_ancestors=[])
|
|
158
|
+
reg.register_record(d)
|
|
159
|
+
assert reg.detect_diamond(d) is None
|
|
160
|
+
|
|
161
|
+
def test_classic_diamond_detected(self) -> None:
|
|
162
|
+
"""A at top; B(A), C(A); D(B, C) -- A is reachable via both B and C."""
|
|
163
|
+
reg = TypeRegistry()
|
|
164
|
+
a = _record("A", parents=[], mro_ancestors=[])
|
|
165
|
+
reg.register_record(a)
|
|
166
|
+
a_ref = _nom("A")
|
|
167
|
+
b = _record("B", parents=[a_ref], mro_ancestors=[a_ref])
|
|
168
|
+
c = _record("C", parents=[a_ref], mro_ancestors=[a_ref])
|
|
169
|
+
reg.register_record(b)
|
|
170
|
+
reg.register_record(c)
|
|
171
|
+
d = _record("D", parents=[_nom("B"), _nom("C")], mro_ancestors=[])
|
|
172
|
+
reg.register_record(d)
|
|
173
|
+
|
|
174
|
+
diamond = reg.detect_diamond(d)
|
|
175
|
+
assert diamond is not None
|
|
176
|
+
anc, p1, p2 = diamond
|
|
177
|
+
assert anc == "A"
|
|
178
|
+
assert {p1, p2} == {"B", "C"}
|
|
179
|
+
|
|
180
|
+
def test_direct_and_indirect_parent_is_diamond(self) -> None:
|
|
181
|
+
"""class C(A, B) where B(A) -- A is both a direct base and an ancestor of B."""
|
|
182
|
+
reg = TypeRegistry()
|
|
183
|
+
a = _record("A", parents=[], mro_ancestors=[])
|
|
184
|
+
reg.register_record(a)
|
|
185
|
+
a_ref = _nom("A")
|
|
186
|
+
b = _record("B", parents=[a_ref], mro_ancestors=[a_ref])
|
|
187
|
+
reg.register_record(b)
|
|
188
|
+
c = _record("C", parents=[a_ref, _nom("B")], mro_ancestors=[])
|
|
189
|
+
reg.register_record(c)
|
|
190
|
+
|
|
191
|
+
diamond = reg.detect_diamond(c)
|
|
192
|
+
assert diamond is not None
|
|
193
|
+
anc, p1, p2 = diamond
|
|
194
|
+
assert anc == "A"
|
|
195
|
+
assert {p1, p2} == {"A", "B"}
|
|
196
|
+
|
|
197
|
+
def test_duplicate_direct_parent_is_diamond(self) -> None:
|
|
198
|
+
"""class D(B, B) -- same parent listed twice."""
|
|
199
|
+
reg = TypeRegistry()
|
|
200
|
+
b = _record("B", parents=[], mro_ancestors=[])
|
|
201
|
+
reg.register_record(b)
|
|
202
|
+
b_ref = _nom("B")
|
|
203
|
+
d = _record("D", parents=[b_ref, b_ref], mro_ancestors=[])
|
|
204
|
+
reg.register_record(d)
|
|
205
|
+
|
|
206
|
+
diamond = reg.detect_diamond(d)
|
|
207
|
+
assert diamond is not None
|
|
208
|
+
assert diamond[0] == "B"
|
tpyc/test_cli_argv.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Unit tests for tpyc argv pre-split helper."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from .cli import _split_tpyc_argv
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_no_separator_returns_empty_script_args():
|
|
9
|
+
assert _split_tpyc_argv(["foo.py", "-o", "out/"]) == (["foo.py", "-o", "out/"], [])
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_empty_argv():
|
|
13
|
+
assert _split_tpyc_argv([]) == ([], [])
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_separator_at_end_yields_empty_script_args():
|
|
17
|
+
assert _split_tpyc_argv(["foo.py", "-x", "--"]) == (["foo.py", "-x"], [])
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_separator_splits_compiler_and_program_args():
|
|
21
|
+
before, after = _split_tpyc_argv(["foo.py", "-x", "--", "a", "b"])
|
|
22
|
+
assert before == ["foo.py", "-x"]
|
|
23
|
+
assert after == ["a", "b"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_tpyc_flags_preserved_before_separator():
|
|
27
|
+
before, after = _split_tpyc_argv(["foo.py", "-o", "out/", "--pcre2=none", "-x", "--", "arg"])
|
|
28
|
+
assert before == ["foo.py", "-o", "out/", "--pcre2=none", "-x"]
|
|
29
|
+
assert after == ["arg"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_multiple_dash_dash_only_first_is_separator():
|
|
33
|
+
before, after = _split_tpyc_argv(["foo.py", "-x", "--", "a", "--", "b"])
|
|
34
|
+
assert before == ["foo.py", "-x"]
|
|
35
|
+
assert after == ["a", "--", "b"]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_leading_separator_with_trailing_tokens_rejected():
|
|
39
|
+
with pytest.raises(ValueError, match="input file must appear before"):
|
|
40
|
+
_split_tpyc_argv(["--", "foo.py"])
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_only_separator_passes_through():
|
|
44
|
+
# `tpyc --` alone: no trailing tokens, so nothing to reject. argparse will
|
|
45
|
+
# then fail with "required: input" which is the right error for this shape.
|
|
46
|
+
assert _split_tpyc_argv(["--"]) == ([], [])
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_flag_like_program_args_preserved():
|
|
50
|
+
before, after = _split_tpyc_argv(["foo.py", "-x", "--", "-O", "--help", "-"])
|
|
51
|
+
assert before == ["foo.py", "-x"]
|
|
52
|
+
assert after == ["-O", "--help", "-"]
|