require "../../spec_helper" describe "Codegen: is_a?" do it "codegens is_a? true for simple type" do run("1.is_a?(Int)").to_b.should be_true end it "codegens is_a? false for simple type" do run("1.is_a?(Bool)").to_b.should be_false end it "codegens is_a? with union gives true" do run("(1 == 1 ? 1 : 'a').is_a?(Int)").to_b.should be_true end it "codegens is_a? with union gives false" do run("(1 == 1 ? 1 : 'a').is_a?(Char)").to_b.should be_false end it "codegens is_a? with union gives false" do run("(1 == 1 ? 1 : 'a').is_a?(Float)").to_b.should be_false end it "codegens is_a? with union gives true" do run("(1 == 1 ? 1 : 'a').is_a?(Object)").to_b.should be_true end it "codegens is_a? with nilable gives true" do run("(1 == 1 ? nil : Reference.new).is_a?(Nil)").to_b.should be_true end it "codegens is_a? with nilable gives false because other type 1" do run("(1 == 1 ? nil : Reference.new).is_a?(Reference)").to_b.should be_false end it "codegens is_a? with nilable gives false because other type 2" do run("(1 == 2 ? nil : Reference.new).is_a?(Reference)").to_b.should be_true end it "codegens is_a? with nilable gives false because no type" do run("(1 == 2 ? nil : Reference.new).is_a?(String)").to_b.should be_false end it "codegens is_a? with nilable gives false because no type" do run("1.is_a?(Object)").to_b.should be_true end it "doesn't error if result is discarded (#14113)" do run(<<-CRYSTAL).to_i.should eq(1) class Foo end (Foo.new || "").is_a?(Foo) 1 CRYSTAL end it "evaluate method on filtered type" do run("a = 1; a = 'a'; if a.is_a?(Char); a.ord; else; 0; end").to_i.chr.should eq('a') end it "evaluate method on filtered type nilable type not-nil" do run(<<-CRYSTAL).to_i.should eq(1) class Foo def foo 1 end end a = nil a = Foo.new if a.is_a?(Foo) a.foo else 2 end CRYSTAL end it "evaluate method on filtered type nilable type nil" do run(<<-CRYSTAL).to_i.should eq(1) struct Nil def foo 1 end end class Foo end a = Foo.new a = nil if a.is_a?(Nil) a.foo else 2 end CRYSTAL end it "evaluates method on filtered union type" do run(<<-CRYSTAL).to_i.should eq(2) class Foo def initialize(x : Int32) @x = x end def x @x end end a = 1 a = Foo.new(2) if a.is_a?(Reference) a.x else 0 end CRYSTAL end it "evaluates method on filtered union type 2" do run(<<-CRYSTAL).to_i.should eq(3) class Foo def initialize(x : Int32) @x = x end def x @x end end class Bar def initialize(x : Int32) @x = x end def x @x end end a = 1 a = Foo.new(2) a = Bar.new(3) if a.is_a?(Reference) a.x else 0 end CRYSTAL end it "evaluates method on filtered union type 3" do run(<<-CRYSTAL).to_i.should eq(5) require "prelude" a = 1 a = [1.1] a = [5] if a.is_a?(Enumerable) a[0] else 0 end.to_i CRYSTAL end it "codegens when is_a? is always false but properties are used" do run(<<-CRYSTAL).to_b.should be_false require "prelude" class Foo def obj; 1 end end foo = 1 foo.is_a?(Foo) && foo.obj && foo.obj CRYSTAL end it "codegens is_a? on right side of and" do run(<<-CRYSTAL).to_i.should eq(1) class Foo def bar true end end foo = Foo.new || nil if 1 == 1 && foo.is_a?(Foo) && foo.bar 1 else 2 end CRYSTAL end it "codegens is_a? with virtual" do run(<<-CRYSTAL).to_i.should eq(1) class Foo end class Bar < Foo end foo = Bar.new || Foo.new foo.is_a?(Bar) ? 1 : 2 CRYSTAL end it "codegens is_a? with virtual and nil" do run(<<-CRYSTAL).to_i.should eq(1) class Foo end class Bar < Foo end f = Foo.new || Bar.new || nil f.is_a?(Foo) ? 1 : 2 CRYSTAL end it "codegens is_a? with virtual and module" do run(<<-CRYSTAL).to_b.should be_true module Bar end abstract class FooBase2 end abstract class FooBase < FooBase2 include Bar end class Foo < FooBase end class Foo2 < FooBase2 end f = Foo.new || Foo2.new f.is_a?(Bar) CRYSTAL end it "restricts simple type with union" do run(<<-CRYSTAL).to_i.should eq(2) a = 1 if a.is_a?(Int32 | Char) a &+ 1 else 0 end CRYSTAL end it "restricts union with union" do run(<<-CRYSTAL).to_i.should eq(3) struct Char def &+(other : Int32) other end end struct Bool def foo 2 end end a = 1 || 'a' || false if a.is_a?(Int32 | Char) a &+ 2 else a.foo end CRYSTAL end it "codegens is_a? with a Const does comparison and gives true" do run(<<-CRYSTAL).to_b.should be_true require "prelude" CONST = 1 1.is_a?(CONST) CRYSTAL end it "codegens is_a? with a Const does comparison and gives false" do run(<<-CRYSTAL).to_b.should be_false require "prelude" CONST = 1 2.is_a?(CONST) CRYSTAL end it "gives false if generic type doesn't match exactly" do run(<<-CRYSTAL).to_i.should eq(2) class Foo(T) end foo = Foo(Int32 | Float64).new foo.is_a?(Foo(Int32)) ? 1 : 2 CRYSTAL end it "does is_a? with more strict virtual type" do run(<<-CRYSTAL).to_i.should eq(2) class Foo end class Bar < Foo def foo 2 end end f = Bar.new || Foo.new if f.is_a?(Bar) f.foo else 1 end CRYSTAL end it "codegens is_a? casts union to nilable" do run(<<-CRYSTAL).to_i.should eq(2) class Foo; end var = "hello" || Foo.new || nil if var.is_a?(Foo | Nil) var2 = var 1 else 2 end CRYSTAL end it "codegens is_a? casts union to nilable in method" do run(<<-CRYSTAL).to_i.should eq(2) class Foo; end def foo(var) if var.is_a?(Foo | Nil) var2 = var 1 else 2 end end var = "hello" || Foo.new || nil foo(var) CRYSTAL end it "codegens is_a? from virtual type to module" do run(<<-CRYSTAL).to_i.should eq(1) module Moo end class Foo end class Bar < Foo include Moo end class Baz < Foo include Moo end f = Bar.new || Baz.new if f.is_a?(Moo) g = f 1 else 2 end CRYSTAL end it "codegens is_a? from nilable reference union type to nil" do run(<<-CRYSTAL).to_i.should eq(2) class Foo end class Bar end a = Foo.new || Bar.new || nil if a.is_a?(Nil) b = a 1 else 2 end CRYSTAL end it "codegens is_a? from nilable reference union type to type" do run(<<-CRYSTAL).to_i.should eq(1) class Foo end class Bar end a = Foo.new || Bar.new || nil if a.is_a?(Foo) b = a 1 else 2 end CRYSTAL end it "says false for value.is_a?(Class)" do run(<<-CRYSTAL).to_b.should be_false 1.is_a?(Class) CRYSTAL end it "restricts type in else but lazily" do run(<<-CRYSTAL).to_i.should eq(2) class Foo def initialize(@x : Int32) end def x @x end end foo = Foo.new(1) x = foo.x if x.is_a?(Int32) z = x &+ 1 else z = x.foo_bar end z CRYSTAL end it "works with inherited generic class against an instantiation" do run(<<-CRYSTAL).to_b.should be_true class Foo(T) end class Bar < Foo(Int32) end bar = Bar.new bar.is_a?(Foo(Int32)) CRYSTAL end it "doesn't work with inherited generic class against an instantiation (2)" do run(<<-CRYSTAL).to_b.should be_false class Class1 end class Class2 < Class1 end class Foo(T) end class Bar < Foo(Class2) end bar = Bar.new bar.is_a?(Foo(Class1)) CRYSTAL end it "works with inherited generic class against an instantiation (3)" do run(<<-CRYSTAL).to_b.should be_false class Foo(T) end class Bar < Foo(Int32) end bar = Bar.new bar.is_a?(Foo(Float32)) CRYSTAL end it "doesn't type merge (1) (#548)" do run(<<-CRYSTAL).to_b.should be_false class Base; end class Base1 < Base; end class Base2 < Base; end class Base3 < Base; end Base3.new.is_a?(Base1 | Base2) CRYSTAL end it "doesn't type merge (2) (#548)" do run(<<-CRYSTAL).to_b.should be_true class Base; end class Base1 < Base; end class Base2 < Base; end class Base3 < Base; end Base1.new.is_a?(Base1 | Base2) && Base2.new.is_a?(Base1 | Base2) CRYSTAL end it "doesn't skip assignment when used in combination with .is_a? (true case, then) (#1121)" do run(<<-CRYSTAL).to_i.should eq(124) a = 123 if (b = a).is_a?(Int32) b &+ 1 else a end CRYSTAL end it "doesn't skip assignment when used in combination with .is_a? (true case, else) (#1121)" do run(<<-CRYSTAL).to_i.should eq(125) a = 123 if (b = a).is_a?(Int32) a &+ 2 else b end CRYSTAL end it "doesn't skip assignment when used in combination with .is_a? (false case) (#1121)" do run(<<-CRYSTAL).to_i.should eq(124) a = 123 if (b = a).is_a?(Char) b else b &+ 1 end CRYSTAL end it "doesn't skip assignment when used in combination with .is_a? and && (#1121)" do run(<<-CRYSTAL).to_i.should eq(124) a = 123 if (1 == 1) && (b = a).is_a?(Char) b else a end b ? b &+ 1 : 0 CRYSTAL end it "transforms then if condition is always truthy" do run(<<-CRYSTAL).to_i.should eq(456) def foo 123 && 456 end if 1.is_a?(Int32) foo else 999 end CRYSTAL end it "transforms else if condition is always falsey" do run(<<-CRYSTAL).to_i.should eq(456) def foo 123 && 456 end if 1.is_a?(Char) 999 else foo end CRYSTAL end it "resets truthy state after visiting nodes (bug)" do run(<<-CRYSTAL).to_i.should eq(123) a = 123 if !1.is_a?(Int32) a = 456 end a CRYSTAL end it "does is_a? with generic class metaclass" do run(<<-CRYSTAL).to_b.should be_true class Foo(T) end Foo(Int32).is_a?(Foo.class) CRYSTAL end it "says false for GenericChild(Base).is_a?(GenericBase(Child)) (#1294)" do run(<<-CRYSTAL).to_b.should be_false class Base end class Child < Base end class GenericBase(T) end class GenericChild(T) < GenericBase(T) end GenericChild(Base).new.is_a?(GenericBase(Child)) CRYSTAL end it "does is_a?/responds_to? twice (#1451)" do run(<<-CRYSTAL).to_i.should eq(4) a = 1 == 2 ? 1 : false if a.is_a?(Int32) && a.is_a?(Int32) 3 else 4 end CRYSTAL end it "does is_a? with && and true condition" do run(<<-CRYSTAL).to_i.should eq(3) a = 1 == 1 ? 1 : false if a.is_a?(Int32) && 1 == 1 3 else 4 end CRYSTAL end it "does is_a? for union of module and type" do run(<<-CRYSTAL).to_i.should eq(2) module Moo def moo 2 end end class Foo include Moo end class Bar include Moo end def foo(io) if io.is_a?(Moo) io.moo else 3 end end io = Foo.new.as(Moo) || 1 foo(io) CRYSTAL end it "does is_a? for virtual generic instance type against generic" do run(<<-CRYSTAL).to_i.should eq(2) class Foo(T) end class Bar(T) < Foo(T) end def foo(x : Bar) end Bar(Int32).new.as(Foo(Int32)).is_a?(Bar) ? 2 : 3 CRYSTAL end it "does is_a?(generic type) for nested generic inheritance (1) (#9660)" do run(<<-CRYSTAL, inject_primitives: false).to_b.should be_true class Cxx end class Foo(T) end class Bar(T) < Foo(T) end class Baz < Bar(Cxx) end Baz.new.is_a?(Foo) CRYSTAL end it "does is_a?(generic type) for nested generic inheritance (2)" do run(<<-CRYSTAL, inject_primitives: false).to_b.should be_true class Cxx end class Foo(T) end class Bar(T) < Foo(T) end class Baz(T) < Bar(T) end Baz(Cxx).new.is_a?(Foo) CRYSTAL end it "does is_a?(generic type) for nested generic inheritance, through upcast (1)" do run(<<-CRYSTAL, inject_primitives: false).to_b.should be_true class Cxx end class Foo(T) end class Bar(T) < Foo(T) end class Baz < Bar(Cxx) end Baz.new.as(Foo(Cxx)).is_a?(Bar) CRYSTAL end it "does is_a?(generic type) for nested generic inheritance, through upcast (2)" do run(<<-CRYSTAL, inject_primitives: false).to_b.should be_true class Cxx end class Foo(T) end class Bar(T) < Foo(T) end class Baz(T) < Bar(T) end Baz(Cxx).new.as(Foo(Cxx)).is_a?(Bar) CRYSTAL end it "doesn't consider generic type to be a generic type of a recursive alias (#3524)" do run(<<-CRYSTAL).to_b.should be_false class Gen(T) end alias Type = Int32 | Gen(Type) a = Gen(Int32).new a.is_a?(Type) CRYSTAL end it "codegens untyped var (#4009)" do codegen(<<-CRYSTAL) i = 1 1 || i.is_a?(Int32) ? "" : i CRYSTAL end it "visits 1.to_s twice, may trigger enclosing_call (#4364)" do run(<<-CRYSTAL).to_b.should be_true require "prelude" B = String 1.to_s.is_a?(B) CRYSTAL end it "says true for Class.is_a?(Class.class) (#4374)" do run(<<-CRYSTAL).to_b.should be_true Class.is_a?(Class.class) CRYSTAL end it "says true for Class.is_a?(Class.class.class) (#4374)" do run(<<-CRYSTAL).to_b.should be_true Class.is_a?(Class.class.class) CRYSTAL end it "passes is_a? with generic module type on virtual type (#10302)" do run(<<-CRYSTAL).to_b.should be_true module Mod(T) end abstract struct Sup include Mod(Sup) end struct Sub < Sup end Sub.new.is_a?(Mod(Sup)) CRYSTAL end it "restricts metaclass against virtual metaclass type" do run(<<-CRYSTAL).to_i.should eq(1) class A end class B < A end x = B || A if x.is_a?(B.class) 1 elsif x.is_a?(A.class) 2 else 3 end CRYSTAL end it "restricts virtual metaclass against virtual metaclass type" do run(<<-CRYSTAL).to_i.should eq(1) class A end class B < A end class C < B end x = B || A if x.is_a?(B.class) 1 elsif x.is_a?(A.class) 2 else 3 end CRYSTAL end it "does is_a? with union type, don't resolve to virtual type (#10244)" do run(<<-CRYSTAL).to_b.should be_false class A end class B < A end class C < A end class D < A end x = D.new || C.new x.is_a?(B | C) CRYSTAL end it "does is_a? with union type as Union(X, Y), don't resolve to virtual type (#10244)" do run(<<-CRYSTAL).to_b.should be_false class A end class B < A end class C < A end class D < A end x = D.new || C.new x.is_a?(Union(B, C)) CRYSTAL end it "restricts union metaclass to metaclass (#12295)" do run(<<-CRYSTAL).to_i.should eq(2) x = true ? Union(String | Int32) : String if x.is_a?(String.class) 1 else 2 end CRYSTAL end it "does is_a? for generic type against generic class instance type (#12304)" do run(<<-CRYSTAL).to_b.should be_true require "prelude" class A end class B(T) < A end a = B(Int32).new.as(A) b = a.as(B) b.is_a?(B(Int32)) CRYSTAL end it "virtual metaclass type is not virtual instance type (#12628)" do run(<<-CRYSTAL).to_b.should be_false abstract struct Base end struct Impl < Base end Base.as(Base | Base.class).is_a?(Base | Impl) CRYSTAL end end