fpy2 0.2.4__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. fpy2-0.3.0/LICENSE +202 -0
  2. fpy2-0.3.0/NOTICE +2 -0
  3. {fpy2-0.2.4 → fpy2-0.3.0}/PKG-INFO +3 -2
  4. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/alias.py +157 -50
  5. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/digit_bound/__init__.py +1 -1
  6. fpy2-0.3.0/fpy2/analysis/digit_bound/infer.py +2713 -0
  7. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/digit_bound/solver.py +19 -11
  8. fpy2-0.3.0/fpy2/analysis/digit_bound/spaces.py +120 -0
  9. fpy2-0.3.0/fpy2/analysis/digit_bound/store.py +376 -0
  10. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/escape.py +27 -28
  11. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/format_infer/analysis.py +398 -165
  12. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/value_class.py +6 -11
  13. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/cpp/compiler.py +0 -2
  14. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/cpp/emitter.py +33 -15
  15. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/triton/emitter.py +39 -2
  16. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/triton/launcher.py +19 -1
  17. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/triton/target.py +55 -14
  18. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/triton/unfold_round.py +20 -5
  19. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/unfold_round.py +64 -18
  20. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/ops.py +43 -4
  21. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/specialize.py +48 -39
  22. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2.egg-info/PKG-INFO +3 -2
  23. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2.egg-info/SOURCES.txt +2 -0
  24. {fpy2-0.2.4 → fpy2-0.3.0}/pyproject.toml +2 -2
  25. fpy2-0.2.4/LICENSE +0 -21
  26. fpy2-0.2.4/fpy2/analysis/digit_bound/infer.py +0 -1778
  27. fpy2-0.2.4/fpy2/analysis/digit_bound/store.py +0 -265
  28. {fpy2-0.2.4 → fpy2-0.3.0}/README.md +0 -0
  29. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/__init__.py +0 -0
  30. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/__init__.py +0 -0
  31. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/array_size.py +0 -0
  32. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/call_graph.py +0 -0
  33. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/context_use.py +0 -0
  34. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/define_use.py +0 -0
  35. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/defs.py +0 -0
  36. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/format_infer/__init__.py +0 -0
  37. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/format_infer/double_round.py +0 -0
  38. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/format_infer/format.py +0 -0
  39. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/hoistability.py +0 -0
  40. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/live_vars.py +0 -0
  41. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/partial_eval.py +0 -0
  42. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/purity.py +0 -0
  43. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/reachability.py +0 -0
  44. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/reaching_defs.py +0 -0
  45. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/storage_infer.py +0 -0
  46. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/syntax_check.py +0 -0
  47. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/analysis/type_infer.py +0 -0
  48. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/ast/__init__.py +0 -0
  49. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/ast/accessors.py +0 -0
  50. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/ast/formatter.py +0 -0
  51. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/ast/fpyast.py +0 -0
  52. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/ast/visitor.py +0 -0
  53. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/__init__.py +0 -0
  54. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/backend.py +0 -0
  55. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/cpp/__init__.py +0 -0
  56. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/cpp/ops.py +0 -0
  57. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/cpp/storage.py +0 -0
  58. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/cpp/target.py +0 -0
  59. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/cpp/types.py +0 -0
  60. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/cpp/unbox.py +0 -0
  61. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/cpp/unfold_round.py +0 -0
  62. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/cpp/utils.py +0 -0
  63. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/cpp/variables.py +0 -0
  64. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/fpc.py +0 -0
  65. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/triton/__init__.py +0 -0
  66. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/triton/compiler.py +0 -0
  67. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/triton/normalize.py +0 -0
  68. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/triton/split_names.py +0 -0
  69. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/triton/storage.py +0 -0
  70. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/triton/types.py +0 -0
  71. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/backend/triton/vectorize.py +0 -0
  72. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/decorator.py +0 -0
  73. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/env.py +0 -0
  74. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/fpc_context.py +0 -0
  75. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/frontend/__init__.py +0 -0
  76. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/frontend/fpc.py +0 -0
  77. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/frontend/parser.py +0 -0
  78. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/function.py +0 -0
  79. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/interpret/__init__.py +0 -0
  80. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/interpret/byte.py +0 -0
  81. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/interpret/interpreter.py +0 -0
  82. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/interpret/value.py +0 -0
  83. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/libraries/__init__.py +0 -0
  84. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/libraries/base.py +0 -0
  85. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/libraries/core.py +0 -0
  86. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/libraries/eft.py +0 -0
  87. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/libraries/matrix.py +0 -0
  88. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/libraries/metrics.py +0 -0
  89. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/libraries/vector.py +0 -0
  90. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/module.py +0 -0
  91. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/__init__.py +0 -0
  92. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/__init__.py +0 -0
  93. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/context.py +0 -0
  94. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/efloat.py +0 -0
  95. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/exponential.py +0 -0
  96. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/fixed.py +0 -0
  97. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/format.py +0 -0
  98. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/ieee754.py +0 -0
  99. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/mp_fixed.py +0 -0
  100. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/mp_float.py +0 -0
  101. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/mpb_fixed.py +0 -0
  102. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/mpb_float.py +0 -0
  103. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/mps_float.py +0 -0
  104. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/real.py +0 -0
  105. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/context/sm_fixed.py +0 -0
  106. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/engine/__init__.py +0 -0
  107. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/engine/engine.py +0 -0
  108. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/engine/gmp.py +0 -0
  109. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/engine/real.py +0 -0
  110. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/format.py +0 -0
  111. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/globals.py +0 -0
  112. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/gmputils.py +0 -0
  113. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/native.py +0 -0
  114. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/number/__init__.py +0 -0
  115. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/number/flags.py +0 -0
  116. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/number/floats.py +0 -0
  117. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/number/reals.py +0 -0
  118. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/number/round.py +0 -0
  119. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/primitive.py +0 -0
  120. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/rewrite/__init__.py +0 -0
  121. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/rewrite/applier.py +0 -0
  122. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/rewrite/matcher.py +0 -0
  123. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/rewrite/pattern.py +0 -0
  124. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/rewrite/rewrite.py +0 -0
  125. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/rewrite/search.py +0 -0
  126. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/rewrite/subst.py +0 -0
  127. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/runner.py +0 -0
  128. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/__init__.py +0 -0
  129. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/anf.py +0 -0
  130. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/assert_elim.py +0 -0
  131. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/comp_lower.py +0 -0
  132. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/context_lift.py +0 -0
  133. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/exit_single.py +0 -0
  134. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/fixed_rescale.py +0 -0
  135. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/float_lower.py +0 -0
  136. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/free_var.py +0 -0
  137. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/func_inline.py +0 -0
  138. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/hoistable.py +0 -0
  139. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/if_simplify.py +0 -0
  140. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/invariant_hoist.py +0 -0
  141. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/iter_elim.py +0 -0
  142. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/iter_unfold.py +0 -0
  143. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/loop_split.py +0 -0
  144. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/loop_unroll.py +0 -0
  145. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/mono.py +0 -0
  146. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/neg_zero_unfold.py +0 -0
  147. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/overflow_unfold.py +0 -0
  148. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/reduce_fusion.py +0 -0
  149. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/round_elim.py +0 -0
  150. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/round_insert.py +0 -0
  151. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/round_split.py +0 -0
  152. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/scale_hoist.py +0 -0
  153. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/simple.py +0 -0
  154. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/sites.py +0 -0
  155. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/strategies/special_unfold.py +0 -0
  156. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/__init__.py +0 -0
  157. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/anf.py +0 -0
  158. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/assert_elim.py +0 -0
  159. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/comp_to_loop.py +0 -0
  160. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/const_fold.py +0 -0
  161. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/copy_propagate.py +0 -0
  162. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/cursor.py +0 -0
  163. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/dead_code.py +0 -0
  164. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/enumerate_elim.py +0 -0
  165. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/error.py +0 -0
  166. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/float_to_fixed.py +0 -0
  167. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/for_bundling.py +0 -0
  168. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/for_unpack.py +0 -0
  169. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/for_unroll.py +0 -0
  170. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/free_var_elim.py +0 -0
  171. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/func_inline.py +0 -0
  172. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/hoist_invariant.py +0 -0
  173. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/hoist_scale.py +0 -0
  174. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/hoistable.py +0 -0
  175. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/if_bundling.py +0 -0
  176. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/iter_elim.py +0 -0
  177. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/lift_context.py +0 -0
  178. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/monomorphize.py +0 -0
  179. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/path.py +0 -0
  180. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/reduce_fusion.py +0 -0
  181. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/rename_target.py +0 -0
  182. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/rescale_fixed.py +0 -0
  183. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/round_elim.py +0 -0
  184. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/round_insert.py +0 -0
  185. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/simplify.py +0 -0
  186. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/simplify_if.py +0 -0
  187. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/single_exit.py +0 -0
  188. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/split_loop.py +0 -0
  189. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/split_round.py +0 -0
  190. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/statement_form.py +0 -0
  191. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/subst_var.py +0 -0
  192. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/unfold_iter.py +0 -0
  193. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/unfold_neg_zero.py +0 -0
  194. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/unfold_overflow.py +0 -0
  195. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/unfold_special.py +0 -0
  196. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/unnest_context.py +0 -0
  197. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/utils.py +0 -0
  198. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/while_bundling.py +0 -0
  199. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/while_unroll.py +0 -0
  200. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/transform/zip_elim.py +0 -0
  201. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/types.py +0 -0
  202. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/__init__.py +0 -0
  203. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/bits.py +0 -0
  204. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/compare.py +0 -0
  205. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/decorator.py +0 -0
  206. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/default.py +0 -0
  207. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/float_params.py +0 -0
  208. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/fractions.py +0 -0
  209. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/gensym.py +0 -0
  210. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/identifier.py +0 -0
  211. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/inspect.py +0 -0
  212. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/iterator.py +0 -0
  213. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/loader.py +0 -0
  214. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/location.py +0 -0
  215. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/ordering.py +0 -0
  216. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/string.py +0 -0
  217. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/uninit.py +0 -0
  218. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2/utils/unionfind.py +0 -0
  219. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2.egg-info/dependency_links.txt +0 -0
  220. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2.egg-info/requires.txt +0 -0
  221. {fpy2-0.2.4 → fpy2-0.3.0}/fpy2.egg-info/top_level.txt +0 -0
  222. {fpy2-0.2.4 → fpy2-0.3.0}/setup.cfg +0 -0
