{% skip_file if flag?(:without_interpreter) %} require "./spec_helper" private macro assert_overflows(code, file = __FILE__, line = __LINE__) it "overflows on {{code}}", file: {{file}}, line: {{line}} do interpret(%( class OverflowError < Exception; end fun __crystal_raise_overflow : NoReturn raise OverflowError.new end @[Primitive(:interpreter_raise_without_backtrace)] def raise(exception : Exception) : NoReturn end begin a = {{code}} 1 rescue OverflowError 2 end )).should eq(2) end end describe Crystal::Repl::Interpreter do context "literals" do it "interprets nil" do interpret("nil").should be_nil end it "interprets a bool (false)" do interpret("false").should be_false end it "interprets a bool (true)" do interpret("true").should be_true end it "interprets an Int8" do interpret("123_i8").should eq(123_i8) end it "interprets an UInt8" do interpret("145_u8").should eq(145_u8) end it "interprets an Int16" do interpret("12345_i16").should eq(12345_i16) end it "interprets an UInt16" do interpret("12389_u16").should eq(12389_u16) end it "interprets an Int32" do interpret("123456789_i32").should eq(123456789) end it "interprets an UInt32" do interpret("323456789_u32").should eq(323456789_u32) end it "interprets an Int64" do interpret("123_i64").should eq(123_i64) end it "interprets an UInt64" do interpret("123_u64").should eq(123_u64) end it "interprets an Int128" do interpret("123_i128").should eq(123_i128) end it "interprets an UInt128" do interpret("123_u128").should eq(123_u128) end it "interprets a Float32" do interpret("1.5_f32").should eq(1.5_f32) end it "interprets a Float64" do interpret("1.5").should eq(1.5) end it "interprets a char" do interpret("'a'").should eq('a') end it "interprets a String literal" do interpret(%("Hello world!")).should eq("Hello world!") end it "uses a string pool" do interpret(<<-CRYSTAL).should be_true "a".object_id == "a".object_id CRYSTAL end it "precomputes string literal length" do interpret(<<-CRYSTAL).should eq(1) "旅".@length CRYSTAL end end context "local variables" do it "interprets variable set" do interpret(<<-CRYSTAL).should eq(1) a = 1 CRYSTAL end it "interprets variable set with type restriction (#13023)" do interpret(<<-CRYSTAL).should eq(1) a : Int32 = 1 CRYSTAL end it "interprets variable set and get" do interpret(<<-CRYSTAL).should eq(1) a = 1 a CRYSTAL end it "interprets variable set and get, second local var" do interpret(<<-CRYSTAL).should eq(1) x = 10 a = 1 a CRYSTAL end it "interprets variable set and get with operations" do interpret(<<-CRYSTAL).should eq(6) a = 1 b = 2 c = 3 a + b + c CRYSTAL end it "interprets uninitialized" do interpret(<<-CRYSTAL).should eq(3) a = uninitialized Int32 a = 3 a CRYSTAL end it "doesn't declare variable with no type" do interpret(<<-CRYSTAL).should be_nil x = nil if x y = x end CRYSTAL end it "doesn't declare variable with no type inside method" do interpret(<<-CRYSTAL).should be_nil def foo(x) if x y = x end end foo(nil) CRYSTAL end it "assigns to underscore" do interpret(<<-CRYSTAL).should eq(1) _ = (a = 1) a CRYSTAL end it "doesn't discard underscore right hand side" do interpret(<<-CRYSTAL).should eq(1) a = (_ = 1) a CRYSTAL end it "interprets at the class level" do interpret(<<-CRYSTAL).should eq(1) x = 0 class Foo x = self.foo def self.foo bar end def self.bar 1 end end x CRYSTAL end it "interprets local variable declaration (#12229)" do interpret(<<-CRYSTAL).should eq(1) a : Int32 = 1 a CRYSTAL end end context "conversion" do {% for target_type in %w(u8 i8 u16 i16 u32 i32 u i u64 i64 f32 f64).map(&.id) %} it "interprets Int8::MAX#to_{{target_type}}!" do interpret("#{Int8::MAX}_i8.to_{{target_type}}!").should eq(Int8::MAX.to_{{target_type}}!) end it "interprets Int8::MIN#to_{{target_type}}!" do interpret("#{Int8::MIN}_i8.to_{{target_type}}!").should eq(Int8::MIN.to_{{target_type}}!) end it "interprets UInt8::MAX#to_{{target_type}}!" do interpret("#{UInt8::MAX}_u8.to_{{target_type}}!").should eq(UInt8::MAX.to_{{target_type}}!) end it "interprets Int16::MAX#to_{{target_type}}!" do interpret("#{Int16::MAX}_i16.to_{{target_type}}!").should eq(Int16::MAX.to_{{target_type}}!) end it "interprets Int16::MIN#to_{{target_type}}!" do interpret("#{Int16::MIN}_i16.to_{{target_type}}!").should eq(Int16::MIN.to_{{target_type}}!) end it "interprets UInt16::MAX#to_{{target_type}}!" do interpret("#{UInt16::MAX}_u16.to_{{target_type}}!").should eq(UInt16::MAX.to_{{target_type}}!) end it "interprets Int32::MAX#to_{{target_type}}!" do interpret("#{Int32::MAX}.to_{{target_type}}!").should eq(Int32::MAX.to_{{target_type}}!) end it "interprets Int32::MIN#to_{{target_type}}!" do interpret("#{Int32::MIN}.to_{{target_type}}!").should eq(Int32::MIN.to_{{target_type}}!) end it "interprets UInt32::MAX#to_{{target_type}}!" do interpret("#{UInt32::MAX}_u32.to_{{target_type}}!").should eq(UInt32::MAX.to_{{target_type}}!) end it "interprets Int64::MAX#to_{{target_type}}!" do interpret("#{Int64::MAX}_i64.to_{{target_type}}!").should eq(Int64::MAX.to_{{target_type}}!) end it "interprets Int64::MIN#to_{{target_type}}!" do interpret("#{Int64::MIN}_i64.to_{{target_type}}!").should eq(Int64::MIN.to_{{target_type}}!) end it "interprets UInt64::MAX#to_{{target_type}}!" do interpret("#{UInt64::MAX}_u64.to_{{target_type}}!").should eq(UInt64::MAX.to_{{target_type}}!) end it "interprets Float32#to_{{target_type}}! (positive)" do f = 23.8_f32 interpret("23.8_f32.to_{{target_type}}!").should eq(f.to_{{target_type}}!) end it "interprets Float64#to_{{target_type}}! (positive)" do f = 23.8_f64 interpret("23.8_f64.to_{{target_type}}!").should eq(f.to_{{target_type}}!) end {% unless target_type.starts_with?("u") %} # Do not test undefined behavior that might differ (#13736) it "interprets Float32#to_{{target_type}}! (negative)" do f = -23.8_f32 interpret("-23.8_f32.to_{{target_type}}!").should eq(f.to_{{target_type}}!) end it "interprets Float64#to_{{target_type}}! (negative)" do f = -23.8_f64 interpret("-23.8_f64.to_{{target_type}}!").should eq(f.to_{{target_type}}!) end {% end %} {% end %} it "interprets Char#ord" do interpret("'a'.ord").should eq('a'.ord) end it "Int32#unsafe_chr" do interpret("97.unsafe_chr").should eq(97.unsafe_chr) end it "UInt8#unsafe_chr" do interpret("97_u8.unsafe_chr").should eq(97.unsafe_chr) end it "discards conversion" do interpret(<<-CRYSTAL).should eq(3) 1.to_i8! 3 CRYSTAL end it "discards conversion with local var" do interpret(<<-CRYSTAL).should eq(3) x = 1 x.to_i8! 3 CRYSTAL end end context "overflow" do context "+" do assert_overflows 1_u8 + 255 assert_overflows 1_i8 + 128 assert_overflows 1_u16 + 65535 assert_overflows 1_i16 + 32767 assert_overflows 1_u32 + 4294967295 assert_overflows 1_i32 + 2147483647 assert_overflows 1_u64 + 18446744073709551615u64 assert_overflows 1_i64 + 9223372036854775807 end context "-" do assert_overflows 1_u8 - 2 assert_overflows 1_i8 - 256 assert_overflows 1_u16 - 2 assert_overflows 1_i16 - 32770 assert_overflows 1_u32 - 2 assert_overflows 1_i32 - 2147483650 assert_overflows 1_u64 - 2 assert_overflows 1_i64 - 9223372036854775810u64 end context "*" do assert_overflows 10_u8 * 26 assert_overflows 10_i8 * 14 assert_overflows 10_u16 * 6600 assert_overflows 10_i16 * 3300 assert_overflows 20_u32 * 429496729 assert_overflows 20_i32 * 214748364 assert_overflows 20_u64 * 1844674407370955161 assert_overflows 20_i64 * 922337203685477580 end context "conversion" do assert_overflows 128_u8.to_i8 assert_overflows -1_i8.to_u8 assert_overflows -1_i8.to_u16 assert_overflows -1_i8.to_u32 assert_overflows -1_i8.to_u64 assert_overflows 128_u16.to_i8 assert_overflows 32768_u16.to_i16 assert_overflows -1_i16.to_u8 assert_overflows -1_i16.to_u16 assert_overflows -1_i16.to_u32 assert_overflows -1_i16.to_u64 assert_overflows 128_u32.to_i8 assert_overflows 32768_u32.to_i16 assert_overflows 2147483648_u32.to_i32 assert_overflows -1_i32.to_u8 assert_overflows -1_i32.to_u16 assert_overflows -1_i32.to_u32 assert_overflows -1_i32.to_u64 assert_overflows 128_u64.to_i8 assert_overflows 32768_u64.to_i16 assert_overflows 2147483648_u64.to_i32 assert_overflows 9223372036854775808_u64.to_i64 assert_overflows -1_i64.to_u8 assert_overflows -1_i64.to_u16 assert_overflows -1_i64.to_u32 assert_overflows -1_i64.to_u64 assert_overflows 256_f32.to_u8 assert_overflows 128_f32.to_i8 assert_overflows 65536_f32.to_u16 assert_overflows 32768_f32.to_i16 # TODO: uncomment these once they also overflow on compiled Crystal # assert_overflows 4294967296_f32.to_u32 # assert_overflows 2147483648_f32.to_i32 # assert_overflows 18446744073709551616_f32.to_u64 # assert_overflows 9223372036854775808_f32.to_i64 assert_overflows 256_f64.to_u8 assert_overflows 128_f64.to_i8 assert_overflows 65536_f64.to_u16 assert_overflows 32768_f64.to_i16 assert_overflows 4294967296_f64.to_u32 assert_overflows 2147483648_f64.to_i32 # TODO: uncomment these once they also overflow on compiled Crystal # assert_overflows 18446744073709551616_f64.to_u64 # assert_overflows 9223372036854775808_f64.to_i64 assert_overflows 1.7976931348623157e+308.to_f32 end end context "math" do it "interprets Int32 + Int32" do interpret("1 + 2").should eq(3) end it "interprets Int32 &+ Int32" do interpret("1 &+ 2").should eq(3) end it "interprets Int64 + Int64" do interpret("1_i64 + 2_i64").should eq(3) end it "interprets Int32 - Int32" do interpret("1 - 2").should eq(-1) end it "interprets Int32 &- Int32" do interpret("1 &- 2").should eq(-1) end it "interprets Int32 * Int32" do interpret("2 * 3").should eq(6) end it "interprets Int32 &* Int32" do interpret("2 &* 3").should eq(6) end it "interprets UInt64 * Int32" do interpret("2_u64 * 3").should eq(6) end it "interprets UInt8 | Int32" do interpret("1_u8 | 2").should eq(3) end it "interprets UInt64 | UInt32" do interpret("1_u64 | 2_u32").should eq(3) end it "interprets UInt32 - Int32" do interpret("3_u32 - 2").should eq(1) end it "interprets Int32 + Float64" do interpret("1 + 2.5").should eq(3.5) end it "interprets Float32 + Float64" do interpret("1.0_f32 + 0.0").should eq(1.0_f32) end it "interprets Float64 + Int32" do interpret("2.5 + 1").should eq(3.5) end it "interprets Float64 + Float64" do interpret("2.5 + 2.3").should eq(4.8) end it "interprets Float64 - Float64" do interpret("2.5 - 2.3").should eq(2.5 - 2.3) end it "interprets Float64 * Float64" do interpret("2.5 * 2.3").should eq(2.5 * 2.3) end it "interprets Int8 + Int8" do interpret("1_i8 + 2_i8").should eq(3) end it "interprets UInt64 & Int32" do interpret("604_u64 & 4095").should eq(604) end it "interprets Int128 + Int32" do interpret("1_i128 + 2").should eq(3) end it "discards math" do interpret("1 + 2; 4").should eq(4) end it "interprets Int32.unsafe_shl(Int32) with self" do interpret(<<-CRYSTAL).should eq(4) struct Int32 def shl2 unsafe_shl(2) end end a = 1 a.shl2 CRYSTAL end end context "comparisons" do it "interprets Bool == Bool (false)" do interpret("true == false").should be_false end it "interprets Bool == Bool (true)" do interpret("true == true").should be_true end it "interprets Bool != Bool (false)" do interpret("true != true").should be_false end it "interprets Bool != Bool (true)" do interpret("true != false").should be_true end it "interprets Int32 < Int32" do interpret("1 < 2").should be_true end it "interprets Int32 == Int32 (true)" do interpret("1 == 1").should be_true end it "interprets Int32 == Int32 (false)" do interpret("1 == 2").should be_false end it "interprets Int32 != Int32 (true)" do interpret("1 != 2").should be_true end it "interprets Int32 != Int32 (false)" do interpret("1 != 1").should be_false end it "interprets Int32 == UInt64 (true)" do interpret("1 == 1_u64").should be_true end it "interprets Int32 == UInt64 (false)" do interpret("2 == 1_u64").should be_false end it "interprets Int32 != UInt64 (true)" do interpret("1 != 2_u64").should be_true end it "interprets Int32 != UInt64 (false)" do interpret("1 != 1_u64").should be_false end it "interprets UInt64 != Int32 (true)" do interpret("2_u64 != 1").should be_true end it "interprets UInt64 != Int32 (false)" do interpret("1_u64 != 1").should be_false end it "interprets Int128 == Int128 (false)" do interpret("1_i128 == 2_i128").should be_false end it "interprets Int128 == Int128 (true)" do interpret("1_i128 == 1_i128").should be_true end it "interprets Float32 / Int32" do interpret("2.5_f32 / 2").should eq(2.5_f32 / 2) end it "interprets Float32 / Float32" do interpret("2.5_f32 / 2.1_f32").should eq(2.5_f32 / 2.1_f32) end it "interprets Float64 / Float64" do interpret("2.5 / 2.1").should eq(2.5 / 2.1) end it "interprets Float32 fdiv Float64" do interpret("2.5_f32.fdiv(2.1_f64)").should eq(2.5_f32.fdiv(2.1_f64)) end it "interprets Float64 fdiv Float32" do interpret("2.5_f64.fdiv(2.1_f32)").should eq(2.5_f64.fdiv(2.1_f32)) end it "interprets Int32 == Float64 (true)" do interpret("1 == 1.0").should be_true end it "interprets Int32 == Float64 (false)" do interpret("1 == 1.2").should be_false end it "interprets Int32 > Float64 (true)" do interpret("2 > 1.9").should be_true end it "interprets Int32 > Float64 (false)" do interpret("2 > 2.1").should be_false end it "interprets UInt8 < Int32 (true, right is greater than zero)" do interpret("1_u8 < 2").should be_true end it "interprets UInt8 < Int32 (false, right is greater than zero)" do interpret("1_u8 < 0").should be_false end it "interprets UInt8 < Int32 (false, right is less than zero)" do interpret("1_u8 < -1").should be_false end it "interprets UInt64 < Int32 (true, right is greater than zero)" do interpret("1_u64 < 2").should be_true end it "interprets UInt64 < Int32 (false, right is greater than zero)" do interpret("1_u64 < 0").should be_false end it "interprets UInt64 < Int32 (false, right is less than zero)" do interpret("1_u64 < -1").should be_false end it "interprets UInt64 > UInt32 (true)" do interpret("1_u64 > 0_u32").should be_true end it "interprets UInt64 > UInt32 (false)" do interpret("0_u64 > 1_u32").should be_false end it "interprets UInt32 < Int32 (true)" do interpret("1_u32 < 2").should be_true end it "interprets UInt32 < Int32 (false)" do interpret("1_u32 < 1").should be_false end it "interprets UInt64 == Int32 (false when Int32 < 0)" do interpret("1_u64 == -1").should be_false end it "interprets UInt64 == Int32 (false when Int32 >= 0)" do interpret("1_u64 == 0").should be_false end it "interprets UInt64 == Int32 (true when Int32 >= 0)" do interpret("1_u64 == 1").should be_true end it "interprets Char == Char (false)" do interpret("'a' == 'b'").should be_false end it "interprets Char == Char (true)" do interpret("'a' == 'a'").should be_true end it "interprets Int32 < Float64" do interpret("1 < 2.5").should be_true end it "interprets Float64 < Int32" do interpret("1.2 < 2").should be_true end it "interprets Float64 < Float64" do interpret("1.2 < 2.3").should be_true end it "interprets UInt64.unsafe_mod(UInt64)" do interpret(<<-CRYSTAL).should eq(906272454103984) a = 10097976637018756016_u64 b = 9007199254740992_u64 a.unsafe_mod(b) CRYSTAL end it "discards comparison" do interpret("1 < 2; 3").should eq(3) end end context "logical operations" do it "interprets not for nil" do interpret("!nil").should be_true end it "interprets not for nil type" do interpret("x = 1; !(x = 2; nil); x").should eq(2) end it "interprets not for bool true" do interpret("!true").should be_false end it "interprets not for bool false" do interpret("!false").should be_true end it "discards nil not" do interpret("!nil; 3").should eq(3) end it "discards bool not" do interpret("!false; 3").should eq(3) end it "interprets not for bool false" do interpret("!false").should be_true end it "interprets not for mixed union (nil)" do interpret("!(1 == 1 ? nil : 2)").should be_true end it "interprets not for mixed union (false)" do interpret("!(1 == 1 ? false : 2)").should be_true end it "interprets not for mixed union (true)" do interpret("!(1 == 1 ? true : 2)").should be_false end it "interprets not for mixed union (other)" do interpret("!(1 == 1 ? 2 : true)").should be_false end it "interprets not for nilable type (false)" do interpret(%(!(1 == 1 ? "hello" : nil))).should be_false end it "interprets not for nilable type (true)" do interpret(%(!(1 == 1 ? nil : "hello"))).should be_true end it "interprets not for nilable proc type (true)" do interpret(<<-CRYSTAL).should be_true a = if 1 == 1 nil else ->{ 1 } end !a CRYSTAL end it "interprets not for nilable proc type (false)" do interpret(<<-CRYSTAL).should be_false a = if 1 == 1 ->{ 1 } else nil end !a CRYSTAL end it "interprets not for generic class instance type" do interpret(<<-CRYSTAL).should be_false class Foo(T) end foo = Foo(Int32).new !foo CRYSTAL end it "interprets not for nilable type (false)" do interpret(<<-CRYSTAL).should be_false class Foo end a = if 1 == 1 "a" elsif 1 == 1 Foo.new else nil end !a CRYSTAL end it "interprets not for nilable type (true)" do interpret(<<-CRYSTAL).should be_true class Foo end a = if 1 == 1 nil elsif 1 == 1 Foo.new else "a" end !a CRYSTAL end it "interprets not for module (#12918)" do interpret(<<-CRYSTAL).should be_false module MyModule; end class One include MyModule end !One.new.as(MyModule) CRYSTAL end it "interprets not for generic module" do interpret(<<-CRYSTAL).should be_false module MyModule(T); end class One include MyModule(Int32) end !One.new.as(MyModule(Int32)) CRYSTAL end it "interprets not for generic module metaclass" do interpret(<<-CRYSTAL).should be_false module MyModule(T); end !MyModule(Int32) CRYSTAL end it "interprets not for generic class instance metaclass" do interpret(<<-CRYSTAL).should be_false class MyClass(T); end !MyClass(Int32) CRYSTAL end it "does math primitive on union" do interpret(<<-CRYSTAL).should eq(3) module Test; end a = 1 a.as(Int32 | Test) &+ 2 CRYSTAL end it "does math convert on union" do interpret(<<-CRYSTAL).should eq(1) module Test; end a = 1 a.as(Int32 | Test).to_i64! CRYSTAL end end end