typed.rb 0.0.11

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 (173) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +26 -0
  3. data/bin/typed.rb +110 -0
  4. data/lib/typed/language.rb +131 -0
  5. data/lib/typed/model/tm_abs.rb +104 -0
  6. data/lib/typed/model/tm_array_literal.rb +25 -0
  7. data/lib/typed/model/tm_boolean.rb +15 -0
  8. data/lib/typed/model/tm_boolean_operator.rb +34 -0
  9. data/lib/typed/model/tm_break.rb +24 -0
  10. data/lib/typed/model/tm_case_when.rb +38 -0
  11. data/lib/typed/model/tm_class.rb +63 -0
  12. data/lib/typed/model/tm_const.rb +29 -0
  13. data/lib/typed/model/tm_defined.rb +19 -0
  14. data/lib/typed/model/tm_error.rb +16 -0
  15. data/lib/typed/model/tm_float.rb +15 -0
  16. data/lib/typed/model/tm_for.rb +42 -0
  17. data/lib/typed/model/tm_fun.rb +165 -0
  18. data/lib/typed/model/tm_global_var.rb +22 -0
  19. data/lib/typed/model/tm_global_var_assignment.rb +20 -0
  20. data/lib/typed/model/tm_hash_literal.rb +32 -0
  21. data/lib/typed/model/tm_if_else.rb +24 -0
  22. data/lib/typed/model/tm_instance_var.rb +23 -0
  23. data/lib/typed/model/tm_instance_var_assignment.rb +32 -0
  24. data/lib/typed/model/tm_int.rb +15 -0
  25. data/lib/typed/model/tm_local_var_asgn.rb +35 -0
  26. data/lib/typed/model/tm_mass_asgn.rb +60 -0
  27. data/lib/typed/model/tm_mlhs.rb +87 -0
  28. data/lib/typed/model/tm_module.rb +51 -0
  29. data/lib/typed/model/tm_next.rb +24 -0
  30. data/lib/typed/model/tm_nil.rb +14 -0
  31. data/lib/typed/model/tm_range_literal.rb +30 -0
  32. data/lib/typed/model/tm_regexp.rb +27 -0
  33. data/lib/typed/model/tm_rescue.rb +27 -0
  34. data/lib/typed/model/tm_return.rb +24 -0
  35. data/lib/typed/model/tm_s_class.rb +30 -0
  36. data/lib/typed/model/tm_self.rb +22 -0
  37. data/lib/typed/model/tm_send.rb +300 -0
  38. data/lib/typed/model/tm_sequencing.rb +53 -0
  39. data/lib/typed/model/tm_string.rb +15 -0
  40. data/lib/typed/model/tm_string_interpolation.rb +21 -0
  41. data/lib/typed/model/tm_super.rb +27 -0
  42. data/lib/typed/model/tm_symbol.rb +15 -0
  43. data/lib/typed/model/tm_symbol_interpolation.rb +21 -0
  44. data/lib/typed/model/tm_try.rb +29 -0
  45. data/lib/typed/model/tm_var.rb +28 -0
  46. data/lib/typed/model/tm_while.rb +43 -0
  47. data/lib/typed/model.rb +48 -0
  48. data/lib/typed/prelude.rb +939 -0
  49. data/lib/typed/prelude_existential_registry.bin +0 -0
  50. data/lib/typed/prelude_generic_registry.bin +0 -0
  51. data/lib/typed/prelude_registry.bin +0 -0
  52. data/lib/typed/runtime/ast_parser.rb +589 -0
  53. data/lib/typed/runtime/method_signature_processor.rb +72 -0
  54. data/lib/typed/runtime/normalization/validations.rb +47 -0
  55. data/lib/typed/runtime/normalization.rb +196 -0
  56. data/lib/typed/runtime/parser_context.rb +36 -0
  57. data/lib/typed/runtime/type_parser.rb +215 -0
  58. data/lib/typed/runtime/type_registry.rb +170 -0
  59. data/lib/typed/runtime/type_signature_processor.rb +34 -0
  60. data/lib/typed/runtime.rb +33 -0
  61. data/lib/typed/type_signature/parser.rb +240 -0
  62. data/lib/typed/types/polymorphism/existential_type_variable.rb +13 -0
  63. data/lib/typed/types/polymorphism/generic_comparisons.rb +134 -0
  64. data/lib/typed/types/polymorphism/generic_variables.rb +24 -0
  65. data/lib/typed/types/polymorphism/type_variable.rb +138 -0
  66. data/lib/typed/types/polymorphism/type_variable_register.rb +298 -0
  67. data/lib/typed/types/polymorphism/unification.rb +579 -0
  68. data/lib/typed/types/ty_boolean.rb +15 -0
  69. data/lib/typed/types/ty_dynamic.rb +39 -0
  70. data/lib/typed/types/ty_either.rb +168 -0
  71. data/lib/typed/types/ty_error.rb +18 -0
  72. data/lib/typed/types/ty_existential_type.rb +22 -0
  73. data/lib/typed/types/ty_function.rb +144 -0
  74. data/lib/typed/types/ty_generic_function.rb +115 -0
  75. data/lib/typed/types/ty_generic_object.rb +180 -0
  76. data/lib/typed/types/ty_generic_singleton_object.rb +238 -0
  77. data/lib/typed/types/ty_object.rb +256 -0
  78. data/lib/typed/types/ty_singleton_object.rb +78 -0
  79. data/lib/typed/types/ty_stack_jump.rb +44 -0
  80. data/lib/typed/types/ty_top_level_object.rb +38 -0
  81. data/lib/typed/types.rb +60 -0
  82. data/lib/typed/typing_context.rb +206 -0
  83. data/lib/typed/version.rb +3 -0
  84. data/lib/typed.rb +161 -0
  85. data/spec/lib/ast_parser_spec.rb +101 -0
  86. data/spec/lib/examples/animals.rb +44 -0
  87. data/spec/lib/examples/counter.rb +16 -0
  88. data/spec/lib/examples/if.rb +31 -0
  89. data/spec/lib/language_spec.rb +36 -0
  90. data/spec/lib/model/tm_abs_spec.rb +66 -0
  91. data/spec/lib/model/tm_array_literal_spec.rb +36 -0
  92. data/spec/lib/model/tm_case_when_spec.rb +39 -0
  93. data/spec/lib/model/tm_class_spec.rb +67 -0
  94. data/spec/lib/model/tm_defined_spec.rb +10 -0
  95. data/spec/lib/model/tm_for_spec.rb +150 -0
  96. data/spec/lib/model/tm_fun_spec.rb +11 -0
  97. data/spec/lib/model/tm_hash_literal_spec.rb +40 -0
  98. data/spec/lib/model/tm_mass_asgn_spec.rb +104 -0
  99. data/spec/lib/model/tm_module_spec.rb +42 -0
  100. data/spec/lib/model/tm_regexp_spec.rb +9 -0
  101. data/spec/lib/model/tm_return_spec.rb +47 -0
  102. data/spec/lib/model/tm_s_class_spec.rb +27 -0
  103. data/spec/lib/model/tm_self_spec.rb +19 -0
  104. data/spec/lib/model/tm_string_interpolation_spec.rb +10 -0
  105. data/spec/lib/model/tm_symbol_interpolation_spec.rb +10 -0
  106. data/spec/lib/model/tm_symbol_spec.rb +9 -0
  107. data/spec/lib/model/tm_while_spec.rb +141 -0
  108. data/spec/lib/polymorphism/type_variable_spec.rb +14 -0
  109. data/spec/lib/polymorphism/unification_spec.rb +328 -0
  110. data/spec/lib/prelude/array_spec.rb +263 -0
  111. data/spec/lib/prelude/class_spec.rb +12 -0
  112. data/spec/lib/prelude/enumerable_spec.rb +278 -0
  113. data/spec/lib/prelude/enumerator_spec.rb +101 -0
  114. data/spec/lib/prelude/hash_spec.rb +361 -0
  115. data/spec/lib/prelude/kernel_spec.rb +23 -0
  116. data/spec/lib/prelude/object_spec.rb +22 -0
  117. data/spec/lib/prelude/pair_spec.rb +16 -0
  118. data/spec/lib/prelude/showable_spec.rb +31 -0
  119. data/spec/lib/prelude/string_spec.rb +98 -0
  120. data/spec/lib/runtime/normalization_spec.rb +29 -0
  121. data/spec/lib/runtime/validations_spec.rb +56 -0
  122. data/spec/lib/runtime_spec.rb +503 -0
  123. data/spec/lib/type_signature/parser_spec.rb +239 -0
  124. data/spec/lib/types/comparisons_spec.rb +35 -0
  125. data/spec/lib/types/polymorphism/generic_comparisons_spec.rb +492 -0
  126. data/spec/lib/types/polymorphism/type_variable_register_spec.rb +128 -0
  127. data/spec/lib/types/ty_dynamic_spec.rb +103 -0
  128. data/spec/lib/types/ty_either_spec.rb +288 -0
  129. data/spec/lib/types/ty_error_spec.rb +18 -0
  130. data/spec/lib/types/ty_generic_object_spec.rb +78 -0
  131. data/spec/lib/types/ty_generic_singleton_object_spec.rb +288 -0
  132. data/spec/lib/types/typing_context_spec.rb +86 -0
  133. data/spec/lib/types_spec.rb +174 -0
  134. data/spec/lib/typing/boolean_asgn_spec.rb +134 -0
  135. data/spec/lib/typing/break_spec.rb +79 -0
  136. data/spec/lib/typing/generics_spec.rb +191 -0
  137. data/spec/lib/typing/instance_vars_spec.rb +103 -0
  138. data/spec/lib/typing/next_spec.rb +29 -0
  139. data/spec/lib/typing/op_asgn_spec.rb +104 -0
  140. data/spec/lib/typing/overriden_methods_spec.rb +31 -0
  141. data/spec/lib/typing/subtyping_spec.rb +112 -0
  142. data/spec/lib/typing/tm_boolean_operator_spec.rb +100 -0
  143. data/spec/lib/typing/tm_boolean_spec.rb +61 -0
  144. data/spec/lib/typing/tm_const_spec.rb +28 -0
  145. data/spec/lib/typing/tm_defined_spec.rb +12 -0
  146. data/spec/lib/typing/tm_fun_spec.rb +347 -0
  147. data/spec/lib/typing/tm_global_var_spec.rb +33 -0
  148. data/spec/lib/typing/tm_if_else_spec.rb +104 -0
  149. data/spec/lib/typing/tm_ignore_spec.rb +24 -0
  150. data/spec/lib/typing/tm_instance_vars_spec.rb +117 -0
  151. data/spec/lib/typing/tm_local_var_asgn_spec.rb +134 -0
  152. data/spec/lib/typing/tm_mlhs_spec.rb +164 -0
  153. data/spec/lib/typing/tm_module_spec.rb +89 -0
  154. data/spec/lib/typing/tm_raise_spec.rb +31 -0
  155. data/spec/lib/typing/tm_range_literal_spec.rb +25 -0
  156. data/spec/lib/typing/tm_regexp_spec.rb +14 -0
  157. data/spec/lib/typing/tm_return_spec.rb +45 -0
  158. data/spec/lib/typing/tm_send_casting_spec.rb +26 -0
  159. data/spec/lib/typing/tm_send_class_methods_spec.rb +42 -0
  160. data/spec/lib/typing/tm_send_generic_apply_spec.rb +103 -0
  161. data/spec/lib/typing/tm_send_generic_methods_spec.rb +77 -0
  162. data/spec/lib/typing/tm_send_initialize_spec.rb +68 -0
  163. data/spec/lib/typing/tm_send_lambda_spec.rb +135 -0
  164. data/spec/lib/typing/tm_send_spec.rb +217 -0
  165. data/spec/lib/typing/tm_send_yield_block_spec.rb +308 -0
  166. data/spec/lib/typing/tm_sequencing_spec.rb +174 -0
  167. data/spec/lib/typing/tm_string_interpolation_spec.rb +19 -0
  168. data/spec/lib/typing/tm_super_spec.rb +63 -0
  169. data/spec/lib/typing/tm_symbol_interpolation_spec.rb +19 -0
  170. data/spec/lib/typing/tm_symbol_spec.rb +14 -0
  171. data/spec/lib/typing/tm_try_spec.rb +73 -0
  172. data/spec/spec_helper.rb +140 -0
  173. metadata +216 -0
