class TypeProf::Type::Array::Elements

Attributes

lead_tys[R]
rest_ty[R]

Public Class Methods

dummy_elements() click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 274
def self.dummy_elements
  Elements.new([], Type.any)
end
new(lead_tys, rest_ty = Type.bot) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 268
def initialize(lead_tys, rest_ty = Type.bot)
  raise unless lead_tys.all? {|ty| ty.is_a?(Type) }
  raise unless rest_ty.is_a?(Type)
  @lead_tys, @rest_ty = lead_tys, rest_ty
end

Public Instance Methods

[](idx) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 373
def [](idx)
  if idx.is_a?(Range)
    if @rest_ty == Type.bot
      lead_tys = @lead_tys[idx]
      if lead_tys
        rest_ty = Type.bot
      else
        return Type.nil
      end
    else
      b, e = idx.begin, idx.end
      b = 0 if !b
      if !e
        lead_tys = @lead_tys[idx] || []
        rest_ty = @rest_ty
      elsif b >= 0
        if e >= 0
          if b <= e
            if e < @lead_tys.size
              lead_tys = @lead_tys[idx]
              rest_ty = Type.bot
            else
              lead_tys = @lead_tys[idx] || []
              rest_ty = @rest_ty
            end
          else
            return Type.nil
          end
        else
          lead_tys = @lead_tys[idx] || []
          e = idx.exclude_end? ? e : e == -1 ? @lead_tys.size : e + 1
          rest_ty = (@lead_tys[e + 1..] || []).inject(@rest_ty) {|ty0, ty1| ty0.union(ty1) }
        end
      else
        lead_tys = []
        if e >= 0
          rest_ty = e < @lead_tys.size ? Type.bot : @rest_ty
          range = [0, @lead_tys.size + b].max .. (idx.exclude_end? ? e - 1 : e)
          rest_ty = @lead_tys[range].inject(rest_ty) {|ty0, ty1| ty0.union(ty1) }
        else
          if b <= e
            range = [0, @lead_tys.size + b].max .. (idx.exclude_end? ? e - 1 : e)
            rest_ty = @lead_tys[range].inject(@rest_ty) {|ty0, ty1| ty0.union(ty1) }
          else
            return Type.nil
          end
        end
      end
    end
    base_ty = Type::Instance.new(Type::Builtin[:ary])
    Array.new(Elements.new(lead_tys, rest_ty), base_ty)
  elsif idx >= 0
    if idx < @lead_tys.size
      @lead_tys[idx]
    elsif @rest_ty == Type.bot
      Type.nil
    else
      @rest_ty
    end
  else
    i = @lead_tys.size + idx
    i = [i, 0].max
    ty = @rest_ty
    @lead_tys[i..].each do |ty2|
      ty = ty.union(ty2)
    end
    ty
  end
end
append(ty) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 477
def append(ty)
  if @rest_ty == Type.bot
    if @lead_tys.size < 5 # XXX: should be configurable, or ...?
      lead_tys = @lead_tys + [ty]
      Elements.new(lead_tys, @rest_ty)
    else
      Elements.new(@lead_tys, ty)
    end
  else
    Elements.new(@lead_tys, @rest_ty.union(ty))
  end
end
each_free_type_variable(&blk) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 351
def each_free_type_variable(&blk)
  @lead_tys.each do |ty|
    ty.each_free_type_variable(&blk)
  end
  @rest_ty&.each_free_type_variable(&blk)
end
globalize(env, visited, depth) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 284
def globalize(env, visited, depth)
  lead_tys = []
  @lead_tys.each do |ty|
    lead_tys << ty.globalize(env, visited, depth)
  end
  rest_ty = @rest_ty&.globalize(env, visited, depth)
  Elements.new(lead_tys, rest_ty)
end
include_untyped?(scratch) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 553
def include_untyped?(scratch)
  return true if @lead_tys.any? {|ty| ty.include_untyped?(scratch) }
  return true if @rest_ty.include_untyped?(scratch)
  false
end
limit_size(limit) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 304
def limit_size(limit)
  Elements.new(@lead_tys.map {|ty| ty.limit_size(limit) }, @rest_ty.limit_size(limit))
end
localize(env, alloc_site, depth) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 293
def localize(env, alloc_site, depth)
  lead_tys = @lead_tys.map.with_index do |ty, i|
    alloc_site2 = alloc_site.add_id(i)
    env, ty = ty.localize(env, alloc_site2, depth)
    ty
  end
  alloc_site_rest = alloc_site.add_id(:rest)
  env, rest_ty = @rest_ty.localize(env, alloc_site_rest, depth)
  return env, Elements.new(lead_tys, rest_ty)
end
match?(other) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 338
def match?(other)
  n = [@lead_tys.size, other.lead_tys.size].min
  rest_ty1 = @lead_tys[n..].inject(@rest_ty) {|ty1, ty2| ty1.union(ty2) }
  rest_ty2 = other.lead_tys[n..].inject(other.rest_ty) {|ty1, ty2| ty1.union(ty2) }
  subst = nil
  (@lead_tys[0, n] + [rest_ty1]).zip(other.lead_tys[0, n] + [rest_ty2]) do |ty0, ty1|
    subst2 = Type.match?(ty0, ty1)
    return nil unless subst2
    subst = Type.merge_substitution(subst, subst2)
  end
  subst
