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,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,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
|