typeprof 0.8.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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 }"