typeprof 0.8.0 → 0.11.0

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 (298) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +6 -5
  3. data/doc/demo.md +1 -1
  4. data/doc/todo.md +133 -0
  5. data/lib/typeprof/analyzer.rb +177 -76
  6. data/lib/typeprof/arguments.rb +2 -0
  7. data/lib/typeprof/block.rb +40 -2
  8. data/lib/typeprof/builtin.rb +279 -111
  9. data/lib/typeprof/cli.rb +5 -0
  10. data/lib/typeprof/config.rb +15 -1
  11. data/lib/typeprof/container-type.rb +5 -4
  12. data/lib/typeprof/export.rb +115 -70
  13. data/lib/typeprof/import.rb +71 -33
  14. data/lib/typeprof/iseq.rb +46 -11
  15. data/lib/typeprof/method.rb +42 -21
  16. data/lib/typeprof/type.rb +93 -15
  17. data/lib/typeprof/version.rb +1 -1
  18. data/smoke/alias.rb +4 -4
  19. data/smoke/alias2.rb +6 -4
  20. data/smoke/any1.rb +1 -1
  21. data/smoke/any2.rb +2 -2
  22. data/smoke/arguments.rb +2 -2
  23. data/smoke/arguments2.rb +10 -10
  24. data/smoke/array-each.rb +1 -1
  25. data/smoke/array-each2.rb +1 -1
  26. data/smoke/array-each3.rb +1 -1
  27. data/smoke/array-ltlt.rb +1 -1
  28. data/smoke/array-ltlt2.rb +1 -1
  29. data/smoke/array-map.rb +1 -1
  30. data/smoke/array-map2.rb +1 -1
  31. data/smoke/array-map3.rb +3 -3
  32. data/smoke/array-mul.rb +2 -2
  33. data/smoke/array-plus1.rb +1 -1
  34. data/smoke/array-plus2.rb +3 -2
  35. data/smoke/array-pop.rb +1 -1
  36. data/smoke/array-range-aref.rb +11 -11
  37. data/smoke/array-replace.rb +1 -1
  38. data/smoke/array-s-aref.rb +1 -1
  39. data/smoke/array1.rb +5 -5
  40. data/smoke/array10.rb +1 -1
  41. data/smoke/array11.rb +1 -1
  42. data/smoke/array12.rb +3 -3
  43. data/smoke/array13.rb +4 -4
  44. data/smoke/array14.rb +1 -1
  45. data/smoke/array15.rb +16 -0
  46. data/smoke/array2.rb +3 -3
  47. data/smoke/array3.rb +5 -4
  48. data/smoke/array4.rb +1 -1
  49. data/smoke/array5.rb +1 -1
  50. data/smoke/array6.rb +3 -2
  51. data/smoke/array7.rb +1 -1
  52. data/smoke/array8.rb +1 -1
  53. data/smoke/array9.rb +1 -1
  54. data/smoke/attr-module.rb +27 -0
  55. data/smoke/attr-vis.rb +43 -0
  56. data/smoke/attr-vis.rbs +4 -0
  57. data/smoke/attr.rb +5 -5
  58. data/smoke/autoload.rb +1 -1
  59. data/smoke/backtrace.rb +3 -3
  60. data/smoke/block-ambiguous.rb +8 -8
  61. data/smoke/block-args1-rest.rb +12 -11
  62. data/smoke/block-args1.rb +10 -10
  63. data/smoke/block-args2-rest.rb +12 -11
  64. data/smoke/block-args2.rb +10 -10
  65. data/smoke/block-args3-rest.rb +14 -13
  66. data/smoke/block-args3.rb +12 -12
  67. data/smoke/block-blockarg.rb +4 -4
  68. data/smoke/block-kwarg.rb +10 -10
  69. data/smoke/block1.rb +1 -1
  70. data/smoke/block10.rb +1 -1
  71. data/smoke/block11.rb +5 -5
  72. data/smoke/block12.rb +2 -2
  73. data/smoke/block14.rb +2 -2
  74. data/smoke/block2.rb +1 -1
  75. data/smoke/block3.rb +3 -3
  76. data/smoke/block4.rb +2 -2
  77. data/smoke/block5.rb +3 -2
  78. data/smoke/block6.rb +2 -2
  79. data/smoke/block7.rb +1 -1
  80. data/smoke/block8.rb +3 -3
  81. data/smoke/block9.rb +1 -1
  82. data/smoke/block_given.rb +37 -0
  83. data/smoke/blown.rb +1 -1
  84. data/smoke/break1.rb +2 -2
  85. data/smoke/break2.rb +1 -1
  86. data/smoke/break3.rb +13 -0
  87. data/smoke/case.rb +1 -1
  88. data/smoke/case2.rb +1 -1
  89. data/smoke/case3.rb +17 -0
  90. data/smoke/class-hierarchy.rb +5 -5
  91. data/smoke/class-hierarchy2.rb +3 -3
  92. data/smoke/class-new.rb +15 -0
  93. data/smoke/class_instance_var.rb +1 -1
  94. data/smoke/class_method.rb +2 -2
  95. data/smoke/class_method2.rb +2 -2
  96. data/smoke/class_method3.rb +2 -2
  97. data/smoke/constant1.rb +6 -6
  98. data/smoke/constant2.rb +5 -4
  99. data/smoke/constant3.rb +1 -1
  100. data/smoke/constant4.rb +1 -1
  101. data/smoke/context-sensitive1.rb +1 -1
  102. data/smoke/cvar.rb +6 -5
  103. data/smoke/cvar2.rb +2 -2
  104. data/smoke/define_method.rb +2 -2
  105. data/smoke/define_method2.rb +2 -2
  106. data/smoke/define_method3.rb +14 -0
  107. data/smoke/define_method3.rbs +3 -0
  108. data/smoke/define_method4.rb +15 -0
  109. data/smoke/define_method4.rbs +3 -0
  110. data/smoke/define_method5.rb +12 -0
  111. data/smoke/define_method6.rb +19 -0
  112. data/smoke/demo.rb +6 -6
  113. data/smoke/demo1.rb +1 -1
  114. data/smoke/demo10.rb +2 -2
  115. data/smoke/demo11.rb +1 -1
  116. data/smoke/demo2.rb +1 -1
  117. data/smoke/demo3.rb +1 -1
  118. data/smoke/demo4.rb +3 -3
  119. data/smoke/demo5.rb +1 -1
  120. data/smoke/demo6.rb +4 -3
  121. data/smoke/demo7.rb +1 -1
  122. data/smoke/demo8.rb +2 -2
  123. data/smoke/demo9.rb +3 -2
  124. data/smoke/dummy-execution1.rb +2 -2
  125. data/smoke/dummy-execution2.rb +2 -2
  126. data/smoke/dummy_element.rb +14 -0
  127. data/smoke/ensure1.rb +2 -2
  128. data/smoke/enum_for.rb +15 -0
  129. data/smoke/enum_for2.rb +17 -0
  130. data/smoke/enumerator.rb +2 -2
  131. data/smoke/expandarray1.rb +1 -1
  132. data/smoke/expandarray2.rb +1 -1
  133. data/smoke/fib.rb +2 -2
  134. data/smoke/flip-flop.rb +28 -0
  135. data/smoke/flow1.rb +1 -1
  136. data/smoke/flow10.rb +17 -0
  137. data/smoke/flow2.rb +1 -1
  138. data/smoke/flow3.rb +1 -1
  139. data/smoke/flow5.rb +1 -1
  140. data/smoke/flow6.rb +1 -1
  141. data/smoke/flow7.rb +1 -1
  142. data/smoke/flow8.rb +1 -1
  143. data/smoke/flow9.rb +12 -0
  144. data/smoke/freeze.rb +1 -1
  145. data/smoke/function.rb +2 -2
  146. data/smoke/gvar.rb +2 -2
  147. data/smoke/gvar2.rb +3 -3
  148. data/smoke/hash-bot.rb +12 -0
  149. data/smoke/hash-fetch.rb +3 -3
  150. data/smoke/hash-merge-bang.rb +1 -1
  151. data/smoke/hash1.rb +2 -2
  152. data/smoke/hash2.rb +1 -1
  153. data/smoke/hash3.rb +1 -1
  154. data/smoke/hash4.rb +1 -1
  155. data/smoke/hash5.rb +1 -1
  156. data/smoke/inheritance.rb +4 -4
  157. data/smoke/inheritance2.rb +2 -2
  158. data/smoke/initialize.rb +6 -5
  159. data/smoke/instance_eval.rb +2 -2
  160. data/smoke/instance_eval2.rb +10 -0
  161. data/smoke/instance_eval3.rb +25 -0
  162. data/smoke/int_times.rb +1 -1
  163. data/smoke/integer.rb +1 -1
  164. data/smoke/ivar.rb +5 -4
  165. data/smoke/ivar2.rb +4 -4
  166. data/smoke/ivar3.rb +4 -3
  167. data/smoke/ivar4.rb +21 -0
  168. data/smoke/kernel-class.rb +1 -1
  169. data/smoke/keyword1.rb +1 -1
  170. data/smoke/keyword2.rb +1 -1
  171. data/smoke/keyword3.rb +1 -1
  172. data/smoke/keyword4.rb +1 -1
  173. data/smoke/keyword5.rb +1 -1
  174. data/smoke/kwrest.rb +3 -2
  175. data/smoke/kwsplat1.rb +4 -4
  176. data/smoke/kwsplat2.rb +1 -1
  177. data/smoke/lit-complex.rb +10 -0
  178. data/smoke/lit-encoding.rb +10 -0
  179. data/smoke/manual-rbs.rb +5 -4
  180. data/smoke/manual-rbs2.rb +1 -1
  181. data/smoke/manual-rbs3.rb +3 -2
  182. data/smoke/masgn1.rb +1 -1
  183. data/smoke/masgn2.rb +2 -2
  184. data/smoke/masgn3.rb +1 -1
  185. data/smoke/method_in_branch.rb +3 -3
  186. data/smoke/method_missing.rb +5 -4
  187. data/smoke/module1.rb +2 -2
  188. data/smoke/module2.rb +1 -1
  189. data/smoke/module3.rb +3 -3
  190. data/smoke/module4.rb +3 -2
  191. data/smoke/module5.rb +17 -0
  192. data/smoke/module6.rb +40 -0
  193. data/smoke/module_function1.rb +4 -3
  194. data/smoke/module_function2.rb +4 -3
  195. data/smoke/multiple-include.rb +2 -1
  196. data/smoke/multiple-superclass.rb +1 -1
  197. data/smoke/next1.rb +2 -2
  198. data/smoke/next2.rb +1 -1
  199. data/smoke/object-send1.rb +3 -3
  200. data/smoke/object-send2.rb +10 -0
  201. data/smoke/object-send3.rb +18 -0
  202. data/smoke/once.rb +1 -1
  203. data/smoke/optional1.rb +1 -1
  204. data/smoke/optional2.rb +1 -1
  205. data/smoke/optional3.rb +1 -1
  206. data/smoke/parameterizedd-self.rb +3 -2
  207. data/smoke/parameterizedd-self2.rb +1 -1
  208. data/smoke/pathname1.rb +1 -1
  209. data/smoke/pathname2.rb +1 -1
  210. data/smoke/pattern-match1.rb +1 -1
  211. data/smoke/pattern-match2.rb +1 -1
  212. data/smoke/prepend1.rb +33 -0
  213. data/smoke/prepend2.rb +10 -0
  214. data/smoke/prepend2.rbs +9 -0
  215. data/smoke/primitive_method.rb +19 -0
  216. data/smoke/printf.rb +2 -2
  217. data/smoke/proc.rb +2 -2
  218. data/smoke/proc2.rb +1 -1
  219. data/smoke/proc3.rb +1 -1
  220. data/smoke/proc4.rb +1 -1
  221. data/smoke/proc5.rb +19 -0
  222. data/smoke/public.rb +38 -0
  223. data/smoke/range.rb +1 -1
  224. data/smoke/rbs-alias.rb +1 -1
  225. data/smoke/rbs-attr.rb +5 -5
  226. data/smoke/rbs-attr2.rb +1 -1
  227. data/smoke/rbs-extend.rb +1 -1
  228. data/smoke/rbs-interface.rb +4 -4
  229. data/smoke/rbs-module.rb +26 -0
  230. data/smoke/rbs-module.rbs +4 -0
  231. data/smoke/rbs-opt-and-rest.rb +10 -0
  232. data/smoke/rbs-opt-and-rest.rbs +3 -0
  233. data/smoke/rbs-proc1.rb +1 -1
  234. data/smoke/rbs-proc2.rb +2 -2
  235. data/smoke/rbs-proc3.rb +1 -1
  236. data/smoke/rbs-record.rb +2 -2
  237. data/smoke/rbs-tyvar.rb +2 -2
  238. data/smoke/rbs-tyvar2.rb +2 -2
  239. data/smoke/rbs-tyvar3.rb +2 -2
  240. data/smoke/rbs-tyvar4.rb +4 -3
  241. data/smoke/rbs-tyvar5.rb +1 -1
  242. data/smoke/rbs-tyvar6.rb +3 -3
  243. data/smoke/rbs-tyvar7.rb +1 -1
  244. data/smoke/rbs-vars.rb +6 -6
  245. data/smoke/redo1.rb +2 -2
  246. data/smoke/redo2.rb +2 -2
  247. data/smoke/req-keyword.rb +1 -1
  248. data/smoke/rescue1.rb +2 -2
  249. data/smoke/rescue2.rb +2 -2
  250. data/smoke/rescue3.rb +20 -0
  251. data/smoke/rescue4.rb +17 -0
  252. data/smoke/respond_to.rb +1 -1
  253. data/smoke/rest-farg.rb +1 -1
  254. data/smoke/rest1.rb +2 -2
  255. data/smoke/rest2.rb +1 -1
  256. data/smoke/rest3.rb +6 -6
  257. data/smoke/rest4.rb +2 -2
  258. data/smoke/rest5.rb +1 -1
  259. data/smoke/rest6.rb +1 -1
  260. data/smoke/retry1.rb +2 -2
  261. data/smoke/return.rb +1 -1
  262. data/smoke/simple.rb +12 -0
  263. data/smoke/singleton_method.rb +1 -1
  264. data/smoke/step.rb +3 -3
  265. data/smoke/string-split.rb +1 -1
  266. data/smoke/struct-keyword_init.rb +10 -0
  267. data/smoke/struct.rb +1 -1
  268. data/smoke/struct2.rb +4 -4
  269. data/smoke/struct3.rb +2 -2
  270. data/smoke/struct4.rb +7 -0
  271. data/smoke/struct5.rb +16 -0
  272. data/smoke/struct6.rb +15 -0
  273. data/smoke/struct7.rb +17 -0
  274. data/smoke/stub-keyword.rb +10 -0
  275. data/smoke/super1.rb +4 -4
  276. data/smoke/super2.rb +1 -1
  277. data/smoke/super3.rb +4 -3
  278. data/smoke/super4.rb +7 -5
  279. data/smoke/super5.rb +6 -4
  280. data/smoke/svar1.rb +1 -1
  281. data/smoke/symbol-proc-attr.rb +22 -0
  282. data/smoke/symbol-proc-attr2.rb +15 -0
  283. data/smoke/symbol-proc-bot.rb +13 -0
  284. data/smoke/symbol-proc.rb +3 -3
  285. data/smoke/tap1.rb +2 -2
  286. data/smoke/toplevel.rb +1 -1
  287. data/smoke/two-map.rb +2 -2
  288. data/smoke/type_var.rb +3 -3
  289. data/smoke/typed_method.rb +1 -1
  290. data/smoke/uninitialize-var.rb +1 -1
  291. data/smoke/union-recv.rb +2 -2
  292. data/smoke/user-demo.rb +3 -3
  293. data/smoke/wrong-extend.rb +3 -2
  294. data/smoke/wrong-include.rb +3 -2
  295. data/smoke/wrong-include2.rb +17 -0
  296. data/typeprof.gemspec +1 -1
  297. metadata +59 -6
  298. data/tools/stackprof-wrapper.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd38b5e501a5d4fbe4db1ad43015c44ab39dbfefc3f7638516faeb5bcafd21a9
