class TypedRb::Model::TmFun

A instance/class function definition expression

Attributes

args[RW]
body[RW]
name[RW]
owner[RW]
owner_type[R]

Public Class Methods

new(owner, name, args, body, node) click to toggle source
Calls superclass method TypedRb::Model::Expr::new
# File lib/typed/model/tm_fun.rb, line 11
def initialize(owner, name, args, body, node)
  super(node)
  @owner = parse_owner(owner)
  @name = name
  @args = args
  @body = body
  @arg_count = args.count { |arg| arg.first != :blockarg }
  @has_block = args.detect { |arg| arg.first == :blockarg }
end
with_fresh_bindings(klass, function_type) { || ... } click to toggle source

TODO: 1 Find free type variables for the generic function. 2 Create a new local typing context for the generic function 3 Add free type variables to the typing context

# File lib/typed/model/tm_fun.rb, line 44
def self.with_fresh_bindings(klass, function_type)
  if function_type.generic?
    Types::TypingContext.push_context(:method)
    function_type.free_type_variables(klass).each do |type_var|
      # This will add the variable to the context
      Types::TypingContext.type_variable_for_function_type(type_var)
    end

    yield if block_given?

    # # Since every single time we find the generic type the same instance
    # # will be returned, the local_typing_context will still be associated.
    # # This is the reason we need to build a new typing context cloning this
    # # one while type materialization.
    function_type.local_typing_context = Types::TypingContext.pop_context
  else
    yield if block_given?
  end
  function_type
end

Public Instance Methods

check_type(context) click to toggle source
# File lib/typed/model/tm_fun.rb, line 21
def check_type(context)
  compute_owner_type(context)

  function_klass_type, function_type = owner_type.find_function_type(name, @arg_count, @has_block)

  if function_type.nil? || function_type.dynamic?
    TypedRb.log_dynamic_warning(node, owner_type, name)
    return Types::TyUnit.new(node)
  end

  context = setup_context(context, function_type)
  # check the body with the new bindings for the args
  TmFun.with_fresh_bindings(function_klass_type, function_type) do
    body_return_type = body.check_type(context)
    TypedRb::Types::TypingContext.function_context_pop
    check_return_type(context, function_type, body_return_type)
  end
end

Private Instance Methods

check_return_type(context, function_type, body_return_type) click to toggle source
# File lib/typed/model/tm_fun.rb, line 132
def check_return_type(context, function_type, body_return_type)
  return function_type.to if function_type.to.instance_of?(Types::TyUnit)
  # Same as before but for the return type
  function_type_to = function_type.to.is_a?(Types::TyGenericSingletonObject) ? function_type.to.clone : function_type.to
  if body_return_type.either?
    compatible_body  = body_return_type[:normal].compatible?(function_type_to, :lt)
    compatible_return = if body_return_type.return?
                          body_return_type[:return].wrapped_type.compatible?(function_type_to, :lt)
                        else
                          true
                        end
    if compatible_body && compatible_return
      return function_type_to
    else
      if compatible_body == false
        fail Types::UncomparableTypes.new(function_type_to, body_return_type[:normal], node)
      else
        fail Types::UncomparableTypes.new(function_type_to, body_return_type[:return], node)
      end
    end
  else
    body_return_type = body_return_type.wrapped_type.check_type(context) if body_return_type.stack_jump?
    return function_type if body_return_type.compatible?(function_type_to, :lt)
  end
  # TODO:
  # A TyObject(Symbol) should be returned not the function type
  # x = def id(x); x; end / => x == :id
  error_message = ". Wrong return type, expected #{function_type.to}, found #{body_return_type}."
  body_return_type.compatible?(function_type_to, :lt)
  fail Types::UncomparableTypes.new(function_type.to, body_return_type, node, error_message)
end
compute_owner_type(context) click to toggle source
# File lib/typed/model/tm_fun.rb, line 74
def compute_owner_type(context)
  @owner_type = if owner == :self
                  context.get_self
                elsif owner.nil?
                  context.get_self.as_object_type
                else
                  owner.check_type(context)
                end
end
parse_owner(owner) click to toggle source
# File lib/typed/model/tm_fun.rb, line 67
def parse_owner(owner)
  return nil if owner.nil?
  return :self if owner == :self || owner.type == :self
  # must be a class or other expression we can check the type
  owner
end
process_arguments(context, function_type) click to toggle source
# File lib/typed/model/tm_fun.rb, line 84
def process_arguments(context, function_type)
  args.each_with_index do |arg, i|
    function_arg_type = function_type.from[i]
    # Generic arguments are parsed by runtime without checking constraints since they are not available at parsing type.
    # We need to run unification in them before using the type to detect invalid type argument applications.
    function_arg_type = function_arg_type.self_materialize.as_object_type if function_arg_type.is_a?(Types::TyGenericSingletonObject)
    context = case arg.first
              when :arg, :restarg
                context.add_binding(arg[1], function_arg_type)
              when :optarg
                declared_arg_type = arg.last.check_type(context)
                context.add_binding(arg[1], function_arg_type) if declared_arg_type.compatible?(function_arg_type)
              when :blockarg
                if function_type.block_type
                  context.add_binding(arg[1], function_type.block_type)
                else
                  fail TypeCheckError.new("Error type checking function #{owner}##{name}: Missing block type for block argument #{arg[1]}", node)
                end
              when :mlhs
                tm_mlhs = arg[1]
                tm_mlhs.check_type(function_arg_type, context)
              else
                fail TypeCheckError.new("Error type checking function #{owner}##{name}: Unknown type of arg #{arg.first}", node)
              end
  end
  context
end
setup_context(context, function_type) click to toggle source
# File lib/typed/model/tm_fun.rb, line 112
def setup_context(context, function_type)
  context = process_arguments(context, function_type)

  # pointing self to the right type
  self_type = if owner_type.is_a?(Types::TyExistentialType)
                owner_type.self_variable
              else
                owner_type
              end
  context = context.add_binding(:self, self_type)

  # adding yield binding if present
  context = context.add_binding(:yield, function_type.block_type) if function_type.block_type

  # set the current function context
  TypedRb::Types::TypingContext.function_context_push(self_type, name, function_type.from)

  context
end