Class: Steep::Interface::MethodType

Inherits:
Object
  • Object
show all
Defined in:
lib/steep/interface/method_type.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type_params:, type:, block:) ⇒ MethodType

Returns a new instance of MethodType.



8
9
10
11
12
# File 'lib/steep/interface/method_type.rb', line 8

def initialize(type_params:, type:, block:)
  @type_params = type_params
  @type = type
  @block = block
end

Instance Attribute Details

#blockObject (readonly)

Returns the value of attribute block.



6
7
8
# File 'lib/steep/interface/method_type.rb', line 6

def block
  @block
end

#typeObject (readonly)

Returns the value of attribute type.



5
6
7
# File 'lib/steep/interface/method_type.rb', line 5

def type
  @type
end

#type_paramsObject (readonly)

Returns the value of attribute type_params.



4
5
6
# File 'lib/steep/interface/method_type.rb', line 4

def type_params
  @type_params
end

Class Method Details

.intersection(type1, type2, check) ⇒ Object



157
158
159
160
161
162
163
164
165
# File 'lib/steep/interface/method_type.rb', line 157

def self.intersection(type1, type2, check)
  try_type_params(
    type1,
    type2,
    check,
    -> (t1, t2) { t1 & t2 },
    -> (original, generated) { Subtyping::Relation.new(sub_type: generated, super_type: original) }
  )
end

.try_type_params(type1, type2, check, generate, relation) ⇒ Object



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/steep/interface/method_type.rb', line 167

def self.try_type_params(type1, type2, check, generate, relation)
  return type1 if type1.equals_modulo_type_params?(type2)

  case
  when type1.type_params.empty? && type2.type_params.empty?
    generate[type1, type2]
  when type1.type_params.empty?
    if mt = generate[type1, type2.with(type_params: [])]
      mt.with(type_params: type2.type_params)
    end
  when type2.type_params.empty?
    if mt = generate[type1.with(type_params: []), type2]
      mt.with(type_params: type1.type_params)
    end
  when type1.type_params.size == type2.type_params.size
    params1, s1 = TypeParam.rename(type1.type_params)
    params2, s2 = TypeParam.rename(type2.type_params)

    type1_ = type1.instantiate(s1)
    type2_ = type2.instantiate(s2)
    if mt = generate[type1_, type2_]
      check.push_variable_bounds(params1 + params2) do
        variables = type1.type_params.map(&:name) + type2.type_params.map(&:name)
        constraints = Subtyping::Constraints.new(unknowns: variables)

        check.with_context(self_type: AST::Builtin.any_type, instance_type: AST::Builtin.any_type, class_type: AST::Builtin.any_type, constraints: constraints) do
          result1 = check.check_method_type(:__method_on_type1, relation[type1.with(type_params: []), mt])
          result2 = check.check_method_type(:__method_on_type2, relation[type2.with(type_params: []), mt])

          if result1.success? && result2.success?
            unless type1.type_params.map(&:name).zip(type2.type_params.map(&:name)).all? {|pair|
              constraints.upper_bound(pair[0]) == constraints.upper_bound(pair[1] || raise) &&
                 constraints.lower_bound(pair[0]) == constraints.lower_bound(pair[1] || raise)
            }
              return
            end

            params2_, s2_ = TypeParam.rename(type2.type_params, type2.type_params.map(&:name), type1.type_params.map(&:name))
            if mt_ = generate[type1.with(type_params: []), type2.instantiate(s2_)]
              mt_.with(
                type_params: type1.type_params.map.with_index {|param1, index|
                  param2 = params2_[index] or raise
                  ub1 = param1.upper_bound
                  ub2 = param2.upper_bound

                  case
                  when ub1 && ub2
                    param1.update(upper_bound: AST::Types::Union.build(types: [ub1, ub2]))
                  when ub2
                    param1.update(upper_bound: ub2)
                  else
                    param1
                  end
                }
              )
            end
          end
        end
      end
    end
  end
