problem sheet • Complete "Hello world" by adding minimum letters and get a prize! def foo "Hello world" if false end puts foo def foo "Hello world" if !false end puts foo !
Ruby 3 applicable to a non-annotated Ruby code • Level-1 type checking •Type signature prototyping •Note: The analysis is not "sound" • It may miss bugs and print wrong signature
type signature/annotation required •Objectives: Testing and understanding • Testing: Warns possible errors of Ruby code •Understanding: Prototypes type signature
foo(n) if n < 10 n else "error" end end foo(42) Fork! Now here; We cannot tell taken true or false as we just know n is Integer Returns Integer Returns String Object#foo :: (Integer) -> (Integer | String)
intra-procedural def foo(n:int):str n.to_s end ret = foo(42) Assume Integer Check if String Check if Integer Assume String Type profiler is inter-procedural def foo(n) n.to_s end foo(42) Calls w/ Integer Returns String
but hard to scale Type checking Abstract interpretation Symbolic execution Type profiler...? Flow analysis Steep Sorbet RDL Excuse: This figure is just my personal opinion
be fair, Type Profiler is never perfect •Can tell lies (false positive) • Requires tests (false negative) •Cannot handle some Ruby features • May be very slow (state explosion)
else x = "str" end if n < 10 x += 1 end String + Integer? Error! This path is not feasible because of the conditions Possible workaround: Please don't write such a untypeable code!
values as a type (class) • But a singleton class is unique to the value •Workaround: Difficult... (TP plugin?) send(method_name) Type Profiler Type Profiler cannot identify the method being called!
10 b = 42 if n < 10 c = 42 if n < 10 d = 42 if n < 10 e = 42 if n < 10 Fork! Fork! Fork! Fork! Fork! 2 4 8 16 32 The number of states Possible workaround: • Write an annotation...? → a::NilClass|Integer • Merge a pair of similar states (not implemented yet)
• Only one choice for no-type Ruby lovers •You can use a type checker if you want a type • You may use Type Profiler to get a prototype of type signatures •I'm still thinking of the improvement • Stay tuned...
bytecode • A variable has a type instead of a value •A branch copies the state for each target •Profiling features • Reports all type errors during execution • Records all method arguments and return values (for type signature prototype)
of the entry program • Until fixed-point •The same states are merged • To avoid redundant execution • TODO: merge "similar" states if n < 10 a=1+1 else a=2+2 end ... The two states are the same
enters its method body 2. Type-defined method • TP just checks argument types and returns its return type • For built-in methods or libraries 3. Custom special method • TP executes custom behavior • (TP plugin can exploit this?) def foo(n) ... end Integer#+ :: (Integer)->Integer Object#require???
features • Limited built-in classes (shown in Demos) •No-support-yet • Almost all built-in classes including Hash • Complex arguments (optional, rest, keywords) • Exceptions • Modules (Mix-in) • etc etc.
2167 LOC, 221 methods •Quantitative result: •Reached 91 methods in 10 minutes (!) •Qualitative result: Found an actual bug •TP said "a method receives NilClass" • It is not intended, and turned out a bug
example, Array#<< is not implemented •Some methods are defined but unused •Idea: virtually call all methods with "any" arguments? a = [] a << Foo.new a[0].bar Foo#bar cannot be reached
hardware emulator written in Ruby • statistics: 4476 LOC, 394 methods •Result: • Reached 40 methods in 3 minutes? • Object#send and Fiber are not implemented yet • CPU uses Object#send for instruction dispatch • GPU uses Fiber for state machine
returns Array or Integer •Calling it forks the state •Idea: Merge similar states more intelligently foo() foo() foo() foo() foo() Fork! Fork! Fork! Fork! Fork! foo :: () -> (Array[Integer] | Integer)
for mruby JIT • This inspired Type Profiler (Thanks!) •HPC Ruby (Koichi Nakamura) • Convert Ruby to C for HPC by abstract interp. •pytype (Google's unofficial project) • Python abstract interpreter for type analysis • More concrete than TP • Limits the stack depth to three
to a non-annotated Ruby code • Based on abstract interpretation technique • Little change for Ruby programming experience •Contribution is really welcome! •The development is very preliminary • https://github.com/mame/ruby-type-profiler
Almost all built-in classes including Hash • Complex arguments (optional, rest, keywords) • Exceptions • Modules (Mix-in) • etc etc. • Write a type definition for built-in classes/methods • Read type definition file • Improve performance • Make it useful • Code coverage • Flow-sensitive analysis • Heuristic type aggregation • Diagnosis feature • Incremental type profiling • etc etc.