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,238 @@
|
|
1
|
+
require_relative 'ty_singleton_object'
|
2
|
+
require_relative 'polymorphism/generic_comparisons'
|
3
|
+
require_relative 'polymorphism/generic_variables'
|
4
|
+
|
5
|
+
module TypedRb
|
6
|
+
module Types
|
7
|
+
class TyGenericSingletonObject < TySingletonObject
|
8
|
+
include Polymorphism::GenericComparisons
|
9
|
+
include Polymorphism::GenericVariables
|
10
|
+
|
11
|
+
attr_accessor :local_typing_context, :super_type
|
12
|
+
|
13
|
+
def initialize(ruby_type, type_vars, super_type = nil, node = nil)
|
14
|
+
super(ruby_type, node)
|
15
|
+
@super_type = super_type
|
16
|
+
@type_vars = type_vars
|
17
|
+
@application_count = 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def materialize_with_type_vars(type_vars, bound_type)
|
21
|
+
TypedRb.log binding, :debug, "Materialising generic singleton object with type vars '#{self}' <= #{type_vars.map(&:to_s).join(',')} :: #{bound_type}"
|
22
|
+
bound_type_vars = self.type_vars.map do |type_var|
|
23
|
+
maybe_class_bound = type_vars.detect do |bound_type_var|
|
24
|
+
type_var.variable == bound_type_var.variable
|
25
|
+
end
|
26
|
+
if maybe_class_bound.nil?
|
27
|
+
# it has to be method generic variable
|
28
|
+
type_var
|
29
|
+
else
|
30
|
+
maybe_class_bound
|
31
|
+
end
|
32
|
+
end
|
33
|
+
materialize(bound_type_vars.map { |bound_type_var| bound_type_var.send(bound_type) })
|
34
|
+
end
|
35
|
+
|
36
|
+
def self_materialize
|
37
|
+
TypedRb.log binding, :debug, "Materialising self for generic singleton object '#{self}'"
|
38
|
+
BasicObject::TypeRegistry.find_generic_type(ruby_type).materialize(type_vars)
|
39
|
+
end
|
40
|
+
|
41
|
+
# materialize will be invoked by the logic handling invocations like:
|
42
|
+
# ts 'MyClass[X][Y]'
|
43
|
+
# class MyClass
|
44
|
+
# ...
|
45
|
+
# end
|
46
|
+
# MyClass.(TypeArg1, TypeArg2) -> make X<TypeArg1, Y<TypeArg2, X>TypeArg1, X>TypeArg2
|
47
|
+
# MyClass.(TypeArg1, TypeArg2) -> Materialize here > make X<TypeArg1, Y<TypeArg2 > Unification
|
48
|
+
def materialize(actual_arguments)
|
49
|
+
TypedRb.log binding, :debug, "Materialising generic singleton object '#{self}' with args [#{actual_arguments.map(&:to_s).join(',')}]"
|
50
|
+
# This can happen when we're dealing with a generic singleton object that has only been
|
51
|
+
# annotated but we don't have the annotated implementation. e.g. Array[T]
|
52
|
+
# We need to provide a default local_type_context based on the upper bounds provided in the
|
53
|
+
# type annotation.
|
54
|
+
compute_minimal_typing_context if @local_typing_context.nil?
|
55
|
+
|
56
|
+
applied_typing_context, substitutions = @local_typing_context.clone(:class)
|
57
|
+
fresh_vars_generic_type = clone_with_substitutions(substitutions)
|
58
|
+
TypingContext.with_context(applied_typing_context) do
|
59
|
+
# Appy constraints for application of Type args
|
60
|
+
apply_type_arguments(fresh_vars_generic_type, actual_arguments)
|
61
|
+
end
|
62
|
+
# got all the constraints here
|
63
|
+
# do something with the context -> unification? merge context?
|
64
|
+
# applied_typing_context.all_constraints.each{|(l,t,r)| puts "#{l} #{t} #{r}" }
|
65
|
+
unification = Polymorphism::Unification.new(applied_typing_context.all_constraints).run
|
66
|
+
applied_typing_context.unlink # these constraints have already been satisfied
|
67
|
+
# - Create a new ty_generic_object for the unified types
|
68
|
+
# - Apply the unified types to all the methods in the class/instance
|
69
|
+
# - this can be dynamically done with the right implementation of find_function_type
|
70
|
+
# - Make the class available for the type checking system, so it can be found when
|
71
|
+
# - this can be done, just returning the new ty_singleton_object with the unified types
|
72
|
+
# - messages will be redirected to that instance and find_function_type/ find_var_type / as_object
|
73
|
+
# will handle the mesage
|
74
|
+
# - looking for messages at the instance level
|
75
|
+
# - this can be accomplished with the overloading version of as_object_type, that will return
|
76
|
+
# an instance of a new class ty_generic_object with overloaded versions of find_function_type /find_var_type
|
77
|
+
########################
|
78
|
+
fresh_vars_generic_type.apply_bindings(unification.bindings_map)
|
79
|
+
end
|
80
|
+
|
81
|
+
# TODO: We do need this for cases like Array.(Int).class_method
|
82
|
+
|
83
|
+
# def find_function_type(message)
|
84
|
+
# function_type = BasicObject::TypeRegistry.find(:class, ruby_type, message)
|
85
|
+
# replace_bound_type_vars(function_type, type_vars)
|
86
|
+
# end
|
87
|
+
|
88
|
+
# def find_function_type(message)
|
89
|
+
# BasicObject::TypeRegistry.find(:class, ruby_type, message)
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# def find_var_type(var)
|
93
|
+
# var_type = BasicObject::TypeRegistry.find(:class_variable, ruby_type, var)
|
94
|
+
# if var_type
|
95
|
+
# var_type
|
96
|
+
# else
|
97
|
+
# Types::TypingContext.type_variable_for(:class_variable, var, hierarchy)
|
98
|
+
# end
|
99
|
+
# end
|
100
|
+
|
101
|
+
def as_object_type
|
102
|
+
# this should only be used to check the body type of this
|
103
|
+
# class. The variables are going to be unbound.
|
104
|
+
# This is also used in instantiation of the generic object.
|
105
|
+
TyGenericObject.new(ruby_type, @type_vars)
|
106
|
+
end
|
107
|
+
|
108
|
+
def compute_minimal_typing_context
|
109
|
+
Model::TmClass.with_fresh_bindings(self, nil, node)
|
110
|
+
end
|
111
|
+
|
112
|
+
def generic?
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
116
|
+
def apply_bindings(bindings_map)
|
117
|
+
type_vars(recursive: false).each_with_index do |var, _i|
|
118
|
+
if var.is_a?(Polymorphism::TypeVariable) && var.bound_to_generic?
|
119
|
+
var.bind(var.bound.apply_bindings(bindings_map))
|
120
|
+
elsif var.is_a?(Polymorphism::TypeVariable)
|
121
|
+
var.apply_bindings(bindings_map)
|
122
|
+
elsif var.is_a?(TyGenericSingletonObject) || var.is_a?(TyGenericObject)
|
123
|
+
var.apply_bindings(bindings_map)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
def clone
|
130
|
+
cloned_type_vars = type_vars.map(&:clone)
|
131
|
+
TyGenericSingletonObject.new(ruby_type, cloned_type_vars, super_type, node)
|
132
|
+
end
|
133
|
+
|
134
|
+
def to_s
|
135
|
+
base_string = super
|
136
|
+
var_types_strings = @type_vars.map do |var_type|
|
137
|
+
if !var_type.is_a?(Polymorphism::TypeVariable)
|
138
|
+
"[#{var_type}]"
|
139
|
+
elsif var_type.bound && var_type.bound.is_a?(Polymorphism::TypeVariable)
|
140
|
+
"[#{var_type.variable} <= #{var_type.bound.bound || var_type.bound.variable}]"
|
141
|
+
else
|
142
|
+
"[#{var_type.bound || var_type.variable}]"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
"#{base_string}#{var_types_strings.join}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def clone_with_substitutions(substitutions)
|
149
|
+
materialized_type_vars = type_vars(recursive: false).map do |type_var|
|
150
|
+
if type_var.is_a?(Polymorphism::TypeVariable) && type_var.bound_to_generic?
|
151
|
+
new_type_var = Polymorphism::TypeVariable.new(type_var.variable, node: type_var.node, gen_name: false)
|
152
|
+
new_type_var.to_wildcard! if type_var.wildcard?
|
153
|
+
bound = type_var.bound.clone_with_substitutions(substitutions)
|
154
|
+
new_type_var.bind(bound)
|
155
|
+
new_type_var.upper_bound = bound if type_var.upper_bound
|
156
|
+
new_type_var.lower_bound = bound if type_var.lower_bound
|
157
|
+
new_type_var
|
158
|
+
elsif type_var.is_a?(Polymorphism::TypeVariable)
|
159
|
+
substitutions[type_var.variable] || type_var.clone
|
160
|
+
elsif type_var.is_a?(TyGenericSingletonObject) || type_var.is_a?(TyGenericObject)
|
161
|
+
type_var.clone_with_substitutions(substitutions)
|
162
|
+
else
|
163
|
+
type_var
|
164
|
+
end
|
165
|
+
end
|
166
|
+
self.class.new(ruby_type, materialized_type_vars, super_type, node)
|
167
|
+
end
|
168
|
+
|
169
|
+
protected
|
170
|
+
|
171
|
+
def apply_type_arguments(fresh_vars_generic_type, actual_arguments)
|
172
|
+
fresh_vars_generic_type.type_vars.each_with_index do |type_var, i|
|
173
|
+
if type_var.bound.is_a?(TyGenericSingletonObject)
|
174
|
+
type_var.bind(apply_type_arguments_recursively(type_var.bound, actual_arguments))
|
175
|
+
else
|
176
|
+
apply_type_argument(actual_arguments[i], type_var)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def apply_type_argument(argument, type_var)
|
182
|
+
if argument.is_a?(Polymorphism::TypeVariable)
|
183
|
+
if argument.wildcard?
|
184
|
+
# Wild card type
|
185
|
+
# If the type is T =:= E < Type1 or E > Type1 only that constraint should be added
|
186
|
+
{ :lt => :upper_bound, :gt => :lower_bound }.each do |relation, bound|
|
187
|
+
if argument.send(bound)
|
188
|
+
value = if argument.send(bound).is_a?(TyGenericSingletonObject)
|
189
|
+
argument.send(bound).clone # .self_materialize
|
190
|
+
else
|
191
|
+
argument.send(bound)
|
192
|
+
end
|
193
|
+
type_var.compatible?(value, relation)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
type_var.to_wildcard! # WILD CARD
|
197
|
+
elsif argument.bound # var type with a particular value
|
198
|
+
argument = argument.bound
|
199
|
+
if argument.is_a?(TyGenericSingletonObject)
|
200
|
+
argument = argument.clone # .self_materialize
|
201
|
+
end
|
202
|
+
# This is only for matches T =:= Type1 -> T < Type1, T > Type1
|
203
|
+
fail Types::UncomparableTypes.new(type_var, argument) unless type_var.compatible?(argument, :lt)
|
204
|
+
fail Types::UncomparableTypes.new(type_var, argument) unless type_var.compatible?(argument, :gt)
|
205
|
+
else
|
206
|
+
# Type variable
|
207
|
+
type_var.bound = argument
|
208
|
+
type_var.lower_bound = argument
|
209
|
+
type_var.upper_bound = argument
|
210
|
+
end
|
211
|
+
else
|
212
|
+
if argument.is_a?(TyGenericSingletonObject)
|
213
|
+
argument = argument.clone # .self_materialize
|
214
|
+
end
|
215
|
+
# This is only for matches T =:= Type1 -> T < Type1, T > Type1
|
216
|
+
fail Types::UncomparableTypes.new(type_var, argument) unless type_var.compatible?(argument, :lt)
|
217
|
+
fail Types::UncomparableTypes.new(type_var, argument) unless type_var.compatible?(argument, :gt)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def apply_type_arguments_recursively(generic_type_bound, actual_arguments)
|
222
|
+
arg_names = actual_arguments_hash(actual_arguments)
|
223
|
+
recursive_actual_arguments = generic_type_bound.type_vars.map do |type_var|
|
224
|
+
arg_names[type_var.variable] || fail("Unbound type variable #{type_var.variable} for recursive generic type #{generic_type_bound}")
|
225
|
+
end
|
226
|
+
generic_type_bound.materialize(recursive_actual_arguments)
|
227
|
+
end
|
228
|
+
|
229
|
+
def actual_arguments_hash(actual_arguments)
|
230
|
+
acc = {}
|
231
|
+
type_vars.each_with_index do |type_var, i|
|
232
|
+
acc[type_var.variable] = actual_arguments[i]
|
233
|
+
end
|
234
|
+
acc
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,256 @@
|
|
1
|
+
module TypedRb
|
2
|
+
module Types
|
3
|
+
class UncomparableTypes < TypeCheckError
|
4
|
+
attr_reader :from, :to
|
5
|
+
def initialize(from, to, node = nil)
|
6
|
+
nodes = [from.node, to.node].compact
|
7
|
+
if node
|
8
|
+
super("Cannot compare types #{from} <=> #{to}", node)
|
9
|
+
elsif nodes.size == 2
|
10
|
+
super("Cannot compare types #{from} <=> #{to}", nodes)
|
11
|
+
elsif nodes.size == 1
|
12
|
+
super("Cannot compare types #{from} <=> #{to}", nodes.first)
|
13
|
+
else
|
14
|
+
super("Cannot compare types #{from} <=> #{to}", nil)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class TyObject < Type
|
20
|
+
include Comparable
|
21
|
+
|
22
|
+
attr_reader :hierarchy, :classes, :modules, :ruby_type, :with_ruby_type
|
23
|
+
|
24
|
+
def initialize(ruby_type, node = nil, classes = [], modules = [])
|
25
|
+
super(node)
|
26
|
+
if ruby_type
|
27
|
+
@ruby_type = ruby_type
|
28
|
+
@hierarchy = ruby_type.ancestors
|
29
|
+
@classes = @hierarchy.select { |klass| klass.instance_of?(Class) }
|
30
|
+
@modules = @hierarchy.select { |klass| klass.instance_of?(Module) }
|
31
|
+
@with_ruby_type = true
|
32
|
+
else
|
33
|
+
@ruby_type = classes.first
|
34
|
+
@classes = classes
|
35
|
+
@modules = modules
|
36
|
+
@hierarchy = (modules + classes).uniq
|
37
|
+
@with_ruby_type = false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def dynamic?
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def generic?
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
def singleton?
|
50
|
+
false
|
51
|
+
end
|
52
|
+
|
53
|
+
def either?
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
def check_type(_context)
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def compatible?(other_type, relation = :lt)
|
62
|
+
if other_type.is_a?(TyObject)
|
63
|
+
begin
|
64
|
+
if relation == :gt
|
65
|
+
self >= other_type
|
66
|
+
elsif relation == :lt
|
67
|
+
self <= other_type
|
68
|
+
end
|
69
|
+
rescue ArgumentError
|
70
|
+
raise UncomparableTypes.new(self, other_type)
|
71
|
+
end
|
72
|
+
else
|
73
|
+
other_type.compatible?(self, relation == :lt ? :gt : :lt)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def as_object_type
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
# Non generic type, the function is alwasy going to be concrete
|
82
|
+
def find_function_type(message, num_args, block)
|
83
|
+
klass, function = find_function_type_in_hierarchy(:instance, message, num_args, block)
|
84
|
+
if klass != ruby_type && function.generic?
|
85
|
+
generic_type = ::BasicObject::TypeRegistry.find_generic_type(klass)
|
86
|
+
if generic_type.nil?
|
87
|
+
return klass, function # generic method in non-generic class
|
88
|
+
elsif generic_type.type_vars.size == 1
|
89
|
+
generic_type.materialize([self]).find_function_type(message, num_args, block)
|
90
|
+
else
|
91
|
+
fail "Undeclared generic type variables for #{ruby_type} super class/mix-in #{klass.class} #{klass}##{message}, please add a 'super' type annotation"
|
92
|
+
end
|
93
|
+
else
|
94
|
+
return klass, function
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def find_var_type(var, _type = ruby_type)
|
99
|
+
# This is only in case the type has been explicitely declared
|
100
|
+
var_type = BasicObject::TypeRegistry.find(:instance_variable, ruby_type, var)
|
101
|
+
if var_type
|
102
|
+
var_type
|
103
|
+
else
|
104
|
+
# If no types has been declared, we'll find a var type in the registry
|
105
|
+
var_type = Types::TypingContext.type_variable_for(:instance_variable, var, hierarchy)
|
106
|
+
var_type.node = node
|
107
|
+
var_type
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def find_function_type_in_hierarchy(kind, message, num_args, block)
|
112
|
+
initial_value = select_matching_function_in_class(@hierarchy.first, kind, message, num_args, block)
|
113
|
+
@hierarchy.drop(1).inject([@hierarchy.first, initial_value]) do |(klass, acc), type|
|
114
|
+
if acc.nil? || acc.is_a?(TyDynamicFunction)
|
115
|
+
maybe_function = select_matching_function_in_class(type, kind, message, num_args, block)
|
116
|
+
[type, (maybe_function || TyDynamicFunction.new(klass, message))]
|
117
|
+
else
|
118
|
+
[klass, acc]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def resolve_ruby_method(message)
|
124
|
+
@ruby_type.instance_method(message)
|
125
|
+
end
|
126
|
+
|
127
|
+
def to_s
|
128
|
+
if @with_ruby_type
|
129
|
+
@ruby_type.name
|
130
|
+
else
|
131
|
+
"#{@classes.first} with [#{@modules.map(&:to_s).join(',')}]"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def union(other_type, node = nil)
|
136
|
+
smaller_common_class = (classes & other_type.classes).first
|
137
|
+
TyObject.new(smaller_common_class, (node || self.node || other_type.node))
|
138
|
+
end
|
139
|
+
|
140
|
+
def <=>(other)
|
141
|
+
if other.is_a?(TyObject)
|
142
|
+
if other.with_ruby_type
|
143
|
+
if with_ruby_type
|
144
|
+
compare_ruby_ruby(other)
|
145
|
+
else
|
146
|
+
compare_with_union(other)
|
147
|
+
end
|
148
|
+
else
|
149
|
+
if with_ruby_type
|
150
|
+
compare_with_union(other)
|
151
|
+
else
|
152
|
+
compare_with_union(other)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
#else
|
156
|
+
# fail UncomparableTypes.new(self, other)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def join(other)
|
161
|
+
common_classes = classes & other.classes
|
162
|
+
common_modules = modules & other.modules
|
163
|
+
if common_modules.size == 1
|
164
|
+
TyObject.new(common_modules.first, (node || other.node))
|
165
|
+
else
|
166
|
+
TyObject.new(nil, (node || other.node), common_classes, common_modules)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
protected
|
171
|
+
|
172
|
+
def compare_ruby_ruby(other)
|
173
|
+
if ruby_type == NilClass && other.ruby_type == NilClass
|
174
|
+
0
|
175
|
+
elsif ruby_type == NilClass
|
176
|
+
-1
|
177
|
+
elsif other.ruby_type == NilClass
|
178
|
+
1
|
179
|
+
else
|
180
|
+
if other.ruby_type == ruby_type
|
181
|
+
0
|
182
|
+
elsif other.hierarchy.include?(ruby_type)
|
183
|
+
1
|
184
|
+
elsif hierarchy.include?(other.ruby_type)
|
185
|
+
-1
|
186
|
+
#else
|
187
|
+
# fail UncomparableTypes.new(self, other)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def compare_with_union(other)
|
193
|
+
all_those_modules_included = other.modules.all? { |m| hierarchy.include?(m) }
|
194
|
+
all_these_modules_included = modules.all? { |m| other.hierarchy.include?(m) }
|
195
|
+
|
196
|
+
if other.ruby_type == ruby_type && all_these_modules_included && all_these_modules_included
|
197
|
+
0
|
198
|
+
elsif other.hierarchy.include?(ruby_type) && all_these_modules_included && !all_those_modules_included
|
199
|
+
1
|
200
|
+
elsif hierarchy.include?(ruby_type) && all_those_modules_included && !all_these_modules_included
|
201
|
+
-1
|
202
|
+
#else
|
203
|
+
# fail UncomparableTypes.new(self, other)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def select_matching_function_in_class(klass, kind, message, num_args, block)
|
208
|
+
functions = BasicObject::TypeRegistry.find(kind, klass, message)
|
209
|
+
initial_values = functions.select { |fn| fn.arg_compatible?(num_args) }
|
210
|
+
if initial_values.count == 2 && block
|
211
|
+
initial_values.detect(&:block_type)
|
212
|
+
elsif initial_values.count == 2 && !block
|
213
|
+
initial_values.detect { |f| f.block_type.nil? }
|
214
|
+
else
|
215
|
+
initial_values.first
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
class TyInteger < TyObject
|
221
|
+
def initialize(node = nil)
|
222
|
+
super(Integer, node)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
class TyFloat < TyObject
|
227
|
+
def initialize(node = nil)
|
228
|
+
super(Float, node)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
class TyString < TyObject
|
233
|
+
def initialize(node = nil)
|
234
|
+
super(String, node)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
class TyUnit < TyObject
|
239
|
+
def initialize(node = nil)
|
240
|
+
super(NilClass, node)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
class TySymbol < TyObject
|
245
|
+
def initialize(node = nil)
|
246
|
+
super(Symbol, node)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
class TyRegexp < TyObject
|
251
|
+
def initialize(node = nil)
|
252
|
+
super(Regexp, node)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module TypedRb
|
2
|
+
module Types
|
3
|
+
class TySingletonObject < TyObject
|
4
|
+
def initialize(ruby_type, node = nil)
|
5
|
+
# super(ruby_type.class)
|
6
|
+
# @ruby_type = ruby_type
|
7
|
+
super(ruby_type, node)
|
8
|
+
end
|
9
|
+
|
10
|
+
# No generic type, function will always be concrete
|
11
|
+
def find_function_type(message, num_args, block)
|
12
|
+
maybe_function = select_matching_function_in_class(ruby_type, :class, message, num_args, block)
|
13
|
+
if maybe_function && !maybe_function.dynamic?
|
14
|
+
[ruby_type, maybe_function]
|
15
|
+
else
|
16
|
+
# This object is a class, we need to look in the hierarhcy of the meta-class
|
17
|
+
find_function_type_in_metaclass_hierarchy(message, num_args, block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_function_type_in_metaclass_hierarchy(message, num_args, block)
|
22
|
+
hierarchy = Class.ancestors
|
23
|
+
initial_value = select_matching_function_in_class(hierarchy.first, :instance, message, num_args, block)
|
24
|
+
hierarchy.drop(1).inject([hierarchy.first, initial_value]) do |(klass, acc), type|
|
25
|
+
if acc.nil? || acc.is_a?(TyDynamicFunction)
|
26
|
+
maybe_function = select_matching_function_in_class(type, :instance, message, num_args, block)
|
27
|
+
[type, (maybe_function || TyDynamicFunction.new(klass, message))]
|
28
|
+
else
|
29
|
+
[klass, acc]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_var_type(var)
|
35
|
+
var_type = BasicObject::TypeRegistry.find(:class_variable, ruby_type, var)
|
36
|
+
if var_type
|
37
|
+
var_type
|
38
|
+
else
|
39
|
+
var_type = Types::TypingContext.type_variable_for(:class_variable, var, hierarchy)
|
40
|
+
var_type.node = node
|
41
|
+
var_type
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def compatible?(other_type, relation = :lt)
|
46
|
+
if other_type.is_a?(TySingletonObject)
|
47
|
+
if ruby_type == Class || other_type.ruby_type == Class
|
48
|
+
if relation == :gt
|
49
|
+
Class.ancestors.include(ruby_type)
|
50
|
+
elsif relation == :lt
|
51
|
+
Class.ancestors.include?(other_type.ruby_type)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
super(other_type, relation)
|
55
|
+
end
|
56
|
+
else
|
57
|
+
super(other_type, relation)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def resolve_ruby_method(message)
|
62
|
+
@ruby_type.singleton_method(message)
|
63
|
+
end
|
64
|
+
|
65
|
+
def as_object_type
|
66
|
+
TyObject.new(ruby_type, node)
|
67
|
+
end
|
68
|
+
|
69
|
+
def singleton?
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_s
|
74
|
+
@ruby_type.name
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module TypedRb
|
2
|
+
module Types
|
3
|
+
class TyStackJump < TyUnit
|
4
|
+
attr_reader :jump_kind, :wrapped_type
|
5
|
+
def initialize(jump_kind, wrapped_type, node=nil)
|
6
|
+
super(node)
|
7
|
+
@jump_kind = jump_kind
|
8
|
+
@wrapped_type = wrapped_type
|
9
|
+
end
|
10
|
+
|
11
|
+
def stack_jump?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.return(return_type, node = nil)
|
16
|
+
TyStackJump.new(:return, return_type, node)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.break(return_type, node = nil)
|
20
|
+
TyStackJump.new(:break, return_type, node)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.next(return_type, node = nil)
|
24
|
+
TyStackJump.new(:next, return_type, node)
|
25
|
+
end
|
26
|
+
|
27
|
+
def return?
|
28
|
+
jump_kind == :return
|
29
|
+
end
|
30
|
+
|
31
|
+
def break?
|
32
|
+
jump_kind == :break
|
33
|
+
end
|
34
|
+
|
35
|
+
def next?
|
36
|
+
jump_kind == :next
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"Jump[#{jump_kind}:#{wrapped_type}]"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module TypedRb
|
2
|
+
module Types
|
3
|
+
class TyTopLevelObject < TyObject
|
4
|
+
def initialize
|
5
|
+
super(TOPLEVEL_BINDING.receiver.class)
|
6
|
+
end
|
7
|
+
|
8
|
+
def compatible?(_other_type)
|
9
|
+
fail StandardError, 'invoking compatible? in the top level object'
|
10
|
+
end
|
11
|
+
|
12
|
+
def as_object_type
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_function_type(message, num_args, block)
|
17
|
+
found_type = select_matching_function_in_class(:main, :instance, message, num_args, block)
|
18
|
+
if found_type && !found_type.is_a?(TyDynamicFunction)
|
19
|
+
[:main, found_type]
|
20
|
+
else
|
21
|
+
TyObject.new(ruby_type, node).find_function_type(message, num_args, block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_var_type(var)
|
26
|
+
super(var, :main)
|
27
|
+
end
|
28
|
+
|
29
|
+
def resolve_ruby_method(message)
|
30
|
+
@ruby_type.method(message)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
'Object[\'main\']'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|