ikra 0.0.1 → 0.0.2

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ast/builder.rb +225 -77
  3. data/lib/ast/host_section_builder.rb +38 -0
  4. data/lib/ast/interpreter.rb +67 -0
  5. data/lib/ast/lexical_variables_enumerator.rb +3 -2
  6. data/lib/ast/nodes.rb +521 -31
  7. data/lib/ast/printer.rb +116 -18
  8. data/lib/ast/ssa_generator.rb +192 -0
  9. data/lib/ast/visitor.rb +235 -21
  10. data/lib/config/configuration.rb +28 -3
  11. data/lib/config/os_configuration.rb +62 -9
  12. data/lib/cpu/cpu_implementation.rb +39 -0
  13. data/lib/ikra.rb +13 -3
  14. data/lib/resources/cuda/allocate_device_memory.cpp +5 -0
  15. data/lib/resources/cuda/allocate_host_memory.cpp +1 -0
  16. data/lib/resources/cuda/allocate_memcpy_environment_to_device.cpp +11 -0
  17. data/lib/resources/cuda/ast/assignment.cpp +1 -0
  18. data/lib/resources/cuda/block_function_head.cpp +7 -1
  19. data/lib/resources/cuda/entry_point.cpp +47 -0
  20. data/lib/resources/cuda/env_builder_copy_array.cpp +8 -2
  21. data/lib/resources/cuda/free_device_memory.cpp +3 -0
  22. data/lib/resources/cuda/free_memory_for_command.cpp +24 -0
  23. data/lib/resources/cuda/header.cpp +23 -9
  24. data/lib/resources/cuda/header_structs.cpp +92 -0
  25. data/lib/resources/cuda/host_section_block_function_head.cpp +12 -0
  26. data/lib/resources/cuda/host_section_entry_point.cpp +55 -0
  27. data/lib/resources/cuda/host_section_free_device_memory.cpp +18 -0
  28. data/lib/resources/cuda/host_section_launch_parallel_section.cpp +14 -0
  29. data/lib/resources/cuda/host_section_malloc_memcpy_device_to_host.cpp +10 -0
  30. data/lib/resources/cuda/kernel.cpp +9 -2
  31. data/lib/resources/cuda/launch_kernel.cpp +5 -0
  32. data/lib/resources/cuda/memcpy_device_to_host.cpp +3 -0
  33. data/lib/resources/cuda/memcpy_device_to_host_expr.cpp +10 -0
  34. data/lib/resources/cuda/reduce_body.cpp +88 -0
  35. data/lib/resources/cuda/stencil_array_reconstruction.cpp +2 -0
  36. data/lib/resources/cuda/stencil_body.cpp +16 -0
  37. data/lib/resources/cuda/struct_definition.cpp +4 -0
  38. data/lib/ruby_core/array.rb +34 -0
  39. data/lib/ruby_core/array_command.rb +313 -0
  40. data/lib/ruby_core/core.rb +103 -0
  41. data/lib/ruby_core/interpreter.rb +16 -0
  42. data/lib/ruby_core/math.rb +32 -0
  43. data/lib/ruby_core/ruby_integration.rb +256 -0
  44. data/lib/symbolic/host_section.rb +115 -0
  45. data/lib/symbolic/input.rb +87 -0
  46. data/lib/symbolic/input_visitor.rb +68 -0
  47. data/lib/symbolic/symbolic.rb +793 -117
  48. data/lib/symbolic/visitor.rb +70 -8
  49. data/lib/translator/array_command_struct_builder.rb +163 -0
  50. data/lib/translator/ast_translator.rb +572 -0
  51. data/lib/translator/block_translator.rb +104 -48
  52. data/lib/translator/commands/array_combine_command.rb +41 -0
  53. data/lib/translator/commands/array_identity_command.rb +28 -0
  54. data/lib/translator/commands/array_index_command.rb +52 -0
  55. data/lib/translator/commands/array_reduce_command.rb +135 -0
  56. data/lib/translator/commands/array_stencil_command.rb +129 -0
  57. data/lib/translator/commands/array_zip_command.rb +30 -0
  58. data/lib/translator/commands/command_translator.rb +264 -0
  59. data/lib/translator/cuda_errors.rb +32 -0
  60. data/lib/translator/environment_builder.rb +263 -0
  61. data/lib/translator/host_section/array_host_section_command.rb +150 -0
  62. data/lib/translator/host_section/array_in_host_section_command.rb +41 -0
  63. data/lib/translator/host_section/ast_translator.rb +14 -0
  64. data/lib/translator/host_section/parallel_section_invocation_visitor.rb +20 -0
  65. data/lib/translator/host_section/program_builder.rb +89 -0
  66. data/lib/translator/input_translator.rb +226 -0
  67. data/lib/translator/kernel_builder.rb +137 -0
  68. data/lib/translator/kernel_launcher/for_loop_kernel_launcher.rb +40 -0
  69. data/lib/translator/kernel_launcher/kernel_launcher.rb +259 -0
  70. data/lib/translator/kernel_launcher/while_loop_kernel_launcher.rb +38 -0
  71. data/lib/translator/last_returns_visitor.rb +19 -10
  72. data/lib/translator/program_builder.rb +197 -0
  73. data/lib/translator/program_launcher.rb +273 -0
  74. data/lib/translator/struct_type.rb +55 -0
  75. data/lib/translator/translator.rb +34 -11
  76. data/lib/translator/variable_classifier_visitor.rb +56 -0
  77. data/lib/types/inference/ast_inference.rb +586 -0
  78. data/lib/types/inference/clear_types_visitor.rb +11 -0
  79. data/lib/types/inference/command_inference.rb +101 -0
  80. data/lib/types/inference/input_inference.rb +62 -0
  81. data/lib/types/{object_tracer.rb → inference/object_tracer.rb} +5 -6
  82. data/lib/types/inference/ruby_extension.rb +35 -0
  83. data/lib/types/inference/symbol_table.rb +131 -0
  84. data/lib/types/types.rb +14 -0
  85. data/lib/types/types/array_command_type.rb +123 -0
  86. data/lib/types/types/array_type.rb +137 -0
  87. data/lib/types/{class_type.rb → types/class_type.rb} +42 -18
  88. data/lib/types/{primitive_type.rb → types/primitive_type.rb} +20 -7
  89. data/lib/types/types/ruby_type.rb +88 -0
  90. data/lib/types/types/struct_type.rb +179 -0
  91. data/lib/types/types/union_type.rb +239 -0
  92. metadata +160 -18
  93. data/lib/ast/method_definition.rb +0 -37
  94. data/lib/ast/translator.rb +0 -264
  95. data/lib/resources/cuda/kernel_launcher.cpp +0 -28
  96. data/lib/scope.rb +0 -166
  97. data/lib/translator/command_translator.rb +0 -421
  98. data/lib/translator/local_variables_enumerator.rb +0 -35
  99. data/lib/translator/method_translator.rb +0 -24
  100. data/lib/types/array_type.rb +0 -51
  101. data/lib/types/ruby_extension.rb +0 -67
  102. data/lib/types/ruby_type.rb +0 -45
  103. data/lib/types/type_inference.rb +0 -382
  104. data/lib/types/union_type.rb +0 -155
