123456789_123456789_123456789_123456789_123456789_

Class: TypeProf::TypedMethodDef

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, MethodDef
Instance Chain:
Inherits: TypeProf::MethodDef
Defined in: lib/typeprof/method.rb

Class Method Summary

Instance Attribute Summary

MethodDef - Inherited

Instance Method Summary

Constructor Details

.new(sig_rets, rbs_source, pub_meth) ⇒ TypedMethodDef

sig_rets: Array<[MethodSignature, (return)Type]>

[ GitHub ]

  
# File 'lib/typeprof/method.rb', line 242

def initialize(sig_rets, rbs_source, pub_meth) # sig_rets: Array<[MethodSignature, (return)Type]>
  @sig_rets = sig_rets
  @rbs_source = rbs_source
  @pub_meth = pub_meth
  @iseq = nil
end

Instance Attribute Details

#iseq (readonly)

[ GitHub ]

  
# File 'lib/typeprof/method.rb', line 249

attr_reader :rbs_source, :iseq

#rbs_source (readonly)

[ GitHub ]

  
# File 'lib/typeprof/method.rb', line 249

attr_reader :rbs_source, :iseq

Instance Method Details

#do_match_iseq_mdef(iseq_mdef, recv, mid, env, ep, scratch)

[ GitHub ]

  
# File 'lib/typeprof/method.rb', line 336

def do_match_iseq_mdef(iseq_mdef, recv, mid, env, ep, scratch)
  recv = scratch.globalize_type(recv, env, ep)
  @sig_rets.each do |msig, _ret_ty|
    iseq_mdef.do_check_send(msig, recv, mid, ep, scratch)
  end
  @iseq ||= iseq_mdef.iseq
end

#do_send(recv_orig, mid, aargs, caller_ep, caller_env, scratch, &ctn)

[ GitHub ]

  
# File 'lib/typeprof/method.rb', line 251

def do_send(recv_orig, mid, aargs, caller_ep, caller_env, scratch, &ctn)
  recv = scratch.globalize_type(recv_orig, caller_env, caller_ep)

  klass, singleton = recv_orig.method_dispatch_info
  cur_subst = {}
  direct_method = true
  scratch.adjust_substitution(klass, singleton, mid, self, recv.generate_substitution) do |subst, direct|
    direct_method &&= direct
    cur_subst = Type.merge_substitution(cur_subst, subst)
  end

  found = false
  aargs = scratch.globalize_type(aargs, caller_env, caller_ep)
  @sig_rets.each do |msig, ret_ty|
    ncaller_env = caller_env
    #pp [mid, aargs, msig]
    # XXX: support self type in msig
    subst = aargs.consistent_with_method_signature?(msig)
    next unless subst

    if direct_method && recv_orig.is_a?(Type::Local)
      ncaller_env = recv_orig.update_container_elem_type(subst, ncaller_env, caller_ep, scratch)
    end

    subst = Type.merge_substitution(subst, cur_subst)
    # need to check self tyvar?
    subst[Type::Var.new(:self)] = recv

    found = true
    if aargs.blk_ty.is_a?(Type::Proc)
      #raise NotImplementedError unless aargs.blk_ty.block_body.is_a?(ISeqBlock) # XXX
      dummy_ctx = TypedContext.new(caller_ep, mid)
      dummy_ep = ExecutionPoint.new(dummy_ctx, -1, caller_ep)
      s_recv = recv
      s_recv = s_recv.base_type while s_recv.respond_to?(:base_type)
      dummy_env = Env.new(StaticEnv.new(s_recv, msig.blk_ty, false, true), [], [], Utils::HashWrapper.new({}))
      if msig.blk_ty.is_a?(Type::Proc)
        scratch.add_callsite!(dummy_ctx, caller_ep, ncaller_env, &ctn)
        bsig = msig.blk_ty.block_body.msig
        alloc_site = AllocationSite.new(caller_ep).add_id(self)
        nlead_tys = (bsig.lead_tys + bsig.opt_tys).map.with_index do |ty, i|
          ty = ty.substitute(subst, Config.current.options[:type_depth_limit]).remove_type_vars
          dummy_env, ty = scratch.localize_type(ty, dummy_env, dummy_ep, alloc_site.add_id(i))
          ty
        end
        0.upto(bsig.opt_tys.size) do |n|
          naargs = ActualArguments.new(nlead_tys[0, bsig.lead_tys.size + n], nil, {}, Type.nil) # XXX: support block to block?
          scratch.do_invoke_block(aargs.blk_ty, naargs, dummy_ep, dummy_env) do |blk_ret_ty, _ep, _env|
            subst2 = Type.match?(blk_ret_ty, msig.blk_ty.block_body.ret_ty)
            if subst2
              subst2 = Type.merge_substitution(subst, subst2)
              if direct_method && recv_orig.is_a?(Type::Local)
                ncaller_env = recv_orig.update_container_elem_type(subst2, ncaller_env, caller_ep, scratch)
                scratch.merge_return_env(caller_ep) {|env| env ? env.merge(ncaller_env) : ncaller_env }
              end
              ret_ty2 = ret_ty.substitute(subst2, Config.current.options[:type_depth_limit]).remove_type_vars
            else
              ret_ty2 = Type.any
            end
            # XXX: check the return type from the block
            # sig.blk_ty.block_body.ret_ty.eql?(_ret_ty) ???
            scratch.add_return_value!(dummy_ctx, ret_ty2)
          end
          # scratch.add_return_value!(dummy_ctx, ret_ty) ?
          # This makes `def foo; 1.times { return "str" }; end` return Integer|String
        end
      else
        # XXX: a block is passed to a method that does not accept block.
        # Should we call the passed block with any arguments?
        ret_ty = ret_ty.remove_type_vars
        ctn[ret_ty, caller_ep, ncaller_env] if ret_ty != Type.bot
      end
    else
      ret_ty = ret_ty.substitute(subst, Config.current.options[:type_depth_limit])
      ret_ty = ret_ty.remove_type_vars
      ctn[ret_ty, caller_ep, ncaller_env] if ret_ty != Type.bot
    end
  end

  unless found
    scratch.error(caller_ep, "failed to resolve overload: #{ recv.screen_name(scratch) }##{ mid }")
    ctn[Type.any, caller_ep, caller_env]
  end
end