fpy2-0.3.0/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
fpy2-0.3.0/NOTICE ADDED
@@ -0,0 +1,2 @@
1
+ FPy
2
+ Copyright 2025-2026 Brett Saiki
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fpy2
3
- Version: 0.2.4
3
+ Version: 0.3.0
4
4
  Summary: An embedded DSL for numerical computing
5
5
  Author-email: Brett Saiki <bsaiki@cs.washington.edu>
6
- License-Expression: MIT
6
+ License-Expression: Apache-2.0
7
7
  Project-URL: Homepage, https://github.com/bksaiki/fpy
8
8
  Project-URL: Issues, https://github.com/bksaiki/fpy/issues
9
9
  Project-URL: Documentation, https://fpy.readthedocs.io/
@@ -12,6 +12,7 @@ Classifier: Operating System :: OS Independent
12
12
  Requires-Python: >=3.11
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
+ License-File: NOTICE
15
16
  Requires-Dist: titanfp==0.1.2
16
17
  Requires-Dist: gmpy2==2.3.1
17
18
  Requires-Dist: numpy
@@ -47,6 +47,9 @@ elements part, ``fld(c, i)`` field *i*):
47
47
  ``for row in xss`` ``reg(row) ≡ elts(xss)``
48
48
  ``xs if c else ys`` ``reg(result) ≡ reg(xs) ≡ reg(ys)``
