Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 8d46a07e07
Fetching contributors…

Cannot retrieve contributors at this time

416 lines (396 sloc) 13.104 kb
require_relative 'spec_helper'
describe 'CFG-based return type inference' do
it 'should infer types based on specified overloads' do
g = cfg <<-EOF
module RTI2
def self.multiply(x, y)
x * y
end
end
EOF
method = ClassRegistry['RTI2'].singleton_class.instance_method(:multiply)
method.return_type_for_types(
Utilities.type_for(ClassRegistry['RTI2']),
[Types::FIXNUM, Types::FLOAT]).should equal_type Types::UnionType.new([Types::FLOAT])
method.return_type_for_types(
Utilities.type_for(ClassRegistry['RTI2']),
[Types::FIXNUM, Types::FIXNUM]).should equal_type Types::UnionType.new([Types::FIXNUM, Types::BIGNUM])
end
it 'should infer type errors on methods with specified overloads' do
g = cfg <<-EOF
module RTI3
def self.sim3
if gets.size > 2
x = 'hi'
else
x = :hi
end
y = 15 * x
end
end
EOF
ClassRegistry['RTI3'].singleton_class.instance_method(:sim3).
return_type_for_types(
Utilities.type_for(ClassRegistry['RTI3'])).should == Types::EMPTY
g.should have_error(NoMatchingTypeSignature).on_line(8).with_message(/\*/)
end
it 'should infer the type resulting from a simple chain of standard-library methods' do
g = cfg <<-EOF
module RTI4
def self.bar
x = gets
qux(baz(x))
end
def self.baz(y)
y.to_sym.size
end
def self.qux(z)
z.zero?
end
end
EOF
ClassRegistry['RTI4'].singleton_class.instance_method(:bar).
return_type_for_types(
Utilities.type_for(ClassRegistry['RTI4'])).should equal_type Types::BOOLEAN
end
it 'should infer the type resulting from Class#new' do
g = cfg <<-EOF
module RTI5
class Foo
def initialize(x, y)
@x = x
@y = y
end
end
def self.make_a_foo(a, b)
Foo.new(a, b)
end
end
EOF
result = Types::UnionType.new([Types::ClassType.new('RTI5::Foo', :invariant)])
ClassRegistry['RTI5'].singleton_class.instance_method(:make_a_foo).
return_type_for_types(
Utilities.type_for(ClassRegistry['RTI5']),
[Types::FIXNUM, Types::FLOAT]).should equal_type result
end
it 'should infer types based on SSA, when appropriate' do
g = cfg <<-EOF
module RTI6
def self.multiply
if $$ > 10
a = 'hello'
else
a = 20
end
a * 3
end
end
EOF
result = Types::UnionType.new([Types::FIXNUM, Types::BIGNUM, Types::STRING])
ClassRegistry['RTI6'].singleton_class.instance_method(:multiply).
return_type_for_types(
Utilities.type_for(ClassRegistry['RTI6'])).should equal_type result
end
it 'should improve type inference due to SSA, when appropriate' do
g = cfg <<-EOF
module RTI7
def self.multiply
if $$ > 10
a = 'hello'
else
a = 20
end
b = a * 3
a = 3.14
a * 20
end
end
EOF
ClassRegistry['RTI7'].singleton_class.instance_method(:multiply).
return_type_for_types(
Utilities.type_for(ClassRegistry['RTI7'])).should equal_type Types::FLOAT
end
it 'should handle, via SSA, uninitialized variable types' do
g = cfg <<-EOF
class RTI8
def self.switch
if $$ > 10
a = 'hello'
end
b = a
end
end
EOF
ClassRegistry['RTI8'].singleton_class.instance_method(:switch).
return_type_for_types(
Utilities.type_for(ClassRegistry['RTI8'])).should equal_type(
Types::UnionType.new([Types::STRING, Types::NILCLASS]))
end
it 'should collect inferred types in global variables' do
g = cfg <<-EOF
module RTI9
def self.bar
$sim9 = x = gets
qux(baz(x))
end
def self.baz(y)
$sim9 = y.to_sym.size
end
def self.qux(z)
$sim9
end
end
EOF
# First, qux should give nil
ClassRegistry['RTI9'].singleton_class.instance_method(:qux).
return_type_for_types(
Utilities.type_for(ClassRegistry['RTI9']), [Types::STRING]).should equal_type Types::NILCLASS
expected_type = Types::UnionType.new(
[Types::STRING, Types::FIXNUM, Types::BIGNUM, Types::NILCLASS])
ClassRegistry['RTI9'].singleton_class.instance_method(:bar).
return_type_for_types(
Utilities.type_for(ClassRegistry['RTI9'])).should equal_type expected_type
Scope::GlobalScope.lookup('$sim9').expr_type.should equal_type expected_type
ClassRegistry['RTI9'].singleton_class.instance_method(:qux).
return_type_for_types(
Utilities.type_for(ClassRegistry['RTI9']), [Types::STRING]).should equal_type expected_type
end
it 'should collect inferred types in instance variables by class' do
g = cfg <<-EOF
class TI1
def set_foo(x)
@foo = x
end
def get_foo
@foo
end
end
class TI2
def set_foo(x)
@foo = x
end
def get_foo
@foo
end
end
EOF
ClassRegistry['TI1'].instance_method(:get_foo).return_type_for_types(
ClassRegistry['TI1'].as_type).should equal_type Types::NILCLASS
ClassRegistry['TI1'].instance_method(:set_foo).return_type_for_types(
ClassRegistry['TI1'].as_type, [Types::STRING]).should equal_type Types::STRING
ClassRegistry['TI1'].instance_method(:get_foo).return_type_for_types(
ClassRegistry['TI1'].as_type).should equal_type(
Types::UnionType.new([Types::NILCLASS, Types::STRING]))
ClassRegistry['TI1'].instance_method(:set_foo).return_type_for_types(
ClassRegistry['TI1'].as_type, [Types::FIXNUM]).should equal_type Types::FIXNUM
ClassRegistry['TI1'].instance_method(:get_foo).return_type_for_types(
ClassRegistry['TI1'].as_type).should equal_type(
Types::UnionType.new([Types::NILCLASS, Types::STRING, Types::FIXNUM]))
ClassRegistry['TI2'].instance_method(:get_foo).return_type_for_types(
ClassRegistry['TI2'].as_type).should equal_type Types::NILCLASS
ClassRegistry['TI2'].instance_method(:set_foo).return_type_for_types(
ClassRegistry['TI2'].as_type, [Types::FIXNUM]).should equal_type Types::FIXNUM
ClassRegistry['TI2'].instance_method(:get_foo).return_type_for_types(
ClassRegistry['TI2'].as_type).should equal_type(
Types::UnionType.new([Types::NILCLASS, Types::FIXNUM]))
end
it 'should extract argument types from rest arguments by index' do
g = cfg <<-EOF
class RTI11
def foo(*args)
args[0]
end
def bar(*args)
args[1]
end
end
EOF
ClassRegistry['RTI11'].instance_method(:foo).return_type_for_types(
ClassRegistry['RTI11'].as_type, [Types::FIXNUM, Types::PROC]).should equal_type Types::FIXNUM
ClassRegistry['RTI11'].instance_method(:bar).return_type_for_types(
ClassRegistry['RTI11'].as_type, [Types::FIXNUM, Types::PROC]).should equal_type Types::PROC
end
it 'should extract argument types from rest arguments by range index' do
g = cfg <<-EOF
class RTI12
def foo(*args)
args[0..1]
end
def bar(*args)
args[1..3]
end
def baz(*args)
args[-3..4]
end
def qux(*args)
args[-4..4]
end
end
EOF
ClassRegistry['RTI12'].instance_method(:foo).return_type_for_types(
ClassRegistry['RTI12'].as_type, [Types::FIXNUM, Types::PROC, Types::TRUECLASS]).should equal_type(
Types::TupleType.new([Types::FIXNUM, Types::PROC]))
ClassRegistry['RTI12'].instance_method(:bar).return_type_for_types(
ClassRegistry['RTI12'].as_type, [Types::STRING, Types::FLOAT, Types::HASH]).should equal_type(
Types::TupleType.new([Types::FLOAT, Types::HASH]))
ClassRegistry['RTI12'].instance_method(:baz).return_type_for_types(
ClassRegistry['RTI12'].as_type, [Types::STRING, Types::FLOAT, Types::HASH]).should equal_type(
Types::TupleType.new([Types::STRING, Types::FLOAT, Types::HASH]))
ClassRegistry['RTI12'].instance_method(:qux).return_type_for_types(
ClassRegistry['RTI12'].as_type, [Types::STRING, Types::FLOAT, Types::HASH]).should equal_type(
Types::NILCLASS)
end
it 'infers tuple types during expansion' do
g = cfg <<-EOF
class RTI13
def foo(*args)
a, b, c = args
a
end
def bar(*args)
a, b, c = args
b
end
def baz(*args)
a, b, c = args
c
end
end
EOF
ClassRegistry['RTI13'].instance_method(:foo).return_type_for_types(
ClassRegistry['RTI13'].as_type, [Types::FIXNUM, Types::PROC, Types::TRUECLASS]).should equal_type(
Types::FIXNUM)
ClassRegistry['RTI13'].instance_method(:bar).return_type_for_types(
ClassRegistry['RTI13'].as_type, [Types::FIXNUM, Types::PROC, Types::TRUECLASS]).should equal_type(
Types::PROC)
ClassRegistry['RTI13'].instance_method(:baz).return_type_for_types(
ClassRegistry['RTI13'].as_type, [Types::FIXNUM, Types::PROC, Types::TRUECLASS]).should equal_type(
Types::TRUECLASS)
ClassRegistry['RTI13'].instance_method(:baz).return_type_for_types(
ClassRegistry['RTI13'].as_type, [Types::FIXNUM, Types::PROC]).should equal_type(
Types::NILCLASS)
end
it 'infers the result of #size on tuples' do
g = cfg <<-EOF
class RTI14
def foo(*args)
args[args.size - 2]
end
def bar(*args)
args[args.length - 2]
end
def baz(*args)
args[args.size - 4]
end
end
EOF
ClassRegistry['RTI14'].instance_method(:foo).return_type_for_types(
ClassRegistry['RTI14'].as_type, [Types::FIXNUM, Types::PROC, Types::TRUECLASS]).should equal_type(
Types::PROC)
ClassRegistry['RTI14'].instance_method(:bar).return_type_for_types(
ClassRegistry['RTI14'].as_type, [Types::FIXNUM, Types::PROC, Types::TRUECLASS]).should equal_type(
Types::PROC)
ClassRegistry['RTI14'].instance_method(:foo).return_type_for_types(
ClassRegistry['RTI14'].as_type, [Types::FIXNUM, Types::PROC]).should equal_type(
Types::FIXNUM)
ClassRegistry['RTI14'].instance_method(:bar).return_type_for_types(
ClassRegistry['RTI14'].as_type, [Types::FIXNUM, Types::PROC]).should equal_type(
Types::FIXNUM)
ClassRegistry['RTI14'].instance_method(:baz).return_type_for_types(
ClassRegistry['RTI14'].as_type, [Types::FIXNUM, Types::PROC, Types::TRUECLASS]).should equal_type(
Types::TRUECLASS)
end
it 'infers the type of #to_a/ary on tuples' do
g = cfg <<-EOF
class RTI15
def foo(*args)
args.to_a.to_ary.to_a.to_a
end
end
EOF
ClassRegistry['RTI15'].instance_method(:foo).return_type_for_types(
ClassRegistry['RTI15'].as_type, [Types::FIXNUM, Types::PROC, Types::TRUECLASS]).should equal_type(
Types::TupleType.new([Types::FIXNUM, Types::PROC, Types::TRUECLASS]))
end
it 'infers tuple types from array literals' do
g = cfg <<-EOF
class RTI16
def foo
[1, :foo, 'string', {}]
end
end
EOF
ClassRegistry['RTI16'].instance_method(:foo).return_type_for_types(
ClassRegistry['RTI16'].as_type).should equal_type(
Types::TupleType.new([Types::FIXNUM, ClassRegistry['Symbol'].as_type,
Types::STRING, Types::HASH]))
end
it 'infers tuple types from array literals with varying components' do
g = cfg <<-EOF
class RTI17
def foo(x)
[x, :foo, 'string', {}]
end
end
EOF
ClassRegistry['RTI17'].instance_method(:foo).return_type_for_types(
ClassRegistry['RTI17'].as_type, [Types::FIXNUM]).should equal_type(
Types::TupleType.new([Types::FIXNUM, ClassRegistry['Symbol'].as_type,
Types::STRING, Types::HASH]))
end
it 'infers through calls to super' do
g = cfg <<-EOF
class RTI18
def foo(x)
"Hello \#{x}"
end
end
class RTI19 < RTI18
def foo(x, y)
super(y).size
end
end
class RTI20 < RTI19
def foo(x, y)
z = super
z.to_s
end
end
EOF
ClassRegistry['RTI18'].instance_method(:foo).return_type_for_types(
ClassRegistry['RTI18'].as_type, [Types::FIXNUM]).should equal_type(Types::STRING)
ClassRegistry['RTI19'].instance_method(:foo).return_type_for_types(
ClassRegistry['RTI19'].as_type, [Types::FIXNUM, Types::STRING]).should equal_type(
Types::FIXNUM | Types::BIGNUM)
ClassRegistry['RTI20'].instance_method(:foo).return_type_for_types(
ClassRegistry['RTI20'].as_type, [Types::FIXNUM, Types::STRING]).should equal_type(
Types::STRING)
end
it 'infers tuple types from tuple addition' do
g = cfg <<-EOF
class RTI22
def foo(x)
[x, :foo] + ['foobar', x]
end
end
EOF
ClassRegistry['RTI22'].instance_method(:foo).return_type_for_types(
ClassRegistry['RTI22'].as_type, [Types::FIXNUM]).should equal_type(
Types::TupleType.new([Types::FIXNUM, ClassRegistry['Symbol'].as_type,
Types::STRING, Types::FIXNUM]))
end
it 'infers tuple types from tuple multiplication' do
g = cfg <<-EOF
class RTI21
def foo(x)
[x, :foo] * 3
end
end
EOF
ClassRegistry['RTI21'].instance_method(:foo).return_type_for_types(
ClassRegistry['RTI21'].as_type, [Types::FIXNUM]).should equal_type(
Types::TupleType.new([Types::FIXNUM, ClassRegistry['Symbol'].as_type,
Types::FIXNUM, ClassRegistry['Symbol'].as_type,
Types::FIXNUM, ClassRegistry['Symbol'].as_type]))
end
end
Jump to Line
Something went wrong with that request. Please try again.