steep 0.1.0.pre2 → 0.1.0
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 +5 -5
- data/.travis.yml +1 -1
- data/README.md +146 -33
- data/bin/smoke_runner.rb +43 -10
- data/lib/steep/ast/annotation/collection.rb +93 -0
- data/lib/steep/ast/annotation.rb +131 -0
- data/lib/steep/ast/buffer.rb +47 -0
- data/lib/steep/ast/location.rb +82 -0
- data/lib/steep/ast/method_type.rb +116 -0
- data/lib/steep/ast/signature/class.rb +33 -0
- data/lib/steep/ast/signature/const.rb +17 -0
- data/lib/steep/ast/signature/env.rb +123 -0
- data/lib/steep/ast/signature/extension.rb +21 -0
- data/lib/steep/ast/signature/gvar.rb +17 -0
- data/lib/steep/ast/signature/interface.rb +31 -0
- data/lib/steep/ast/signature/members.rb +71 -0
- data/lib/steep/ast/signature/module.rb +21 -0
- data/lib/steep/ast/type_params.rb +13 -0
- data/lib/steep/ast/types/any.rb +39 -0
- data/lib/steep/ast/types/bot.rb +39 -0
- data/lib/steep/ast/types/class.rb +35 -0
- data/lib/steep/ast/types/helper.rb +21 -0
- data/lib/steep/ast/types/instance.rb +39 -0
- data/lib/steep/ast/types/intersection.rb +74 -0
- data/lib/steep/ast/types/name.rb +124 -0
- data/lib/steep/ast/types/self.rb +39 -0
- data/lib/steep/ast/types/top.rb +39 -0
- data/lib/steep/ast/types/union.rb +74 -0
- data/lib/steep/ast/types/var.rb +57 -0
- data/lib/steep/ast/types/void.rb +35 -0
- data/lib/steep/cli.rb +28 -1
- data/lib/steep/drivers/annotations.rb +32 -0
- data/lib/steep/drivers/check.rb +53 -77
- data/lib/steep/drivers/scaffold.rb +303 -0
- data/lib/steep/drivers/utils/each_signature.rb +66 -0
- data/lib/steep/drivers/utils/validator.rb +115 -0
- data/lib/steep/drivers/validate.rb +39 -0
- data/lib/steep/errors.rb +291 -19
- data/lib/steep/interface/abstract.rb +44 -0
- data/lib/steep/interface/builder.rb +470 -0
- data/lib/steep/interface/instantiated.rb +126 -0
- data/lib/steep/interface/ivar_chain.rb +26 -0
- data/lib/steep/interface/method.rb +60 -0
- data/lib/steep/{interface.rb → interface/method_type.rb} +111 -100
- data/lib/steep/interface/substitution.rb +65 -0
- data/lib/steep/module_name.rb +116 -0
- data/lib/steep/parser.rb +1314 -814
- data/lib/steep/parser.y +536 -175
- data/lib/steep/source.rb +220 -25
- data/lib/steep/subtyping/check.rb +673 -0
- data/lib/steep/subtyping/constraints.rb +275 -0
- data/lib/steep/subtyping/relation.rb +41 -0
- data/lib/steep/subtyping/result.rb +126 -0
- data/lib/steep/subtyping/trace.rb +48 -0
- data/lib/steep/subtyping/variable_occurrence.rb +49 -0
- data/lib/steep/subtyping/variable_variance.rb +69 -0
- data/lib/steep/type_construction.rb +1630 -524
- data/lib/steep/type_inference/block_params.rb +100 -0
- data/lib/steep/type_inference/constant_env.rb +55 -0
- data/lib/steep/type_inference/send_args.rb +222 -0
- data/lib/steep/type_inference/type_env.rb +226 -0
- data/lib/steep/type_name.rb +27 -7
- data/lib/steep/typing.rb +4 -0
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +71 -16
- data/smoke/and/a.rb +4 -2
- data/smoke/array/a.rb +4 -5
- data/smoke/array/b.rb +4 -4
- data/smoke/block/a.rb +2 -2
- data/smoke/block/a.rbi +2 -0
- data/smoke/block/b.rb +15 -0
- data/smoke/case/a.rb +3 -3
- data/smoke/class/a.rb +3 -3
- data/smoke/class/b.rb +0 -2
- data/smoke/class/d.rb +2 -2
- data/smoke/class/e.rb +1 -1
- data/smoke/class/f.rb +2 -2
- data/smoke/class/g.rb +8 -0
- data/smoke/const/a.rb +3 -3
- data/smoke/dstr/a.rb +1 -1
- data/smoke/ensure/a.rb +22 -0
- data/smoke/enumerator/a.rb +6 -6
- data/smoke/enumerator/b.rb +22 -0
- data/smoke/extension/a.rb +2 -2
- data/smoke/extension/b.rb +3 -3
- data/smoke/extension/c.rb +1 -1
- data/smoke/hello/hello.rb +2 -2
- data/smoke/if/a.rb +4 -2
- data/smoke/kwbegin/a.rb +8 -0
- data/smoke/literal/a.rb +5 -5
- data/smoke/method/a.rb +5 -5
- data/smoke/method/a.rbi +4 -0
- data/smoke/method/b.rb +29 -0
- data/smoke/module/a.rb +3 -3
- data/smoke/module/a.rbi +9 -0
- data/smoke/module/b.rb +2 -2
- data/smoke/module/c.rb +1 -1
- data/smoke/module/d.rb +5 -0
- data/smoke/module/e.rb +13 -0
- data/smoke/module/f.rb +13 -0
- data/smoke/rescue/a.rb +62 -0
- data/smoke/super/a.rb +2 -2
- data/smoke/type_case/a.rb +35 -0
- data/smoke/yield/a.rb +2 -2
- data/stdlib/builtin.rbi +463 -24
- data/steep.gemspec +3 -2
- metadata +91 -29
- data/lib/steep/annotation.rb +0 -223
- data/lib/steep/signature/class.rb +0 -450
- data/lib/steep/signature/extension.rb +0 -51
- data/lib/steep/signature/interface.rb +0 -49
- data/lib/steep/types/any.rb +0 -31
- data/lib/steep/types/class.rb +0 -27
- data/lib/steep/types/instance.rb +0 -27
- data/lib/steep/types/merge.rb +0 -32
- data/lib/steep/types/name.rb +0 -57
- data/lib/steep/types/union.rb +0 -42
- data/lib/steep/types/var.rb +0 -38
- data/lib/steep/types.rb +0 -4
@@ -0,0 +1,275 @@
|
|
1
|
+
module Steep
|
2
|
+
module Subtyping
|
3
|
+
class Constraints
|
4
|
+
class UnsatisfiedInvariantError < StandardError
|
5
|
+
attr_reader :constraints
|
6
|
+
attr_reader :reason
|
7
|
+
|
8
|
+
def initialize(reason:, constraints:)
|
9
|
+
@reason = reason
|
10
|
+
@constraints = constraints
|
11
|
+
super "Invalid constraint: reason=#{reason.message}, constraints=#{constraints.to_s}"
|
12
|
+
end
|
13
|
+
|
14
|
+
class VariablesUnknownsNotDisjoint
|
15
|
+
attr_reader :vars
|
16
|
+
|
17
|
+
def initialize(vars:)
|
18
|
+
@vars = vars
|
19
|
+
end
|
20
|
+
|
21
|
+
def message
|
22
|
+
"Variables and unknowns should be disjoint (#{vars})"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class VariablesFreeVariablesNotDisjoint
|
27
|
+
attr_reader :var
|
28
|
+
attr_reader :lower_bound
|
29
|
+
attr_reader :upper_bound
|
30
|
+
|
31
|
+
def initialize(var: nil, lower_bound: nil, upper_bound: nil)
|
32
|
+
@var = var
|
33
|
+
@lower_bound = lower_bound
|
34
|
+
@upper_bound = upper_bound
|
35
|
+
end
|
36
|
+
|
37
|
+
def message
|
38
|
+
"Variables and FV(constraints) should be disjoint (#{var}, #{lower_bound}, #{upper_bound})"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class UnknownsFreeVariableNotDisjoint
|
43
|
+
attr_reader :var
|
44
|
+
attr_reader :upper_bound
|
45
|
+
attr_reader :lower_bound
|
46
|
+
|
47
|
+
def initialize(var:, lower_bound:, upper_bound:)
|
48
|
+
@var = var
|
49
|
+
@lower_bound = lower_bound
|
50
|
+
@upper_bound = upper_bound
|
51
|
+
end
|
52
|
+
|
53
|
+
def message
|
54
|
+
"Unknowns and FV(constraints) should be disjoint (#{var}, #{lower_bound}, #{upper_bound})"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class UnsatisfiableConstraint < StandardError
|
60
|
+
attr_reader :var
|
61
|
+
attr_reader :sub_type
|
62
|
+
attr_reader :super_type
|
63
|
+
attr_reader :result
|
64
|
+
|
65
|
+
def initialize(var:, sub_type:, super_type:, result:)
|
66
|
+
@var = var
|
67
|
+
@sub_type = sub_type
|
68
|
+
@super_type = super_type
|
69
|
+
@result = result
|
70
|
+
|
71
|
+
super "Unsatisfiable constraint on #{var}: #{sub_type} <: #{super_type}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
attr_reader :dictionary
|
76
|
+
attr_reader :vars
|
77
|
+
|
78
|
+
def initialize(unknowns:)
|
79
|
+
@dictionary = {}
|
80
|
+
@vars = Set.new
|
81
|
+
|
82
|
+
unknowns.each do |var|
|
83
|
+
dictionary[var] = [Set.new, Set.new]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.empty
|
88
|
+
new(unknowns: [])
|
89
|
+
end
|
90
|
+
|
91
|
+
def add_var(*vars)
|
92
|
+
vars.each do |var|
|
93
|
+
self.vars << var
|
94
|
+
end
|
95
|
+
|
96
|
+
unless Set.new(vars).disjoint?(unknowns)
|
97
|
+
raise UnsatisfiedInvariantError.new(
|
98
|
+
reason: UnsatisfiedInvariantError::VariablesUnknownsNotDisjoint.new(vars: vars),
|
99
|
+
constraints: constraints
|
100
|
+
)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def add(var, sub_type: nil, super_type: nil)
|
105
|
+
subs, supers = dictionary[var]
|
106
|
+
|
107
|
+
if super_type && !super_type.is_a?(AST::Types::Top)
|
108
|
+
supers << eliminate_variable(super_type, to: AST::Types::Top.new)
|
109
|
+
end
|
110
|
+
|
111
|
+
if sub_type && !sub_type.is_a?(AST::Types::Bot)
|
112
|
+
subs << eliminate_variable(sub_type, to: AST::Types::Bot.new)
|
113
|
+
end
|
114
|
+
|
115
|
+
super_fvs = supers.each.with_object(Set.new) do |type, fvs|
|
116
|
+
fvs.merge(type.free_variables)
|
117
|
+
end
|
118
|
+
sub_fvs = subs.each.with_object(Set.new) do |type, fvs|
|
119
|
+
fvs.merge(type.free_variables)
|
120
|
+
end
|
121
|
+
|
122
|
+
unless super_fvs.disjoint?(unknowns) || sub_fvs.disjoint?(unknowns)
|
123
|
+
raise UnsatisfiedInvariantError.new(
|
124
|
+
reason: UnsatisfiedInvariantError::UnknownsFreeVariableNotDisjoint.new(
|
125
|
+
var: var,
|
126
|
+
lower_bound: sub_type,
|
127
|
+
upper_bound: super_type
|
128
|
+
),
|
129
|
+
constraints: self
|
130
|
+
)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def eliminate_variable(type, to:)
|
135
|
+
case type
|
136
|
+
when AST::Types::Name
|
137
|
+
type.args.map do |ty|
|
138
|
+
eliminate_variable(ty, to: AST::Types::Any.new)
|
139
|
+
end.yield_self do |args|
|
140
|
+
AST::Types::Name.new(
|
141
|
+
name: type.name,
|
142
|
+
args: args
|
143
|
+
)
|
144
|
+
end
|
145
|
+
when AST::Types::Union
|
146
|
+
type.types.map do |ty|
|
147
|
+
eliminate_variable(ty, to: AST::Types::Any.new)
|
148
|
+
end.yield_self do |types|
|
149
|
+
AST::Types::Union.build(types: types)
|
150
|
+
end
|
151
|
+
when AST::Types::Intersection
|
152
|
+
type.types.map do |ty|
|
153
|
+
eliminate_variable(ty, to: AST::Types::Any.new)
|
154
|
+
end.yield_self do |types|
|
155
|
+
AST::Types::Intersection.build(types: types)
|
156
|
+
end
|
157
|
+
when AST::Types::Var
|
158
|
+
if vars.member?(type.name)
|
159
|
+
to
|
160
|
+
else
|
161
|
+
type
|
162
|
+
end
|
163
|
+
else
|
164
|
+
type
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def unknown?(var)
|
169
|
+
dictionary.key?(var)
|
170
|
+
end
|
171
|
+
|
172
|
+
def unknowns
|
173
|
+
Set.new(dictionary.keys)
|
174
|
+
end
|
175
|
+
|
176
|
+
def empty?
|
177
|
+
dictionary.keys.empty?
|
178
|
+
end
|
179
|
+
|
180
|
+
def upper_bound(var)
|
181
|
+
_, upper_bound = dictionary[var]
|
182
|
+
case upper_bound.size
|
183
|
+
when 0
|
184
|
+
AST::Types::Top.new
|
185
|
+
when 1
|
186
|
+
upper_bound.first
|
187
|
+
else
|
188
|
+
AST::Types::Union.build(types: upper_bound.to_a)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def lower_bound(var)
|
193
|
+
lower_bound, _ = dictionary[var]
|
194
|
+
|
195
|
+
case lower_bound.size
|
196
|
+
when 0
|
197
|
+
AST::Types::Bot.new
|
198
|
+
when 1
|
199
|
+
lower_bound.first
|
200
|
+
else
|
201
|
+
AST::Types::Intersection.build(types: lower_bound.to_a)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def solution(checker, variance:, variables:)
|
206
|
+
vars = []
|
207
|
+
types = []
|
208
|
+
|
209
|
+
dictionary.each_key do |var|
|
210
|
+
if variables.include?(var)
|
211
|
+
if has_constraint?(var)
|
212
|
+
upper_bound = upper_bound(var)
|
213
|
+
lower_bound = lower_bound(var)
|
214
|
+
relation = Relation.new(sub_type: lower_bound, super_type: upper_bound)
|
215
|
+
|
216
|
+
checker.check(relation, constraints: self.class.empty).yield_self do |result|
|
217
|
+
if result.success?
|
218
|
+
vars << var
|
219
|
+
|
220
|
+
type = case
|
221
|
+
when variance.contravariant?(var)
|
222
|
+
upper_bound
|
223
|
+
when variance.covariant?(var)
|
224
|
+
lower_bound
|
225
|
+
else
|
226
|
+
if lower_bound.level.join > upper_bound.level.join
|
227
|
+
upper_bound
|
228
|
+
else
|
229
|
+
lower_bound
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
types << type
|
234
|
+
else
|
235
|
+
raise UnsatisfiableConstraint.new(var: var,
|
236
|
+
sub_type: lower_bound,
|
237
|
+
super_type: upper_bound,
|
238
|
+
result: result)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
else
|
242
|
+
vars << var
|
243
|
+
types << AST::Types::Any.new
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
Interface::Substitution.build(vars, types)
|
249
|
+
end
|
250
|
+
|
251
|
+
def has_constraint?(var)
|
252
|
+
lower, upper = dictionary[var]
|
253
|
+
!lower.empty? || !upper.empty?
|
254
|
+
end
|
255
|
+
|
256
|
+
def each
|
257
|
+
if block_given?
|
258
|
+
dictionary.each_key do |var|
|
259
|
+
yield var, lower_bound(var), upper_bound(var)
|
260
|
+
end
|
261
|
+
else
|
262
|
+
enum_for :each
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def to_s
|
267
|
+
strings = each.map do |var, lower_bound, upper_bound|
|
268
|
+
"#{lower_bound} <: #{var} <: #{upper_bound}"
|
269
|
+
end
|
270
|
+
|
271
|
+
"#{unknowns.to_a.join(",")}/#{vars.to_a.join(",")} |- { #{strings.join(", ")} }"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Steep
|
2
|
+
module Subtyping
|
3
|
+
class Relation
|
4
|
+
attr_reader :sub_type
|
5
|
+
attr_reader :super_type
|
6
|
+
|
7
|
+
def initialize(sub_type:, super_type:)
|
8
|
+
@sub_type = sub_type
|
9
|
+
@super_type = super_type
|
10
|
+
end
|
11
|
+
|
12
|
+
def hash
|
13
|
+
self.class.hash ^ sub_type.hash ^ super_type.hash
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
other.is_a?(self.class) && other.sub_type == sub_type && other.super_type == super_type
|
18
|
+
end
|
19
|
+
|
20
|
+
alias eql? ==
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"#{sub_type} <: #{super_type}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def map
|
27
|
+
self.class.new(
|
28
|
+
sub_type: yield(sub_type),
|
29
|
+
super_type: yield(super_type)
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def flip
|
34
|
+
self.class.new(
|
35
|
+
sub_type: super_type,
|
36
|
+
super_type: sub_type
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Steep
|
2
|
+
module Subtyping
|
3
|
+
module Result
|
4
|
+
class Base
|
5
|
+
def failure?
|
6
|
+
!success?
|
7
|
+
end
|
8
|
+
|
9
|
+
def then
|
10
|
+
if success?
|
11
|
+
yield self
|
12
|
+
else
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def else
|
18
|
+
if failure?
|
19
|
+
yield self
|
20
|
+
else
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Success < Base
|
27
|
+
attr_reader :constraints
|
28
|
+
|
29
|
+
def initialize(constraints:)
|
30
|
+
@constraints = constraints
|
31
|
+
end
|
32
|
+
|
33
|
+
def success?
|
34
|
+
true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Failure < Base
|
39
|
+
class MethodMissingError
|
40
|
+
attr_reader :name
|
41
|
+
|
42
|
+
def initialize(name:)
|
43
|
+
@name = name
|
44
|
+
end
|
45
|
+
|
46
|
+
def message
|
47
|
+
"Method #{name} is missing"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class BlockMismatchError
|
52
|
+
attr_reader :name
|
53
|
+
|
54
|
+
def initialize(name:)
|
55
|
+
@name = name
|
56
|
+
end
|
57
|
+
|
58
|
+
def message
|
59
|
+
"Method #{name} is incompatible for block"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class ParameterMismatchError
|
64
|
+
attr_reader :name
|
65
|
+
|
66
|
+
def initialize(name:)
|
67
|
+
@name = name
|
68
|
+
end
|
69
|
+
|
70
|
+
def message
|
71
|
+
"Method #{name} or its block has incompatible parameters"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class UnknownPairError
|
76
|
+
attr_reader :relation
|
77
|
+
|
78
|
+
def initialize(relation:)
|
79
|
+
@relation = relation
|
80
|
+
end
|
81
|
+
|
82
|
+
def message
|
83
|
+
"#{relation} does not hold"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class PolyMethodSubtyping
|
88
|
+
attr_reader :name
|
89
|
+
|
90
|
+
def initialize(name:)
|
91
|
+
@name = name
|
92
|
+
end
|
93
|
+
|
94
|
+
def message
|
95
|
+
"Method #{name} requires uncheckable polymorphic subtyping"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
attr_reader :error
|
100
|
+
attr_reader :trace
|
101
|
+
|
102
|
+
def initialize(error:, trace:)
|
103
|
+
@error = error
|
104
|
+
@trace = trace.dup
|
105
|
+
end
|
106
|
+
|
107
|
+
def success?
|
108
|
+
false
|
109
|
+
end
|
110
|
+
|
111
|
+
def merge_trace(trace)
|
112
|
+
if trace.empty?
|
113
|
+
self
|
114
|
+
else
|
115
|
+
self.class.new(error: error,
|
116
|
+
trace: trace + self.trace)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def drop(n)
|
121
|
+
self.class.new(error: error, trace: trace.drop(n))
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Steep
|
2
|
+
module Subtyping
|
3
|
+
class Trace
|
4
|
+
attr_reader :array
|
5
|
+
|
6
|
+
def initialize(array: [])
|
7
|
+
@array = array
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(sup, sub)
|
11
|
+
array << [sup, sub]
|
12
|
+
yield
|
13
|
+
ensure
|
14
|
+
array.pop
|
15
|
+
end
|
16
|
+
|
17
|
+
def empty?
|
18
|
+
array.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
def drop(n)
|
22
|
+
self.class.new(array: array.drop(n))
|
23
|
+
end
|
24
|
+
|
25
|
+
def size
|
26
|
+
array.size
|
27
|
+
end
|
28
|
+
|
29
|
+
def +(other)
|
30
|
+
self.class.new(array: array + other.array)
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize_copy(source)
|
34
|
+
@array = source.array.dup
|
35
|
+
end
|
36
|
+
|
37
|
+
def each
|
38
|
+
if block_given?
|
39
|
+
array.each do |pair|
|
40
|
+
yield *pair
|
41
|
+
end
|
42
|
+
else
|
43
|
+
enum_for :each
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Steep
|
2
|
+
module Subtyping
|
3
|
+
class VariableOccurence
|
4
|
+
attr_reader :params
|
5
|
+
attr_reader :returns
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@params = Set.new
|
9
|
+
@returns = Set.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_method_type(method_type)
|
13
|
+
method_type.params.each_type do |type|
|
14
|
+
each_var(type) do |var|
|
15
|
+
params << var
|
16
|
+
end
|
17
|
+
end
|
18
|
+
each_var(method_type.return_type) do |var|
|
19
|
+
returns << var
|
20
|
+
end
|
21
|
+
|
22
|
+
method_type.block&.yield_self do |block|
|
23
|
+
block.params.each_type do |type|
|
24
|
+
each_var(type) do |var|
|
25
|
+
params << var
|
26
|
+
end
|
27
|
+
end
|
28
|
+
each_var(block.return_type) do |var|
|
29
|
+
returns << var
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def each_var(type, &block)
|
35
|
+
type.free_variables.each(&block)
|
36
|
+
end
|
37
|
+
|
38
|
+
def strictly_return?(var)
|
39
|
+
!params.member?(var) && returns.member?(var)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.from_method_type(method_type)
|
43
|
+
self.new.tap do |occurence|
|
44
|
+
occurence.add_method_type(method_type)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Steep
|
2
|
+
module Subtyping
|
3
|
+
class VariableVariance
|
4
|
+
attr_reader :covariants
|
5
|
+
attr_reader :contravariants
|
6
|
+
|
7
|
+
def initialize(covariants:, contravariants:)
|
8
|
+
@covariants = covariants
|
9
|
+
@contravariants = contravariants
|
10
|
+
end
|
11
|
+
|
12
|
+
def covariant?(var)
|
13
|
+
covariants.member?(var) && !contravariants.member?(var)
|
14
|
+
end
|
15
|
+
|
16
|
+
def contravariant?(var)
|
17
|
+
contravariants.member?(var) && !covariants.member?(var)
|
18
|
+
end
|
19
|
+
|
20
|
+
def invariant?(var)
|
21
|
+
covariants.member?(var) && contravariants.member?(var)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.from_method_type(method_type)
|
25
|
+
covariants = Set.new
|
26
|
+
contravariants = Set.new
|
27
|
+
|
28
|
+
add_params(method_type.params, block: false, contravariants: contravariants, covariants: covariants)
|
29
|
+
add_type(method_type.return_type, variance: :covariant, covariants: covariants, contravariants: contravariants)
|
30
|
+
|
31
|
+
method_type.block&.yield_self do |block|
|
32
|
+
add_params(block.params, block: true, contravariants: contravariants, covariants: covariants)
|
33
|
+
add_type(block.return_type, variance: :contravariant, covariants: covariants, contravariants: contravariants)
|
34
|
+
end
|
35
|
+
|
36
|
+
new(covariants: covariants, contravariants: contravariants)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.add_params(params, block:, covariants:, contravariants:)
|
40
|
+
params.each_type do |type|
|
41
|
+
add_type(type, variance: block ? :contravariant : :covariant, covariants: covariants, contravariants: contravariants)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.add_type(type, variance:, covariants:, contravariants:)
|
46
|
+
case type
|
47
|
+
when AST::Types::Var
|
48
|
+
case variance
|
49
|
+
when :covariant
|
50
|
+
covariants << type.name
|
51
|
+
when :contravariant
|
52
|
+
contravariants << type.name
|
53
|
+
when :invariant
|
54
|
+
covariants << type.name
|
55
|
+
contravariants << type.name
|
56
|
+
end
|
57
|
+
when AST::Types::Union, AST::Types::Intersection
|
58
|
+
type.types.each do |ty|
|
59
|
+
add_type(ty, variance: variance, covariants: covariants, contravariants: contravariants)
|
60
|
+
end
|
61
|
+
when AST::Types::Name
|
62
|
+
type.args.each do |arg|
|
63
|
+
add_type(arg, variance: :invariant, covariants: covariants, contravariants: contravariants)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|