Class: Duby::Typer::Simple

Inherits:
BaseTyper show all
Defined in:
lib/duby/typer.rb

Direct Known Subclasses

JVM

Constant Summary

Constants included from Duby

Duby::TransformError

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from BaseTyper

#log, #to_s

Methods included from Duby

compile, parse, plugins, print_error, reset, run, typer_plugins

Constructor Details

#initialize(self_type) ⇒ Simple

Returns a new instance of Simple.



35
36
37
38
39
40
41
42
43
44
# File 'lib/duby/typer.rb', line 35

def initialize(self_type)
  @known_types = {}

  @known_types["self"] = type_reference(self_type)
  @known_types["fixnum"] = type_reference("fixnum")
  @known_types["float"] = type_reference("float")
  @known_types["string"] = type_reference("string")
  @known_types["boolean"] = type_reference("boolean")
  @errors = []
end

Instance Attribute Details

#errorsObject

Returns the value of attribute errors.



33
34
35
# File 'lib/duby/typer.rb', line 33

def errors
  @errors
end

#known_typesObject

Returns the value of attribute known_types.



33
34
35
# File 'lib/duby/typer.rb', line 33

def known_types
  @known_types
end

#last_chanceObject

Returns the value of attribute last_chance.



33
34
35
# File 'lib/duby/typer.rb', line 33

def last_chance
  @last_chance
end

Instance Method Details

#alias_type(short, long) ⇒ Object



262
263
264
265
# File 'lib/duby/typer.rb', line 262

def alias_type(short, long)
  @known_types[type_reference(short, false, false)] = type_reference(long, false, false)
  @known_types[type_reference(short, false, true)] = type_reference(long, false, true)
end

#array_typeObject

to be overridden



83
84
85
# File 'lib/duby/typer.rb', line 83

def array_type
  AST::TypeReference::NullType
end

#boolean_typeObject



70
71
72
# File 'lib/duby/typer.rb', line 70

def boolean_type
  known_types["boolean"]
end

#cycle(count) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/duby/typer.rb', line 224

def cycle(count)
  @cycling = true
  count.times do |i|
    begin
      log "[Cycle #{i}]: Started..."
      yield i
    ensure
      log "[Cycle #{i}]: Complete!"
    end
  end
ensure
  @cycling = false
end

#cycling=(c) ⇒ Object



220
221
222
# File 'lib/duby/typer.rb', line 220

def cycling=(c)
  @cycling = c
end

#cycling?Boolean

Returns:

  • (Boolean)


216
217
218
# File 'lib/duby/typer.rb', line 216

def cycling?
  @cycling
end

#default_typeObject



54
55
56
# File 'lib/duby/typer.rb', line 54

def default_type
  nil
end

#defer(node) ⇒ Object



297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/duby/typer.rb', line 297

def defer(node)
  if @error_next
    log "Marking #{node} as an error"
    @error_next = false
    error(node)
  else
    return if deferred_nodes.include? node
    log "Deferring inference for #{node}"

    deferred_nodes[node] = self_type
  end
end

#deferred_nodesObject



267
268
269
# File 'lib/duby/typer.rb', line 267

def deferred_nodes
  @deferred_nodes ||= {}
end

#define_type(name, superclass, interfaces) ⇒ Object



96
97
98
99
100
101
102
103
104
105
# File 'lib/duby/typer.rb', line 96

def define_type(name, superclass, interfaces)
  log "New type defined: '#{name}' < '#{superclass}'"
  known_types[name] = type_definition(name, superclass, interfaces)

  old_self, known_types["self"] = known_types["self"], known_types[name]
  yield
  known_types["self"] = old_self

  known_types[name]
end

#error(node, error_or_msg = nil, backtrace = nil) ⇒ Object



282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/duby/typer.rb', line 282

def error(node, error_or_msg=nil, backtrace=nil)
  if error_or_msg.kind_of? InferenceError
    error = error_or_msg
  elsif error_or_msg
    error = InferenceError.new(error_or_msg, node)
    error.set_backtrace(backtrace) if backtrace
  else
    error = InferenceError.new("Unable to infer type.", node)
  end
  @errors << error
  node.resolve_if(self) do
    AST.error_type
  end
end

#field_type(cls, name) ⇒ Object



154
155
156
# File 'lib/duby/typer.rb', line 154