end

.union(type1, type2, check) ⇒ Object



147
148
149
150
151
152
153
154
155
# File 'lib/steep/interface/method_type.rb', line 147

def self.union(type1, type2, check)
  try_type_params(
    type1,
    type2,
    check,
    -> (t1, t2) { t1 | t2 },
    -> (original, generated) { Subtyping::Relation.new(sub_type: original, super_type: generated) }
  )
end

Instance Method Details

#&(other) ⇒ Object



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/steep/interface/method_type.rb', line 281

def &(other)
  return self if self == other

  if self.type.params && other.type.params
    params = self.type.params | other.type.params or return
  else
    params = self.type.params || other.type.params
  end

  block =
    case
    when (b = self.block) && (ob = other.block)
      self_type =
        case
        when (self_self = b.self_type) && (other_self = ob.self_type)
          AST::Types::Intersection.build(types: [self_self, other_self])
        when b.self_type || ob.self_type
          AST::Types::Top.instance
        else
          nil
        end

      block_params = b.type.params & ob.type.params or return
      block_return_type = AST::Types::Union.build(types: [b.type.return_type, ob.type.return_type])
      block_type = Function.new(params: block_params, return_type: block_return_type, location: nil)
      Block.new(
        type: block_type,
        optional: b.optional || ob.optional,
        self_type: self_type
      )
    else
      self.block || other.block
    end

  return_type = AST::Types::Intersection.build(types: [self.type.return_type, other.type.return_type])

  MethodType.new(
    type_params: [],
    type: Function.new(params: params, return_type: return_type, location: nil),
    block: block
  )
end

#+(other) ⇒ Object



127
128
129
# File 'lib/steep/interface/method_type.rb', line 127

def +(other)
  unify_overload(other)
end

#==(other) ⇒ Object Also known as: eql?



14
15
16
17
18
19
# File 'lib/steep/interface/method_type.rb', line 14

def ==(other)
  other.is_a?(self.class) &&
    other.type_params == type_params &&
    other.type == type &&
    other.block == block
end

#accept_one_arg?Boolean

Returns:

  • (Boolean)


324
325
326
327
# File 'lib/steep/interface/method_type.rb', line 324

def accept_one_arg?
  return false if block && block.required?
  type.accept_one_arg?
end

#each_type(&block) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/steep/interface/method_type.rb', line 57

def each_type(&block)
  if block
    type.each_type(&block)
    if block()
      yield(block().self_type) if block().self_type
      block().type.params&.each_type(&block)
      yield(block().type.return_type)
    end
  else
    enum_for :each_type
  end
end

#equals_modulo_type_params?(other) ⇒ Boolean

Returns:

  • (Boolean)


131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/steep/interface/method_type.rb', line 131

def equals_modulo_type_params?(other)
  case
  when self.type_params.empty? && other.type_params.empty?
    self == other
  when self.type_params.size == other.type_params.size
    new_names = self.type_params.map(&:name)

    self_params, self_subst = TypeParam.rename(self.type_params, self.type_params.map(&:name), new_names)
    other_params, other_subst = TypeParam.rename(other.type_params, other.type_params.map(&:name), new_names)

    self_params == other_params && self.instantiate(self_subst) == other.instantiate(other_subst)
  else
    false
  end
end

#free_variablesObject



27
28
29
30
31
32
33
34
35
# File 'lib/steep/interface/method_type.rb', line 27

def free_variables
  @fvs ||= Set.new.tap do |set|
    set.merge(type.free_variables)
    if block
      set.merge(block.free_variables)
    end
    set.subtract(type_params.map(&:name))
  end
end

#hashObject



23
24
25
# File 'lib/steep/interface/method_type.rb', line 23

def hash
  type_params.hash ^ type.hash ^ block.hash
end

#instantiate(s) ⇒ Object



70
71
72
73
74
# File 'lib/steep/interface/method_type.rb', line 70

