Class: Mirah::Typer::Simple

Inherits:
Base
  • Object
show all
Defined in:
lib/mirah/typer/simple.rb

Direct Known Subclasses

JVM::Typer

Constant Summary

Constants included from Mirah

Mirah::TransformError, VERSION

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#log, #to_s

Methods included from Mirah

compile, compiler_options, compiler_options=, dest_path, dest_path=, dest_paths, dest_to_source_path, find_dest, find_source, parse, plugins, print_error, reset, run, source_path, source_path=, source_paths, typer_plugins

Constructor Details

#initialize(self_type) ⇒ Simple

Returns a new instance of Simple.



24
25
26
27
28
29
30
31
32
33
# File 'lib/mirah/typer/simple.rb', line 24

def initialize(self_type)
  @known_types = {}

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

Instance Attribute Details

#errorsObject

Returns the value of attribute errors.



22
23
24
# File 'lib/mirah/typer/simple.rb', line 22

def errors
  @errors
end

#known_typesObject

Returns the value of attribute known_types.



22
23
24
# File 'lib/mirah/typer/simple.rb', line 22

def known_types
  @known_types
end

#last_chanceObject

Returns the value of attribute last_chance.



22
23
24
# File 'lib/mirah/typer/simple.rb', line 22

def last_chance
  @last_chance
end

Instance Method Details

#array_typeObject

to be overridden



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

def array_type
  AST::TypeReference::NullType
end

#boolean_typeObject



61
62
63
# File 'lib/mirah/typer/simple.rb', line 61

def boolean_type
  known_types["boolean"]
end

#cycle(count) ⇒ Object



231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/mirah/typer/simple.rb', line 231

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

#cycling=(c) ⇒ Object



227
228
229
# File 'lib/mirah/typer/simple.rb', line 227

def cycling=(c)
  @cycling = c
end

#cycling?Boolean

Returns:

  • (Boolean)


223
224
225
# File 'lib/mirah/typer/simple.rb', line 223

def cycling?
  @cycling
end

#default_typeObject



45
46
47
# File 'lib/mirah/typer/simple.rb', line 45

def default_type
  nil
end

#defer(node, error_message = nil) ⇒ Object



299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/mirah/typer/simple.rb', line 299

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

    deferred_nodes[node] = self_type
  end
end

#deferred_nodesObject



269
270
271
# File 'lib/mirah/typer/simple.rb', line 269

def deferred_nodes
  @deferred_nodes ||= {}
end

#define_type(scope, name, superclass, interfaces) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
# File 'lib/mirah/typer/simple.rb', line 87

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

  # TODO Get rid of known_types["self"]
  old_self, known_types["self"] = known_types["self"], result
  yield
  known_types["self"] = old_self

  result
end

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



284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/mirah/typer/simple.rb', line 284

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



148
149
150
# File 'lib/mirah/typer/simple.rb', line 148

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

#field_type_hash(cls) ⇒ Object



124
125
126
# File 'lib/mirah/typer/simple.rb', line 124

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

#field_typesObject



120
121
122
# File 'lib/mirah/typer/simple.rb', line 120

def field_types
  @field_types ||= {}
end

#fixnum_type(value = 0) ⇒ Object



49
50
51
# File 'lib/mirah/typer/simple.rb', line 49

def fixnum_type(value=0)
  known_types["fixnum"]
end

#float_type(value = 0) ⇒ Object



53
54
55
# File 'lib/mirah/typer/simple.rb', line 53

def float_type(value=0)
  known_types["float"]
end

#get_method_type_hash(target_type, name, parameter_types) ⇒ Object



249
250
251
252
253
254
255
256
257
258
259
# File 'lib/mirah/typer/simple.rb', line 249

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



79
80
81
# File 'lib/mirah/typer/simple.rb', line 79

def hash_type
  AST::TypeReference::NullType
end

#infer(node, expression = true) ⇒ Object



273
274
275
276
277
278
279
280
281
282
# File 'lib/mirah/typer/simple.rb', line 273