49
49
  ``enumerate``/``zip`` ``elts(result) ≡ elts(arg)``
50
+ ``f(xs, ys)`` if ``f`` may keep ``xs``: each list in ``xs`` ≡
51
+ each same-shaped one in ``result``, or in ``ys``
52
+ if ``f`` may write it
50
53
  ========================= ==========================================
51
54
 
52
55
  Equality rather than inclusion is deliberate. Each place denotes exactly one
@@ -64,13 +67,12 @@ The ownership queries stay sound because shared-outward poisons them;
64
67
  :meth:`AliasAnalysis.may_alias` is sound only for the routes above.
65
68
 
66
69
  Two routes leave the function, and they mean opposite things for ownership.
67
- *Shared outward* — handed to a call, or to an unmodelled operation — means
68
- something else may hold the list while this function still does: **E-App** keeps
69
- no store of its own, so a callee writes the caller's cells. *Returned* is
70
- **E-Ret** handing a location out, which is a transfer; see
71
- :meth:`AliasAnalysis.transfers_ownership`. Collapsing the two would forgo every
72
- returned value: 37 of the corpus's 299 allocation sites are fresh values that are
73
- returned and nothing else.
70
+ *Shared outward* — kept by a call, or handed to an unmodelled operation — means
71
+ something else may hold the list while this function still does. A call that
72
+ only writes it is flagged apart (:meth:`AliasAnalysis.callee_writes_at`):
73
+ **E-App** keeps no store of its own, so a callee writes the caller's cells.
74
+ *Returned* is **E-Ret** handing a location out, which is a transfer; see
75
+ :meth:`AliasAnalysis.transfers_ownership`.
74
76
 
