Class: Yadriggy::RubyTypeInferer

Inherits:
RubyTypeChecker show all
Defined in:
lib/yadriggy/ruby_typeinfer.rb

Overview

Type checker for Ruby with type inference.

Direct Known Subclasses

C::ClangTypeChecker

Constant Summary

Constants included from Yadriggy

DynType, Undef, VERSION, Void

Instance Method Summary collapse

Methods inherited from RubyTypeChecker

#clear_references, #get_call_expr_type, #get_name_type, #initialize, #lookup_builtin, #lookup_ruby_classes, #references, #type_assert_params, #type_assert_subsume, #type_parameters

Methods inherited from TypeChecker

#add_typedef, #check, #error_group, #initialize, #make_base_env, #type, #type_as, #type_assert, #type_assert_false, #type_assert_later, #type_env, #typecheck, #typedef

Methods inherited from Checker

all_rules, #apply_typing_rule, #ast, #ast_env, #check, #check_all, check_init_class, #check_later, #error, #error_found!, #error_group, find_rule_entry, #initialize, #make_base_env, #proceed, rule

Methods included from Yadriggy

debug, debug=, define_syntax, reify

Constructor Details

This class inherits a constructor from Yadriggy::RubyTypeChecker

Instance Method Details

#binary_type(bin_expr, right_t, left_t) ⇒ Object



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
229
230
# File 'lib/yadriggy/ruby_typeinfer.rb', line 195

def binary_type(bin_expr, right_t, left_t)
  op = bin_expr.op
  case op
  when :'&&', :'||', :and, :or	# not overridable
    return UnionType.new([right_t, left_t])
  when :>, :>=, :<, :<=, :==, :===, :!=
    if left_t <= RubyClass::Numeric
      return RubyClass::Boolean
    end
  when :**, :*, :/, :%, :+, :-
    if left_t <= RubyClass::Numeric
      if left_t <= RubyClass::Float || right_t <= RubyClass::Float
        return RubyClass::Float
      else
        return RubyClass::Integer
      end
    end
  when :<<, :>>, :&, :|, :^
    return RubyClass::Integer if left_t <= RubyClass::Integer
  # when :=~, :!~, :<=>
  end

  if left_t <= RubyClass::String
    if op == :% || op == :+ || op == :<<
      return RubyClass::String
    elsif op == :=~ || op == :<=>
      return UnionType.new(RubyClass::Integer, RubyClass::NilClass)
    elsif op == :!~
      return RubyClass::Boolean
    end
  end

  call_expr = Call.make(receiver: bin_expr.left, name: op,
                        args: [bin_expr.right], parent: bin_expr.parent)
  return get_call_expr_type(call_expr, type_env, op)
end

#bind_local_var(env, ast, var_type, is_def = true) ⇒ Object

Binds a local variable name to a type.

Parameters:

  • env (TypeEnv)

    a type environment.

  • ast (ASTnode)

    a local variable name.

  • var_type (Type)

    a type.

  • is_def (Boolean) (defaults to: true)

    true if the variable is initialized there.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/yadriggy/ruby_typeinfer.rb', line 17

def bind_local_var(env, ast, var_type, is_def=true)
  unless var_type.nil?
    t = if UnionType.role(var_type)
          ts = UnionType.role(var_type).types
          UnionType.new(ts.map {|t| to_non_instance_type(t) })
        else
          ins_t = InstanceType.role(var_type)
          to_non_instance_type(var_type)
        end
    lvt = LocalVarType.new(t.copy(OptionalRole), is_def ? ast : nil)
    env.bind_name(ast, lvt)
    @typetable[ast] = lvt
  end
end

#get_instance_variable_type(key, ivar, is_valid_type, value_type) ⇒ Type

Obtains the type of the given instance variable ‘ivar` declared in the given class (i.e. module) or the instance object `key`. If the type of `ivar` is not defined, `value_type` is recorded as its type.

Parameters:

  • key (Module|Object)

    the key when looking into the typedef table.

  • ivar (InstanceVariable)

    an instance variable.

  • is_valid_type (Boolean)

    true if ‘value_type` is valid.

  • value_type (Type)

    the type suggested for ‘ivar`.

Returns:

  • (Type)

    the type of ‘ivar`.



158
159
160
161
162
163
164
165
166
167
168
# File 'lib/yadriggy/ruby_typeinfer.rb', line 158

def get_instance_variable_type(key, ivar, is_valid_type, value_type)
  td = add_typedef(key)
  ivar_t = td[ivar]
  if ivar_t.nil?
    td[ivar] = value_type
  else
    type_assert_subsume(ivar_t, value_type,
              "bad type value for #{ivar.name}") if is_valid_type
    ivar_t
  end
end

#get_return_type(an_ast, mthd, new_tenv, arg_types) ⇒ Object



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/yadriggy/ruby_typeinfer.rb', line 269

def get_return_type(an_ast, mthd, new_tenv, arg_types)
  m_ast = an_ast.root.reify(mthd)
  type_assert_false(m_ast.nil?, "no source code: for #{mthd}")
  (@syntax.check(m_ast.tree) || @syntax.raise_error) if @syntax

  m_ast.tree.params.each_with_index do |p, i|
    bind_local_var(new_tenv, p, arg_types[i])
  end

  nparams = m_ast.tree.params.length
  m_ast.tree.optionals.each_with_index do |p, i|
    bind_local_var(new_tenv, p, arg_types[nparams + i])
  end

  mtype = MethodType.role(type(m_ast.tree, new_tenv))
  type_assert(mtype, 'not a method type')
  type_assert_params(mtype.params, arg_types, 'argument type mismatch')
  mtype.result
end

#is_attr_accessor?(expr, tenv, name) ⇒ Boolean

Returns:

  • (Boolean)


90
91
92
93
94
95
# File 'lib/yadriggy/ruby_typeinfer.rb', line 90

def is_attr_accessor?(expr, tenv, name)
  self_t = type_env.context
  !self_t.nil? &&
    (self_t.method_defined?(name) ||
     self_t.private_method_defined?(name))
end

#to_non_instance_type(t) ⇒ Object

When the initial value of a variable is an InstanceType, the type of the variable has to be a RubyCass type corresponding to that instance type. The variable type can be set to that InstanceType only when it is guaranteed that the value of the variable is never changed later.



39
40
41
42
43
44
45
46
# File 'lib/yadriggy/ruby_typeinfer.rb', line 39

def to_non_instance_type(t)
  ins_t = InstanceType.role(t)
  if ins_t.nil?
    t
  else
    ins_t.supertype
  end
end