@@ -0,0 +1,579 @@
1
+ require 'stringio'
2
+
3
+ module TypedRb
4
+ module Types
5
+ # Polymorphic additions to Featherweight Ruby
6
+ module Polymorphism
7
+ class UnificationError < TypedRb::TypeCheckError
8
+ def initialize(message)
9
+ super(message)
10
+ end
11
+ end
12
+
13
+ # Common operations on types and restrictions.
14
+ module TypeOperations
15
+ # Check if two types are compatible for a certain restriction.
16
+ # If no join type is possible for a particular restriction, a
17
+ # UncomparableTypes error is raised.
18
+ def compatible_type?(value_l, t, value_r)
19
+ if value_l.nil? || value_r.nil?
20
+ value_l || value_r
21
+ else
22
+ case t
23
+ when :gt # assignations, e.g v = Int, v = Num => Num
24
+ compatible_gt_type?(value_l, value_r)
25
+ when :lt # applications, return e.g. return (Int, Num) => Int
26
+ compatible_lt_type?(value_l, value_r)
27
+ when :send
28
+ compatible_send_type?(value_l, value_r)
29
+ else
30
+ fail UnificationError, "Unknown type constraint #{t}"
31
+ end
32
+ end
33
+ end
34
+
35
+ def compatible_gt_type?(value_l, value_r, join_if_false = true)
36
+ value_l > value_r ? value_l : value_r
37
+ rescue Types::UncomparableTypes, ArgumentError
38
+ if join_if_false
39
+ value_l.join(value_r)
40
+ else
41
+ raise Types::UncomparableTypes.new(value_l, value_r)
42
+ end
43
+ end
44
+
45
+ def compatible_lt_type?(value_l, value_r)
46
+ error_message = "Error checking type, #{value_l} is not a subtype of #{value_r}"
47
+ begin
48
+ value_l <= value_r ? value_l : fail(UnificationError, error_message)
49
+ rescue ArgumentError
50
+ raise(Types::UncomparableTypes.new(value_l, value_r))
51
+ end
52
+ end
53
+
54
+ # This function does not return the infered type.
55
+ # Types are assigned as a side effect.
56
+ def compatible_send_type?(receiver, send_args)
57
+ return_type = send_args[:return]
58
+ arg_types = send_args[:args]
59
+ message = send_args[:message]
60
+ inferred_receiver = infer_receiver(receiver)
61
+ if inferred_receiver
62
+ klass, function = inferred_receiver.find_function_type(message, arg_types.size, false)
63
+ if function.is_a?(Types::TyDynamicFunction)
64
+ # TODO: should I bind the var to type dynamic in this case?
65
+ graph[return_type][:upper_type] = Types::TyDynamic.new(Object)
66
+ graph[return_type][:lower_type] = Types::TyDynamic.new(Object)
67
+ return true
68
+ end
69
+ if function && can_apply?(function, arg_types)
70
+ if return_type && graph[return_type][:upper_type]
71
+ compatible_gt_type?(graph[return_type][:upper_type], function.to, false)
72
+ else
73
+ graph[return_type][:upper_type] = function.to
74
+ end
75
+ else
76
+ return true if klass != inferred_receiver.ruby_type
77
+ fail UnificationError, "Message #{message} not found for type variable #{receiver}"
78
+ end
79
+ else
80
+ add_to_send_bubble_constraints(receiver, send_args)
81
+ end
82
+ end
83
+
84
+ def infer_receiver(receiver)
85
+ if receiver.is_a?(Hash)
86
+ receiver[:upper_type] = receiver[:lower_type] if receiver[:upper_type].nil?
87
+ receiver[:upper_type].as_object_type if receiver[:upper_type]
88
+ else
89
+ receiver
90
+ end
91
+ end
92
+
93
+ # if the type variable is not bound, we try to
94
+ # find the variable in an upper typing context
95
+ # to satisfy the constraint there.
96
+ # If no container context is found, we fail
97
+ # the unification run unless unbound vars are allowed.
98
+ def add_to_send_bubble_constraints(receiver, send_args)
99
+ return check_unbound_receivers(receiver, send_args) if receiver.nil?
100
+ vars = receiver[:vars].keys.select do |var|
101
+ Types::TypingContext.include?(var.variable)
102
+ end
103
+ return check_unbound_receivers(receiver, send_args) if vars.empty?
104
+ vars.each do |var|
105
+ @to_bubble << [var, send_args]
106
+ end
107
+ end
108
+
109
+ def check_unbound_receivers(receiver, send_args)
110
+ return if @allow_unbound_receivers
111
+ fail UnificationError, "Unbound variable #{receiver} type acting as receiver for #{send_args[:message]}"
112
+ end
113
+
114
+ def can_apply?(fn, arg_types)
115
+ if fn.dynamic?
116
+ true
117
+ else
118
+ arg_types.each_with_index do |arg, i|
119
+ fn_arg = fn.from[i]
120
+ if arg.is_a?(TypeVariable)
121
+ if graph[arg][:lower_type]
122
+ type = compatible_lt_type?(graph[arg][:lower_type], fn_arg)
123
+ graph[arg][:lower_type] = type
124
+ else
125
+ graph[arg][:lower_type] = fn_arg
126
+ end
127
+ else
128
+ compatible_lt_type?(arg, fn_arg)
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ # Keeps a graph of the var types according to the subsumption order
136
+ # relationship.
137
+ class Topography
138
+ include Polymorphism::TypeOperations
139
+ attr_reader :mapping, :groups
140
+
141
+ # Create the graph based on the provided constraints as unlinked
142
+ # nodes.
143
+ def initialize(constraints)
144
+ vars = constraints.reduce([]) do |acc, (l, _t, r)|
145
+ vals = [l]
146
+ if r.is_a?(Hash)
147
+ vals << r[:return]
148
+ else
149
+ vals << r
150
+ end
151
+ acc + vals.select { |v| v.is_a?(TypeVariable) }
152
+ end.uniq
153
+
154
+ @groups = vars.each_with_object({}) do |var, groups|
155
+ # lower_type, and upper_type can come from a bubbled up :send constraint type variable
156
+ groups[var] = make_group(var => true)
157
+ end
158
+ end
159
+
160
+ # Is the variable in a group of variables?
161
+ def grouped?(var)
162
+ groups[var][:grouped]
163
+ end
164
+
165
+ def [](var)
166
+ if var.is_a?(TypeVariable)
167
+ groups[var]
168
+ else
169
+ var
170
+ end
171
+ end
172
+
173
+ def vars
174
+ groups.keys
175
+ end
176
+
177
+ def merge(l, r)
178
+ merge_groups(groups[l], groups[r])
179
+ end
180
+
181
+ def replace_groups(constraints)
182
+ groups.values.each do |group|
183
+ group[:vars].keys.each do |l|
184
+ constraints = replace(constraints, l, group)
185
+ end
186
+ end
187
+ constraints
188
+ end
189
+
190
+ def do_bindings!
191
+ text = StringIO.new
192
+ text << "Doing bindings:\n"
193
+
194
+ num_bindings = 0
195
+ groups.values.uniq.each do |group|
196
+ next if group [:upper_type].nil? && group[:lower_type].nil?
197
+ group[:vars].keys.each do |var|
198
+ final_lower_type = find_type(group[:lower_type], :lower_type)
199
+ var.upper_bound = final_lower_type
200
+ final_upper_type = find_type(group[:upper_type], :upper_type)
201
+ var.lower_bound = final_upper_type
202
+ # if var.wildcard?
203
+ # final_binding_type = if final_lower_type == final_upper_type
204
+ # final_upper_type
205
+ # elsif final_lower_type && final_upper_type
206
+ # final_lower_type
207
+ # #elsif final_lower_type && final_upper_type.nil?
208
+ # # final_lower_type
209
+ # #else
210
+ # # final_upper_type
211
+ # end
212
+ # binding_string = "[#{var.lower_bound ? var.lower_bound : '?'},#{var.upper_bound ? var.upper_bound : '?'}]"
213
+ # if final_binding_type
214
+ # num_bindings += 1
215
+ # text << "Final binding: #{var.variable} -> #{binding_string} : #{final_binding_type}\n"
216
+ # var.bind(final_binding_type)
217
+ # else
218
+ # text << "Final binding: #{var.variable} -> #{binding_string} : UNKNOWN\n"
219
+ # end
220
+ # else
221
+ final_binding_type = if final_lower_type == final_upper_type
222
+ final_upper_type
223
+ elsif final_lower_type && final_upper_type.nil?
224
+ final_lower_type
225
+ else
226
+ final_upper_type
227
+ end
228
+ binding_string = "[#{var.lower_bound ? var.lower_bound : '?'},#{var.upper_bound ? var.upper_bound : '?'}]"
229
+ if final_binding_type
230
+ num_bindings += 1
231
+ text << "Final binding: #{var.variable} -> #{binding_string} : #{final_binding_type}\n"
232
+ var.bind(final_binding_type)
233
+ else
234
+ text << "Final binding: #{var.variable} -> #{binding_string} : UNKNOWN\n"
235
+ end
236
+ # end
237
+ end
238
+ end
239
+ text << "Found #{num_bindings} bindings"
240
+ TypedRb.log(binding, :debug, text.string)
241
+ end
242
+
243
+ def check_bindings
244
+ groups.values.each do |group|
245
+ next if group[:upper_type].nil? && group[:lower_type].nil?
246
+ final_lower_type = find_type(group[:lower_type], :lower_type)
247
+ final_upper_type = find_type(group[:upper_type], :upper_type)
248
+ if final_lower_type && final_upper_type && final_lower_type != final_upper_type
249
+ # final lower <= final upper
250
+ compatible_lt_type?(final_upper_type, final_lower_type)
251
+ end
252
+ end
253
+ end
254
+
255
+ def find_type(value, type)
256
+ # type variable
257
+ if value.is_a?(TypeVariable)
258
+ value = if type == :lower_type
259
+ value.upper_bound
260
+ else
261
+ value.lower_bound
262
+ end
263
+ find_type(value, type)
264
+ # group
265
+ elsif value.is_a?(Hash) && value[type]
266
+ find_type(value[type], type)
267
+ # type
268
+ elsif value.is_a?(Type)
269
+ value
270
+ # nil
271
+ else
272
+ value
273
+ # fail UnificationError, 'Cannot find type in type_variable binding' if value.nil?
274
+ end
275
+ end
276
+
277
+ def print_groups
278
+ TypedRb.log(binding, :debug, 'Variable groups:')
279
+ groups.values.uniq.each do |group|
280
+ vars = group[:vars].keys.map(&:to_s).join(',')
281
+ lower_type = group[:lower_type] ? group[:lower_type].to_s : '?'
282
+ upper_type = group[:upper_type] ? group[:upper_type].to_s : '?'
283
+ TypedRb.log(binding, :debug, "#{vars}:[#{lower_type},#{upper_type}]")
284
+ end
285
+ end
286
+
287
+ protected
288
+
289
+ def make_group(vars)
290
+ { vars: vars,
291
+ grouped: vars.keys.size > 1,
292
+ lower_type: nil,
293
+ upper_type: nil }
294
+ end
295
+
296
+ def merge_groups(group_l, group_r)
297
+ vars_common = group_l[:vars].merge(group_r[:vars])
298
+ group_common = make_group(vars_common)
299
+ group_common[:grouped] = true
300
+ # TODO: types???
301
+ group_common[:lower_type] = max_type(group_l[:lower_type], group_r[:lower_type])
302
+ group_common[:upper_type] = min_type(group_l[:upper_type], group_r[:upper_type])
303
+ vars_common.keys.each { |var| groups[var] = group_common }
304
+ end
305
+
306
+ def max_type(type_a, type_b)
307
+ if type_a.nil? || type_b.nil?
308
+ type_a || type_b
309
+ else
310
+ compatible_type?(type_a, :gt, type_b)
311
+ end
312
+ end
313
+
314
+ def min_type(type_a, type_b)
315
+ if type_a.nil? || type_b.nil?
316
+ type_a || type_b
317
+ else
318
+ compatible_type?(type_a, :lt, type_b)
319
+ end
320
+ end
321
+
322
+ def replace(rest, l, r, acc = [])
323
+ if rest.empty?
324
+ acc
325
+ else
326
+ a, t, b = rest.first
327
+ acc << [a == l ? r : a, t, b == l ? r : b]
328
+ replace(rest.drop(1), l, r, acc)
329
+ end
330
+ end
331
+ end
332
+
333
+ # Implements a unification algorithm for variable types
334
+ # with support for subtyping and restrictions on variable types.
335
+ class Unification
336
+ include Polymorphism::TypeOperations
337
+ attr_reader :constraints, :graph
338
+
339
+ def initialize(constraints, options = {})
340
+ @allow_unbound_receivers = options[:allow_unbound_receivers] || false
341
+ @constraints = canonical_form(constraints)
342
+ @constraints = expand_constraints
343
+ @gt_constraints = @constraints.select { |(_, t, _r)| t == :gt }.sort do |(_, _, r1), (_, _, r2)|
344
+ -(r1 <=> r2) || 0 rescue 0
345
+ end
346
+ @lt_constraints = @constraints.select { |(_, t, _r)| t == :lt }.sort do |(_, _, r1), (_, _, r2)|
347
+ (r1 <=> r2) || 0 rescue 0
348
+ end
349
+ @send_constraints = @constraints.select { |(_, t, _r)| t == :send }
350
+ @graph = Topography.new(@constraints)
351
+ @to_bubble = []
352
+ end
353
+
354
+ def expand_constraints
355
+ expanded = constraints.reduce([]) do |acc, (l,_,r)|
356
+ acc + expand_constraint(l) + expand_constraint(r)
357
+ end
358
+ expanded + @constraints
359
+ end
360
+
361
+ def expand_constraint(element)
362
+ expanded = []
363
+ if element.is_a?(TypeVariable) && element.bound.nil?
364
+ expanded << [element, :gt, element.lower_bound] if element.lower_bound
365
+ expanded << [element, :lt, element.upper_bound] if element.upper_bound
366
+ end
367
+ expanded
368
+ end
369
+
370
+ def canonical_form(constraints)
371
+ disambiguation = {}
372
+
373
+ constraints.inject([]) do |acc, (l, t, r)|
374
+ next acc if r.nil? || (r.respond_to?(:ruby_type) && r.ruby_type == NilClass) || r.is_a?(Types::TyDynamic)
375
+ if l.is_a?(TypeVariable)
376
+ l = disambiguation[l.variable] || l
377
+ disambiguation[l.variable] = l
378
+ end
379
+ if r.is_a?(TypeVariable)
380
+ r = disambiguation[r.variable] || r
381
+ disambiguation[r.variable] = r
382
+ end
383
+ if l.is_a?(TypeVariable) && r.is_a?(TypeVariable) && t == :lt
384
+ acc << [r, :gt, l]
385
+ else
386
+ acc << [l, t, r]
387
+ end
388
+ end
389
+ end
390
+
391
+ def run(bind_variables = true)
392
+ print_constraints
393
+ unify(@gt_constraints) # we create links between vars in unify, we need to fold groups afterwards
394
+ # this just references to vars in the same group, by the group itself
395
+ # in the remaining @lt constraints
396
+ @lt_constraints = graph.replace_groups(@lt_constraints)
397
+ unify(@lt_constraints)
398
+ unify(@send_constraints)
399
+ graph.check_bindings
400
+ graph.print_groups
401
+ bubble_send_constraints
402
+ graph.do_bindings! if bind_variables
403
+ self
404
+ end
405
+
406
+ def bubble_send_constraints
407
+ @to_bubble.each do |(var, constraint)|
408
+ return_var = constraint[:return]
409
+ message = constraint[:message]
410
+ args = constraint[:args]
411
+ lower_type = @graph[return_var][:lower_type]
412
+ upper_type = @graph[return_var][:upper_type]
413
+ fresh_return_var = var.add_message_constraint(message, args)
414
+ Types::TypingContext.add_constraint(fresh_return_var.variable, :lt, lower_type) if lower_type
415
+ Types::TypingContext.add_constraint(fresh_return_var.variable, :gt, upper_type) if upper_type
416
+ end
417
+ end
418
+ def bindings
419
+ graph.vars
420
+ end
421
+
422
+ def bindings_map
423
+ graph.vars.each_with_object({}) do |var, acc|
424
+ acc[var.variable] = var if var.bound
425
+ end
426
+ end
427
+
428
+ def print_constraints
429
+ text = StringIO.new
430
+ text << "Running unification on #{constraints.size} constraints:\n"
431
+ @gt_constraints.each do |(l, _t, r)|
432
+ l = if l.is_a?(Hash)
433
+ l.keys.map(&:to_s).join(',')
434
+ else
435
+ l.to_s
436
+ end
437
+ text << "#{l} :gt #{r}\n"
438
+ end
439
+ @lt_constraints.each do |(l, _t, r)|
440
+ l = if l.is_a?(Hash)
441
+ l.keys.map(&:to_s).join(',')
442
+ else
443
+ l.to_s
444
+ end
445
+ text << "#{l} :lt #{r}\n"
446
+ end
447
+ @send_constraints.each do |(l, _t, send)|
448
+ return_type = send[:return]
449
+ arg_types = send[:args].map(&:to_s)
450
+ message = send[:message]
451
+ l = if l.is_a?(Hash)
452
+ l.keys.map(&:to_s).join(',')
453
+ else
454
+ l.to_s
455
+ end
456
+ text << "#{l} :send #{message}[ #{arg_types.join(',')} -> #{return_type}]\n"
457
+ end
458
+
459
+ TypedRb.log binding, :debug, text.string
460
+ end
461
+
462
+ protected
463
+
464
+ def unify(constraints)
465
+ return if constraints.empty?
466
+ (l, t, r) = constraints.first
467
+ rest = constraints.drop(1)
468
+ if l == r
469
+ unify(rest)
470
+ else
471
+ if r.is_a?(TypeVariable)
472
+ # this is only going to happen in the first invocation to unify
473
+ graph.merge(l, r)
474
+ else
475
+ # - In the first invocation to unify, l must always be a TypeVar
476
+ # t :gt and r a type variable or a type,
477
+ # - In the second invocation to unify, l must always be a group
478
+ # t :lt and r a type variable,
479
+ check_constraint(l, t, r, t != :send) # we don't bind if constraint is send
480
+ end
481
+ unify(rest)
482
+ end
483
+ end
484
+
485
+ def check_constraint(l, t, r, bind = true)
486
+ # ONE BOUND
487
+ value_l = if t == :lt
488
+ graph[l][:lower_type]
489
+ elsif t == :gt
490
+ graph[l][:upper_type]
491
+ else
492
+ graph[l]
493
+ end
494
+ # value_r = r
495
+ value_r = if r.is_a?(Hash) && t != :send
496
+ if t == :lt
497
+ graph[r][:lower_type]
498
+ elsif t == :gt
499
+ graph[r][:upper_type]
500
+ else
501
+ graph[r]
502
+ end
503
+ else
504
+ r
505
+ end
506
+
507
+ # this will throw an exception if types no compatible
508
+
509
+ compatible_type = compatible_type?(value_l, t, value_r)
510
+ if t == :lt
511
+ graph[l][:lower_type] = compatible_type if bind
512
+ elsif t == :gt
513
+ graph[l][:upper_type] = compatible_type if bind
514
+ end
515
+
516
+ # # OTHER BOUND
517
+ #
518
+ # value_l = if t == :lt
519
+ # graph[l][:upper_type]
520
+ # elsif t == :gt
521
+ # graph[l][:lower_type]
522
+ # else
523
+ # graph[l]
524
+ # end
525
+ # #value_r = r
526
+ # value_r = if r.is_a?(Hash)
527
+ # if t == :lt
528
+ # graph[r][:upper_type]
529
+ # elsif t == :gt
530
+ # graph[r][:lower_type]
531
+ # else
532
+ # graph[r]
533
+ # end
534
+ # else
535
+ # r
536
+ # end
537
+ #
538
+ # # this will throw an exception if types no compatible
539
+ # compatible_type = compatible_type?(value_l, (t == :gt ? :lt : :gt), value_r)
540
+ # if t == :lt
541
+ # graph[l][:upper_type] = compatible_type if bind
542
+ # elsif t == :gt
543
+ # graph[l][:lower_type] = compatible_type if bind
544
+ # end
545
+ end
546
+
547
+ # def check_constraint(l, t, r, bind = true)
548
+ # value_l = if t == :lt
549
+ # graph[l][:lower_type]
550
+ # elsif t == :gt
551
+ # graph[l][:upper_type]
552
+ # else
553
+ # graph[l]
554
+ # end
555
+ # #value_r = r
556
+ # value_r = if r.is_a?(Hash)
557
+ # if t == :lt
558
+ # graph[r][:lower_type]
559
+ # elsif t == :gt
560
+ # graph[r][:upper_type]
561
+ # else
562
+ # graph[r]
563
+ # end
564
+ # else
565
+ # r
566
+ # end
567
+ #
568
+ # # this will throw an exception if types no compatible
569
+ # compatible_type = compatible_type?(value_l, t, value_r)
570
+ # if t == :lt
571
+ # graph[l][:lower_type] = compatible_type if bind
572
+ # elsif t == :gt
573
+ # graph[l][:upper_type] = compatible_type if bind
574
+ # end
575
+ # end
576
+ end
577
+ end
578
+ end
579
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'ty_object'
2
+
3
+ module TypedRb
4
+ module Types
5
+ class TyBoolean < TyObject
6
+ def initialize(node = nil)
7
+ super(TrueClass, node)
8
+ end
9
+
10
+ def to_s
11
+ 'Boolean'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,39 @@
1
+ require_relative './ty_function'
2
+
3
+ module TypedRb
4
+ module Types
5
+ class TyDynamic < TyObject
6
+ def compatible?(_other_type, _relation = :lt)
7
+ true
8
+ end
9
+
10
+ def dynamic?
11
+ true
12
+ end
13
+ end
14
+
15
+ class TyDynamicFunction < TyFunction
16
+ def initialize(klass, message, with_block = true, node = nil)
17
+ super([], TyDynamic.new(Object, node), nil, node)
18
+ @klass = klass
19
+ @message = message
20
+ @arity = Float::INFINITY
21
+ @block_type = TyDynamicFunction.new(Proc, :cal, false) if with_block
22
+ end
23
+
24
+ def dynamic?
25
+ true
26
+ end
27
+
28
+ def arg_compatible?(_)
29
+ true
30
+ end
31
+
32
+ def check_args_application; end
33
+
34
+ def compatible?(_other_type, _relation = :lt)
35
+ true
36
+ end
37
+ end
38
+ end
39
+ end