75
77
  Limitations
76
78
  -----------
@@ -79,12 +81,13 @@ One *summary* store for the whole function, not one per program point, so it is
79
81
  flow-insensitive: ``ys = xs`` marks ``xs`` shared even if ``ys`` is dead
80
82
  immediately afterwards, and a container part counts as a referrer even if the
81
83
  container itself is never read — except :attr:`AliasAnalysis.consumed_defs`,
82
- which buys back one flow-sensitive fact under narrow guards. Intraprocedural: a
83
- list handed to a call is shared outward, without asking whether the callee
84
- retains it. Index-insensitive: ``xs[0]`` and ``xs[1]`` are one part. All three
84
+ which buys back one flow-sensitive fact under narrow guards. Context-insensitive:
85
+ one summary of a callee (:mod:`fpy2.analysis.escape`) answers for every call to
86
+ it. Index-insensitive: ``xs[0]`` and ``xs[1]`` are one part. All three
85
87
  cost precision, not soundness.
86
88
  """
87
89
 
90
+ from collections.abc import Iterator
88
91
  from dataclasses import dataclass, field
89
92
  from typing import TYPE_CHECKING, Literal, overload
90
93
 
@@ -121,7 +124,7 @@ from ..ast import (
121
124
  Zip,
122
125
  )
123
126
  from ..function import Function
124
- from ..types import ListType, TupleType, Type
127
+ from ..types import ListType, TupleType, Type, VarType
125
128
  from ..utils import Unionfind
126
129
  from .array_size import (
127
130
  ArraySizeAnalysis,
@@ -132,7 +135,7 @@ from .array_size import (
132
135
  )
133
136
  from .define_use import DefineUse, DefineUseAnalysis
134
137
  from .reaching_defs import AssignDef, Definition, same_object_defs
135
- from .type_infer import TypeAnalysis, TypeInfer
138
+ from .type_infer import TypeAnalysis, TypeInfer, TypeInferError
136
139
 
137
140
  if TYPE_CHECKING:
138
141
  # `escape` builds on this module; the dependency only goes the other way
@@ -156,10 +159,10 @@ def _carries_list(ty: Type | None) -> bool:
156
159
  """Whether a value of type *ty* can hold a list reference anywhere inside it.
157
160
 
158
161
  Values that cannot are not given regions at all — nothing about them is
