123456789_123456789_123456789_123456789_123456789_

Class: TypeProf::Type

Relationships & Source Files
Namespace Children
Classes:
Extension / Inclusion / Inheritance Descendants
Subclasses:
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
Inherits: Object
Defined in: lib/typeprof/type.rb,
lib/typeprof/container-type.rb

Overview

or AbstractValue

Constant Summary

Class Method Summary

Instance Method Summary

Constructor Details

.newType

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 5

def initialize
  raise "cannot instantiate abstract type"
end

Class Method Details

.any

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 427

def self.any
  Thread.current[:any] ||= Any.new
end

.bool

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 435

def self.bool
  Thread.current[:bool] ||= Union.new(Utils::Set[
    Instance.new(Type::Builtin[:true]),
    Instance.new(Type::Builtin[:false])
  ], nil)
end

.bot

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 431

def self.bot
  Thread.current[:bot] ||= Union.new(Utils::Set[], nil)
end

.builtin_global_variable_type(var)

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 814

def self.builtin_global_variable_type(var)
  case var
  when :$_, :$/, :$\, :$,, :$;
    Type.optional(Type::Instance.new(Type::Builtin[:str]))
  when :$0, :$PROGRAM_NAME
    Type::Instance.new(Type::Builtin[:str])
  when :$~
    # optional type is tentatively disabled; it is too conservative
    #Type.optional(Type::Instance.new(Type::Builtin[:matchdata]))
    Type::Instance.new(Type::Builtin[:matchdata])
  when :$., :$$
    Type::Instance.new(Type::Builtin[:int])
  when :$?
    Type.optional(Type::Instance.new(Type::Builtin[:int]))
  when :$!
    Type.optional(Type::Instance.new(Type::Builtin[:exc]))
  when :$@
    str = Type::Instance.new(Type::Builtin[:str])
    base_ty = Type::Instance.new(Type::Builtin[:ary])
    Type.optional(Type::Array.new(Type::Array::Elements.new([], str), base_ty))
  when :$*, :$:, :$LOAD_PATH, :$", :$LOADED_FEATURES
    str = Type::Instance.new(Type::Builtin[:str])
    base_ty = Type::Instance.new(Type::Builtin[:ary])
    Type::Array.new(Type::Array::Elements.new([], str), base_ty)
  when :$<
    :ARGF
  when :$>
    :STDOUT
  when :$DEBUG
    Type.bool
  when :$FILENAME
    Type::Instance.new(Type::Builtin[:str])
  when :$stdin
    :STDIN
  when :$stdout
    :STDOUT
  when :$stderr
    :STDERR
  when :$VERBOSE
    Type.bool.union(Type.nil)
  else
    nil
  end
end

.gen_hash(base_ty = Type::Instance.new(Type::Builtin[:hash])) {|hg| ... }

Yields:

  • (hg)
[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 755

def self.gen_hash(base_ty = Type::Instance.new(Type::Builtin[:hash]))
  hg = HashGenerator.new
  yield hg
  Type::Hash.new(Type::Hash::Elements.new(hg.map_tys), base_ty)
end

.guess_literal_type(obj)

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 761

def self.guess_literal_type(obj)
  case obj
  when ::Symbol
    Type::Symbol.new(obj, Type::Instance.new(Type::Builtin[:sym]))
  when ::Integer
    Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:int]))
  when ::Rational
    Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:rational]))
  when ::Complex
    Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:complex]))
  when ::Float
    Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:float]))
  when ::Class
    return Type.any if obj < Exception
    case obj
    when ::Object
      Type::Builtin[:obj]
    when ::Array
      Type::Builtin[:ary]
    else
      raise "unknown class: #{ obj.inspect }"
    end
  when ::TrueClass
    Type::Instance.new(Type::Builtin[:true])
  when ::FalseClass
    Type::Instance.new(Type::Builtin[:false])
  when ::Array
    base_ty = Type::Instance.new(Type::Builtin[:ary])
    lead_tys = obj.map {|arg| guess_literal_type(arg) }
    Type::Array.new(Type::Array::Elements.new(lead_tys), base_ty)
  when ::Hash
    Type.gen_hash do |h|
      obj.each do |k, v|
        k_ty = guess_literal_type(k).globalize(nil, {}, Config.current.options[:type_depth_limit])
        v_ty = guess_literal_type(v)
        h[k_ty] = v_ty
      end
    end
  when ::String
    Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:str]))
  when ::Regexp
    Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:regexp]))
  when ::NilClass
    Type.nil
  when ::Range
    Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:range]))
  when ::Encoding
    Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:encoding]))
  else
    raise "unknown object: #{ obj.inspect }"
  end
end

.match?(ty1, ty2) ⇒ Boolean

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 23