@@ -0,0 +1,586 @@
1
+ # No explicit `require`s. This file should be includes via types.rb
2
+
3
+ require "set"
4
+
5
+ require_relative "../../ast/nodes.rb"
6
+ require_relative "../../ast/visitor.rb"
7
+ require_relative "clear_types_visitor.rb"
8
+
9
+ module Ikra
10
+ module AST
11
+ class TreeNode
12
+ def get_type
13
+ @type ||= Types::UnionType.new
14
+ return @type.dup
15
+ end
16
+
17
+ def merge_union_type(union_type)
18
+ @type ||= Types::UnionType.new
19
+
20
+ if not @type.include_all?(union_type)
21
+ register_type_change
22
+ end
23
+
24
+ return @type.expand_return_type(union_type).dup
25
+ end
26
+
27
+ def symbol_table
28
+ return parent.symbol_table
29
+ end
30
+
31
+ def register_type_change
32
+ if parent != nil
33
+ parent.register_type_change
34
+ else
35
+ # This node is not part of a full AST, i.e., it does not have a [BehaviorNode]
36
+ # as a parent. Do nothing.
37
+ end
38
+ end
39
+ end
40
+
41
+ class ClassDefNode
42
+ def get_type
43
+ return ruby_class.to_ikra_type
44
+ end
45
+ end
46
+
47
+ class LVarWriteNode
48
+ attr_accessor :variable_type
49
+ end
50
+
51
+ class MethDefNode
52
+ attr_accessor :receiver_type
53
+
54
+ def initialize_types(receiver_type:, return_type: Types::UnionType.new)
55
+ @receiver_type = receiver_type
56
+ @type = return_type
57
+ end
58
+
59
+ def self.new_with_types(
60
+ name:,
61
+ body:,
62
+ parameters_names_and_types:,
63
+ ruby_method:,
64
+ receiver_type:,
65
+ return_type: Types::UnionType.new,
66
+ method_binding: nil)
67
+
68
+ instance = new(name: name, body: body, ruby_method: ruby_method, method_binding: method_binding)
69
+ instance.initialize_types(receiver_type: receiver_type, return_type: return_type)
70
+ instance.parameters_names_and_types = parameters_names_and_types
71
+ return instance
72
+ end
73
+
74
+ def callers
75
+ @callers ||= []
76
+ end
77
+ end
78
+
79
+ class BehaviorNode
80
+ def get_type
81
+ @type ||= Types::UnionType.new
82
+ end
83
+
84
+ def merge_union_type(union_type)
85
+ type = @type ||= Types::UnionType.new
86
+
87
+ if not @type.include_all?(union_type)
88
+ register_type_change
89
+ end
90
+
91
+ return type.expand_return_type(union_type).dup
92
+ end
93
+
94
+ # Mapping: parameter name -> UnionType
95
+ def parameters_names_and_types
96
+ @parameters_names_and_types ||= {}
97
+ end
98
+
99
+ def parameters_names_and_types=(value)
100
+ @parameters_names_and_types = value
101
+ end
102
+
103
+ # Mapping: lexical variable name -> UnionType
104
+ def lexical_variables_names_and_types
105
+ @lexical_variables_names_and_types ||= {}
106
+ end
107
+
108
+ # Mapping: local variable name -> UnionType
109
+ def local_variables_names_and_types
110
+ @local_variables_names_and_types ||= {}
111
+ end
112
+
113
+ def local_variables_names_and_types=(value)
114
+ @local_variables_names_and_types = value
115
+ end
116
+
117
+ def symbol_table
118
+ @symbol_table ||= TypeInference::SymbolTable.new
119
+ end
120
+
121
+ def types_changed?
122
+ return @types_changed ||= false
123
+ end
124
+
125
+ def reset_types_changed
126
+ @types_changed = false
127
+ end
128
+
129
+ def register_type_change
130
+ @types_changed = true
131
+ end
132
+ end
133
+
134
+ class SendNode
135
+ attr_writer :return_type_by_recv_type
136
+
137
+ # Mapping: Receiver type --> Return value of send
138
+ def return_type_by_recv_type
139
+ @return_type_by_recv_type ||= {}
140
+ end
141
+ end
142
+ end
143
+
144
+ module TypeInference
145
+ class Visitor < AST::Visitor
146
+
147
+ # If this error is thrown, type inference should start from the beginning (with
148
+ # an empty symbol table).
149
+ class RestartTypeInferenceError < RuntimeError
150
+
151
+ end
152
+
153
+ attr_reader :classes
154
+
155
+ def initialize
156
+ # Ikra type -> ClassDefNode
157
+ @classes = {}
158
+ @classes.default_proc = proc do |hash, key|
159
+ hash[key] = AST::ClassDefNode.new(
160
+ name: key.cls.name,
161
+ ruby_class: key.cls)
162
+ end
163
+
164
+ # Top of stack is the method that is currently processed
165
+ @work_stack = []
166
+
167
+ # Block/method definitions that must be processed
168
+ @worklist = Set.new
169
+ end
170
+
171
+ def all_methods
172
+ return classes.values.map do |class_|
173
+ class_.instance_methods
174
+ end.flatten
175
+ end
176
+
177
+ def symbol_table
178
+ current_method_or_block.symbol_table
179
+ end
180
+
181
+ def binding
182
+ current_method_or_block.binding
183
+ end
184
+
185
+ def current_method_or_block
186
+ @work_stack.last
187
+ end
188
+
189
+ # This is used as an entry point for the visitor
190
+ def process_block(block_def_node)
191
+ Log.info("Type inference: proceed into block(#{Types::UnionType.parameter_hash_to_s(block_def_node.parameters_names_and_types)})")
192
+
193
+ @work_stack.push(block_def_node)
194
+ body_ast = block_def_node.body
195
+
196
+ # Variables that are not defined inside the block
197
+ predefined_variables = []
198
+
199
+ # Add parameters to symbol table (name -> type)
200
+ block_def_node.parameters_names_and_types.each do |name, type|
201
+ symbol_table.declare_variable(name, type: type)
202
+ predefined_variables.push(name)
203
+ end
204
+
205
+ # Add lexical variables to symbol table (name -> type)
206
+ block_def_node.lexical_variables_names_and_types.each do |name, type|
207
+ # Variable might be shadowed by parameter
208
+ symbol_table.ensure_variable_declared(name, type: type, kind: :lexical)
209
+ predefined_variables.push(name)
210
+ end
211
+
212
+ begin
213
+ # Infer types
214
+ body_ast.accept(self)
215
+ rescue RestartTypeInferenceError
216
+ # Reset all type information
217
+ symbol_table.clear!
218
+ block_def_node.accept(ClearTypesVisitor.new)
219
+
220
+ # Remove block from stack
221
+ @work_stack.pop
222
+
223
+ Log.info("Changed AST during type inference. Restarting type inference.")
224
+
225
+ # Restart inference
226
+ return process_block(block_def_node)
227
+ end
228
+
229
+ # Get local variable definitons
230
+ for variable_name in symbol_table.read_and_written_variables
231
+ if !predefined_variables.include?(variable_name)
232
+ block_def_node.local_variables_names_and_types[variable_name] =
233
+ symbol_table[variable_name]
234
+ end
235
+ end
236
+
237
+ return_value_type = symbol_table.return_type
238
+ Log.info("Type inference: block return type is #{return_value_type.to_s}")
239
+
240
+ @work_stack.pop
241
+
242
+ return_type = block_def_node.merge_union_type(return_value_type)
243
+
244
+ if block_def_node.types_changed?
245
+ # Types changed, do another pass. This is not efficient and there are better
246
+ # ways to do type inference (e.g., constraint solving), but it works for now.
247
+ block_def_node.reset_types_changed
248
+ return process_block(block_def_node)
249
+ else
250
+ return return_type
251
+ end
252
+ end
253
+
254
+ # This is used as an entry point for the visitor
255
+ def process_method(method_def_node)
256
+ Log.info("Type inference: proceed into method #{method_def_node.receiver_type}.#{method_def_node.name}(#{Types::UnionType.parameter_hash_to_s(method_def_node.parameters_names_and_types)})")
257
+
258
+ @work_stack.push(method_def_node)
259
+ body_ast = method_def_node.body
260
+
261
+ # TODO: handle multiple receiver types
262
+ recv_type = method_def_node.receiver_type
263
+
264
+ # Variables that are not defined inside the method
265
+ predefined_variables = []
266
+
267
+ # Add parameters to symbol table (name -> type)
268
+ method_def_node.parameters_names_and_types.each do |name, type|
269
+ symbol_table.declare_variable(name, type: type)
270
+ predefined_variables.push(name)
271
+ end
272
+
273
+ # Add lexical variables to symbol table (name -> type)
274
+ method_def_node.lexical_variables_names_and_types.each do |name, type|
275
+ # Variable might be shadowed by parameter
276
+ symbol_table.ensure_variable_declared(name, type: type, kind: :lexical)
277
+ predefined_variables.push(name)
278
+ end
279
+
280
+ # Add return statements
281
+ body_ast.accept(Translator::LastStatementReturnsVisitor.new)
282
+
283
+ # Infer types
284
+ body_ast.accept(self)
285
+
286
+ # Get local variable definitons
287
+ for variable_name in symbol_table.read_and_written_variables
288
+ if !predefined_variables.include?(variable_name)
289
+ method_def_node.local_variables_names_and_types[variable_name] =
290
+ symbol_table[variable_name]
291
+ end
292
+ end
293
+
294
+ return_value_type = symbol_table.return_type
295
+ Log.info("Type inference: method return type is #{return_value_type.to_s}")
296
+
297
+ @work_stack.pop
298
+
299
+ return_type = method_def_node.merge_union_type(return_value_type)
300
+
301
+ if method_def_node.types_changed?
302
+ # Types changed, do another pass. This is not efficient and there are better
303
+ # ways to do type inference (e.g., constraint solving), but it works for now.
304
+ method_def_node.reset_types_changed
305
+ return process_method(method_def_node)
306
+ else
307
+ return return_type
308
+ end
309
+ end
310
+
311
+ # This is not an actual Visitor method. It is called from visit_send_node.
312
+ def visit_method_call(send_node, recv_singleton_type)
313
+ selector = send_node.selector
314
+
315
+ if recv_singleton_type.is_primitive?
316
+ raise NotImplementedError.new("#{recv_singleton_type}.#{selector} not implemented (#{send_node.to_s})")
317
+ end
318
+
319
+ parameter_names = recv_singleton_type.method_parameters(selector)
320
+ arg_types = send_node.arguments.map do |arg| arg.get_type end
321
+ ast = recv_singleton_type.method_ast(selector)
322
+ method_visited_before = nil
323
+
324
+ if not @classes[recv_singleton_type].has_instance_method?(selector)
325
+ # This method was never visited before
326
+ method_def_node = AST::MethDefNode.new_with_types(
327
+ name: selector,
328
+ body: ast,
329
+ parameters_names_and_types: Hash[*parameter_names.zip(
330
+ Array.new(arg_types.size) do |arg_index|
331
+ Types::UnionType.new
332
+ end).flatten],
333
+ ruby_method: nil,
334
+ receiver_type: recv_singleton_type,
335
+ method_binding: recv_singleton_type.method_binding(selector))
336
+ @classes[recv_singleton_type].add_instance_method(method_def_node)
337
+ method_visited_before = false
338
+ else
339
+ method_visited_before = true
340
+ end
341
+
342
+ method_def_node = @classes[recv_singleton_type].instance_method(selector)
343
+
344
+ parameter_types_expanded = parameter_names.map.with_index do |name, index|
345
+ # returns true if expanded
346
+ method_def_node.parameters_names_and_types[name].expand(arg_types[index])
347
+ end.reduce(:|)
348
+
349
+ # Method needs processing if any parameter is expanded (or method was never visited before)
350
+ needs_processing = !method_visited_before or parameter_types_expanded
351
+
352
+ # Return value type from the last pass
353
+ # TODO: Have to make a copy here?
354
+ last_return_type = method_def_node.get_type
355
+
356
+ if needs_processing
357
+ process_method(method_def_node)
358
+ end
359
+
360
+ if not last_return_type.include_all?(method_def_node.get_type)
361
+ # Return type was expanded during this pass, reprocess all callers (except for current method)
362
+ @worklist += (method_def_node.callers - [current_method_or_block])
363
+ end
364
+
365
+ method_def_node.callers.push(current_method_or_block)
366
+
367
+ return method_def_node.get_type
368
+ end
369
+
370
+ def assert_singleton_type(union_type, expected_type)
371
+ if union_type.singleton_type != expected_type
372
+ raise AssertionError.new(
373
+ "Expected type #{expected_type} but found #{union_type.singleton_type}")
374
+ end
375
+ end
376
+
377
+ def visit_const_node(node)
378
+ if not binding
379
+ raise AssertionError.new("Unable to resolve constants without Binding")
380
+ end
381
+
382
+ constant = binding.eval(node.identifier.to_s)
383
+ node.merge_union_type(constant.ikra_type.to_union_type)
384
+ end
385
+
386
+ def visit_root_node(node)
387
+ node.merge_union_type(node.single_child.accept(self))
388
+ end
389
+
390
+ def visit_lvar_read_node(node)
391
+ symbol_table.read!(node.identifier)
392
+
393
+ # Extend type of variable
394
+ return node.merge_union_type(symbol_table[node.identifier])
395
+ end
396
+
397
+ def visit_lvar_write_node(node)
398
+ type = node.value.accept(self)
399
+
400
+ # Declare/extend type in symbol table
401
+ symbol_table.ensure_variable_declared(node.identifier, type: type)
402
+ symbol_table.written!(node.identifier)
403
+
404
+ node.variable_type = symbol_table[node.identifier]
405
+
406
+ # Extend type of variable
407
+ # Note: Return value of this expression != type of the variable
408
+ node.merge_union_type(type)
409
+ end
410
+
411
+ def visit_ivar_read_node(node)
412
+ cls_type = node.enclosing_class.ruby_class.to_ikra_type
413
+ cls_type.inst_var_read!(node.identifier)
414
+ cls_type.inst_vars_types[node.identifier]
415
+ end
416
+
417
+ def visit_int_node(node)
418
+ node.merge_union_type(Types::UnionType.create_int)
419
+ end
420
+
421
+ def visit_nil_node(node)
422
+ node.merge_union_type(Types::UnionType.create_nil)
423
+ end
424
+
425
+ def visit_float_node(node)
426
+ node.merge_union_type(Types::UnionType.create_float)
427
+ end
428
+
429
+ def visit_bool_node(node)
430
+ node.merge_union_type(Types::UnionType.create_bool)
431
+ end
432
+
433
+ def visit_string_node(node)
434
+ # Use [Types::ClassType] for the moment
435
+ return node.value.ikra_type.to_union_type
436
+ end
437
+
438
+ def visit_symbol_node(node)
439
+ # Use [Types::ClassType] for the moment
440
+ return node.value.ikra_type.to_union_type
441
+ end
442
+
443
+ def visit_hash_node(node)
444
+ # Use [Types::ClassType] for the moment
445
+ return Hash.to_ikra_type.to_union_type
446
+ end
447
+
448
+ def visit_for_node(node)
449
+ assert_singleton_type(node.range_from.accept(self), Types::PrimitiveType::Int)
450
+ assert_singleton_type(node.range_to.accept(self), Types::PrimitiveType::Int)
451
+
452
+ changed = symbol_table.ensure_variable_declared(node.iterator_identifier, type: Types::UnionType.create_int)
453
+ symbol_table.written!(node.iterator_identifier)
454
+
455
+ super(node)
456
+
457
+ # TODO: Should return range
458
+
459
+ node.merge_union_type(Types::UnionType.create_int)
460
+ end
461
+
462
+ def visit_break_node(node)
463
+ Types::UnionType.create_void
464
+ end
465
+
466
+ def visit_if_node(node)
467
+ assert_singleton_type(node.condition.accept(self), Types::PrimitiveType::Bool)
468
+
469
+ type = Types::UnionType.new
470
+ type.expand(node.true_body_stmts.accept(self)) # Begin always has type of last stmt
471
+
472
+ if node.false_body_stmts == nil
473
+ type.expand(Types::UnionType.create_void)
474
+ else
475
+ type.expand(node.false_body_stmts.accept(self))
476
+ end
477
+
478
+ node.merge_union_type(type)
479
+ end
480
+
481
+ def visit_ternary_node(node)
482
+ assert_singleton_type(node.condition.accept(self), Types::PrimitiveType::Bool)
483
+
484
+ type = Types::UnionType.new
485
+ type.expand(node.true_val.accept(self))
486
+ type.expand(node.false_val.accept(self))
487
+
488
+ node.merge_union_type(type)
489
+ end
490
+
491
+ def visit_begin_node(node)
492
+ node.body_stmts[0...-1].each do |stmt|
493
+ stmt.accept(self)
494
+ end
495
+
496
+ if node.body_stmts.empty?
497
+ type = Types::UnionType.new
498
+ else
499
+ type = node.body_stmts.last.accept(self)
500
+ end
501
+
502
+ node.merge_union_type(type)
503
+ end
504
+
505
+ def visit_return_node(node)
506
+ type = node.value.accept(self)
507
+ symbol_table.expand_return_type(type)
508
+ node.merge_union_type(type)
509
+ end
510
+
511
+ def visit_source_code_expr_node(node)
512
+ # This is a synthetic node. No type inference. Return the type that was set
513
+ # manually before (if any).
514
+ return node.get_type
515
+ end
516
+
517
+ def visit_send_node_singleton_receiver(sing_type, node)
518
+ if RubyIntegration.is_interpreter_only?(sing_type)
519
+ return Types::InterpreterOnlyType.new.to_union_type
520
+ elsif RubyIntegration.has_implementation?(sing_type, node.selector)
521
+ arg_types = node.arguments.map do |arg| arg.get_type end
522
+
523
+ begin
524
+ return_type = RubyIntegration.get_return_type(
525
+ sing_type, node.selector, *arg_types, send_node: node)
526
+ return return_type
527
+ rescue RubyIntegration::CycleDetectedError => cycle_error
528
+ # Cannot do further symbolic execution, i.e., kernel fusion here,
529
+ # because we are in a loop.
530
+
531
+ # Invoke parallel section: change to `RECV` to `RECV.__call__.to_command`
532
+ node.replace_child(
533
+ node.receiver,
534
+ AST::SendNode.new(
535
+ receiver: AST::SendNode.new(
536
+ receiver: node.receiver, selector: :__call__),
537
+ selector: :to_command))
538
+
539
+ # Start fresh
540
+ raise RestartTypeInferenceError.new
541
+ end
542
+ elsif sing_type.is_a?(Types::StructType)
543
+ # This is a struct type, special type inference rules apply
544
+ return sing_type.get_return_type(node.selector, *node.arguments)
545
+ else
546
+ Log.info("Translate call to ordinary Ruby method #{sing_type}.#{node.selector}")
547
+ return visit_method_call(node, sing_type)
548
+ end
549
+ end
550
+
551
+ def visit_send_node_union_type(receiver_type, node)
552
+ type = Types::UnionType.new
553
+
554
+ for sing_type in receiver_type
555
+ return_type = visit_send_node_singleton_receiver(sing_type, node)
556
+ node.return_type_by_recv_type[sing_type] = return_type
557
+ type.expand(return_type)
558
+ end
559
+
560
+ node.merge_union_type(type)
561
+
562
+ return type
563
+ end
564
+
565
+ def visit_send_node(node)
566
+ # TODO: handle self sends
567
+ receiver_type = nil
568
+
569
+ if node.receiver == nil
570
+ Logger.warn("No receiver given for node #{node.to_s}")
571
+ receiver_type = Types::UnionType.create_int
572
+ else
573
+ receiver_type = node.receiver.accept(self)
574
+ end
575
+
576
+ node.arguments.each do |arg|
577
+ arg.accept(self)
578
+ end
579
+
580
+ visit_send_node_union_type(receiver_type, node)
581
+
582
+ return node.get_type
583
+ end
584
+ end
585
+ end
586
+ end