159
- observable through aliasing.
162
+ observable through aliasing. A type variable may be a list.
160
163
  """
161
164
  match ty:
162
- case ListType():
165
+ case ListType() | VarType():
163
166
  return True
164
167
  case TupleType():
165
168
  return any(_carries_list(elt) for elt in ty.elts)
@@ -167,6 +170,17 @@ def _carries_list(ty: Type | None) -> bool:
167
170
  return False
168
171
 
169
172
 
173
+ def _shape(ty: Type | None) -> object:
174
+ """*ty*'s list and tuple nesting, without sizes or scalar types."""
175
+ match ty:
176
+ case ListType():
177
+ return ('list', _shape(ty.elt))
178
+ case TupleType():
179
+ return tuple(_shape(elt) for elt in ty.elts)
180
+ case _:
181
+ return None
182
+
183
+
170
184
  class AllocSite:
171
185
  """A place a list value comes into existence.
172
186
 
@@ -233,12 +247,14 @@ class _Regions:
233
247
  # things for ownership -- see `AliasAnalysis.transfers_ownership`.
234
248
  self._shared_out: set[Region] = set()
235
249
  self._returned: set[Region] = set()
250
+ # handed to a callee that may store into it, which need not keep it
251
+ self._callee_written: set[Region] = set()
236
252
  self._parts: dict[Region, dict[_PartKey, Region]] = {}
237
253
 
238
254
  @property
239
255
  def _flags(self) -> tuple[set[Region], ...]:
240
256
  """Every downward-propagating class flag, so each is handled alike."""
241
- return (self._shared_out, self._returned)
257
+ return (self._shared_out, self._returned, self._callee_written)
242
258
 
243
259
  def new(
244
260
  self, kind: str, *, name: NamedId | None = None, slot: bool = False,
@@ -363,14 +379,20 @@ class _Regions:
363
379
  inherit the flag in :meth:`part`, so the two directions together are
364
380
  order-independent.
365
381
  """
382
+ flag.update(self.within(c))
383
+
384
+ def parts(self, c: Region) -> dict[_PartKey, Region]:
385
+ return {k: self.find(r) for k, r in self._parts.get(self.find(c), {}).items()}
386
+
387
+ def within(self, c: Region) -> set[Region]:
388
+ """*c* and every class reachable inside it, through elements and fields."""
366
389
  pending, seen = [self.find(c)], set()
367
390
  while pending:
368
391
  root = self.find(pending.pop())
369
- if root in seen:
370
- continue
371
- seen.add(root)
372
- flag.add(root)
373
- pending.extend(self._parts.get(root, {}).values())
392
+ if root not in seen:
393
+ seen.add(root)
394
+ pending.extend(self._parts.get(root, {}).values())
395
+ return seen
374
396
 
375
397
  def mark_shared_out(self, c: Region) -> None:
376
398
  """*c* may be held by something outside while this function holds it."""
@@ -380,9 +402,16 @@ class _Regions:
380
402
  """*c* is handed to the caller by a ``return``."""
381
403
  self._spread(self._returned, c)
382
404
 
405
+ def mark_callee_written(self, c: Region) -> None:
406
+ """*c* is handed to a callee that may store into it."""
407
+ self._spread(self._callee_written, c)
408
+
383
409
  def is_shared_out(self, c: Region) -> bool:
384
410
  return self.find(c) in self._shared_out
385
411
 
412
+ def is_callee_written(self, c: Region) -> bool:
413
+ return self.find(c) in self._callee_written
414
+
386
415
  def is_returned(self, c: Region) -> bool:
387
416
  return self.find(c) in self._returned
388
417
 
@@ -453,6 +482,14 @@ class AliasAnalysis:
453
482
  """The region *depth* list levels inside *region*."""
454
483
  return self._walk(region, depth)
455
484
 
485
+ def parts(self, region: Region) -> dict[_PartKey, Region]:
486
+ """*region*'s parts by key: its elements (:data:`ELTS`) or field *i*."""
487
+ return self._regions.parts(region)
488
+
489
+ def within(self, region: Region) -> frozenset[Region]:
490
+ """*region* and every region it holds, through elements and fields."""
491
+ return frozenset(self._regions.within(region))
492
+
456
493
  def region_field(self, region: Region | None, i: int) -> Region | None:
