module TypedRb::Types::Polymorphism::TypeOperations

Common operations on types and restrictions.

Public Instance Methods

add_to_send_bubble_constraints(receiver, send_args) click to toggle source

if the type variable is not bound, we try to find the variable in an upper typing context to satisfy the constraint there. If no container context is found, we fail the unification run unless unbound vars are allowed.

# File lib/typed/types/polymorphism/unification.rb, line 98
def add_to_send_bubble_constraints(receiver, send_args)
  return check_unbound_receivers(receiver, send_args) if receiver.nil?
  vars = receiver[:vars].keys.select do |var|
    Types::TypingContext.include?(var.variable)
  end
  return check_unbound_receivers(receiver, send_args) if vars.empty?
  vars.each do |var|
    @to_bubble << [var, send_args]
  end
end
can_apply?(fn, arg_types) click to toggle source
# File lib/typed/types/polymorphism/unification.rb, line 114
def can_apply?(fn, arg_types)
  if fn.dynamic?
    true
  else
    arg_types.each_with_index do |arg, i|
      fn_arg = fn.from[i]
      if arg.is_a?(TypeVariable)
        if graph[arg][:lower_type]
          begin
            type = [graph[arg][:lower_type], fn_arg].min
            graph[arg][:lower_type] = type
          rescue TypedRb::Types::Polymorphism::UnificationError
            value_l = graph[arg][:lower_type]
            value_r = fn_arg
            raise TypedRb::Types::UncomparableTypes.new(value_l, value_r, nil, ", #{value_l} cannot be compared to #{value_r}")
          end
        else
          graph[arg][:lower_type] = fn_arg
        end
      else
        compatible_lt_type?(arg, fn_arg)
      end
    end
  end
end
check_unbound_receivers(receiver, send_args) click to toggle source
# File lib/typed/types/polymorphism/unification.rb, line 109
def check_unbound_receivers(receiver, send_args)
  return if @allow_unbound_receivers
  fail UnificationError, "Unbound variable #{receiver} type acting as receiver for #{send_args[:message]}"
end
compatible_gt_type?(value_l, value_r, join_if_false = true) click to toggle source
# File lib/typed/types/polymorphism/unification.rb, line 35
def compatible_gt_type?(value_l, value_r, join_if_false = true)
  value_l > value_r ? value_l : value_r
rescue Types::UncomparableTypes, ArgumentError
  if join_if_false
    value_l.join(value_r)
  else
    raise Types::UncomparableTypes.new(value_l, value_r)
  end
end
compatible_lt_type?(value_l, value_r) click to toggle source
# File lib/typed/types/polymorphism/unification.rb, line 45
def compatible_lt_type?(value_l, value_r)
  error_message = "Error checking type, #{value_l} is not a subtype of #{value_r}"
  begin
    value_l <= value_r ? value_l : fail(UnificationError, error_message)
  rescue ArgumentError
    raise(Types::UncomparableTypes.new(value_l, value_r, nil, ", #{value_l} is not a subtype of #{value_r}"))
  end
end
compatible_send_type?(receiver, send_args) click to toggle source

This function does not return the infered type. Types are assigned as a side effect.

# File lib/typed/types/polymorphism/unification.rb, line 56
def compatible_send_type?(receiver, send_args)
  return_type = send_args[:return]
  arg_types = send_args[:args]
  message = send_args[:message]
  inferred_receiver = infer_receiver(receiver)
  if inferred_receiver
    klass, function = inferred_receiver.find_function_type(message, arg_types.size, false)
    if function.is_a?(Types::TyDynamicFunction)
      # TODO: should I bind the var to type dynamic in this case?
      graph[return_type][:upper_type] = Types::TyDynamic.new(Object)
      graph[return_type][:lower_type] = Types::TyDynamic.new(Object)
      return true
    end
    if function && can_apply?(function, arg_types)
      if return_type && graph[return_type][:upper_type]
        compatible_gt_type?(graph[return_type][:upper_type], function.to, false)
      else
        graph[return_type][:upper_type] = function.to
      end
    else
      return true if klass != inferred_receiver.ruby_type
      fail UnificationError, "Message #{message} not found for type variable #{receiver}"
    end
  else
    add_to_send_bubble_constraints(receiver, send_args)
  end
end
compatible_type?(value_l, t, value_r) click to toggle source

Check if two types are compatible for a certain restriction. If no join type is possible for a particular restriction, a UncomparableTypes error is raised.

# File lib/typed/types/polymorphism/unification.rb, line 18
def compatible_type?(value_l, t, value_r)
  if value_l.nil? || value_r.nil?
    value_l || value_r
  else
    case t
    when :gt # assignations, e.g v = Int, v = Num => Num
      compatible_gt_type?(value_l, value_r)
    when :lt # applications, return e.g. return (Int, Num) => Int
      compatible_lt_type?(value_l, value_r)
    when :send
      compatible_send_type?(value_l, value_r)
    else
      fail UnificationError, "Unknown type constraint #{t}"
    end
  end
end
infer_receiver(receiver) click to toggle source
# File lib/typed/types/polymorphism/unification.rb, line 84
def infer_receiver(receiver)
  if receiver.is_a?(Hash)
    receiver[:upper_type] = receiver[:lower_type] if receiver[:upper_type].nil?
    receiver[:upper_type].as_object_type if receiver[:upper_type]
  else
    receiver
  end
end