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