457
494
  """The region held by field *i* of *region*'s tuple."""
458
495
  if region is None:
@@ -509,9 +546,13 @@ class AliasAnalysis:
509
546
 
510
547
  def escapes_at(self, region: Region) -> bool:
511
548
  """Whether *region* is held by something outside while this function
512
- still holds it — handed to a call, or to an unmodelled operation."""
549
+ still holds it — kept by a call, or handed to an unmodelled operation."""
513
550
  return self._regions.is_shared_out(region)
514
551
 
552
+ def callee_writes_at(self, region: Region) -> bool:
553
+ """Whether a callee *region* is handed to may store into it."""
554
+ return self._regions.is_callee_written(region)
555
+
515
556
  def returned_at(self, region: Region) -> bool:
516
557
  """Whether *region* is handed to the caller by a ``return``."""
517
558
  return self._regions.is_returned(region)
@@ -537,7 +578,7 @@ class AliasAnalysis:
537
578
 
538
579
  def escapes(self, site: AllocSite) -> bool:
539
580
  """Whether *site*, or a container holding it, leaves the function at all
540
- — handed to a call, or returned.
581
+ — kept by a call, or returned.
541
582
 
542
583
  Says nothing about *which*; :meth:`transfers_ownership` separates them.
543
584
  """
@@ -591,11 +632,31 @@ class AliasAnalysis:
591
632
  return False
592
633
  return self._regions.find(ca) is self._regions.find(cb)
593
634
 
635
+ def may_write(self, region: Region) -> bool:
636
+ """Whether this function may store into *region*, or into a list it
637
+ holds, itself or through a call it hands one to."""
638
+ return any(
639
+ r in self.written_regions or r in self.stored_regions
640
+ or self.escapes_at(r) or self.callee_writes_at(r)
641
+ for r in self.within(region)
642
+ )
643
+
644
+ def param_regions(self, func: FuncDef, def_use: DefineUseAnalysis) -> dict[int, Region]:
645
+ """The region of each of *func*'s list parameters, by position."""
646
+ out: dict[int, Region] = {}
647
+ for i, arg in enumerate(func.args):
648
+ if isinstance(arg.name, NamedId):
649
+ r = self.region_of(def_use.find_def_from_site(arg.name, arg))
650
+ if r is not None:
651
+ out[i] = r
652
+ return out
653
+
594
654
  def may_change(self, d: Definition) -> bool:
595
655
  """Whether the list *d* names may have an element replaced, through
596
656
  any name here or by a call it is handed to."""
597
657
  r = self.region_of(d)
598
- return r is None or r in self.stored_regions or self.escapes_at(r)
658
+ return (r is None or r in self.stored_regions
659
+ or self.escapes_at(r) or self.callee_writes_at(r))
599
660
 
600
661
 
601
662
  class _EscapeVars(DefaultVisitor):
@@ -628,11 +689,13 @@ class _Builder(DefaultVisitor):
628
689
 
629
690
  def __init__(self, func: FuncDef, def_use: DefineUseAnalysis,
630
691
  type_info: TypeAnalysis,
631
- summaries: 'dict[FuncDef, EscapeSummary]'):
692
+ summaries: 'dict[FuncDef, EscapeSummary]',
693
+ linked: frozenset[tuple[int, int]]):
632
694
  self.func = func
633
695
  self.def_use = def_use
634
696
  self.types = type_info
635
697
  self.summaries = summaries
698
+ self.linked = linked
636
699
  self.regions = _Regions()
637
700
  self.sites: list[AllocSite] = []
638
701
  self.by_def: dict[Definition, Region] = {}
@@ -721,13 +784,28 @@ class _Builder(DefaultVisitor):
721
784
  self.regions.merge(self._reg(d), self._reg(self.def_use.defs[i]))
722
785
 
723
786
  def _seed_params(self) -> None:
724
- for arg in self.func.args:
787
+ params: dict[int, tuple[Region, Type | None]] = {}
788
+ for i, arg in enumerate(self.func.args):
725
789
  if not isinstance(arg.name, NamedId):
726
790
  continue
727
791
  d = self.def_use.find_def_from_site(arg.name, arg)
728
792
  ty = self.types.by_def.get(d)
729
793
  if _carries_list(ty):
730
794
  self._seed(self._reg(d), ty, arg, 0)
795
+ params[i] = (self._reg(d), ty)
796
+ for i, j in self.linked:
797
+ if i in params and j in params:
798
+ self._merge_by_shape(*params[i], *params[j])
799
+
800
+ def _merge_by_shape(
801
+ self, a: Region, a_ty: Type | None, b: Region, b_ty: Type | None,
802
+ ) -> None:
803
+ """Merge each list in *a* with each list in *b* of the same shape."""
804
+ theirs = list(self._lists_in(b, b_ty))
805
+ for r, ty in list(self._lists_in(a, a_ty)):
806
+ for q, qty in theirs:
807
+ if _shape(ty) == _shape(qty):
808
+ self.regions.merge(r, q)
731
809
 
732
810
  def _seed(self, region: Region, ty: Type | None, arg: Argument,
733
811
  depth: int) -> None:
@@ -738,6 +816,8 @@ class _Builder(DefaultVisitor):
738
816
  self._site('param', arg, region, depth)
739
817
  if _carries_list(ty.elt):
740
818
  self._seed(self._part(region), ty.elt, arg, depth + 1)
819
+ case VarType():
820
+ self._site('param', arg, region, depth)
741
821
  case TupleType():
742
822
  for i, elt in enumerate(ty.elts):
743
823
  if _carries_list(elt):
@@ -823,9 +903,8 @@ class _Builder(DefaultVisitor):
823
903
  )
824
904
  return region
825
905
  case Call():
826
- region = self._alloc('call', e, deep=True)
827
- self._escape_args(e)
828
- return region
906
+ # linked to what the callee keeps by `_visit_call`
907
+ return self._alloc('call', e, deep=True)
829
908
  case Range1() | Range2() | Range3() | Empty():
830
909
  # A fresh sequence over *integer* arguments: there is nothing
831
910
  # list-shaped for it to retain, so unlike the catch-all below it
@@ -874,23 +953,32 @@ class _Builder(DefaultVisitor):
874
953
  for i, elt in enumerate(target.elts):
875
954
  self._bind(elt, self._part(region, i), site)
876
955
 
877
- def _escape_args(self, e: Call) -> None:
878
- """A list handed to a call may be kept by the callee.
956
+ def _summary(self, e: Call) -> 'EscapeSummary | None':
957
+ """The callee's summary, computed when not given; ``None`` for a foreign
958
+ function or one that does not type on its own, which means the
959
+ conservative answer."""
960
+ if not isinstance(e.fn, Function):
961
+ return None
962
+ fd = e.fn.ast
963
+ if fd not in self.summaries:
964
+ from .escape import Escape
965
+ try:
966
+ self.summaries[fd] = Escape.analyze(fd, self.summaries)
967
+ except TypeInferError:
968
+ return None
969
+ return self.summaries[fd]
879
970
 
880
- Unless its summary says otherwise: a callee that only reads or writes
881
- its argument holds nothing once it returns, so the caller's list is no
882
- more shared than before the call. No summary — a foreign function, or
883
- one not yet analyzed — means the conservative answer.
884
- """
885
- summary = None
886
- if isinstance(e.fn, Function):
887
- summary = self.summaries.get(e.fn.ast)
888
- for i, a in enumerate(e.args):
889
- ac = self._region_for(a)
890
- if ac is None:
891
- continue
892
- if summary is None or summary.retains(i):
893
- self.regions.mark_shared_out(ac)
971
+ def _lists_in(self, region: Region, ty: Type | None) -> Iterator[tuple[Region, Type]]:
972
+ """Every list *region* holds, itself included, with its type."""
973
+ match ty:
974
+ case ListType():
975
+ yield region, ty
976
+ if _carries_list(ty.elt):
977
+ yield from self._lists_in(self._part(region), ty.elt)
978
+ case TupleType():
979
+ for i, elt in enumerate(ty.elts):
980
+ if _carries_list(elt):
981
+ yield from self._lists_in(self._part(region, i), elt)
894
982
 
