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,168 @@
1
+ module TypedRb
2
+ module Types
3
+ class TyEither < TyObject
4
+ attr_accessor :options
5
+ def initialize(node=nil)
6
+ super(NilClass, node)
7
+ @options = { :normal => TypedRb::Types::TyUnit.new }
8
+ end
9
+
10
+ def self.wrap(type)
11
+ if type.either?
12
+ type
13
+ elsif type.stack_jump?
14
+ either = TyEither.new(type.node)
15
+ either[type.jump_kind] = type
16
+ either
17
+ else
18
+ either = TyEither.new(type.node)
19
+ either[:normal] = type
20
+ either
21
+ end
22
+ end
23
+
24
+ def unwrap
25
+ normal = self[:normal].is_a?(TypedRb::Types::TyUnit) ? nil : self[:normal]
26
+ wrapped_types = [normal, self[:return], self[:break], self[:next]].compact
27
+ if wrapped_types.count > 1
28
+ self
29
+ elsif wrapped_types.count == 1
30
+ wrapped_types.first
31
+ else
32
+ TypedRb::Types::TyUnit.new
33
+ end
34
+ end
35
+
36
+ def either?
37
+ true
38
+ end
39
+
40
+ def return?
41
+ !options[:return].nil? && options[:return].return?
42
+ end
43
+
44
+ def break?
45
+ !options[:break].nil? && options[:break].break?
46
+ end
47
+
48
+ def next?
49
+ !options[:next].nil? && options[:next].next?
50
+ end
51
+
52
+ def has_jump?
53
+ !(options[:return] || options[:next] || options[:break]).nil?
54
+ end
55
+
56
+ def kinds
57
+ [:return, :next, :break]
58
+ end
59
+
60
+ def all_kinds
61
+ [:normal] + kinds
62
+ end
63
+
64
+ def [](kind)
65
+ valid_kind?(kind)
66
+ options[kind]
67
+ end
68
+
69
+ def []=(kind, value)
70
+ valid_kind?(kind)
71
+ options[kind] = value
72
+ end
73
+
74
+ def check_type(context, types=[:return])
75
+ relevant_types = types.map { |type| self[type] }.reject(&:nil?)
76
+ relevant_types = relevant_types.map { |type| type.stack_jump? ? type.wrapped_type : type }
77
+ relevant_types = relevant_types.map { |type| type.check_type(context) }
78
+ relevant_types.max rescue relevant_types.reduce { |type_a, type_b| type_a.union(type_b) }
79
+ end
80
+
81
+ # This compatible function is to use the normal wrapped type in regular comparisons
82
+ def compatible?(other_type, relation = :lt)
83
+ (options[:normal] || TyUnit.new(node)).compatible?(other_type, relation)
84
+ end
85
+
86
+ # This compatible function is to build the comparison in conditional terms
87
+ def compatible_either?(other_type)
88
+ if other_type.either? # either vs either
89
+ kinds.each do |kind|
90
+ check_jump_kind(kind, other_type[kind])
91
+ end
92
+ check_normal_kind(other_type[:normal])
93
+ elsif other_type.stack_jump? # either vs jump
94
+ check_jump_kind(other_type.jump_kind, other_type)
95
+ else # either vs normal flow
96
+ check_normal_kind(other_type)
97
+ end
98
+ self
99
+ end
100
+
101
+ def to_s
102
+ vals = options.to_a.reject {|(k,v)| v.nil? }.map{ |k,v| "#{k}:#{v}" }.join(" | ")
103
+ "Either[#{vals}]"
104
+ end
105
+
106
+ def apply_bindings(bindings_map)
107
+ all_kinds.each do |kind|
108
+ if self[kind]
109
+ if self[kind].is_a?(Polymorphism::TypeVariable)
110
+ self[kind].apply_bindings(bindings_map)
111
+ self[kind] = self[kind].bound if self[kind].bound
112
+ elsif self[kind].is_a?(TyGenericSingletonObject) || self[kind].is_a?(TyGenericObject)
113
+ self[kind].apply_bindings(bindings_map)
114
+ end
115
+ end
116
+ end
117
+ self
118
+ end
119
+
120
+ private
121
+
122
+ def check_jump_kind(kind, other_type)
123
+ if self[kind].nil? && other_type
124
+ self[kind] = other_type
125
+ elsif self[kind] && other_type
126
+ max_type = max(self[kind].wrapped_type, other_type.wrapped_type)
127
+ self[kind] = TyStackJump.new(kind, max_type)
128
+ end
129
+ end
130
+
131
+ def check_normal_kind(other_type)
132
+ self[:normal] = max(self[:normal], other_type)
133
+ end
134
+
135
+ def max(type_a, type_b)
136
+ return (type_a || type_b) if type_a.nil? || type_b.nil?
137
+ return type_b if type_a.is_a?(Types::TyDynamic) || type_a.is_a?(Types::TyDynamicFunction)
138
+ return type_a if type_b.is_a?(Types::TyDynamic) || type_b.is_a?(Types::TyDynamicFunction)
139
+ return type_b if type_a.is_a?(Types::TyError)
140
+ return type_a if type_b.is_a?(Types::TyError)
141
+
142
+ type_vars = [type_a, type_b].select { |type| type.is_a?(Polymorphism::TypeVariable) }
143
+ if type_vars.count == 2
144
+ type_vars[0].compatible?(type_vars[1], :lt)
145
+ type_vars[1].compatible?(type_vars[0], :lt)
146
+ type_vars[0]
147
+ elsif type_vars.count == 1
148
+ type_var = type_vars.first
149
+ non_type_var = ([type_a, type_b] - type_vars).first
150
+ type_var.compatible?(non_type_var, :gt)
151
+ type_var
152
+ else
153
+ begin
154
+ [type_a, type_b].max
155
+ rescue Exception => ex
156
+ type_a.union(type_b)
157
+ end
158
+ end
159
+ end
160
+
161
+ def valid_kind?(kind)
162
+ unless kind == :normal || kinds.include?(kind)
163
+ fail Exception, "Invalid kind of either type #{kind}"
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,18 @@
1
+ module TypedRb
2
+ module Types
3
+ class TyError < Type
4
+ def initialize(node = nil)
5
+ super(node)
6
+ end
7
+
8
+ def to_s
9
+ 'error'
10
+ end
11
+
12
+ def compatible?(_relation, _other_type = :lt)
13
+ true
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'ty_object'
2
+
3
+ module TypedRb
4
+ module Types
5
+ class TyExistentialType < TyObject
6
+ attr_accessor :local_typing_context, :self_variable
7
+
8
+ def initialize(ruby_type, node = nil)
9
+ super(ruby_type, node)
10
+ end
11
+
12
+ def check_inclusion(self_type)
13
+ cloned_context, _ = local_typing_context.clone(:module_self)
14
+ Types::TypingContext.with_context(cloned_context) do
15
+ context_self_type = Types::TypingContext.type_variable_for(ruby_type, :module_self, [ruby_type])
16
+ context_self_type.compatible?(self_type, :lt)
17
+ Types::Polymorphism::Unification.new(Types::TypingContext.all_constraints).run(false)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,144 @@
1
+ module TypedRb
2
+ module Types
3
+ class TyFunction < Type
4
+ include Comparable
5
+
6
+ attr_accessor :from, :to, :parameters_info, :block_type, :arity, :min_arity
7
+ attr_writer :name
8
+
9
+ def initialize(from, to, parameters_info = nil, node = nil)
10
+ super(node)
11
+ @from = from.is_a?(Array) ? from : [from]
12
+ @to = to
13
+ @parameters_info = parameters_info
14
+ if @parameters_info.nil?
15
+ @parameters_info = @from.map { |type| [:req, type] }
16
+ end
17
+ @arity = parse_function_arity
18
+ @min_arity = parse_min_function_arity
19
+ @block_type = nil
20
+ end
21
+
22
+ def with_block_type(type)
23
+ @block_type = type
24
+ self
25
+ end
26
+
27
+ def arg_compatible?(num_args)
28
+ num_args >= min_arity && (arity == Float::INFINITY || arity == num_args)
29
+ end
30
+
31
+ def generic?
32
+ false
33
+ end
34
+
35
+ def dynamic?
36
+ false
37
+ end
38
+
39
+ def to_s
40
+ args = @from.map(&:to_s).join(', ')
41
+ args = "#{args}, &#{block_type}" if block_type
42
+ "(#{args} -> #{@to})"
43
+ end
44
+
45
+ def name
46
+ @name || 'lambda'
47
+ end
48
+
49
+ def check_args_application(actual_arguments, context)
50
+ parameters_info.each_with_index do |(require_info, arg_name), index|
51
+ actual_argument = actual_arguments[index]
52
+ from_type = from[index]
53
+ if actual_argument.nil? && require_info != :opt
54
+ error_msg = "Type error checking function '#{name}': Missing mandatory argument #{arg_name} in #{receiver_type}##{message}"
55
+ fail TypeCheckError.new(error_msg, node)
56
+ else
57
+ unless actual_argument.nil? # opt if this is nil
58
+ actual_argument_type = actual_argument.check_type(context)
59
+ unless actual_argument_type.compatible?(from_type, :lt)
60
+ error_message = "Type error checking function '#{name}': #{error_message} #{from_type} expected, #{argument_type} found"
61
+ fail TypeCheckError.new(error_message, node)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ self
67
+ end
68
+
69
+ # (S1 -> S2) < (T1 -> T2) => T1 < S1 && S2 < T2
70
+ # Contravariant in the input, covariant in the output
71
+ def compatible?(other_type, relation = :lt)
72
+ if other_type.is_a?(TyGenericFunction)
73
+ other_type.compatible?(self, relation == :lt ? :gt : :lt)
74
+ elsif other_type.is_a?(TyFunction)
75
+ other_from = deconstruct_from_arguments(other_type)
76
+ from.each_with_index do |arg, i|
77
+ other_arg = other_from[i]
78
+ return false unless arg.compatible?(other_arg, :gt)
79
+ end
80
+ return false unless to.compatible?(other_type.to, :lt)
81
+ else
82
+ fail TypeCheckError.new("Type error checking function '#{name}': Comparing function type with no function type")
83
+ end
84
+ true
85
+ end
86
+
87
+ def deconstruct_from_arguments(other_type)
88
+ if from.size == other_type.from.size
89
+ other_type.from
90
+ elsif from.size > 1 && other_type.from.size == 1 && other_type.from.first.ruby_type.ancestors.include?(Pair)
91
+ other_type.from.first.type_vars(recursive: false)
92
+ elsif from.size > 1 && other_type.from.size == 1 && other_type.from.first.ruby_type.ancestors.include?(Array)
93
+ other_type.from.first.type_vars(recursive: false) * from.size
94
+ else
95
+ other_type.from
96
+ end
97
+ end
98
+
99
+ def apply_bindings(bindings_map)
100
+ from.each_with_index do |from_type, i|
101
+ if from_type.is_a?(Polymorphism::TypeVariable)
102
+ from_type.apply_bindings(bindings_map)
103
+ from[i] = from_type.bound if from_type.bound
104
+ elsif from_type.is_a?(TyGenericSingletonObject) || from_type.is_a?(TyGenericObject)
105
+ from_type.apply_bindings(bindings_map)
106
+ end
107
+ end
108
+
109
+ if to.is_a?(Polymorphism::TypeVariable)
110
+ @to = to.apply_bindings(bindings_map)
111
+ @to = to.bound if to.bound
112
+ elsif to.respond_to?(:apply_bindings)
113
+ @to = to.apply_bindings(bindings_map)
114
+ end
115
+
116
+ block_type.apply_bindings(bindings_map) if block_type && block_type.generic?
117
+ self
118
+ end
119
+
120
+ def <=>(other)
121
+ if other.is_a?(TyFunction)
122
+ # TODO: implement!
123
+ raise 'Non implemented yet'
124
+ else
125
+ TyObject.new(Method, node) <=> other
126
+ end
127
+ end
128
+
129
+
130
+ protected
131
+
132
+ def parse_function_arity
133
+ return Float::INFINITY if parameters_info.detect { |arg| arg.is_a?(Hash) && arg[:kind] == :rest }
134
+ parameters_info.reject { |arg| arg.is_a?(Hash) && arg[:kind] == :block_arg }.count
135
+ end
136
+
137
+ def parse_min_function_arity
138
+ parameters_info.select do |arg|
139
+ !(arg.is_a?(Hash) && (arg[:kind] == :rest || arg[:kind] == :block_arg ))
140
+ end.count
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,115 @@
1
+ module TypedRb
2
+ module Types
3
+ class TyGenericFunction < TyFunction
4
+ attr_accessor :local_typing_context
5
+
6
+ def initialize(from, to, parameters_info = nil, node = nil)
7
+ super(from, to, parameters_info, node)
8
+ @application_count = 0
9
+ end
10
+
11
+ def generic?
12
+ true
13
+ end
14
+
15
+ # Creates a new instance of a generic function with fresh type variables.
16
+ # Yields the function so new constraints can be added.
17
+ # Finally, it runs unification on the function typing context and returns
18
+ # the materialized function with the bound variables.
19
+ def materialize
20
+ TypedRb.log binding, :debug, "Materialising function '#{self}'"
21
+ applied_typing_context, substitutions = create_context
22
+ materialized_function = clone_with_substitutions(self, substitutions)
23
+ TypingContext.with_context(applied_typing_context) do
24
+ # Adding constraints for the generic vriables in the materialised function
25
+ yield materialized_function
26
+ end
27
+ # got all the constraints here
28
+ # do something with the context -> unification? merge context?
29
+ unification = Polymorphism::Unification.new(applied_typing_context.all_constraints).run
30
+ applied_typing_context.unlink # these constraints have already been satisfied
31
+ materialized_function.apply_bindings(unification.bindings_map)
32
+ end
33
+
34
+ def free_type_variables(klass)
35
+ return type_variables if klass == :main
36
+ class_type = Runtime::TypeParser.parse_singleton_object_type(klass.name)
37
+ if class_type.generic?
38
+ type_variables.reject do |type_var|
39
+ class_type.type_vars.detect { |class_type_var| class_type_var.variable == type_var.variable }
40
+ end
41
+ else
42
+ type_variables
43
+ end
44
+ end
45
+
46
+ def check_args_application(actual_arguments, context)
47
+ if @local_typing_context.kind != :lambda
48
+ super(actual_arguments, context)
49
+ else
50
+ materialize do |materialized_function|
51
+ materialized_function.check_args_application(actual_arguments, context)
52
+ end
53
+ end
54
+ end
55
+
56
+ def compatible?(other_type, relation = :lt)
57
+ materialize do |materialized_function|
58
+ materialized_function.compatible?(other_type, relation)
59
+ end
60
+ end
61
+
62
+ protected
63
+
64
+ def create_context
65
+ @application_count += 1
66
+ @local_typing_context.clone(:method)
67
+ end
68
+
69
+ def clone_with_substitutions(function, substitutions)
70
+ # var types coming from a generic method, not a generic type will be nil
71
+ materialized_from = function.from.map do |from_var|
72
+ if from_var.is_a?(Polymorphism::TypeVariable)
73
+ substitutions[from_var.variable] || from_var.clone
74
+ elsif from_var.is_a?(TyGenericSingletonObject) || from_var.is_a?(TyGenericObject)
75
+ from_var.send(:clone_with_substitutions, substitutions)
76
+ else
77
+ from_var
78
+ end
79
+ end
80
+ to_var = function.to
81
+ materialized_to = if to_var.is_a?(Polymorphism::TypeVariable)
82
+ substitutions[to_var.variable] || to_var.clone
83
+ elsif to_var.is_a?(TyGenericSingletonObject) || to_var.is_a?(TyGenericObject)
84
+ to_var.send(:clone_with_substitutions, substitutions)
85
+ else
86
+ to_var
87
+ end
88
+ materialized_function = TyFunction.new(materialized_from, materialized_to, parameters_info, node)
89
+ materialized_function.name = function.name
90
+ materialized_function.block_type = clone_with_substitutions(function.block_type, substitutions) if function.block_type
91
+ materialized_function
92
+ end
93
+
94
+ def type_variables
95
+ vars = (from + [to]).map do |arg|
96
+ if arg.is_a?(Polymorphism::TypeVariable) && arg.bound_to_generic?
97
+ arg.bound.type_vars
98
+ elsif arg.is_a?(Polymorphism::TypeVariable)
99
+ arg
100
+ elsif arg.generic?
101
+ arg.type_vars
102
+ end
103
+ end
104
+ vars = vars.flatten.compact
105
+
106
+ vars += block_type.type_variables if block_type && block_type.generic?
107
+
108
+ # vars.each_with_object({}) do |type_var, acc|
109
+ # acc[type_var.variable] = type_var
110
+ # end.values
111
+ vars
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,180 @@
1
+ require_relative 'ty_object'
2
+ require_relative 'polymorphism/generic_comparisons'
3
+ require_relative 'polymorphism/generic_variables'
4
+ module TypedRb
5
+ module Types
6
+ class TyGenericObject < TyObject
7
+ include Polymorphism::GenericComparisons
8
+ include Polymorphism::GenericVariables
9
+
10
+ def initialize(ruby_type, type_vars, node = nil)
11
+ super(ruby_type, node)
12
+ @type_vars = type_vars
13
+ end
14
+
15
+ # This object has concrete type parameters
16
+ # The generic Function we retrieve from the registry might be generic
17
+ # If it is generic we apply the bound parameters and we obtain a concrete function type
18
+ def find_function_type(message, num_args, block)
19
+ function_klass_type, function_type = find_function_type_in_hierarchy(:instance, message, num_args, block)
20
+ if function_klass_type != ruby_type && ancestor_of_super_type?(generic_singleton_object.super_type, function_klass_type)
21
+ TypedRb.log binding, :debug, "Found message '#{message}', generic function: #{function_type}, explicit super type #{generic_singleton_object.super_type}"
22
+ target_class = generic_singleton_object.super_type
23
+ target_type_vars = generic_singleton_object.super_type.type_vars
24
+ materialize_super_type_found_function(message, num_args, block, target_class, target_type_vars)
25
+ elsif function_klass_type != ruby_type && BasicObject::TypeRegistry.find_generic_type(function_klass_type)
26
+ TypedRb.log binding, :debug, "Found message '#{message}', generic function: #{function_type}, implict super type #{function_klass_type}"
27
+ target_class = BasicObject::TypeRegistry.find_generic_type(function_klass_type)
28
+ materialize_super_type_found_function(message, num_args, block, target_class, type_vars)
29
+ else
30
+ TypedRb.log binding, :debug, "Found message '#{message}', generic function: #{function_type}"
31
+ materialized_function = materialize_found_function(function_type)
32
+ TypedRb.log binding, :debug, "Found message '#{message}', materialized generic function: #{materialized_function}"
33
+ [function_klass_type, materialized_function]
34
+ end
35
+ end
36
+
37
+ def generic?
38
+ true
39
+ end
40
+
41
+ def materialize_found_function(function_type)
42
+ return function_type unless function_type.generic?
43
+ from_args = function_type.from.map { |arg| materialize_found_function_arg(arg) }
44
+ to_arg = materialize_found_function_arg(function_type.to)
45
+ if function_type.block_type
46
+ materialized_block_type = materialize_found_function(function_type.block_type)
47
+ end
48
+
49
+ generic_function = (from_args + [to_arg, materialized_block_type]).any? do |arg|
50
+ arg.is_a?(Polymorphism::TypeVariable) ||
51
+ (arg.respond_to?(:generic?) && arg.generic?)
52
+ end
53
+
54
+ if generic_function
55
+ materialized_function = TyGenericFunction.new(from_args, to_arg, function_type.parameters_info, node)
56
+ materialized_function.local_typing_context = function_type.local_typing_context
57
+ else
58
+ materialized_function = TyFunction.new(from_args, to_arg, function_type.parameters_info, node)
59
+ end
60
+
61
+ materialized_function.with_block_type(materialized_block_type)
62
+ end
63
+
64
+ def materialize_super_type_found_function(message, num_args, block,
65
+ super_type,
66
+ super_type_vars)
67
+ super_type_materialization_args = parse_super_type_materialization_args(super_type_vars)
68
+ # we build the concrete type for the arguments based in the subclass bindings and the
69
+ # super type parsed value
70
+ materialized_super_type_in_context = super_type.materialize(super_type_materialization_args).type_vars(recursive: false)
71
+ # Now we check if the parsed type is valid provided the constraints of the super class
72
+ super_type_generic_object = BasicObject::TypeRegistry.find_generic_type(super_type.ruby_type)
73
+ materialized_super_type = super_type_generic_object.materialize(materialized_super_type_in_context)
74
+
75
+ # materialized_super_type.type_vars = super_type.type_vars # ...
76
+ materialized_super_type.as_object_type.find_function_type(message, num_args, block)
77
+ end
78
+
79
+ def parse_super_type_materialization_args(super_type_vars)
80
+ super_type_vars.map do |super_type_var|
81
+ parse_super_type_materialization_arg(super_type_var)
82
+ end
83
+ end
84
+
85
+ def parse_super_type_materialization_arg(super_type_var)
86
+ return super_type_var if super_type_var.bound
87
+ found_matching_var = type_vars.detect do |var|
88
+ var_name = var.name.split(':').last
89
+ super_type_var.name.index(var_name)
90
+ end
91
+ if found_matching_var
92
+ base_matching_var = found_matching_var.dup
93
+ base_matching_var.name = super_type_var.name
94
+ base_matching_var.variable = super_type_var.variable
95
+ base_matching_var
96
+ else
97
+ fail TypedRb::TypeCheckError,
98
+ "Error materializing super type annotation for variable #{generic_singleton_object.ruby_type} '#{super_type_var.split(':').last}' not found in base class #{ruby_type}"
99
+ end
100
+ end
101
+
102
+ def ancestor_of_super_type?(super_type_klass, function_klass_type)
103
+ return false if super_type_klass.nil?
104
+ super_type_klass.ruby_type.ancestors.include?(function_klass_type)
105
+ end
106
+
107
+ def materialize_found_function_arg(arg)
108
+ if arg.is_a?(Polymorphism::TypeVariable)
109
+ matching_var = generic_type_var_to_applied_type_var(arg)
110
+
111
+ # if matching_var && matching_var.wildcard? && matching_var.lower_bound
112
+ # matching_var.lower_bound
113
+ # elsif matching_var
114
+ # WILDCARD
115
+ if matching_var
116
+ # Type variables and generic methods => function will still be generic
117
+ (matching_var.is_a?(Polymorphism::TypeVariable) && matching_var.bound) || matching_var
118
+ else
119
+ # generic_function = true
120
+ # TyUnboundType.new(matching_var.variable, :lower_bound)
121
+ # fail TypeCheckError, "Cannot find matching type var for #{arg.variable} instantiating #{self}", arg.node
122
+ # method generic var?
123
+ arg
124
+ end
125
+ elsif arg.is_a?(TyGenericSingletonObject)
126
+ arg.materialize_with_type_vars(type_vars, :lower_bound).as_object_type
127
+ else
128
+ arg
129
+ end
130
+ end
131
+
132
+ def to_s
133
+ base_string = super
134
+ var_types_strings = @type_vars.map do |var_type|
135
+ if var_type.respond_to?(:bound) && var_type.bound
136
+ # "[#{var_type.variable} <= #{var_type.bound}]"
137
+ "[#{var_type.bound}]"
138
+ else
139
+ "[#{var_type}]"
140
+ end
141
+ end
142
+ "#{base_string}#{var_types_strings.join}"
143
+ end
144
+
145
+ def clone_with_substitutions(substitutions)
146
+ materialized_type_vars = type_vars(recursive: false).map do |type_var|
147
+ if type_var.is_a?(Polymorphism::TypeVariable)
148
+ substitutions[type_var.variable] || type_var.clone
149
+ elsif type_var.is_a?(TyGenericSingletonObject) || type_var.is_a?(TyGenericObject)
150
+ type_var.clone_with_substitutions(substitutions)
151
+ else
152
+ type_var
153
+ end
154
+ end
155
+ self.class.new(ruby_type, materialized_type_vars, node)
156
+ end
157
+
158
+ def apply_bindings(bindings_map)
159
+ type_vars.each_with_index do |var, i|
160
+ if var.is_a?(Polymorphism::TypeVariable)
161
+ var.apply_bindings(bindings_map)
162
+ type_vars[i] = var.bound if var.bound && var.bound.is_a?(Polymorphism::TypeVariable)
163
+ elsif var.is_a?(TyGenericSingletonObject) || var.is_a?(TyGenericObject)
164
+ var.apply_bindings(bindings_map)
165
+ end
166
+ end
167
+ self
168
+ end
169
+
170
+ def generic_singleton_object
171
+ @generic_singleton_object ||= BasicObject::TypeRegistry.find_generic_type(ruby_type)
172
+ end
173
+
174
+ def generic_type_var_to_applied_type_var(type_var)
175
+ i = TypeRegistry.find_generic_type(ruby_type).type_vars.find_index { |generic_type_var| generic_type_var.variable == type_var.variable }
176
+ i && type_vars[i]
177
+ end
178
+ end
179
+ end
180
+ end