def instantiate(s)
  self.class.new(type_params: [],
                 type: type.subst(s),
                 block: block&.subst(s))
end

#map_type(&block) ⇒ Object



91
92
93
94
95
# File 'lib/steep/interface/method_type.rb', line 91

def map_type(&block)
  self.class.new(type_params: type_params,
                 type: type.map_type(&block),
                 block: self.block&.yield_self {|blk| blk.map_type(&block) })
end

#subst(s) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/steep/interface/method_type.rb', line 37

def subst(s)
  return self if s.empty?
  return self if each_type.none? {|t| s.apply?(t) }

  if type_params.any? {|param| s.key?(param.name) }
    s_ = s.except(type_params.map(&:name))
  else
    s_ = s
  end

  ty = type.subst(s_)
  bl = block&.subst(s_)

  if ty == type && bl == block
    self
  else
    self.class.new(type_params: type_params, type: ty, block: bl)
  end
end

#to_sObject



82
83
84
85
86
87
88
89
# File 'lib/steep/interface/method_type.rb', line 82

def to_s
  type_params = !self.type_params.empty? ? "[#{self.type_params.join(", ")}] " : ""
  params = type.params.to_s
  return_type = type.return_type
  block = self.block ? " #{self.block}" : ""

  "#{type_params}#{params}#{block} -> #{return_type}"
end

#unify_overload(other) ⇒ Object

Returns a new method type which can be used for the method implementation type of both ‘self` and `other`.



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/steep/interface/method_type.rb', line 99

def unify_overload(other)
  type_params_1, s1 = TypeParam.rename(self.type_params)
  type_params_2, s2 = TypeParam.rename(other.type_params)
  type_params = type_params_1 + type_params_2

  block =
    case
    when (b = self.block) && (ob = other.block)
      b.subst(s1) + ob.subst(s2)
    when b = self.block
      b.to_optional.subst(s1)
    when ob = other.block
      ob.to_optional.subst(s2)
    end

  self.class.new(
    type_params: type_params,
    type: Function.new(
      params: type.params && other.type.params ? type.params.subst(s1) + other.type.params.subst(s2) : nil,
      return_type: AST::Types::Union.build(
        types: [type.return_type.subst(s1), other.type.return_type.subst(s2)]
      ),
      location: nil
    ),
    block: block
  )
end

#with(type_params: self.type_params, type: self.type, block: self.block) ⇒ Object



76
77
78
79
80
# File 'lib/steep/interface/method_type.rb', line 76

def with(type_params: self.type_params, type: self.type, block: self.block)
  self.class.new(type_params: type_params,
                 type: type,
                 block: block)
end

#|(other) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/steep/interface/method_type.rb', line 230

def |(other)
  return self if other == self

  params = self.type.params & other.type.params or return
  block =
    case
    when (b = block()) && (ob = other.block)
      self_type =
        case
        when (self_self = b.self_type) && (other_self = ob.self_type)
          AST::Types::Union.build(types: [self_self, other_self])
        when b.self_type || ob.self_type
          AST::Types::Bot.instance
        else
          nil
        end

      if b.type.params && ob.type.params
        # Return when the two block parameters are imcompatible.
        return unless b.type.params & ob.type.params
        block_params = b.type.params | ob.type.params or return
      else
        block_params = b.type.params || ob.type.params
      end

      block_return_type = AST::Types::Intersection.build(types: [b.type.return_type, ob.type.return_type])
      block_type = Function.new(params: block_params, return_type: block_return_type, location: nil)

      Block.new(
        type: block_type,
        optional: b.optional && ob.optional,
        self_type: self_type
      )
    when (b = block()) && b.optional?
      b
    when other.block && other.block.optional?
      other.block
    when !self.block && !other.block
      nil
    else
      return
    end
  return_type = AST::Types::Union.build(types: [self.type.return_type, other.type.return_type])

  MethodType.new(
    type_params: [],
    type: Function.new(params: params, return_type: return_type, location: nil),
    block: block
  )
end