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.
- checksums.yaml +7 -0
- data/Rakefile +26 -0
- data/bin/typed.rb +110 -0
- data/lib/typed/language.rb +131 -0
- data/lib/typed/model/tm_abs.rb +104 -0
- data/lib/typed/model/tm_array_literal.rb +25 -0
- data/lib/typed/model/tm_boolean.rb +15 -0
- data/lib/typed/model/tm_boolean_operator.rb +34 -0
- data/lib/typed/model/tm_break.rb +24 -0
- data/lib/typed/model/tm_case_when.rb +38 -0
- data/lib/typed/model/tm_class.rb +63 -0
- data/lib/typed/model/tm_const.rb +29 -0
- data/lib/typed/model/tm_defined.rb +19 -0
- data/lib/typed/model/tm_error.rb +16 -0
- data/lib/typed/model/tm_float.rb +15 -0
- data/lib/typed/model/tm_for.rb +42 -0
- data/lib/typed/model/tm_fun.rb +165 -0
- data/lib/typed/model/tm_global_var.rb +22 -0
- data/lib/typed/model/tm_global_var_assignment.rb +20 -0
- data/lib/typed/model/tm_hash_literal.rb +32 -0
- data/lib/typed/model/tm_if_else.rb +24 -0
- data/lib/typed/model/tm_instance_var.rb +23 -0
- data/lib/typed/model/tm_instance_var_assignment.rb +32 -0
- data/lib/typed/model/tm_int.rb +15 -0
- data/lib/typed/model/tm_local_var_asgn.rb +35 -0
- data/lib/typed/model/tm_mass_asgn.rb +60 -0
- data/lib/typed/model/tm_mlhs.rb +87 -0
- data/lib/typed/model/tm_module.rb +51 -0
- data/lib/typed/model/tm_next.rb +24 -0
- data/lib/typed/model/tm_nil.rb +14 -0
- data/lib/typed/model/tm_range_literal.rb +30 -0
- data/lib/typed/model/tm_regexp.rb +27 -0
- data/lib/typed/model/tm_rescue.rb +27 -0
- data/lib/typed/model/tm_return.rb +24 -0
- data/lib/typed/model/tm_s_class.rb +30 -0
- data/lib/typed/model/tm_self.rb +22 -0
- data/lib/typed/model/tm_send.rb +300 -0
- data/lib/typed/model/tm_sequencing.rb +53 -0
- data/lib/typed/model/tm_string.rb +15 -0
- data/lib/typed/model/tm_string_interpolation.rb +21 -0
- data/lib/typed/model/tm_super.rb +27 -0
- data/lib/typed/model/tm_symbol.rb +15 -0
- data/lib/typed/model/tm_symbol_interpolation.rb +21 -0
- data/lib/typed/model/tm_try.rb +29 -0
- data/lib/typed/model/tm_var.rb +28 -0
- data/lib/typed/model/tm_while.rb +43 -0
- data/lib/typed/model.rb +48 -0
- data/lib/typed/prelude.rb +939 -0
- data/lib/typed/prelude_existential_registry.bin +0 -0
- data/lib/typed/prelude_generic_registry.bin +0 -0
- data/lib/typed/prelude_registry.bin +0 -0
- data/lib/typed/runtime/ast_parser.rb +589 -0
- data/lib/typed/runtime/method_signature_processor.rb +72 -0
- data/lib/typed/runtime/normalization/validations.rb +47 -0
- data/lib/typed/runtime/normalization.rb +196 -0
- data/lib/typed/runtime/parser_context.rb +36 -0
- data/lib/typed/runtime/type_parser.rb +215 -0
- data/lib/typed/runtime/type_registry.rb +170 -0
- data/lib/typed/runtime/type_signature_processor.rb +34 -0
- data/lib/typed/runtime.rb +33 -0
- data/lib/typed/type_signature/parser.rb +240 -0
- data/lib/typed/types/polymorphism/existential_type_variable.rb +13 -0
- data/lib/typed/types/polymorphism/generic_comparisons.rb +134 -0
- data/lib/typed/types/polymorphism/generic_variables.rb +24 -0
- data/lib/typed/types/polymorphism/type_variable.rb +138 -0
- data/lib/typed/types/polymorphism/type_variable_register.rb +298 -0
- data/lib/typed/types/polymorphism/unification.rb +579 -0
- data/lib/typed/types/ty_boolean.rb +15 -0
- data/lib/typed/types/ty_dynamic.rb +39 -0
- data/lib/typed/types/ty_either.rb +168 -0
- data/lib/typed/types/ty_error.rb +18 -0
- data/lib/typed/types/ty_existential_type.rb +22 -0
- data/lib/typed/types/ty_function.rb +144 -0
- data/lib/typed/types/ty_generic_function.rb +115 -0
- data/lib/typed/types/ty_generic_object.rb +180 -0
- data/lib/typed/types/ty_generic_singleton_object.rb +238 -0
- data/lib/typed/types/ty_object.rb +256 -0
- data/lib/typed/types/ty_singleton_object.rb +78 -0
- data/lib/typed/types/ty_stack_jump.rb +44 -0
- data/lib/typed/types/ty_top_level_object.rb +38 -0
- data/lib/typed/types.rb +60 -0
- data/lib/typed/typing_context.rb +206 -0
- data/lib/typed/version.rb +3 -0
- data/lib/typed.rb +161 -0
- data/spec/lib/ast_parser_spec.rb +101 -0
- data/spec/lib/examples/animals.rb +44 -0
- data/spec/lib/examples/counter.rb +16 -0
- data/spec/lib/examples/if.rb +31 -0
- data/spec/lib/language_spec.rb +36 -0
- data/spec/lib/model/tm_abs_spec.rb +66 -0
- data/spec/lib/model/tm_array_literal_spec.rb +36 -0
- data/spec/lib/model/tm_case_when_spec.rb +39 -0
- data/spec/lib/model/tm_class_spec.rb +67 -0
- data/spec/lib/model/tm_defined_spec.rb +10 -0
- data/spec/lib/model/tm_for_spec.rb +150 -0
- data/spec/lib/model/tm_fun_spec.rb +11 -0
- data/spec/lib/model/tm_hash_literal_spec.rb +40 -0
- data/spec/lib/model/tm_mass_asgn_spec.rb +104 -0
- data/spec/lib/model/tm_module_spec.rb +42 -0
- data/spec/lib/model/tm_regexp_spec.rb +9 -0
- data/spec/lib/model/tm_return_spec.rb +47 -0
- data/spec/lib/model/tm_s_class_spec.rb +27 -0
- data/spec/lib/model/tm_self_spec.rb +19 -0
- data/spec/lib/model/tm_string_interpolation_spec.rb +10 -0
- data/spec/lib/model/tm_symbol_interpolation_spec.rb +10 -0
- data/spec/lib/model/tm_symbol_spec.rb +9 -0
- data/spec/lib/model/tm_while_spec.rb +141 -0
- data/spec/lib/polymorphism/type_variable_spec.rb +14 -0
- data/spec/lib/polymorphism/unification_spec.rb +328 -0
- data/spec/lib/prelude/array_spec.rb +263 -0
- data/spec/lib/prelude/class_spec.rb +12 -0
- data/spec/lib/prelude/enumerable_spec.rb +278 -0
- data/spec/lib/prelude/enumerator_spec.rb +101 -0
- data/spec/lib/prelude/hash_spec.rb +361 -0
- data/spec/lib/prelude/kernel_spec.rb +23 -0
- data/spec/lib/prelude/object_spec.rb +22 -0
- data/spec/lib/prelude/pair_spec.rb +16 -0
- data/spec/lib/prelude/showable_spec.rb +31 -0
- data/spec/lib/prelude/string_spec.rb +98 -0
- data/spec/lib/runtime/normalization_spec.rb +29 -0
- data/spec/lib/runtime/validations_spec.rb +56 -0
- data/spec/lib/runtime_spec.rb +503 -0
- data/spec/lib/type_signature/parser_spec.rb +239 -0
- data/spec/lib/types/comparisons_spec.rb +35 -0
- data/spec/lib/types/polymorphism/generic_comparisons_spec.rb +492 -0
- data/spec/lib/types/polymorphism/type_variable_register_spec.rb +128 -0
- data/spec/lib/types/ty_dynamic_spec.rb +103 -0
- data/spec/lib/types/ty_either_spec.rb +288 -0
- data/spec/lib/types/ty_error_spec.rb +18 -0
- data/spec/lib/types/ty_generic_object_spec.rb +78 -0
- data/spec/lib/types/ty_generic_singleton_object_spec.rb +288 -0
- data/spec/lib/types/typing_context_spec.rb +86 -0
- data/spec/lib/types_spec.rb +174 -0
- data/spec/lib/typing/boolean_asgn_spec.rb +134 -0
- data/spec/lib/typing/break_spec.rb +79 -0
- data/spec/lib/typing/generics_spec.rb +191 -0
- data/spec/lib/typing/instance_vars_spec.rb +103 -0
- data/spec/lib/typing/next_spec.rb +29 -0
- data/spec/lib/typing/op_asgn_spec.rb +104 -0
- data/spec/lib/typing/overriden_methods_spec.rb +31 -0
- data/spec/lib/typing/subtyping_spec.rb +112 -0
- data/spec/lib/typing/tm_boolean_operator_spec.rb +100 -0
- data/spec/lib/typing/tm_boolean_spec.rb +61 -0
- data/spec/lib/typing/tm_const_spec.rb +28 -0
- data/spec/lib/typing/tm_defined_spec.rb +12 -0
- data/spec/lib/typing/tm_fun_spec.rb +347 -0
- data/spec/lib/typing/tm_global_var_spec.rb +33 -0
- data/spec/lib/typing/tm_if_else_spec.rb +104 -0
- data/spec/lib/typing/tm_ignore_spec.rb +24 -0
- data/spec/lib/typing/tm_instance_vars_spec.rb +117 -0
- data/spec/lib/typing/tm_local_var_asgn_spec.rb +134 -0
- data/spec/lib/typing/tm_mlhs_spec.rb +164 -0
- data/spec/lib/typing/tm_module_spec.rb +89 -0
- data/spec/lib/typing/tm_raise_spec.rb +31 -0
- data/spec/lib/typing/tm_range_literal_spec.rb +25 -0
- data/spec/lib/typing/tm_regexp_spec.rb +14 -0
- data/spec/lib/typing/tm_return_spec.rb +45 -0
- data/spec/lib/typing/tm_send_casting_spec.rb +26 -0
- data/spec/lib/typing/tm_send_class_methods_spec.rb +42 -0
- data/spec/lib/typing/tm_send_generic_apply_spec.rb +103 -0
- data/spec/lib/typing/tm_send_generic_methods_spec.rb +77 -0
- data/spec/lib/typing/tm_send_initialize_spec.rb +68 -0
- data/spec/lib/typing/tm_send_lambda_spec.rb +135 -0
- data/spec/lib/typing/tm_send_spec.rb +217 -0
- data/spec/lib/typing/tm_send_yield_block_spec.rb +308 -0
- data/spec/lib/typing/tm_sequencing_spec.rb +174 -0
- data/spec/lib/typing/tm_string_interpolation_spec.rb +19 -0
- data/spec/lib/typing/tm_super_spec.rb +63 -0
- data/spec/lib/typing/tm_symbol_interpolation_spec.rb +19 -0
- data/spec/lib/typing/tm_symbol_spec.rb +14 -0
- data/spec/lib/typing/tm_try_spec.rb +73 -0
- data/spec/spec_helper.rb +140 -0
- 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,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
|