def self.match?(ty1, ty2)
  # both ty1 and ty2 should be global
  # ty1 is always concrete; it should not have type variables
  # ty2 might be abstract; it may have type variables
  case ty2
  when Type::Var
    { ty2 => ty1 }
  when Type::Any
    {}
  when Type::Union
    subst = nil
    ty2.each_child_global do |ty2|
      # this is very conservative to create subst:
      # Type.match?( int | str, int | X) creates { X => int | str } but should be { X => str }???
      subst2 = Type.match?(ty1, ty2)
      next unless subst2
      subst = Type.merge_substitution(subst, subst2)
    end
    subst
  else
    case ty1
    when Type::Var then raise "should not occur"
    when Type::Any
      subst = {}
      ty2.each_free_type_variable do |tyvar|
        subst[tyvar] = Type.any
      end
      subst
    when Type::Union
      subst = nil
      ty1.each_child_global do |ty1|
        subst2 = Type.match?(ty1, ty2)
        next unless subst2
        subst = Type.merge_substitution(subst, subst2)
      end
      subst
    else
      if ty2.is_a?(Type::ContainerType)
        # ty2 may have type variables
        if ty1.class == ty2.class
          ty1.match?(ty2)
        else
          Type.match?(ty1, ty2.base_type)
        end
      elsif ty1.is_a?(Type::ContainerType)
        Type.match?(ty1.base_type, ty2)
      else
        ty1.consistent?(ty2) ? {} : nil
      end
    end
  end
end

.merge_substitution(subst1, subst2)

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 76

def self.merge_substitution(subst1, subst2)
  if subst1
    subst1 = subst1.dup
    subst2.each do |tyvar, ty|
      if subst1[tyvar]
        subst1[tyvar] = subst1[tyvar].union(ty)
      else
        subst1[tyvar] = ty
      end
    end
    subst1
  else
    subst2
  end
end

.nil

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 442

def self.nil
  Thread.current[:nil] ||= Instance.new(Type::Builtin[:nil])
end

.optional(ty)

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 446

def self.optional(ty)
  ty.union(Type.nil)
end

Instance Method Details

#each_child {|_self| ... }

Yields:

  • (_self)

Yield Parameters:

  • _self (Type)

    the object that the method was called on

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 92

def each_child
  yield self
end

#each_child_global {|_self| ... }

Yields:

  • (_self)

Yield Parameters:

  • _self (Type)

    the object that the method was called on

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 96

def each_child_global
  yield self
end

#each_free_type_variable

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 100

def each_free_type_variable
end

#generate_substitution

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 164

def generate_substitution
  {}
end

#globalize(_env, _visited, _depth)

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 11

def globalize(_env, _visited, _depth)
  self
end

#include_untyped?(_scratch) ⇒ Boolean

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 177

def include_untyped?(_scratch)
  false
end

#limit_size(limit)

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 19

def limit_size(limit)
  self
end

#localize(env, _alloc_site, _depth)

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 15

def localize(env, _alloc_site, _depth)
  return env, self
end

#remove_type_vars

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 173

def remove_type_vars
  substitute(DummySubstitution, Config.current.options[:type_depth_limit])
end

#substitute(_subst, _depth)

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 160

def substitute(_subst, _depth)
  raise "cannot substitute abstract type: #{ self.class }"
end

#union(other)

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 103

def union(other)
  return self if self == other # fastpath

  ty1, ty2 = self, other

  case
  when ty1.is_a?(Union)
    ty1_types = ty1.types
    ty1_elems = ty1.elems
  when ty1.is_a?(Array) || ty1.is_a?(Hash)
    ty1_types = Utils::Set[]
    ty1_elems = {[ty1.class, ty1.base_type] => ty1.elems}
  else
    ty1_types = ty1_elems = nil
  end

  case
  when ty2.is_a?(Union)
    ty2_types = ty2.types
    ty2_elems = ty2.elems
  when ty2.is_a?(Array) || ty2.is_a?(Hash)
    ty2_types = Utils::Set[]
    ty2_elems = {[ty2.class, ty2.base_type] => ty2.elems}
  else
    ty2_types = ty2_elems = nil
  end

  if ty1_types && ty2_types
    ty = ty1_types.sum(ty2_types)
    all_elems = ty1_elems.dup || {}
    ty2_elems&.each do |key, elems|
      all_elems[key] = union_elems(all_elems[key], elems)
    end
    all_elems = nil if all_elems.empty?

    Type::Union.create(ty, all_elems)
  elsif ty1_types
    Type::Union.create(ty1_types.add(ty2), ty1_elems)
  elsif ty2_types
    Type::Union.create(ty2_types.add(ty1), ty2_elems)
  else
    Type::Union.create(Utils::Set[ty1, ty2], nil)
  end
end

#union_elems(e1, e2) (private)

[ GitHub ]

  
# File 'lib/typeprof/type.rb', line 148

private def union_elems(e1, e2)
  if e1
    if e2
      e1.union(e2)
    else
      e1
    end
  else
    e2
  end
end