end
pretty_print(q) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 330
def pretty_print(q)
  q.group(9, "Elements[", "]") do
    q.seplist(@lead_tys + [@rest_ty]) do |elem|
      q.pp elem
    end
  end
end
screen_name(scratch) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 308
def screen_name(scratch)
  if @rest_ty == Type.bot
    if @lead_tys.empty?
      # This is heuristic: in general, an empty array is a wrong guess.
      # Note that an empty array is representable as "[ ]" in RBS, but
      # users often have to modify it to "Array[something]".
      # In this term, "Array[untyped]" is considered more useful than "[ ]".
      return "Array[untyped]"
    end
    s = @lead_tys.map do |ty|
      ty.screen_name(scratch)
    end
    s << "*" + @rest_ty.screen_name(scratch) if @rest_ty != Type.bot
    return "[#{ s.join(", ") }]"
  end

  "*[#{ squash.screen_name(scratch) }]"
rescue SystemStackError
  p squash
  exit!
end
squash() click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 364
def squash
  @lead_tys.inject(@rest_ty) {|ty1, ty2| ty1.union(ty2) } #.union(Type.nil) # is this needed?
end
squash_or_any() click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 368
def squash_or_any
  ty = squash
  ty == Type.bot ? Type.any : ty
end
substitute(subst, depth) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 358
def substitute(subst, depth)
  lead_tys = @lead_tys.map {|ty| ty.substitute(subst, depth) }
  rest_ty = @rest_ty.substitute(subst, depth)
  Elements.new(lead_tys, rest_ty)
end
take_first(num) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 507
def take_first(num)
  base_ty = Type::Instance.new(Type::Builtin[:ary])
  if @lead_tys.size >= num
    lead_tys = @lead_tys[0, num]
    rest_ary_ty = Array.new(Elements.new(@lead_tys[num..-1], @rest_ty), base_ty)
    return lead_tys, rest_ary_ty
  else
    lead_tys = @lead_tys.dup
    until lead_tys.size == num
      # .union(Type.nil) is needed for `a, b, c = [42]` to assign nil to b and c
      lead_tys << @rest_ty.union(Type.nil)
    end
    rest_ary_ty = Array.new(Elements.new([], @rest_ty), base_ty)
    return lead_tys, rest_ary_ty
  end
end
take_last(num) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 524
def take_last(num)
  base_ty = Type::Instance.new(Type::Builtin[:ary])
  if @rest_ty == Type.bot
    if @lead_tys.size >= num
      following_tys = @lead_tys[-num, num]
      rest_ary_ty = Array.new(Elements.new(@lead_tys[0...-num], Type.bot), base_ty)
      return rest_ary_ty, following_tys
    else
      following_tys = @lead_tys[-num, num] || []
      until following_tys.size == num
        following_tys.unshift(Type.nil)
      end
      rest_ary_ty = Array.new(Elements.new([], Type.bot), base_ty)
      return rest_ary_ty, following_tys
    end
  else
    lead_tys = @lead_tys.dup
    last_ty = rest_ty
    following_tys = []
    until following_tys.size == num
      last_ty = last_ty.union(lead_tys.pop) unless lead_tys.empty?
      following_tys.unshift(last_ty)
    end
    rest_ty = lead_tys.inject(last_ty) {|ty1, ty2| ty1.union(ty2) }
    rest_ary_ty = Array.new(Elements.new([], rest_ty), base_ty)
    return rest_ary_ty, following_tys
  end
end
to_local_type(id, base_ty) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 280
def to_local_type(id, base_ty)
  Type::Local.new(Array, id, base_ty)
end
union(other) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 490
def union(other)
  return self if self == other
  raise "Hash::Elements merge Array::Elements" if other.is_a?(Hash::Elements)

  lead_count = [@lead_tys.size, other.lead_tys.size].min
  lead_tys = (0...lead_count).map do |i|
    @lead_tys[i].union(other.lead_tys[i])
  end

  rest_ty = @rest_ty.union(other.rest_ty)
  (@lead_tys[lead_count..-1] + other.lead_tys[lead_count..-1]).each do |ty|
    rest_ty = rest_ty.union(ty)
  end

  Elements.new(lead_tys, rest_ty)
end
update(idx, ty) click to toggle source
# File typeprof-0.21.9/lib/typeprof/container-type.rb, line 443
def update(idx, ty)
  if idx
    if idx >= 0
      if idx < @lead_tys.size
        lead_tys = Utils.array_update(@lead_tys, idx, ty)
        Elements.new(lead_tys, @rest_ty)
      else
        rest_ty = @rest_ty.union(ty)
        Elements.new(@lead_tys, rest_ty)
      end
    else
      i = @lead_tys.size + idx
      if @rest_ty == Type.bot
        if i >= 0
          lead_tys = Utils.array_update(@lead_tys, i, ty)
          Elements.new(lead_tys, Type.bot)
        else
          # TODO: out of bound? should we emit an error?
          Elements.new(@lead_tys, Type.bot)
        end
      else
        i = [i, 0].max
        lead_tys = @lead_tys[0, i] + @lead_tys[i..].map {|ty2| ty2.union(ty) }
        rest_ty = @rest_ty.union(ty)
        Elements.new(@lead_tys, rest_ty)
      end
    end
  else
    lead_tys = @lead_tys.map {|ty1| ty1.union(ty) }
    rest_ty = @rest_ty.union(ty)
    Elements.new(lead_tys, rest_ty)
  end
end