typed.rb 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
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