def field_type(cls, name)
  field_type_hash(cls)[name]
end

#field_type_hash(cls) ⇒ Object



138
139
140
# File 'lib/duby/typer.rb', line 138

def field_type_hash(cls)
  field_types[cls] ||= {}
end

#field_typesObject



134
135
136
# File 'lib/duby/typer.rb', line 134

def field_types
  @field_types ||= {}
end

#fixnum_typeObject



58
59
60
# File 'lib/duby/typer.rb', line 58

def fixnum_type
  known_types["fixnum"]
end

#float_typeObject



62
63
64
# File 'lib/duby/typer.rb', line 62

def float_type
  known_types["float"]
end

#get_method_type_hash(target_type, name, parameter_types) ⇒ Object



242
243
244
245
246
247
248
249
250
251
252
# File 'lib/duby/typer.rb', line 242

def get_method_type_hash(target_type, name, parameter_types)
  method_types[target_type] ||= {}
  method_types[target_type][name] ||= {}
  method_types[target_type][name][parameter_types.size] ||= {}

  current = method_types[target_type][name][parameter_types.size]

  parameter_types.each {|type| current[type] ||= {}; current = current[type]}

  current
end

#hash_typeObject

to be overridden



88
89
90
# File 'lib/duby/typer.rb', line 88

def hash_type
  AST::TypeReference::NullType
end

#infer(node) ⇒ Object



271
272
273
274
275
276
277
278
279
280
# File 'lib/duby/typer.rb', line 271

def infer(node)
  begin
    node.infer(self)
  rescue InferenceError => ex
    ex.node ||= node
    error(node, ex)
  rescue Exception => ex
    error(node, ex.message, ex.backtrace)
  end
end

#infer_signature(method_def) ⇒ Object



142
143
# File 'lib/duby/typer.rb', line 142

def infer_signature(method_def)
end

#known_type(name) ⇒ Object



92
93
94
# File 'lib/duby/typer.rb', line 92

def known_type(name)
  @known_types[name]
end

#learn_field_type(cls, name, type) ⇒ Object



145
146
147
148
149
150
151
152
# File 'lib/duby/typer.rb', line 145

def learn_field_type(cls, name, type)
  log "Learned field type under #{cls} : #{name} = #{type}"

  # TODO check for compatibility?
  field_type_hash(cls)[name] ||= known_types[type] || type

  type
end

#learn_local_type(scope, name, type) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
# File 'lib/duby/typer.rb', line 107

def learn_local_type(scope, name, type)
  existing_type = local_type_hash(scope)[name]
  if existing_type
    # TODO check for compatibility?
    existing_type
  elsif type
    log "Learned local type under #{scope} : #{name} = #{type}"

    local_type_hash(scope)[name] = known_types[type] || type
  end
end

#learn_method_type(target_type, name, parameter_types, type, exceptions) ⇒ Object



158
159
160
161
162
163
164
165
166
# File 'lib/duby/typer.rb', line 158

def learn_method_type(target_type, name, parameter_types, type, exceptions)
  log "Learned method #{name} (#{parameter_types}) on #{target_type} = #{type}"

  get_method_type_hash(target_type, name, parameter_types)[:type] = known_types[type] || type

  # if it's any args are imported types, also add a mapping for the expanded name
  imported_types = parameter_types.map {|param| known_types[param] || param}
  get_method_type_hash(target_type, name, imported_types)[:type] = type
end

#local_type(scope, name) ⇒ Object



119
120
121
122
123
124
# File 'lib/duby/typer.rb', line 119

def local_type(scope, name)
  type = local_type_hash(scope)[name]
  log "Retrieved local type in #{scope} : #{name} = #{type}" if type

  type
end

#local_type_hash(scope) ⇒ Object



130
131
132
# File 'lib/duby/typer.rb', line 130

def local_type_hash(scope)
  local_types[scope] ||= {}
end

#local_typesObject



126
127
128
# File 'lib/duby/typer.rb', line 126

def local_types
  @local_types ||= {}
end

#method_type(target_type, name, parameter_types) ⇒ Object



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
# File 'lib/duby/typer.rb', line 168

