typeprof 0.10.0 → 0.14.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/Gemfile.lock +10 -10
- data/doc/demo.md +2 -2
- data/doc/todo.md +133 -0
- data/lib/typeprof/analyzer.rb +120 -46
- data/lib/typeprof/block.rb +7 -6
- data/lib/typeprof/builtin.rb +49 -7
- data/lib/typeprof/cli.rb +5 -0
- data/lib/typeprof/config.rb +18 -14
- data/lib/typeprof/container-type.rb +24 -0
- data/lib/typeprof/export.rb +30 -15
- data/lib/typeprof/import.rb +37 -11
- data/lib/typeprof/iseq.rb +100 -41
- data/lib/typeprof/type.rb +34 -0
- data/lib/typeprof/version.rb +1 -1
- data/smoke/break4.rb +17 -0
- data/smoke/class_eval.rb +22 -0
- data/smoke/define_method7.rb +18 -0
- data/smoke/extended.rb +38 -0
- data/smoke/flow11.rb +17 -0
- data/smoke/included.rb +38 -0
- data/smoke/inherited.rb +26 -0
- data/smoke/instance_eval.rb +1 -1
- data/smoke/instance_eval4.rb +12 -0
- data/smoke/struct-keyword_init.rb +1 -1
- data/smoke/struct.rb +1 -1
- data/smoke/struct2.rb +1 -1
- data/smoke/struct3.rb +1 -1
- data/smoke/struct4.rb +1 -1
- data/smoke/struct5.rb +1 -1
- data/smoke/struct6.rb +1 -1
- data/smoke/struct7.rb +1 -1
- data/testbed/goodcheck-Gemfile.lock +1 -1
- data/typeprof.gemspec +1 -1
- metadata +14 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5828bf5dda203756a758bba9809d3b1fe81b284cc1373f00ff75ca175afe998
|
4
|
+
data.tar.gz: c274d8bda556407a33c70227a4caea57f1ae91f79ee546f9076d6ffac01891a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e32f209894645a18b36a24c2347e86e249ffa0bcd2c93565d1920551ef143e73f6f3c58011250a545d761e9212aee032e99a44503e4dad5e1975d73de412d3c9
|
7
|
+
data.tar.gz: f3463cbae97beff6b359acb14a3a871f83d0ed37332e46ce38d846b2c486a9af3e6864a5450fe82e0abae79763b71ed55a26f1ab03d6a04aa9eb62232dd435fe
|
data/.github/workflows/main.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,25 +1,25 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
typeprof (0.
|
5
|
-
rbs (>=
|
4
|
+
typeprof (0.14.1)
|
5
|
+
rbs (>= 1.2.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.
|
12
|
-
power_assert (
|
11
|
+
docile (1.4.0)
|
12
|
+
power_assert (2.0.0)
|
13
13
|
rake (13.0.1)
|
14
|
-
rbs (
|
15
|
-
simplecov (0.
|
14
|
+
rbs (1.2.0)
|
15
|
+
simplecov (0.21.2)
|
16
16
|
docile (~> 1.1)
|
17
17
|
simplecov-html (~> 0.11)
|
18
18
|
simplecov_json_formatter (~> 0.1)
|
19
19
|
simplecov-html (0.12.3)
|
20
|
-
simplecov_json_formatter (0.1.
|
21
|
-
stackprof (0.2.
|
22
|
-
test-unit (3.
|
20
|
+
simplecov_json_formatter (0.1.3)
|
21
|
+
stackprof (0.2.17)
|
22
|
+
test-unit (3.4.1)
|
23
23
|
power_assert
|
24
24
|
|
25
25
|
PLATFORMS
|
@@ -36,4 +36,4 @@ DEPENDENCIES
|
|
36
36
|
typeprof!
|
37
37
|
|
38
38
|
BUNDLED WITH
|
39
|
-
2.
|
39
|
+
2.3.0.dev
|
data/doc/demo.md
CHANGED
@@ -39,7 +39,7 @@ class Object
|
|
39
39
|
end
|
40
40
|
```
|
41
41
|
|
42
|
-
|
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
|
|
@@ -190,7 +190,7 @@ p [:a, :b, :c] #=> [:a, :b, :c]
|
|
190
190
|
# A Hash is a "type-to-type" map
|
191
191
|
h = { "int" => 1, "float" => 1.0 }
|
192
192
|
p h #=> {String=>Float | Integer}
|
193
|
-
p h["int"] #=> Float |
|
193
|
+
p h["int"] #=> Float | Integer
|
194
194
|
|
195
195
|
# Symbol-key hashes (a.k.a. records) can have distinct types for each key as Symbols are concrete
|
196
196
|
h = { int: 1, float: 1.0 }
|
data/doc/todo.md
ADDED
@@ -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.
|
data/lib/typeprof/analyzer.rb
CHANGED
@@ -42,6 +42,10 @@ module TypeProf
|
|
42
42
|
"<builtin>"
|
43
43
|
end
|
44
44
|
end
|
45
|
+
|
46
|
+
def replace_cref(cref)
|
47
|
+
Context.new(@iseq, cref, @mid)
|
48
|
+
end
|
45
49
|
end
|
46
50
|
|
47
51
|
class TypedContext
|
@@ -61,6 +65,10 @@ module TypeProf
|
|
61
65
|
"<typed-context:#{ @mid }>"
|
62
66
|
end
|
63
67
|
end
|
68
|
+
|
69
|
+
def replace_cref(cref)
|
70
|
+
# What to do?
|
71
|
+
end
|
64
72
|
end
|
65
73
|
|
66
74
|
class ExecutionPoint
|
@@ -86,6 +94,10 @@ module TypeProf
|
|
86
94
|
ExecutionPoint.new(@ctx, @pc + 1, @outer)
|
87
95
|
end
|
88
96
|
|
97
|
+
def replace_cref(cref)
|
98
|
+
ExecutionPoint.new(@ctx.replace_cref(cref), @pc, @outer)
|
99
|
+
end
|
100
|
+
|
89
101
|
def source_location
|
90
102
|
@ctx.source_location(@pc)
|
91
103
|
end
|
@@ -238,6 +250,8 @@ module TypeProf
|
|
238
250
|
end
|
239
251
|
|
240
252
|
def initialize
|
253
|
+
@entrypoints = []
|
254
|
+
|
241
255
|
@worklist = Utils::WorkList.new
|
242
256
|
|
243
257
|
@ep2env = {}
|
@@ -272,6 +286,10 @@ module TypeProf
|
|
272
286
|
@anonymous_struct_gen_id = 0
|
273
287
|
end
|
274
288
|
|
289
|
+
def add_entrypoint(iseq)
|
290
|
+
@entrypoints << iseq
|
291
|
+
end
|
292
|
+
|
275
293
|
attr_reader :return_envs, :loaded_features, :rbs_reader
|
276
294
|
|
277
295
|
def get_env(ep)
|
@@ -878,56 +896,68 @@ module TypeProf
|
|
878
896
|
iter_counter = 0
|
879
897
|
stat_eps = Utils::MutableSet.new
|
880
898
|
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
899
|
+
prologue_ctx = Context.new(nil, nil, nil)
|
900
|
+
prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
|
901
|
+
prologue_env = Env.new(StaticEnv.new(Type.bot, Type.nil, false, true), [], [], Utils::HashWrapper.new({}))
|
902
|
+
|
903
|
+
until @entrypoints.empty?
|
904
|
+
iseq = @entrypoints.shift
|
905
|
+
ep, env = TypeProf.starting_state(iseq)
|
906
|
+
merge_env(ep, env)
|
907
|
+
add_callsite!(ep.ctx, prologue_ep, prologue_env) {|ty, ep| }
|
908
|
+
|
909
|
+
while true
|
910
|
+
until @worklist.empty?
|
911
|
+
ep = @worklist.deletemin
|
912
|
+
|
913
|
+
iter_counter += 1
|
914
|
+
if Config.options[:show_indicator]
|
915
|
+
tick2 = Time.now
|
916
|
+
if tick2 - tick >= 1
|
917
|
+
tick = tick2
|
918
|
+
$stderr << "\rType Profiling... (%d instructions @ %s)\e[K" % [iter_counter, ep.source_location]
|
919
|
+
$stderr.flush
|
920
|
+
end
|
892
921
|
end
|
893
|
-
end
|
894
922
|
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
923
|
+
if (Config.max_sec && Time.now - start_time >= Config.max_sec) || (Config.max_iter && Config.max_iter <= iter_counter)
|
924
|
+
@terminated = true
|
925
|
+
break
|
926
|
+
end
|
899
927
|
|
900
|
-
|
901
|
-
|
902
|
-
|
928
|
+
stat_eps << ep
|
929
|
+
step(ep)
|
930
|
+
end
|
903
931
|
|
904
|
-
|
932
|
+
break if @terminated
|
905
933
|
|
906
|
-
|
934
|
+
break unless Config.options[:stub_execution]
|
907
935
|
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
936
|
+
begin
|
937
|
+
iseq, (kind, dummy_continuation) = @pending_execution.first
|
938
|
+
break if !iseq
|
939
|
+
@pending_execution.delete(iseq)
|
940
|
+
end while @executed_iseqs.include?(iseq)
|
913
941
|
|
914
|
-
|
942
|
+
puts "DEBUG: trigger stub execution (#{ iseq&.name || "(nil)" }): rest #{ @pending_execution.size }" if Config.verbose >= 2
|
915
943
|
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
merge_env(ep, env)
|
921
|
-
add_iseq_method_call!(meth, ep.ctx)
|
922
|
-
|
923
|
-
when :block
|
924
|
-
blk, epenvs = dummy_continuation
|
925
|
-
epenvs.each do |ep, env|
|
944
|
+
break if !iseq
|
945
|
+
case kind
|
946
|
+
when :method
|
947
|
+
meth, ep, env = dummy_continuation
|
926
948
|
merge_env(ep, env)
|
927
|
-
|
949
|
+
add_iseq_method_call!(meth, ep.ctx)
|
950
|
+
|
951
|
+
when :block
|
952
|
+
blk, epenvs = dummy_continuation
|
953
|
+
epenvs.each do |ep, env|
|
954
|
+
merge_env(ep, env)
|
955
|
+
add_block_to_ctx!(blk.block_body, ep.ctx)
|
956
|
+
end
|
928
957
|
end
|
929
958
|
end
|
930
959
|
end
|
960
|
+
|
931
961
|
$stderr.print "\r\e[K" if Config.options[:show_indicator]
|
932
962
|
|
933
963
|
stat_eps
|
@@ -1245,7 +1275,13 @@ module TypeProf
|
|
1245
1275
|
end
|
1246
1276
|
if cbase.is_a?(Type::Class)
|
1247
1277
|
klass = new_class(cbase, id, [], superclass, ep.ctx.iseq.absolute_path)
|
1248
|
-
|
1278
|
+
if superclass
|
1279
|
+
add_superclass_type_args!(klass, superclass.type_params.map { Type.any })
|
1280
|
+
|
1281
|
+
# inherited hook
|
1282
|
+
aargs = ActualArguments.new([klass], nil, {}, Type.nil)
|
1283
|
+
do_send(superclass, :inherited, aargs, ep, env) {|_ret_ty, _ep| }
|
1284
|
+
end
|
1249
1285
|
else
|
1250
1286
|
klass = Type.any
|
1251
1287
|
end
|
@@ -1288,7 +1324,7 @@ module TypeProf
|
|
1288
1324
|
end
|
1289
1325
|
end
|
1290
1326
|
return
|
1291
|
-
when :
|
1327
|
+
when :recv_getlocal_send_branch
|
1292
1328
|
getlocal_operands, send_operands, branch_operands = operands
|
1293
1329
|
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
1294
1330
|
recvs = Type.any if recvs == Type.bot
|
@@ -1316,6 +1352,39 @@ module TypeProf
|
|
1316
1352
|
end
|
1317
1353
|
end
|
1318
1354
|
return
|
1355
|
+
when :arg_getlocal_send_branch
|
1356
|
+
getlocal_operands, send_operands, branch_operands = operands
|
1357
|
+
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
1358
|
+
raise if aargs.lead_tys.size != 1
|
1359
|
+
aarg = aargs.lead_tys[0]
|
1360
|
+
aarg = Type.any if aarg == Type.bot
|
1361
|
+
recvs.each_child do |recv|
|
1362
|
+
aarg.each_child do |aarg|
|
1363
|
+
aargs_tmp = ActualArguments.new([aarg], nil, {}, aargs.blk_ty)
|
1364
|
+
do_send(recv, mid, aargs_tmp, ep, env) do |ret_ty, ep, env|
|
1365
|
+
env, ret_ty, = localize_type(ret_ty, env, ep)
|
1366
|
+
|
1367
|
+
branchtype, target, = branch_operands
|
1368
|
+
# branchtype: :if or :unless or :nil
|
1369
|
+
ep_then = ep.next
|
1370
|
+
ep_else = ep.jump(target)
|
1371
|
+
|
1372
|
+
var_idx, _scope_idx, _escaped = getlocal_operands
|
1373
|
+
flow_env = env.local_update(-var_idx+2, aarg)
|
1374
|
+
|
1375
|
+
case ret_ty
|
1376
|
+
when Type::Instance.new(Type::Builtin[:true])
|
1377
|
+
merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
|
1378
|
+
when Type::Instance.new(Type::Builtin[:false])
|
1379
|
+
merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
|
1380
|
+
else
|
1381
|
+
merge_env(ep_then, env)
|
1382
|
+
merge_env(ep_else, env)
|
1383
|
+
end
|
1384
|
+
end
|
1385
|
+
end
|
1386
|
+
end
|
1387
|
+
return
|
1319
1388
|
when :send_branch
|
1320
1389
|
send_operands, branch_operands = operands
|
1321
1390
|
env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
|
@@ -1419,7 +1488,7 @@ module TypeProf
|
|
1419
1488
|
end
|
1420
1489
|
_type, _iseq, cont, stack_depth = tmp_ep.ctx.iseq.catch_table[tmp_ep.pc]&.find {|type,| type == :break }
|
1421
1490
|
if cont
|
1422
|
-
nenv = @return_envs[tmp_ep]
|
1491
|
+
nenv = @return_envs[tmp_ep] || env
|
1423
1492
|
nenv, = nenv.pop(nenv.stack.size - stack_depth)
|
1424
1493
|
nenv = nenv.push(ty)
|
1425
1494
|
tmp_ep = tmp_ep.jump(cont)
|
@@ -1778,8 +1847,10 @@ module TypeProf
|
|
1778
1847
|
when :nop
|
1779
1848
|
when :setn
|
1780
1849
|
idx, = operands
|
1781
|
-
|
1782
|
-
|
1850
|
+
if idx >= 1
|
1851
|
+
env, (ty,) = env.pop(1)
|
1852
|
+
env = env.setn(idx, ty).push(ty)
|
1853
|
+
end
|
1783
1854
|
when :topn
|
1784
1855
|
idx, = operands
|
1785
1856
|
env = env.topn(idx)
|
@@ -2091,10 +2162,10 @@ module TypeProf
|
|
2091
2162
|
end
|
2092
2163
|
end
|
2093
2164
|
|
2094
|
-
def do_invoke_block(blk, aargs, ep, env, replace_recv_ty: nil, &ctn)
|
2165
|
+
def do_invoke_block(blk, aargs, ep, env, replace_recv_ty: nil, replace_cref: nil, &ctn)
|
2095
2166
|
blk.each_child do |blk|
|
2096
2167
|
if blk.is_a?(Type::Proc)
|
2097
|
-
blk.block_body.do_call(aargs, ep, env, self, replace_recv_ty: replace_recv_ty, &ctn)
|
2168
|
+
blk.block_body.do_call(aargs, ep, env, self, replace_recv_ty: replace_recv_ty, replace_cref: replace_cref, &ctn)
|
2098
2169
|
else
|
2099
2170
|
warn(ep, "non-proc is passed as a block")
|
2100
2171
|
ctn[Type.any, ep, env]
|
@@ -2192,10 +2263,13 @@ module TypeProf
|
|
2192
2263
|
farg_tys = @method_signatures[ctx]
|
2193
2264
|
ret_ty = @return_values[ctx] || Type.bot
|
2194
2265
|
|
2266
|
+
untyped = farg_tys.include_untyped?(self) || ret_ty.include_untyped?(self)
|
2267
|
+
|
2195
2268
|
farg_tys = farg_tys.screen_name(ctx.iseq, self)
|
2196
2269
|
ret_ty = ret_ty.screen_name(self)
|
2197
2270
|
ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
|
2198
|
-
|
2271
|
+
|
2272
|
+
["#{ (farg_tys.empty? ? "" : "#{ farg_tys } ") }-> #{ ret_ty }", untyped]
|
2199
2273
|
end
|
2200
2274
|
end
|
2201
2275
|
end
|