def infer(node, expression=true)
  begin
    node.infer(self, expression)
  rescue InferenceError => ex
    ex.node ||= node
    error(node, ex)
  rescue Exception => ex
    raise Mirah::InternalCompilerError.wrap(ex, node)
  end
end

#infer_signature(method_def) ⇒ Object



136
137
# File 'lib/mirah/typer/simple.rb', line 136

def infer_signature(method_def)
end

#known_type(scope, name) ⇒ Object



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

def known_type(scope, name)
  @known_types[name]
end

#learn_field_type(cls, name, type) ⇒ Object



139
140
141
142
143
144
145
146
# File 'lib/mirah/typer/simple.rb', line 139

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

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

  type
end

#learn_local_type(scope, name, type) ⇒ Object



99
100
101
102
103
104
# File 'lib/mirah/typer/simple.rb', line 99

def learn_local_type(scope, name, type)
  return if type.null? && !@last_chance
  type = scope.learn_local_type(name, known_types[type] || type)
  log "Learned local type under #{scope} : #{name} = #{type}" if type
  type
end

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



165
166
167
168
169
170
171
172
173
# File 'lib/mirah/typer/simple.rb', line 165

def learn_method_type(target_type, name, parameter_types, type, exceptions)
  log "Learned method #{name} (#{parameter_types}) on #{target_type} = #{type}" if 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

#learn_static_field_type(cls, name, type) ⇒ Object



152
153
154
155
156
157
158
159
# File 'lib/mirah/typer/simple.rb', line 152

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

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

  type
end

#local_type(scope, name) ⇒ Object



106
107
108
109
110
# File 'lib/mirah/typer/simple.rb', line 106

def local_type(scope, name)
  type = scope.local_type(name)
  log "Retrieved local type in #{scope} : #{name} = #{type}" if type
  type
end

#local_type_hash(scope) ⇒ Object



116
117
118
# File 'lib/mirah/typer/simple.rb', line 116

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

#local_typesObject



112
113
114
# File 'lib/mirah/typer/simple.rb', line 112

def local_types
  @local_types ||= {}
end

#method_type(target_type, name, parameter_types) ⇒ Object



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
# File 'lib/mirah/typer/simple.rb', line 175

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



245
246
247
# File 'lib/mirah/typer/simple.rb', line 245

def method_types
  @method_types ||= {}
end

#nameObject



35
36
37
# File 'lib/mirah/typer/simple.rb', line 35

def name
  "Simple"
end

#no_typeObject



69
70
71
# File 'lib/mirah/typer/simple.rb', line 69

def no_type
  AST::TypeReference::NoType
end

#null_typeObject



65
66
67
# File 'lib/mirah/typer/simple.rb', line 65

def null_type
  AST::TypeReference::NullType
end

#pluginsObject



210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/mirah/typer/simple.rb', line 210

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

      result = yield plugin
      return result if result
    end
  end

  nil
end

#resolve(raise = false) ⇒ Object



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
372
373
374
# File 'lib/mirah/typer/simple.rb', line 313

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.empty?
      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



41
42
43
# File 'lib/mirah/typer/simple.rb', line 41

def self_type
  known_types["self"]
end

#set_filename(scope, name) ⇒ Object



39
# File 'lib/mirah/typer/simple.rb', line 39

def set_filename(scope, name); end

#static_field_type(cls, name) ⇒ Object



161
162
163
# File 'lib/mirah/typer/simple.rb', line 161

def static_field_type(cls, name)
  static_field_type_hash(cls)[name]
end

#static_field_type_hash(cls) ⇒ Object



132
133
134
# File 'lib/mirah/typer/simple.rb', line 132

def static_field_type_hash(cls)
  static_field_types[cls] ||= {}
end

#static_field_typesObject



128
129
130
# File 'lib/mirah/typer/simple.rb', line 128

def static_field_types
  @static_field_types ||= {}
end

#string_typeObject



57
58
59
# File 'lib/mirah/typer/simple.rb', line 57

def string_type
  known_types["string"]
end

#type_definition(scope, name, superclass, interfaces) ⇒ Object



265
266
267
# File 'lib/mirah/typer/simple.rb', line 265

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

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



261
262
263
# File 'lib/mirah/typer/simple.rb', line 261

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