4
- data.tar.gz: 000f140ab750e4ddd57863851c21a830bfe470c0eb7be9e38442ca535f4ef041
3
+ metadata.gz: 88521dfb48f311cdb5b651e88afc2627a28040a466a22b50303415531e663cfd
4
+ data.tar.gz: 4bd17f9d016d6e7a60ed72804eb050a5611af0869275e63d3486700a53befaaf
5
5
  SHA512:
6
- metadata.gz: '00924963d5f91c4609224615507fcfb240a140c8119bf76f24aa28973342e6b61c528fc579781690f9f0392bf0d125c8bdb70499525791603c111ed9251da931'
7
- data.tar.gz: ee07e0636cd3e26271c468e1927e676a1b320ddefe13dc2412c254cac9aaeac7ca5db1936a26ccc477c2494473fde1d2bdde7a28bd1fa384c6995d7ae4d085ea
6
+ metadata.gz: f88b069345f02729a09c0516baf4ff31c8aef640adf5a2c53f62f72a9fb8b96b4309993ae79e37ece4ec33972f762005b3898ca7c894cda65c3727513b812b6b
7
+ data.tar.gz: aa5fec1acd9dd6395d3d16e044f08e7b72fac0f41dc6168683c631fcadca33daf2415b9956ba00cd22c573c6732719fad9eeef3d601a16943d59be6986703c20
@@ -1,17 +1,17 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- typeprof (0.8.0)
5
- rbs (>= 0.17.0)
4
+ typeprof (0.11.0)
5
+ rbs (>= 1.0.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  coverage-helpers (1.0.0)
11
- docile (1.3.2)
11
+ docile (1.3.4)
12
12
  power_assert (1.2.0)
13
13
  rake (13.0.1)
14
- rbs (0.18.1)
14
+ rbs (1.0.0)
15
15
  simplecov (0.20.0)
16
16
  docile (~> 1.1)
17
17
  simplecov-html (~> 0.11)
@@ -24,6 +24,7 @@ GEM
24
24
 
25
25
  PLATFORMS
26
26
  ruby
27
+ x86_64-linux
27
28
 
28
29
  DEPENDENCIES
29
30
  coverage-helpers
@@ -35,4 +36,4 @@ DEPENDENCIES
35
36
  typeprof!
36
37
 
37
38
  BUNDLED WITH
38
- 2.2.0.rc.1
39
+ 2.2.3
@@ -39,7 +39,7 @@ class Object
39
39
  end
40
40
  ```
41
41
 
42
- Yoy can [try this analysis online](https://mame.github.io/typeprof-playground/#rb=def+hello_message%28user%29%0A++%22The+name+is+%22+%2B+user.name%0Aend%0A%0Adef+type_error_demo%28user%29%0A++%22The+age+is+%22+%2B+user.age%0Aend%0A%0Auser+%3D+User.new%28name%3A+%22John%22%2C+age%3A+20%29%0A%0Ahello_message%28user%29%0Atype_error_demo%28user%29&rbs=class+User%0A++attr_reader+name%3A+String%0A++attr_reader+age%3A+Integer%0A%0A++def+initialize%3A+%28name%3A+String%2C+age%3A+Integer%29+-%3E+void%0Aend).
42
+ You can [try this analysis online](https://mame.github.io/typeprof-playground/#rb=def+hello_message%28user%29%0A++%22The+name+is+%22+%2B+user.name%0Aend%0A%0Adef+type_error_demo%28user%29%0A++%22The+age+is+%22+%2B+user.age%0Aend%0A%0Auser+%3D+User.new%28name%3A+%22John%22%2C+age%3A+20%29%0A%0Ahello_message%28user%29%0Atype_error_demo%28user%29&rbs=class+User%0A++attr_reader+name%3A+String%0A++attr_reader+age%3A+Integer%0A%0A++def+initialize%3A+%28name%3A+String%2C+age%3A+Integer%29+-%3E+void%0Aend).
43
43
 
44
44
  ## A simple demo to generate the signature prototype of "User" class
45
45
 
@@ -0,0 +1,133 @@
1
+ # TypeProf milestones and TODOs
2
+
3
+ ## Big milestones
4
+
5
+ ### Rails support
6
+
7
+ There are many known issues for the analysis of typical Ruby programs including Rails apps.
8
+
9
+ * The main difficulty is that they use many language extensions like `ActiveSupport`.
10
+ Some features (for example, `blank?` and `Time.now + 1.day`) are trivial to support,
11
+ but others (for example, `ActiveSupport::Concern` and Zeitwerk) will require special support.
12
+ * The other difficulty is that they heavily use meta-programming features like `ActiveRecord`.
13
+ It dynamically defines some methods based on external data (such as DB schema) from the code.
14
+
15
+ Currently, projects called [`gem_rbs`](https://github.com/ruby/gem_rbs) and [`rbs_rails`](https://github.com/pocke/rbs_rails) are in progress.
16
+ The former provides several RBS files for some major gems including Rails.
17
+ The latter is a tool to generate RBS prototype of a target Rails application by introspection (executing it and monitoring DB schema, etc).
18
+ TypeProf can use their results to improve analysis precision and performance.
19
+
20
+ What we need to do:
21
+
22
+ * Experimentally apply TypeProf to some Rails programs and identify problems
23
+ * Make TypeProf able to work together with `rbs_rails` for supporting trivial core extensions and `ActiveRecord`.
24
+ * Implement special support for some fundamental language extensions of Rails like `ActiveSupport::Concern`.
25
+ (It would be best if TypeProf has a plugin system and if we can factor out the special support as a plugin for Rails.)
26
+
27
+ ### Error detection and diagnosis feature
28
+
29
+ At present, TypeProf focuses on generation of RBS prototype from no-type-annotated Ruby code.
30
+ However, it is possible for TypeProf to report possible errors found during the analysis.
31
+ In fact, an option `-v` experimentally shows possible errors found.
32
+ There are some reasons why it is disabled by default:
33
+
34
+ * (1) There are too many false positives.
35
+ * (2) Some kind of error reporting is not implemented yet.
36
+ * (3) Some reported errors are difficult for a user to understand.
37
+
38
+ For (1), we will research how we can avoid false positives to support typical Ruby coding patterns as much as possible.
39
+ The primary way is to improve the analysis precision, e.g., enhancing flow-sensitive analysis.
40
+ If the S/N ratio of an error type is too low, we need to consider to suppress the kind of reports.
41
+ Also, we may try allowing users to guide TypeProf to analyze their program well.
42
+ (The simplest way is to write inline type casts in the code, but we need to find more Ruby/RBS way.)
43
+ We may also explore a "TypeProf-friendly coding style" which TypeProf can analyze well.
44
+ (In principle, the plainer code is, the better TypeProf can analyze.)
45
+
46
+ For (2), currently, TypeProf checks the argument types of a call to a method whose type signature is declared in RBS.
47
+ However, it does not check the return type yet. Redefinition of constants should be warned too.
48
+ We will survey what errors and warnings TypeProf can print, and evaluate the S/N ratio of each report.
49
+
50
+ For (3), since TypeProf uses whole program analysis, an error may be reported at a very different place from its root bug.
51
+ Thus, if TypeProf shows a possible type error, a diagnosis feature is needed to answer why TypeProf thinks that the error may occur.
52
+ TypeProf has already implemented a very primitive diagnosis feature, `Kernel#p`, to check what type an expression has.
53
+ Another idea is to create a pseudo backtrace why TypeProf thought the possible type error may occur.
54
+ We should consider this feature with LSP support.
55
+
56
+ ### Performance improvement
57
+
58
+ Currently, TypeProf is painfully slow. Even if a target application is small.
59
+
60
+ The main reason is that TypeProf analyzes not only the application code but also library code:
61
+ if an application requires `"foo"`, TypeProf actually loads `foo.rb` even from a gem,
62
+ and furthermore, if `foo.rb` requires `"bar"`, it loads `bar.rb` recursively.
63
+
64
+ RBS will help to stop this cascade;
65
+ when an application requires `"foo"`, TypeProf loads `sig/foo.rbs` instead of `foo.rb` if the `foo` gem contains both.
66
+ Such a RBS file is optional for TypeProf but required for Steep.
67
+ So, we think many gems will eventually equip their RBS declarations.
68
+
69
+ That being said, we should continue to improve the analysis performance of TypeProf. We have some ideas.
70
+
71
+ * Unfortunately, TypeProf often analyzes one method more than once when it accepts multiple types.
72
+ As TypeProf squashes the argument types to a union, this duplicated analysis is not necessarily needed.
73
+ But when TypeProf first analyzes a method, it is difficult to determine if the method will accept another type in further analysis.
74
+ So, we need good heuristics to guess whether a method accepts multiple types or not, and if so, delay its analysis.
75
+ * Currently, TypeProf executes the bytecode instructions step by step.
76
+ This requires creating an environment object after each instruction, which is very heavy.
77
+ Many environment creations can be omitted by executing each basic block instead of each instruction.
78
+ (Basic block execution will also make flow-sensitive analysis easier.)
79
+ * The slowest calculation in TypeProf is to create an instance of a Type class.
80
+ The creation uses memoization; TypeProf keeps all Type instances created so far, and reuses them if already exist.
81
+ However, it is very heavy to check if an instance already exists or not.
82
+ (Currently, it is very simply implemented by a big Hash table.)
83
+ We've already improved the memoization routine several times but looks like it is still the No.1 bottleneck.
84
+ We need to investigate and try improving more.
85
+ * TypeProf heavily uses Hash objects (including above) mainly to represent a set.
86
+ A union of sets is done by `Hash#merge`, which takes O(n).
87
+ A more lightweight data structure may make TypeProf faster.
88
+ (But clever structure often has a big constant term, so we need to evaluate the performance carefully.)
89
+ * Reusing an old analysis and incrementally updating it will bring a super big improvement.
90
+ This would be especially helpful for LSP support, so we need to tackle it after the analysis approach is mature.
91
+
92
+ ### Language Server Protocol (LSP) support
93
+
94
+ In the future, we want TypeProf to serve as a language server to show the result in IDE in real-time.
95
+ However, the current analysis approach is too slow for IDE. So we need to improve the performance first.
96
+
97
+ Even if TypeProf becomes fast enough, its approach has a fundamental problem.
98
+ Since TypeProf uses whole program analysis, one edit may cause a cascade of propagation:
99
+ if a user write `foo(42)`, an Integer is propagated to a method `foo`,
100
+ and if `foo` passes its argument to a method `bar`, it is propagated to `bar`, ...
101
+ So, a breakthrough for LSP may be still needed, e.g, limiting the propagation range in real-time analysis,
102
+ assuming that a type interface of module boundary is fixed, etc.
103
+
104
+ ## Relatively smaller TODOs
105
+
106
+ * Support more RBS features
107
+ * TypeProf does not deal with some RBS types well yet.
108
+ * For example, the `instance` type is handled as `untyped.
109
+ * The `self` type is handled well only when it is used as a return type.
110
+ * Using a value of the `void` type should be warned appropriately.
111
+ * RBS's `interface` is supported just like a module (i.e., `include _Foo` is explicitly required in RBS),
112
+ but it should be checked structually (i.e., it should be determined as a method set.)
113
+ * The variance of type parameters is currently ignored.
114
+
115
+ * Support more Ruby features
116
+ * Some meta-programming features like `Class.new`, `Object#method`, etc.
117
+ * It is possible to support `Class.new` by per-allocation-site approach:
118
+ e.g., In TypeProf, `A = Class.new; B = Class.new` will create two classes, but `2.times { Class.new }` will create one class.
119
+ * The analysis precision can be improved more for some Ruby features like pattern matching, keyword arguments, etc.
120
+ * For example, `foo(*args, k:1)` is currently compiled as if it is `foo(*(args + [{ :k => 1 }]))` into Ruby bytecode.
121
+ This mixes the keyword arguments to a rest array, and makes it difficult for TypeProf to track the keyword arguments.
122
+ * Support Enumerator as an Array-type container.
123
+ * Support `Module#protect` (but RBS does not yet).
124
+ * More heuristics may help such as `==` returns a bool regardless to its receiver and argument types.
125
+
126
+ * Make TypeProf more useful as a tool
127
+ * Currently, TypeProf provides only the analysis engine and a minimal set of features.
128
+ * The analysis result would be useful not only to generate RBS prototype
129
+ but also identifying the source location of a method definition, listing callsites of a method,
130
+ searching a method call by its argument types, etc.
131
+ * Sometimes, TypeProf prints very big union type, such as `Integer | Float | Complex | Rational | ...`.
132
+ Worse, the same big type is printed multiple times.
133
+ It may be useful to factor out such a long type by using type alias, for example.
@@ -34,6 +34,14 @@ module TypeProf
34
34
  end
35
35
 
36
36
  attr_reader :iseq, :cref, :mid
37
+
38
+ def source_location(pc)
39
+ if @iseq
40
+ @iseq.source_location(pc)
41
+ else
42
+ "<builtin>"
43
+ end
44
+ end
37
45
  end
38
46
 
39
47
  class TypedContext
@@ -45,6 +53,14 @@ module TypeProf
45
53
  end
46
54
 
47
55
  attr_reader :caller_ep, :mid
56
+
57
+ def source_location(_pc)
58
+ if @caller_ep
59
+ @caller_ep.source_location
60
+ else
61
+ "<typed-context:#{ @mid }>"
62
+ end
63
+ end
48
64
  end
49
65
 
50
66
  class ExecutionPoint
@@ -71,12 +87,7 @@ module TypeProf
71
87
  end
72
88
 
73
89
  def source_location
74
- iseq = @ctx.iseq
75
- if iseq
76
- iseq.source_location(@pc)
77
- else
78
- "<builtin>"
79
- end
90
+ @ctx.source_location(@pc)
80
91
  end
81
92
  end
82
93
 
@@ -91,7 +102,7 @@ module TypeProf
91
102
 
92
103
  return if recv_ty == :top #OK
93
104
  recv_ty.each_child_global do |ty|
94
- raise ty.inspect if !ty.is_a?(Type::Instance) && !ty.is_a?(Type::Class) && ty != Type.any
105
+ raise ty.inspect if !ty.is_a?(Type::Instance) && !ty.is_a?(Type::Class) && !ty.is_a?(Type::Symbol) && ty != Type.any
95
106
  end
96
107
  end
97
108
 
@@ -211,6 +222,11 @@ module TypeProf
211
222
  Env.new(senv, @locals, @stack, @type_params)
212
223
  end
213
224
 
225
+ def replace_blk_ty(ty)
226
+ senv = StaticEnv.new(@static_env.recv_ty, ty, @static_env.mod_func, @static_env.pub_meth)
227
+ Env.new(senv, @locals, @stack, @type_params)
228
+ end
229
+
214
230
  def inspect
215
231
  "Env[#{ @static_env.inspect }, locals:#{ @locals.inspect }, stack:#{ @stack.inspect }, type_params:#{ (@type_params&.internal_hash).inspect }]"
216
232
  end
@@ -284,7 +300,10 @@ module TypeProf
284
300
  def initialize(kind, name, absolute_path)
285
301
  raise unless name.is_a?(Array)
286
302
  @kind = kind
287
- @modules = { true => [], false => [] }
303
+ @modules = {
304
+ :before => { true => [], false => [] }, # before = include/extend
305
+ :after => { true => [], false => [] }, # after = prepend
306
+ }
288
307
  @name = name
289
308
  @consts = {}
290
309
  @methods = {}
@@ -297,13 +316,13 @@ module TypeProf
297
316
  attr_reader :kind, :modules, :consts, :methods, :ivars, :cvars, :absolute_path
298
317
  attr_accessor :name, :klass_obj
299
318
 
300
- def include_module(mod, type_args, singleton, absolute_path)
301
- module_type_args, _, absolute_paths = @modules[singleton].find {|m,| m == mod }
302
- if module_type_args
303
- raise "inconsistent include/extend type args in RBS?" if module_type_args != type_args && type_args != [] && type_args != nil
319
+ def mix_module(kind, mod, type_args, singleton, absolute_path)
320
+ mod_, module_type_args, absolute_paths = @modules[kind][singleton].find {|m,| m == mod }
321
+ if mod_
322
+ raise "inconsistent #{ kind == :after ? "include/extend" : "prepend" } type args in RBS?" if module_type_args != type_args && type_args != [] && type_args != nil
304
323
  else
305
324
  absolute_paths = Utils::MutableSet.new
306
- @modules[singleton].unshift([mod, type_args, absolute_paths])
325
+ @modules[kind][singleton].unshift([mod, type_args, absolute_paths])
307
326
  end
308
327
  absolute_paths << absolute_path
309
328
  end
@@ -320,10 +339,8 @@ module TypeProf
320
339
  @consts[name] = [ty, absolute_path]
321
340
  end
322
341
 
323
- def adjust_substitution(singleton, mid, mthd, subst, direct, &blk)
324
- mthds = @methods[[singleton, mid]]
325
- yield subst, direct if mthds&.include?(mthd)
326
- @modules[singleton].each do |mod_def, type_args,|
342
+ def adjust_substitution_for_module(mods, mid, mthd, subst, &blk)
343
+ mods.each do |mod_def, type_args,|
327
344
  if mod_def.klass_obj.type_params && type_args
328
345
  subst2 = {}
329
346
  mod_def.klass_obj.type_params.zip(type_args) do |(tyvar, *), tyarg|
@@ -335,11 +352,29 @@ module TypeProf
335
352
  end
336
353
  end
337
354
 
338
- def search_method(singleton, mid, &blk)
355
+ def adjust_substitution(singleton, mid, mthd, subst, direct, &blk)
356
+ adjust_substitution_for_module(@modules[:before][singleton], mid, mthd, subst, &blk)
357
+
358
+ mthds = @methods[[singleton, mid]]
359
+ yield subst, direct if mthds&.include?(mthd)
360
+
361
+ adjust_substitution_for_module(@modules[:after][singleton], mid, mthd, subst, &blk)
362
+ end
363
+
364
+ def search_method(singleton, mid, visited, &blk)
365
+ # Currently, circular inclusion of modules is allowed
366
+ return if visited[self]
367
+ visited[self] = true
368
+
369
+ @modules[:before][singleton].each do |mod_def,|
370
+ mod_def.search_method(false, mid, visited, &blk)
371
+ end
372
+
339
373
  mthds = @methods[[singleton, mid]]
340
374
  yield mthds, @klass_obj, singleton if mthds
341
- @modules[singleton].each do |mod_def,|
342
- mod_def.search_method(false, mid, &blk)
375
+
376
+ @modules[:after][singleton].each do |mod_def,|
377
+ mod_def.search_method(false, mid, visited, &blk)
343
378
  end
344
379
  end
345
380
 
@@ -367,17 +402,17 @@ module TypeProf
367
402
  end
368
403
  end
369
404
 
370
- def include_module(including_mod, included_mod, type_args, singleton, absolute_path)
371
- return if included_mod == Type.any
405
+ def mix_module(kind, mixing_mod, mixed_mod, type_args, singleton, caller_ep)
406
+ return if mixed_mod == Type.any
372
407
 
373
- including_mod = @class_defs[including_mod.idx]
374
- included_mod.each_child do |included_mod|
375
- if included_mod.is_a?(Type::Class)
376
- included_mod = @class_defs[included_mod.idx]
377
- if included_mod && included_mod.kind == :module
378
- including_mod.include_module(included_mod, type_args, singleton, absolute_path)
408
+ mixing_mod = @class_defs[mixing_mod.idx]
409
+ mixed_mod.each_child do |mixed_mod|
410
+ if mixed_mod.is_a?(Type::Class)
411
+ mixed_mod = @class_defs[mixed_mod.idx]
412
+ if mixed_mod && mixed_mod.kind == :module
413
+ mixing_mod.mix_module(kind, mixed_mod, type_args, singleton, caller_ep ? caller_ep.ctx.iseq.absolute_path : nil)
379
414
  else
380
- warn "including something that is not a module"
415
+ warn(caller_ep, "attempted to #{ kind == :after ? "include/extend" : "prepend" } non-module; ignored")
381
416
  end
382
417
  end
383
418
  end
@@ -479,13 +514,13 @@ module TypeProf
479
514
  if klass.kind == :class
480
515
  while klass != :__root__
481
516
  class_def = @class_defs[klass.idx]
482
- class_def.search_method(singleton, mid, &blk)
517
+ class_def.search_method(singleton, mid, {}, &blk)
483
518
  klass = klass.superclass
484
519
  end
485
520
  else
486
521
  # module
487
522
  class_def = @class_defs[klass.idx]
488
- class_def.search_method(singleton, mid, &blk)
523
+ class_def.search_method(singleton, mid, {}, &blk)
489
524
  end
490
525
  if singleton
491
526
  search_method(Type::Builtin[klass_orig.kind], false, mid, &blk)
@@ -545,9 +580,7 @@ module TypeProf
545
580
  end
546
581
 
547
582
  def add_constant(klass, name, value, user_defined)
548
- if klass == Type.any
549
- self
550
- else
583
+ if klass.is_a?(Type::Class)
551
584
  @class_defs[klass.idx].add_constant(name, value, user_defined)
552
585
  end
553
586
  end
@@ -566,12 +599,12 @@ module TypeProf
566
599
  mdef
567
600
  end
568
601
 
569
- def add_attr_method(klass, absolute_path, mid, ivar, kind)
602
+ def add_attr_method(klass, mid, ivar, kind, pub_meth, ep)
570
603
  if kind == :reader || kind == :accessor
571
- add_method(klass, mid, false, AttrMethodDef.new(ivar, :reader, absolute_path))
604
+ add_method(klass, mid, false, AttrMethodDef.new(ivar, :reader, pub_meth, ep))
572
605
  end
573
606
  if kind == :writer || kind == :accessor
574
- add_method(klass, :"#{ mid }=", false, AttrMethodDef.new(ivar, :writer, absolute_path))
607
+ add_method(klass, :"#{ mid }=", false, AttrMethodDef.new(ivar, :writer, pub_meth, ep))
575
608
  end
576
609
  end
577
610
 
@@ -583,22 +616,22 @@ module TypeProf
583
616
  add_method(klass, mid, true, ISeqMethodDef.new(iseq, cref, outer_ep, pub_meth))
584
617
  end
585
618
 
586
- def set_custom_method(klass, mid, impl)
587
- set_method(klass, mid, false, CustomMethodDef.new(impl))
619
+ def set_custom_method(klass, mid, impl, pub_meth = true)
620
+ set_method(klass, mid, false, CustomMethodDef.new(impl, pub_meth))
588
621
  end
589
622
 
590
- def set_singleton_custom_method(klass, mid, impl)
591
- set_method(klass, mid, true, CustomMethodDef.new(impl))
623
+ def set_singleton_custom_method(klass, mid, impl, pub_meth = true)
624
+ set_method(klass, mid, true, CustomMethodDef.new(impl, pub_meth))
592
625
  end
593
626
 
594
- def alias_method(klass, singleton, new, old)
627
+ def alias_method(klass, singleton, alias_mid, orig_mid, ep)
595
628
  if klass == Type.any
596
629
  self
597
630
  else
598
- mdefs = get_method(klass, singleton, old)
631
+ mdefs = get_method(klass, singleton, orig_mid)
599
632
  if mdefs
600
633
  mdefs.each do |mdef|
601
- @class_defs[klass.idx].add_method(new, singleton, mdef)
634
+ @class_defs[klass.idx].add_method(alias_mid, singleton, AliasMethodDef.new(orig_mid, mdef, ep))
602
635
  end
603
636
  end
604
637
  end
@@ -613,6 +646,10 @@ module TypeProf
613
646
  @iseq_method_to_ctxs[iseq_mdef] << ctx
614
647
  end
615
648
 
649
+ def add_executed_iseq(iseq)
650
+ @executed_iseqs << iseq
651
+ end
652
+
616
653
  def add_callsite!(callee_ctx, caller_ep, caller_env, &ctn)
617
654
  @executed_iseqs << callee_ctx.iseq if callee_ctx.is_a?(Context)
618
655
 
@@ -672,8 +709,10 @@ module TypeProf
672
709
  def add_read!(site, ep, &ctn)
673
710
  entry = @tbl[site] ||= Entry.new(false, {}, Type.bot, Utils::MutableSet.new)
674
711
  entry.read_continuations[ep] = ctn
675
- entry.absolute_paths << ep.ctx.iseq.absolute_path
676
- ctn[entry.type, ep]
712
+ entry.absolute_paths << ep.ctx.iseq.absolute_path if ep.ctx.is_a?(Context)
713
+ ty = entry.type
714
+ ty = Type.nil if ty == Type.bot
715
+ ctn[ty, ep]
677
716
  end
678
717
 
679
718
  def add_write!(site, ty, ep, scratch)
@@ -681,7 +720,7 @@ module TypeProf
681
720
  if ep
682
721
  if entry.rbs_declared
683
722
  unless Type.match?(ty, entry.type)
684
- scratch.warn(ep, "inconsistent assignment to RBS-declared global variable")
723
+ scratch.warn(ep, "inconsistent assignment to RBS-declared variable")
685
724
  return
686
725
  end
687
726
  end
@@ -731,6 +770,7 @@ module TypeProf
731
770
 
732
771
  def add_cvar_read!(klass, var, ep, &ctn)
733
772
  klass.each_child do |klass|
773
+ next unless klass.is_a?(Type::Class)
734
774
  class_def = @class_defs[klass.idx]
735
775
  next unless class_def
736
776
  class_def.cvars.add_read!(var, ep, &ctn)
@@ -739,6 +779,7 @@ module TypeProf
739
779
 
740
780
  def add_cvar_write!(klass, var, ty, ep)
741
781
  klass.each_child do |klass|
782
+ next unless klass.is_a?(Type::Class)
742
783
  class_def = @class_defs[klass.idx]
743
784
  next unless class_def
744
785
  class_def.cvars.add_write!(var, ty, ep, self)
@@ -870,7 +911,7 @@ module TypeProf
870
911
  @pending_execution.delete(iseq)
871
912
  end while @executed_iseqs.include?(iseq)
872
913
 
873
- puts "DEBUG: trigger dummy execution (#{ iseq&.name || "(nil)" }): rest #{ @pending_execution.size }" if Config.verbose >= 2
914
+ puts "DEBUG: trigger stub execution (#{ iseq&.name || "(nil)" }): rest #{ @pending_execution.size }" if Config.verbose >= 2
874
915
 
875
916
  break if !iseq
876
917
  case kind
@@ -974,10 +1015,14 @@ module TypeProf
974
1015
 
975
1016
  def pend_block_dummy_execution(blk, iseq, nep, nenv)
976
1017
  @pending_execution[iseq] ||= [:block, [blk, {}]]
977
- if @pending_execution[iseq][1][1][nep]
978
- @pending_execution[iseq][1][1][nep] = @pending_execution[iseq][1][1][nep].merge(nenv)
1018
+ if @pending_execution[iseq][0] == :block
1019
+ if @pending_execution[iseq][1][1][nep]
1020
+ @pending_execution[iseq][1][1][nep] = @pending_execution[iseq][1][1][nep].merge(nenv)
1021
+ else
1022
+ @pending_execution[iseq][1][1][nep] = nenv
1023
+ end
979
1024
  else
980
- @pending_execution[iseq][1][1][nep] = nenv
1025
+ # XXX: what to do?
981
1026
  end
982
1027
  end
983
1028
 
@@ -1026,9 +1071,9 @@ module TypeProf
1026
1071
  block_start = iseq.fargs_format[:block_start]
1027
1072
 
1028
1073
  lead_tys = env.locals[0, lead_num].map {|ty| globalize_type(ty, env, ep) }
1029
- opt_tys = opt.size > 1 ? env.locals[lead_num, opt.size - 1].map {|ty| globalize_type(ty, env, ep) } : nil
1074
+ opt_tys = opt.size > 1 ? env.locals[lead_num, opt.size - 1].map {|ty| globalize_type(ty, env, ep) } : []
1030
1075
  if rest_start # XXX:squash
1031
- ty = globalize_type(env.locals[lead_num + opt.size - 1], env, ep)
1076
+ ty = globalize_type(env.locals[rest_start], env, ep)
1032
1077
  rest_ty = Type.bot
1033
1078
  ty.each_child_global do |ty|
1034
1079
  if ty.is_a?(Type::Array)
@@ -1063,6 +1108,7 @@ module TypeProf
1063
1108
  end
1064
1109
  end
1065
1110
  kw_rest_ty = globalize_type(env.locals[kw_rest], env, ep) if kw_rest
1111
+ kw_rest_ty = nil if kw_rest_ty == Type.nil
1066
1112
  if block_start
1067
1113
  blk_ty = globalize_type(env.locals[block_start], env, ep)
1068
1114
  elsif iseq.type == :method
@@ -1197,11 +1243,11 @@ module TypeProf
1197
1243
  else # module
1198
1244
  superclass = nil
1199
1245
  end
1200
- if cbase == Type.any
1201
- klass = Type.any
1202
- else
1246
+ if cbase.is_a?(Type::Class)
1203
1247
  klass = new_class(cbase, id, [], superclass, ep.ctx.iseq.absolute_path)
1204
1248
  add_superclass_type_args!(klass, superclass.type_params.map { Type.any }) if superclass
1249
+ else
1250
+ klass = Type.any
1205
1251
  end
1206
1252
  end
1207
1253
  singleton = false
@@ -1242,7 +1288,7 @@ module TypeProf
1242
1288
  end
1243
1289
  end
1244
1290
  return
1245
- when :send_branch
1291
+ when :getlocal_send_branch
1246
1292
  getlocal_operands, send_operands, branch_operands = operands
1247
1293
  env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
1248
1294
  recvs = Type.any if recvs == Type.bot
@@ -1270,6 +1316,31 @@ module TypeProf
1270
1316
  end
1271
1317
  end
1272
1318
  return
1319
+ when :send_branch
1320
+ send_operands, branch_operands = operands
1321
+ env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
1322
+ recvs = Type.any if recvs == Type.bot
1323
+ recvs.each_child do |recv|
1324
+ do_send(recv, mid, aargs, ep, env) do |ret_ty, ep, env|
1325
+ env, ret_ty, = localize_type(ret_ty, env, ep)
1326
+
1327
+ branchtype, target, = branch_operands
1328
+ # branchtype: :if or :unless or :nil
1329
+ ep_then = ep.next
1330
+ ep_else = ep.jump(target)
1331
+
1332
+ case ret_ty
1333
+ when Type::Instance.new(Type::Builtin[:true])
1334
+ merge_env(branchtype == :if ? ep_else : ep_then, env)
1335
+ when Type::Instance.new(Type::Builtin[:false])
1336
+ merge_env(branchtype == :if ? ep_then : ep_else, env)
1337
+ else
1338
+ merge_env(ep_then, env)
1339
+ merge_env(ep_else, env)
1340
+ end
1341
+ end
1342
+ end
1343
+ return
1273
1344
  when :invokeblock
1274
1345
  env, recvs, mid, aargs = setup_actual_arguments(:block, operands, ep, env)
1275
1346
  blk = env.static_env.blk_ty
@@ -1339,11 +1410,24 @@ module TypeProf
1339
1410
  return
1340
1411
  when :break
1341
1412
  tmp_ep = ep
1342
- tmp_ep = tmp_ep.outer while tmp_ep.ctx.iseq.type != :block
1343
- tmp_ep = tmp_ep.outer
1344
- nenv = @return_envs[tmp_ep].push(ty)
1345
- merge_env(tmp_ep.next, nenv)
1346
- # TODO: jump to ensure?
1413
+ while true
1414
+ if tmp_ep.ctx.iseq.type == :block
1415
+ tmp_ep = tmp_ep.outer
1416
+ nenv = @return_envs[tmp_ep].push(ty)
1417
+ merge_env(tmp_ep.next, nenv)
1418
+ break
1419
+ end
1420
+ _type, _iseq, cont, stack_depth = tmp_ep.ctx.iseq.catch_table[tmp_ep.pc]&.find {|type,| type == :break }
1421
+ if cont
1422
+ nenv = @return_envs[tmp_ep]
1423
+ nenv, = nenv.pop(nenv.stack.size - stack_depth)
1424
+ nenv = nenv.push(ty)
1425
+ tmp_ep = tmp_ep.jump(cont)
1426
+ merge_env(tmp_ep, nenv)
1427
+ break
1428
+ end
1429
+ tmp_ep = tmp_ep.outer
1430
+ end
1347
1431
  when :next, :redo
1348
1432
  # begin; rescue; next; end
1349
1433
  tmp_ep = ep.outer
@@ -1585,7 +1669,7 @@ module TypeProf
1585
1669
  warn(ep, "already initialized constant #{ Type::Instance.new(cbase).screen_name(self) }::#{ name }")
1586
1670
  end
1587
1671
  ty.each_child do |ty|
1588
- if ty.is_a?(Type::Class) && ty.superclass == Type::Builtin[:struct]
1672
+ if ty.is_a?(Type::Class) && cbase.is_a?(Type::Class) && ty.superclass == Type::Builtin[:struct]
1589
1673
  @class_defs[ty.idx].name = cbase_path(cbase) + [name]
1590
1674
  end
1591
1675
  end
@@ -1594,7 +1678,6 @@ module TypeProf
1594
1678
  when :getspecial
1595
1679
  key, type = operands
1596
1680
  if type == 0
1597
- raise NotImplementedError
1598
1681
  case key
1599
1682
  when 0 # VM_SVAR_LASTLINE
1600
1683
  env = env.push(Type.any) # or String | NilClass only?
@@ -1612,8 +1695,12 @@ module TypeProf
1612
1695
  return
1613
1696
  end
1614
1697
  when :setspecial
1615
- # flip-flop
1616
- raise NotImplementedError, "setspecial"
1698
+ key, = operands
1699
+ if key >= 2 # flip-flop
1700
+ env, = env.pop(1)
1701
+ else
1702
+ raise "unknown setspecial key: #{ key }"
1703
+ end
1617
1704
 
1618
1705
  when :dup
1619
1706
  env, (ty,) = env.pop(1)
@@ -1664,10 +1751,16 @@ module TypeProf
1664
1751
  env = env.push(Type.optional(sym_ty))
1665
1752
  when :checkmatch
1666
1753
  flag, = operands
1754
+
1755
+ # This flag means that the stack top is an array, and the check needs to be applied to find all elements
1756
+ # However, currently TypeProf uses very conservative interpretation (all check returns both true and false),
1757
+ # so we just ignore the flag now
1667
1758
  _array = flag & 4 != 0
1759
+
1668
1760
  case flag & 3
1669
- when 1
1670
- raise NotImplementedError
1761
+ when 1 # VM_CHECKMATCH_TYPE_WHEN
1762
+ env, = env.pop(2)
1763
+ env = env.push(Type.bool)
1671
1764
  when 2 # VM_CHECKMATCH_TYPE_CASE
1672
1765
  env, = env.pop(2)
1673
1766
  env = env.push(Type.bool)
@@ -1877,6 +1970,10 @@ module TypeProf
1877
1970
  rest_ty = aargs.last
1878
1971
  aargs = aargs[0..-2]
1879
1972
  if flag_args_kw_splat
1973
+ # XXX: The types contained in ActualArguments are expected to be all local types.
1974
+ # This "globalize_type" breaks the invariant, and violates the assertion of Union#globalize that asserts @elems be nil.
1975
+ # To fix this issue fundamentally, ActualArguments should keep all arguments as-is (as like the VM does),
1976
+ # and globalize some types on the on-demand bases.
1880
1977
  ty = globalize_type(rest_ty, env, ep)
1881
1978
  if ty.is_a?(Type::Array)
1882
1979
  _, (ty,) = ty.elems.take_last(1)
@@ -1909,6 +2006,10 @@ module TypeProf
1909
2006
  aargs = ActualArguments.new(aargs, rest_ty, kw_tys, blk_ty)
1910
2007
  elsif flag_args_kw_splat
1911
2008
  last = aargs.last
2009
+ # XXX: The types contained in ActualArguments are expected to be all local types.
2010
+ # This "globalize_type" breaks the invariant, and violates the assertion of Union#globalize that asserts @elems be nil.
2011
+ # To fix this issue fundamentally, ActualArguments should keep all arguments as-is (as like the VM does),
2012
+ # and globalize some types on the on-demand bases.
1912
2013
  ty = globalize_type(last, env, ep)
1913
2014
  case ty
1914
2015
  when Type::Hash
@@ -2003,10 +2104,10 @@ module TypeProf
2003
2104
 
2004
2105
  def do_define_iseq_method(ep, env, mid, iseq, outer_ep)
2005
2106
  cref = ep.ctx.cref
2006
- recv = env.static_env.recv_ty
2007
2107
  if cref.klass.is_a?(Type::Class)
2008
2108
  typed_mdef = check_typed_method(cref.klass, mid, ep.ctx.cref.singleton)
2009
- recv = Type::Instance.new(recv) if recv.is_a?(Type::Class)
2109
+ recv = cref.klass
2110
+ recv = Type::Instance.new(recv) unless ep.ctx.cref.singleton
2010
2111
  if typed_mdef
2011
2112
  mdef = ISeqMethodDef.new(iseq, cref, outer_ep, env.static_env.pub_meth)
2012
2113
  typed_mdef.each do |typed_mdef|
@@ -2021,9 +2122,9 @@ module TypeProf
2021
2122
  add_singleton_iseq_method(cref.klass, mid, iseq, cref, outer_ep, true)
2022
2123
  end
2023
2124
  end
2024
-
2025
- pend_method_execution(iseq, meth, recv, mid, ep.ctx.cref, outer_ep)
2026
2125
  end
2126
+
2127
+ pend_method_execution(iseq, meth, recv, mid, ep.ctx.cref, outer_ep)
2027
2128
  else
2028
2129
  # XXX: what to do?
2029
2130
  end
@@ -2052,7 +2153,7 @@ module TypeProf
2052
2153
 
2053
2154
  bsig ||= BlockSignature.new([], [], nil, Type.nil)
2054
2155
 
2055
- bsig = bsig.screen_name(self)#, block: true)
2156
+ bsig = bsig.screen_name(nil, self)
2056
2157
  ret_ty = ret_ty.screen_name(self)
2057
2158
  ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
2058
2159
 
@@ -2069,7 +2170,7 @@ module TypeProf
2069
2170
  next unless @block_to_ctx[blk.block_body] # this occurs when screen_name is called before type-profiling finished (e.g., error message)
2070
2171
  @block_to_ctx[blk.block_body].each do |blk_ctx|
2071
2172
  if farg_tys
2072
- farg_tys = farg_tys.merge(@method_signatures[blk_ctx])
2173
+ farg_tys = farg_tys.merge_as_block_arguments(@method_signatures[blk_ctx])
2073
2174
  else
2074
2175
  farg_tys = @method_signatures[blk_ctx]
2075
2176
  end
@@ -2079,7 +2180,7 @@ module TypeProf
2079
2180
  end
2080
2181
  end
2081
2182
 
2082
- farg_tys = farg_tys ? farg_tys.screen_name(self) : "(unknown)"
2183
+ farg_tys = farg_tys ? farg_tys.screen_name(nil, self) : "(unknown)"
2083
2184
  ret_ty = ret_ty.screen_name(self)
2084
2185
  ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
2085
2186
 
@@ -2091,7 +2192,7 @@ module TypeProf
2091
2192
  farg_tys = @method_signatures[ctx]
2092
2193
  ret_ty = @return_values[ctx] || Type.bot
2093
2194
 
2094
- farg_tys = farg_tys.screen_name(self)
2195
+ farg_tys = farg_tys.screen_name(ctx.iseq, self)
2095
2196
  ret_ty = ret_ty.screen_name(self)
2096
2197
  ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
2097
2198
  "#{ (farg_tys.empty? ? "" : "#{ farg_tys } ") }-> #{ ret_ty }"