def method_type(target_type, name, parameter_types)
  if (target_type && target_type.error?) ||
      parameter_types.any? {|t| t && t.error?}
    return AST.error_type
  end
  constructor = (name == 'new' && target_type && target_type.meta?)

  if constructor
    # constructor handled different from other methods
    simple_type = get_method_type_hash(target_type.unmeta, 'initialize', parameter_types)[:type]
  else
    simple_type = get_method_type_hash(target_type, name, parameter_types)[:type]
  end


  if !simple_type
    log "Method type for \"#{name}\" #{parameter_types} on #{target_type} not found."

    # allow plugins a go if we're in the inference phase
    simple_type = plugins do |plugin|
      plugin.method_type(self, target_type, name, parameter_types)
    end
  end

  return nil unless simple_type

  if constructor
    log "Method type for \"#{name}\" #{parameter_types} on #{target_type} = #{target_type}"
    target_type.unmeta
  else
    log "Method type for \"#{name}\" #{parameter_types} on #{target_type} = #{simple_type}"
    simple_type
  end
end

#method_typesObject



238
239
240
# File 'lib/duby/typer.rb', line 238

def method_types
  @method_types ||= {}
end

#nameObject



46
47
48
# File 'lib/duby/typer.rb', line 46

def name
  "Simple"
end

#no_typeObject



78
79
80
# File 'lib/duby/typer.rb', line 78

def no_type
  AST::TypeReference::NoType
end

#null_typeObject



74
75
76
# File 'lib/duby/typer.rb', line 74

def null_type
  AST::TypeReference::NullType
end

#pluginsObject



203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/duby/typer.rb', line 203

def plugins
  if cycling?
    Duby.typer_plugins.each do |plugin|
      log "Invoking plugin: #{plugin}"

      result = yield plugin
      return result if result
    end
  end

  nil
end

#resolve(raise = false) ⇒ Object



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/duby/typer.rb', line 310

def resolve(raise = false)
  count = deferred_nodes.size + 1

  log "Entering type inference cycle"

  retried = false
  cycle(count) do |i|
    old_deferred = @deferred_nodes
    @deferred_nodes = {}
    old_deferred.each do |node, saved_type|
      known_types["self"] = saved_type
      type = infer(node)

      log "[Cycle #{i}]: Inferred type for #{node}: #{type || 'FAILED'}"

      if type == default_type
        @deferred_nodes[node] = saved_type
      end
    end

    if @deferred_nodes.size == 0
      log "[Cycle #{i}]:  Resolved all types, exiting"
      break
    elsif old_deferred == @deferred_nodes
      if @error_next || retried
        log "[Cycle #{i}]: Made no progress, bailing out"
        break
      elsif @last_chance
        # Retry this iteration, and mark the first deferred
        # type as an error.
        retried = true
        @error_next = true
        redo
      else
        # This is a hack for default constructor support.  The right fix
        # is probably to check the AST for constructors. Instead we
        # tell the plugins that we're near the end of inference so they
        # can assume no new constructors are being added.  You could
        # easily write some circular constructors that would compile
        # with this technique but fail to run.
        @last_chance = true
        redo
      end
    end
    retried = false
  end

  # done with n sweeps, if any remain mark them as errors
  error_nodes = @errors.map {|e| e.node}
  (deferred_nodes.keys - error_nodes).each do |deferred_node|
    error_nodes << deferred_node
    error(deferred_node)
  end
  if raise && !error_nodes.empty?
    msg = "Could not infer typing for nodes:"
    error_nodes.map do |e|
      msg << "\n  "
      msg << "#{e.inspect} at line #{e.line_number} (child of #{e.parent})"
    end
    raise InferenceError.new(msg)
  end
end

#self_typeObject



50
51
52
# File 'lib/duby/typer.rb', line 50

def self_type
  known_types["self"]
end

#string_typeObject



66
67
68
# File 'lib/duby/typer.rb', line 66

def string_type
  known_types["string"]
end

#type_definition(name, superclass, interfaces) ⇒ Object



258
259
260
# File 'lib/duby/typer.rb', line 258

def type_definition(name, superclass, interfaces)
  AST::TypeDefinition.new(name, AST::TypeReference.new(superclass), interfaces)
end

#type_reference(name, array = false, meta = false) ⇒ Object



254
255
256
# File 'lib/duby/typer.rb', line 254

def type_reference(name, array=false, meta=false)
  AST::TypeReference.new(name, array, meta)
end