895
983
  # -- constraint-generating hooks ---------------------------------------
896
984
  #
@@ -904,9 +992,23 @@ class _Builder(DefaultVisitor):
904
992
  return super()._visit_expr(e, ctx)
905
993
 
906
994
  def _visit_call(self, e: Call, ctx):
907
- """A list handed to a call escapes: whether the callee retains it is not
908
- decidable here."""
909
- self._escape_args(e)
995
+ """An argument the callee may write is marked so; one it may keep is
996
+ shared outward and joins each same-shaped list in the result or in a
997
+ written argument."""
998
+ summary = self._summary(e)
999
+ written = [a for i, a in enumerate(e.args) if summary is None or summary.writes(i)]
1000
+ retained = [a for i, a in enumerate(e.args) if summary is None or summary.retains(i)]
1001
+ reach = [(x, xc) for x in (e, *written) if (xc := self._region_for(x)) is not None]
1002
+ for x, xc in reach:
1003
+ if x is not e:
1004
+ self.regions.mark_callee_written(xc)
1005
+ for a in retained:
1006
+ if (ac := self._region_for(a)) is None:
1007
+ continue
1008
+ self.regions.mark_shared_out(ac)
1009
+ for x, xc in reach:
1010
+ self._merge_by_shape(
1011
+ ac, self.types.by_expr.get(a), xc, self.types.by_expr.get(x))
910
1012
  super()._visit_call(e, ctx)
911
1013
 
912
1014
  def _visit_assign(self, stmt: Assign, ctx):
@@ -1170,6 +1272,7 @@ class Alias:
1170
1272
  def_use: DefineUseAnalysis | None = None,
1171
1273
  type_info: TypeAnalysis | None = None,
1172
1274
  summaries: 'dict[FuncDef, EscapeSummary] | None' = None,
1275
+ linked: frozenset[tuple[int, int]] = frozenset(),
1173
1276
  ) -> AliasAnalysis:
1174
1277
  """Compute what may refer to each list in *func*.
1175
1278
 
@@ -1179,8 +1282,11 @@ class Alias:
1179
1282
  type_info: reuse an existing type analysis (needed to tell which
1180
1283
  expressions carry lists).
1181
1284
  summaries: escape summaries for the callees, so an argument a callee
1182
- does not retain is not treated as shared. A callee absent from
1183
- it is assumed to retain everything.
1285
+ does not retain is not treated as shared. A memo: one absent
1286
+ from it is computed and added. A foreign callee, or one that
1287
+ does not type on its own, is assumed to retain everything.
1288
+ linked: pairs of parameters a caller may pass one list to, at any
1289
+ depth; their same-shaped lists share regions.
1184
1290
  """
1185
1291
  if not isinstance(func, FuncDef):
1186
1292
  raise TypeError(f"expected a 'FuncDef', got {func}")
@@ -1188,4 +1294,5 @@ class Alias:
1188
1294
  def_use = DefineUse.analyze(func)
1189
1295
  if type_info is None:
1190
1296
  type_info = TypeInfer.check(func, def_use=def_use)
1191
- return _Builder(func, def_use, type_info, summaries or {}).run()
1297
+ summaries = {} if summaries is None else summaries
1298
+ return _Builder(func, def_use, type_info, summaries, linked).run()
@@ -2,7 +2,7 @@
2
2
 
3
3
  Answers one question -- how many binades separate a value from the position it
4
4
  is rounded at -- which is the precision of the resulting fixed-point value.
5
- See ``docs/todos/digit-bound-inference.md``.
5
+ See ``docs/todos/digit-bound.md``.
6
6
 
7
7
  :mod:`solver` is the constraint language and its backend, :mod:`store` the
8
8
  domain and its queries, and :mod:`infer` the pass over a program.