require "../../spec_helper" describe "Semantic: instance var" do it "declares instance var" do assert_type(<<-CRYSTAL) { int32 } class Foo @x : Int32 def initialize x = 1 @x = x end def x @x end end Foo.new.x CRYSTAL end it "declares instance var multiple times, last one wins" do assert_type(<<-CRYSTAL) { union_of(int32, float64) } class Foo @x : Int32 @x : Int32 | Float64 def initialize x = 1 @x = x end def x @x end end Foo.new.x CRYSTAL end it "doesn't error when redeclaring subclass variable with the same type" do assert_type(<<-CRYSTAL) { int32 } class Foo @x : Int32 def initialize x = 1 @x = x end def x @x end end class Bar < Foo @x : Int32 end Bar.new.x CRYSTAL end it "errors when redeclaring subclass variable with a different type" do assert_error <<-CRYSTAL, "instance variable '@x' of Foo, with Bar < Foo, is already declared as Int32" class Foo @x : Int32 def initialize x = 1 @x = x end def x @x end end class Bar < Foo @x : String end Bar.new.x CRYSTAL end it "declares instance var in module, inherits to type" do assert_type(<<-CRYSTAL) { int32 } module Moo @x : Int32 def initialize x = 1 @x = x end def x @x end end class Foo include Moo end Foo.new.x CRYSTAL end it "declares instance var in module, inherits to type recursively" do assert_type(<<-CRYSTAL) { int32 } module Moo @x : Int32 def initialize x = 1 @x = x end def x @x end end module Moo2 include Moo end class Foo include Moo2 end Foo.new.x CRYSTAL end it "declares instance var of generic type" do assert_type(<<-CRYSTAL) { int32 } class Foo(T) @x : T def initialize(@x : T) end def x @x end end Foo.new(1).x CRYSTAL end it "declares instance var of generic type, with no type parameter" do assert_type(<<-CRYSTAL) { int32 } class Foo(T) @x : Int32 def initialize(@x : Int32) end def x @x end end Foo(Char).new(1).x CRYSTAL end it "declares instance var of generic type, with generic type" do assert_type(<<-CRYSTAL) { generic_class "Gen", int32 } class Gen(T) end class Foo(T) @x : Gen(T) def initialize(@x : Gen(T)) end def x @x end end Foo.new(Gen(Int32).new).x CRYSTAL end it "declares instance var of generic type, with union" do assert_type(<<-CRYSTAL) { union_of int32, char } class Foo(T) @x : T | Char def initialize(@x) end def x @x end end Foo(Int32).new(1).x CRYSTAL end it "declares instance var of generic type, with proc" do assert_type(<<-CRYSTAL) { proc_of([char, char, int32]) } class Foo(T) @x : T, T -> Int32 def initialize(@x : T, T -> Int32) end def x @x end end Foo(Char).new(->(x : Char, y : Char) { 1 }).x CRYSTAL end it "declares instance var of generic type, with tuple" do assert_type(<<-CRYSTAL) { tuple_of([char, int32, char]) } class Foo(T) @x : {T, Int32, T} def initialize(@x : {T, Int32, T}) end def x @x end end Foo(Char).new({'a', 1, 'b'}).x CRYSTAL end it "declares instance var of generic type, with metaclass" do assert_type(<<-CRYSTAL) { int32.metaclass } class Foo(T) @x : T.class def initialize(@x : T.class) end def x @x end end Foo(Int32).new(Int32).x CRYSTAL end it "declares instance var of generic type, with virtual metaclass" do assert_type(<<-CRYSTAL) { types["Bar"].virtual_type!.metaclass } class Bar; end class Baz < Bar; end class Foo(T) @x : T.class def initialize(@x : T.class) end def x @x end end Foo(Bar).new(Bar).x CRYSTAL end it "declares instance var of generic type, with static array" do assert_type(<<-CRYSTAL) { static_array_of(uint8, 3) } class Foo(T) @x : UInt8[T] def initialize(@x : UInt8[T]) end def x @x end end z = uninitialized UInt8[3] Foo(3).new(z).x CRYSTAL end it "declares instance var of generic type, with splat" do assert_type(<<-CRYSTAL) { generic_class "Gen", int32, string } class Gen(*T) end class Foo(*T) @x : Gen(*T) def initialize(@x : Gen(*T)) end def x @x end end Foo.new(Gen(Int32, String).new).x CRYSTAL end it "declares instance var of generic type, with splat inside Tuple" do assert_type(<<-CRYSTAL) { tuple_of([int32, string]) } class Foo(*T) @x : Tuple(*T) def initialize(@x : Tuple(*T)) end def x @x end end Foo.new({1, ""}).x CRYSTAL end it "declares instance var of generic type, with splat inside Proc" do assert_type(<<-CRYSTAL) { proc_of([int32, string, bool]) } class Foo(R, *T) @x : Proc(*T, R) def initialize(@x : Proc(*T, R)) end def x @x end end Foo.new(->(x : Int32, y : String) { true }).x CRYSTAL end it "declares instance var with self, on generic" do assert_type(<<-CRYSTAL) { nilable generic_class("Foo", int32) } class Foo(T) @x : self | Nil def x @x end end Foo(Int32).new.x CRYSTAL end it "errors if declaring variable with number" do assert_error <<-CRYSTAL, "can't declare variable with NumberLiteral" class Foo(T) @x : T def initialize(@x : T) end def x @x end end Foo(3).new(3).x CRYSTAL end it "declares instance var of generic type through module" do assert_type(<<-CRYSTAL) { int32 } module Moo @x : Int32 def initialize a = 1 @x = a end def x @x end end class Foo(T) include Moo end Foo(Float64).new.x CRYSTAL end it "declares instance var of generic type subclass" do assert_type(<<-CRYSTAL) { int32 } class Foo(T) @x : T def initialize(@x) end def x @x end end class Bar(T) < Foo(T) end Bar(Int32).new(1).x CRYSTAL end it "declares instance var of generic module" do assert_type(<<-CRYSTAL) { int32 } module Moo(T) @x : T def x @x end end class Foo(T) include Moo(T) def initialize a = 1 @x = a end end Foo(Int32).new.x CRYSTAL end it "declares instance var of generic module (2)" do assert_type(<<-CRYSTAL) { int32 } module Moo(U) @x : U def x @x end end class Foo(T) include Moo(T) def initialize a = 1 @x = a end end Foo(Int32).new.x CRYSTAL end it "declares instance var of generic module from non-generic module" do assert_type(<<-CRYSTAL) { int32 } module Moo @x : Int32 def initialize(@x) end def x @x end end module Moo2(T) include Moo end class Foo(T) include Moo2(T) def initialize super(1) a = 1 @x = a end end Foo(Float64).new.x CRYSTAL end it "infers type from number literal" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x = 1 end def x @x end end Foo.new.x CRYSTAL end it "infers type from char literal" do assert_type(<<-CRYSTAL) { char } class Foo def initialize @x = 'a' end def x @x end end Foo.new.x CRYSTAL end it "infers type from bool literal" do assert_type(<<-CRYSTAL) { bool } class Foo def initialize @x = true end def x @x end end Foo.new.x CRYSTAL end it "infers type from string literal" do assert_type(<<-CRYSTAL) { string } class Foo def initialize @x = "hi" end def x @x end end Foo.new.x CRYSTAL end it "infers type from string interpolation" do assert_type(<<-CRYSTAL) { string } require "prelude" class Foo def initialize @x = "foo\#{1}" end def x @x end end Foo.new.x CRYSTAL end it "infers type from symbol literal" do assert_type(<<-CRYSTAL) { symbol } class Foo def initialize @x = :hi end def x @x end end Foo.new.x CRYSTAL end it "infers type from array literal with of" do assert_type(<<-CRYSTAL) { array_of int32 } class Foo def initialize @x = [] of Int32 end def x @x end end Foo.new.x CRYSTAL end it "infers type from array literal with of metaclass" do assert_type(<<-CRYSTAL) { array_of int32.metaclass } class Foo def initialize @x = [] of Int32.class end def x @x end end Foo.new.x CRYSTAL end it "infers type from array literal from its literals" do assert_type(<<-CRYSTAL) { array_of union_of(int32, char) } require "prelude" class Foo def initialize @x = [1, 'a'] end def x @x end end Foo.new.x CRYSTAL end it "infers type from hash literal with of" do assert_type(<<-CRYSTAL) { hash_of int32, string } class Foo def initialize @x = {} of Int32 => String end def x @x end end Foo.new.x CRYSTAL end it "infers type from hash literal from elements" do assert_type(<<-CRYSTAL) { hash_of(union_of(int32, char), union_of(string, bool)) } require "prelude" class Foo def initialize @x = {1 => "foo", 'a' => true} end def x @x end end Foo.new.x CRYSTAL end it "infers type from range literal" do assert_type(<<-CRYSTAL) { range_of(int32, char) } require "prelude" class Foo def initialize @x = 1..'a' end def x @x end end Foo.new.x CRYSTAL end it "infers type from regex literal" do assert_type(<<-CRYSTAL) { types["Regex"] } require "prelude" class Foo def initialize @x = /foo/ end def x @x end end Foo.new.x CRYSTAL end it "infers type from regex literal with interpolation" do assert_type(<<-CRYSTAL) { types["Regex"] } require "prelude" class Foo def initialize @x = /foo\#{1}/ end def x @x end end Foo.new.x CRYSTAL end it "infers type from tuple literal" do assert_type(<<-CRYSTAL) { tuple_of([int32, string]) } class Foo def initialize @x = {1, "foo"} end def x @x end end Foo.new.x CRYSTAL end it "infers type from named tuple literal" do assert_type(<<-CRYSTAL) { named_tuple_of({"x": int32, "y": string}) } class Foo def initialize @x = {x: 1, y: "foo"} end def x @x end end Foo.new.x CRYSTAL end it "infers type from proc literal with return type" do assert_type(<<-CRYSTAL) { proc_of([int32, bool, string]) } class Foo def initialize @x = ->(x : Int32, y : Bool) : String { "" } end def x @x end end Foo.new.x CRYSTAL end it "infers type from new expression" do assert_type(<<-CRYSTAL) { types["Bar"] } class Bar end class Foo def initialize @x = Bar.new end def x @x end end Foo.new.x CRYSTAL end it "infers type from as" do assert_type(<<-CRYSTAL, inject_primitives: true) { int32 } class Foo def initialize @x = (1 + 2).as(Int32) end def x @x end end Foo.new.x CRYSTAL end it "infers type from as?" do assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 } class Foo def initialize @x = (1 + 2).as?(Int32) end def x @x end end Foo.new.x CRYSTAL end it "infers type from argument restriction" do assert_type(<<-CRYSTAL) { nilable int32 } class Foo def x=(@x : Int32) end def x @x end end Foo.new.x CRYSTAL end it "infers type from argument default value" do assert_type(<<-CRYSTAL) { nilable int32 } class Foo def set(@x = 1) end def x @x end end Foo.new.x CRYSTAL end it "infers type from lib fun call" do assert_type(<<-CRYSTAL) { types["LibFoo"].types["Bar"] } lib LibFoo struct Bar x : Int32 end fun foo : Bar end class Foo def initialize @x = LibFoo.foo end def x @x end end Foo.new.x CRYSTAL end it "infers type from lib variable" do assert_type(<<-CRYSTAL) { types["LibFoo"].types["Bar"] } lib LibFoo struct Bar x : Int32 end $foo : Bar end class Foo def initialize @x = LibFoo.foo end def x @x end end Foo.new.x CRYSTAL end it "infers type from ||" do assert_type(<<-CRYSTAL) { union_of(int32, bool) } class Foo def initialize @x = 1 || true end def x @x end end Foo.new.x CRYSTAL end it "infers type from &&" do assert_type(<<-CRYSTAL) { union_of(int32, bool) } class Foo def initialize @x = 1 && true end def x @x end end Foo.new.x CRYSTAL end it "infers type from ||=" do assert_type(<<-CRYSTAL) { nilable int32 } class Foo def x @x ||= 1 end end Foo.new.@x CRYSTAL end it "infers type from ||= inside another assignment" do assert_type(<<-CRYSTAL) { nilable int32 } class Foo def x x = @x ||= 1 end end Foo.new.@x CRYSTAL end it "infers type from if" do assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, bool) } class Foo def initialize @x = 1 == 1 ? 1 : true end def x @x end end Foo.new.x CRYSTAL end it "infers type from case" do assert_type(<<-CRYSTAL) { union_of(char, bool) } require "prelude" class Foo def initialize @x = case 1 when 2 'a' else true end end def x @x end end Foo.new.x CRYSTAL end it "infers type from unless" do assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, bool) } class Foo def initialize @x = unless 1 == 1 1 else true end end def x @x end end Foo.new.x CRYSTAL end it "infers type from begin" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x = begin 'a' 1 end end def x @x end end Foo.new.x CRYSTAL end it "infers type from assign (1)" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x = @y = 1 end def x @x end end Foo.new.x CRYSTAL end it "infers type from assign (2)" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x = @y = 1 end def y @y end end Foo.new.y CRYSTAL end it "infers type from block argument" do assert_type(<<-CRYSTAL) { nilable proc_of(int32, int32) } class Foo def set(&@x : Int32 -> Int32) end def x @x end end Foo.new.x CRYSTAL end it "infers type from block argument without restriction" do assert_type(<<-CRYSTAL) { nilable proc_of(void) } class Foo def set(&@x) end def x @x end end Foo.new.x CRYSTAL end it "infers type from !" do assert_type(<<-CRYSTAL) { bool } class Foo def initialize @x = !1 end def x @x end end Foo.new.x CRYSTAL end it "infers type from is_a?" do assert_type(<<-CRYSTAL) { bool } class Foo def initialize @x = 1.is_a?(Char) end def x @x end end Foo.new.x CRYSTAL end it "infers type from responds_to?" do assert_type(<<-CRYSTAL) { bool } class Foo def initialize @x = 1.responds_to?(:foo) end def x @x end end Foo.new.x CRYSTAL end it "infers type from sizeof" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x = sizeof(Int32) end def x @x end end Foo.new.x CRYSTAL end it "infers type from instance_sizeof" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x = instance_sizeof(Foo) end def x @x end end Foo.new.x CRYSTAL end it "infers type from offsetof" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x = offsetof(Bar, @x) end def x @x end end struct Bar @x = 0 end Foo.new.x CRYSTAL end it "infers type from path that is a type" do assert_type(<<-CRYSTAL) { types["Bar"].virtual_type!.metaclass } class Bar; end class Baz < Bar; end class Foo def initialize @x = Bar end def x @x end end Foo.new.x CRYSTAL end it "infers type from path that is a constant" do assert_type(<<-CRYSTAL) { int32 } CONST = 1 class Foo def initialize @x = CONST end def x @x end end Foo.new.x CRYSTAL end it "doesn't infer type from redefined method" do assert_type(<<-CRYSTAL) { nilable char } class Foo def foo @x = 1 end def foo @x = 'a' end def x @x end end Foo.new.x CRYSTAL end it "infers type from redefined method if calls previous_def" do assert_type(<<-CRYSTAL) { union_of(nil_type, int32, char) } class Foo def foo @x = 1 end def foo previous_def @x = 'a' end def x @x end end Foo.new.x CRYSTAL end it "infers type in multi assign" do assert_type(<<-CRYSTAL) { tuple_of([int32, char]) } class Foo def initialize @x, @y = 1, 'a' end def x @x end def y @y end end {Foo.new.x, Foo.new.y} CRYSTAL end it "infers type from enum member" do assert_type(<<-CRYSTAL) { types["Color"] } enum Color Red Green Blue end class Foo def initialize @x = Color::Red end def x @x end end Foo.new.x CRYSTAL end it "infers type from two literals" do assert_type(<<-CRYSTAL) { union_of int32, float64 } class Foo def initialize @x = 1 @x = 1.5 end def x @x end end Foo.new.x CRYSTAL end it "infers type from literal outside def" do assert_type(<<-CRYSTAL) { int32 } class Foo @x = 1 def x @x end end Foo.new.x CRYSTAL end it "infers type from literal outside def with initialize and type restriction" do assert_type(<<-CRYSTAL) { int32 } class Foo @x : Int32 @x = 1 def initialize end def x @x end end Foo.new.x CRYSTAL end it "infers type from lib out (1)" do assert_type(<<-CRYSTAL) { types["LibFoo"].types["Bar"] } lib LibFoo struct Bar x : Int32 end fun foo(x : Int32, y : Bar*) : Int32 end class Foo def initialize LibFoo.foo(1, out @two) end def two @two end end Foo.new.two CRYSTAL end it "infers type from lib out (2)" do assert_type(<<-CRYSTAL) { float64 } lib LibFoo fun foo(x : Int32, y : Float64*) : Int32 end class Foo def initialize @err = LibFoo.foo(1, out @two) end def two @two end end Foo.new.two CRYSTAL end it "infers type from lib out (3)" do assert_type(<<-CRYSTAL) { int32 } lib LibFoo fun foo(x : Int32, y : Float64*) : Int32 end class Foo def initialize @err = LibFoo.foo(1, out @two) end def err @err end end Foo.new.err CRYSTAL end it "infers type from uninitialized" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x = uninitialized Int32 end def x @x end end Foo.new.x CRYSTAL end it "doesn't infer for subclass if assigns another type (1)" do assert_error <<-CRYSTAL, "instance variable '@x' of Foo must be Int32, not Float64" class Foo def initialize @x = 1 end def x @x end end class Bar < Foo def foo @x = 1.5 end end Bar.new.foo CRYSTAL end it "doesn't infer for subclass if assigns another type (2)" do assert_error <<-CRYSTAL, "instance variable '@x' of Foo must be Int32, not Float64" class Foo end class Bar < Foo def foo @x = 1.5 end end class Foo def initialize @x = 1 end def x @x end end Bar.new.foo CRYSTAL end it "infers type from included module" do assert_type(<<-CRYSTAL) { int32 } module Moo def initialize @x = 1 end def x @x end end class Foo include Moo end Foo.new.x CRYSTAL end it "infers type from included module, outside def" do assert_type(<<-CRYSTAL) { int32 } module Moo @x = 1 def x @x end end class Foo include Moo end Foo.new.x CRYSTAL end it "infers type from included module recursively" do assert_type(<<-CRYSTAL) { int32 } module Moo def initialize @x = 1 end def x @x end end module Moo2 include Moo end class Foo include Moo2 end Foo.new.x CRYSTAL end it "infers type for generic class, with literal" do assert_type(<<-CRYSTAL) { int32 } class Foo(T) def initialize @x = 1 end def x @x end end Foo(Float64).new.x CRYSTAL end it "infers type for generic class, with T.new" do assert_type(<<-CRYSTAL) { types["Bar"] } class Bar end class Foo(T) def initialize @x = T.new end def x @x end end Foo(Bar).new.x CRYSTAL end it "infers type for generic class, with T.new and literal" do assert_type(<<-CRYSTAL) { union_of types["Bar"], int32 } class Bar end class Foo(T) def initialize @x = T.new @x = 1 end def x @x end end Foo(Bar).new.x CRYSTAL end it "infers type for generic class, with lib call" do assert_type(<<-CRYSTAL) { types["LibFoo"].types["Bar"] } lib LibFoo struct Bar x : Int32 end fun foo : Bar end class Foo(T) def initialize @x = LibFoo.foo end def x @x end end Foo(Float64).new.x CRYSTAL end it "infers type for generic class, with &&" do assert_type(<<-CRYSTAL) { union_of(types["Foo"], types["Bar"]) } class Foo end class Bar end class Gen(T) def initialize @x = T.new || Foo.new end def x @x end end Gen(Bar).new.x CRYSTAL end it "infers type for generic class, with begin" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo end class Gen(T) def initialize @x = begin 1 T.new end end def x @x end end Gen(Foo).new.x CRYSTAL end it "infers type for generic class, with if" do assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(types["Foo"], types["Bar"]) } class Foo end class Bar end class Gen(T) def initialize @x = 1 == 2 ? T.new : Foo.new end def x @x end end Gen(Bar).new.x CRYSTAL end it "infers type for generic class, with case" do assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(types["Foo"], types["Bar"]) } class Object def ===(other) self == other end end class Foo end class Bar end class Gen(T) def initialize @x = case 1 when 2 then T.new else Foo.new end end def x @x end end Gen(Bar).new.x CRYSTAL end it "infers type for generic class, with assign (1)" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo end class Gen(T) def initialize @x = @y = T.new end def x @x end end Gen(Foo).new.x CRYSTAL end it "infers type for generic class, with assign (2)" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo end class Gen(T) def initialize @x = @y = T.new end def y @y end end Gen(Foo).new.y CRYSTAL end it "infers type for non-generic class, with assign" do assert_type(<<-CRYSTAL) { int32 } class Foo @x : Int32 @y : Int32 def initialize @x = @y = 1 end def y @y end end Foo.new.y CRYSTAL end it "infers type for generic module" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo end module Moo(T) def initialize @x = T.new end def x @x end end class Gen(T) include Moo(T) end Gen(Foo).new.x CRYSTAL end it "infers type to be nilable if not initialized" do assert_type(<<-CRYSTAL) { nilable int32 } class Foo def x @x = 1 @x end end Foo.new.x CRYSTAL end it "infers type to be non-nilable if initialized in all initialize" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x = 1 end def initialize(@x : Int32) end def x @x end end Foo.new.x CRYSTAL end it "errors if not initialized in all initialize" do assert_error <<-CRYSTAL, "this 'initialize' doesn't explicitly initialize instance variable '@x' of Foo, rendering it nilable" class Foo def initialize @x = 1 end def initialize(x) end def x @x end end Foo.new.x CRYSTAL end it "doesn't error if not initializes in all initialize because declared as nilable" do assert_type(<<-CRYSTAL) { nilable int32 } class Foo @x : Int32? def initialize @x = 1 end def initialize(x) end def x @x end end Foo.new.x CRYSTAL end it "infers type from argument with restriction, in generic" do assert_type(<<-CRYSTAL) { int32 } class Foo(T) def initialize(@x : T) end def x @x end end Foo.new(1).x CRYSTAL end it "says undefined instance variable on read" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Foo" class Foo def x @x end end Foo.new.x CRYSTAL end it "says undefined instance variable on assign" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Foo" class Foo def x a = 1 @x = a end end Foo.new.x CRYSTAL end it "errors if declaring instance var and turns out to be nilable" do assert_error <<-CRYSTAL, "instance variable '@x' of Foo was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported." class Foo @x : Int32 end CRYSTAL end it "doesn't if declaring nilable instance var and turns out to be nilable" do assert_type(<<-CRYSTAL) { nilable int32 } class Foo @x : Int32? def x @x end end Foo.new.x CRYSTAL end it "errors if declaring instance var and turns out to be nilable, in generic type" do assert_error <<-CRYSTAL, "instance variable '@x' of Foo(T) was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported." class Foo(T) @x : T end CRYSTAL end it "errors if declaring instance var and turns out to be nilable, in generic module type" do assert_error <<-CRYSTAL, "instance variable '@x' of Foo was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported." module Moo(T) @x : T end class Foo include Moo(Int32) end CRYSTAL end it "doesn't error if declaring instance var and doesn't out to be nilable, in generic module type" do assert_type(<<-CRYSTAL) { int32 } module Moo(T) @x : T def x @x end end class Foo include Moo(Int32) def initialize(@x) end end foo = Foo.new(1) foo.x CRYSTAL end it "errors if declaring instance var and turns out to be nilable, in generic module type in generic type" do assert_error <<-CRYSTAL, "instance variable '@x' of Foo(T) was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported." module Moo(T) @x : T end class Foo(T) include Moo(T) end CRYSTAL end it "doesn't error if not initializing variables but calling super" do assert_type(<<-CRYSTAL) { int32 } class Foo @x : Int32 def initialize @x = 1 end def x @x end end class Bar < Foo def initialize(x) super() end end Bar.new(10).x CRYSTAL end it "doesn't error if not initializing variables but calling previous_def (#3210)" do assert_type(<<-CRYSTAL) { int32 } class Some def initialize @a = 1 end def initialize previous_def end def a @a end end Some.new.a CRYSTAL end it "doesn't error if not initializing variables but calling super and previous_def" do assert_type(<<-CRYSTAL) { int32 } class Foo @x : Int32 def initialize @x = 1 end def x @x end end class Bar < Foo def initialize(x) super() end def initialize(x) previous_def(x) end end Bar.new(10).x CRYSTAL end it "doesn't error if not initializing variables but calling previous_def (2) (#3210)" do assert_type(<<-CRYSTAL, inject_primitives: true) { int32 } class Some def initialize @a = 1 @b = 2 end def initialize previous_def @b = @a end def a @a end def b @b end end Some.new.a + Some.new.b CRYSTAL end it "errors if not initializing super variables" do assert_error <<-CRYSTAL, "this 'initialize' doesn't initialize instance variable '@x' of Foo, with Bar < Foo, rendering it nilable" class Foo @x : Int32 def initialize @x = 1 end end class Bar < Foo def initialize end end CRYSTAL end it "errors if not initializing super variables (2)" do assert_error <<-CRYSTAL, "this 'initialize' doesn't initialize instance variable '@x' of Foo, with Bar < Foo, rendering it nilable" class Foo @x : Int32 def initialize @x = 1 end end class Bar < Foo def initialize @y = 2 end end CRYSTAL end it "errors if not initializing super variables (3)" do assert_error <<-CRYSTAL, "this 'initialize' doesn't initialize instance variable '@x' of Foo, with Bar < Foo, rendering it nilable" class Foo def initialize @x = 1 end end class Bar < Foo def initialize @y = 2 end end CRYSTAL end it "errors if not initializing super variable in generic" do assert_error <<-CRYSTAL, "this 'initialize' doesn't initialize instance variable '@x' of Foo(T), with Bar(T) < Foo(T), rendering it nilable" class Foo(T) def initialize @x = 1 end end class Bar(T) < Foo(T) def initialize @y = 2 end end CRYSTAL end it "doesn't error if not calling super but initializing all variables" do assert_type(<<-CRYSTAL) { int32 } class Foo @x : Int32 def initialize @x = 1 end def x @x end end class Bar < Foo def initialize(x) @x = 2 end end Bar.new(10).x CRYSTAL end it "doesn't error if not initializing variables but calling super in parent parent" do assert_type(<<-CRYSTAL) { int32 } class Foo @x : Int32 def initialize @x = 1 end def x @x end end class Bar < Foo end class Baz < Bar def initialize(x) super() end end Baz.new(10).x CRYSTAL end it "doesn't error if not initializing variables but calling super for module" do assert_type(<<-CRYSTAL) { int32 } module Moo @x : Int32 def initialize @x = 1 end def x @x end end class Foo include Moo def initialize(x) super() end end Foo.new(10).x CRYSTAL end it "doesn't error if not initializing variables but calling super for generic module" do assert_type(<<-CRYSTAL) { int32 } module Moo(T) @x : T def initialize(@x) end def x @x end end class Foo include Moo(Int32) def initialize(x) super(x) end end Foo.new(10).x CRYSTAL end it "ignores redefined initialize (#456)" do assert_type(<<-CRYSTAL, inject_primitives: true) { int32 } class Foo def initialize @a = 1 end def initialize @a = 1 @b = 2 end def a @a end def b @b end end a = Foo.new a.a + a.b CRYSTAL end it "ignores super module initialize (#456)" do assert_type(<<-CRYSTAL, inject_primitives: true) { int32 } module Moo def initialize @a = 1 end end class Foo include Moo def initialize @a = 1 @b = 2 end def a @a end def b @b end end a = Foo.new a.a + a.b CRYSTAL end it "obeys super module initialize (#456)" do assert_type(<<-CRYSTAL, inject_primitives: true) { int32 } module Moo def initialize @a = 1 end def a @a end end class Foo include Moo def initialize @b = 2 super end def b @b end end b = Foo.new b.a + b.b CRYSTAL end it "doesn't error if initializing var in superclass, and then empty initialize" do assert_type(<<-CRYSTAL) { int32 } class Foo @x : Int32 @x = 1 def x @x end end class Bar < Foo def initialize end end Bar.new.x CRYSTAL end it "doesn't error if calling initialize from another initialize (1)" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize(@x : Int32) end def initialize initialize(1) end def x @x end end Foo.new.x CRYSTAL end it "doesn't error if calling initialize from another initialize (2)" do assert_type(<<-CRYSTAL) { nilable int32 } class Foo def initialize(@x : Int32) @y = nil end def initialize initialize(1) @y = 2 end def y @y end end Foo.new.y CRYSTAL end it "infers nilable instance var of generic type" do assert_type(<<-CRYSTAL) { nilable int32 } class Foo(T) def set @coco = 2 end def coco @coco end end f = Foo(Int32).new f.coco CRYSTAL end it "infers nilable instance var of generic module" do assert_type(<<-CRYSTAL) { nilable int32 } module Moo(T) def set @coco = 2 end def coco @coco end end class Foo(T) include Moo(T) end f = Foo(Int32).new f.coco CRYSTAL end it "infers type to be nilable if self is used before assigning to a variable" do assert_type(<<-CRYSTAL) { nilable int32 } class Foo def initialize self @x = 1 end def x @x end end Foo.new.x CRYSTAL end it "infers type to be nilable if self is used in same assign" do assert_type(<<-CRYSTAL) { nilable int32 } def foo(x) end class Foo def initialize @x = 1 || foo(self) end def x @x end end Foo.new.x CRYSTAL end it "doesn't infer type to be nilable if using self.class" do assert_type(<<-CRYSTAL, inject_primitives: true) { int32 } class Foo def initialize self.class @x = 1 end def x @x end end Foo.new.x CRYSTAL end pending "doesn't infer type to be nilable if using self.class in call in assign" do assert_type(<<-CRYSTAL) { int32 } def foo(x) end class Foo def initialize @x = 1 || foo(self.class) end def x @x end end Foo.new.x CRYSTAL end it "doesn't error if not initializing nilable var in subclass" do assert_type(<<-CRYSTAL) { nilable int32 } class Foo @x : Int32? def initialize(@x) end def x @x end end class Bar < Foo def initialize end end Bar.new.x CRYSTAL end it "considers var as assigned in multi-assign" do assert_type(<<-CRYSTAL, inject_primitives: true) { int32 } def some {1, 2} end class Foo @x : Int32 @y : Int32 def initialize @x, @y = some end def x @x end def y @y end end foo = Foo.new foo.x + foo.y CRYSTAL end it "infers from another instance var" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x = 1 @y = @x end def y @y end end Foo.new.y CRYSTAL end it "infers from another instance var with type declaration" do assert_type(<<-CRYSTAL) { int32 } class Foo @x : Int32 def initialize(@x) @y = @x end def y @y end end Foo.new(1).y CRYSTAL end it "infers from another instance var in generic type" do assert_type(<<-CRYSTAL) { types["Bar"] } class Bar end class Foo(T) def initialize @x = T.new @y = @x end def y @y end end Foo(Bar).new.y CRYSTAL end it "infers from another instance var in generic type with type declaration" do assert_type(<<-CRYSTAL) { types["Bar"] } class Bar end class Foo(T) @x : T def initialize(@x) @y = @x end def y @y end end Foo(Bar).new(Bar.new).y CRYSTAL end it "errors on undefined instance var and subclass calling super" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Bar" class Foo def initialize(@x) end def x @x end end class Bar < Foo def initialize(x) super @x = x end end point = Bar.new(1) Foo.new(1).x CRYSTAL end it "infers type from array literal in generic type" do assert_type(<<-CRYSTAL) { array_of(int32) } class Foo(T) def initialize @array = [] of T end def array @array end end Foo(Int32).new.array CRYSTAL end it "infers type from hash literal in generic type" do assert_type(<<-CRYSTAL) { hash_of(int32, float64) } class Foo(T) def initialize @array = {} of T => Float64 end def array @array end end Foo(Int32).new.array CRYSTAL end it "infers type from array literal with literals in generic type" do assert_type(<<-CRYSTAL) { array_of(int32) } require "prelude" class Foo(T) def initialize @array = [0] end def array @array end end Foo(Float64).new.array CRYSTAL end it "infers type from hash literal with literals in generic type" do assert_type(<<-CRYSTAL) { hash_of(int32, symbol) } require "prelude" class Foo(T) def initialize @hash = {0 => :foo} end def hash @hash end end Foo(Float64).new.hash CRYSTAL end it "infers from restriction using virtual type" do assert_type(<<-CRYSTAL) { types["Foo"].virtual_type! } class Foo; end class Bar < Foo; end class Baz def initialize(@x : Foo) end def x @x end end Baz.new(Foo.new).x CRYSTAL end it "doesn't duplicate instance var in subclass" do result = semantic(<<-CRYSTAL) class Foo def initialize(@x : Int32) end def x @x end end class Bar < Foo @x : Int32 end CRYSTAL foo = result.program.types["Foo"].as(NonGenericClassType) foo.instance_vars["@x"].type.should eq(result.program.int32) bar = result.program.types["Bar"].as(NonGenericClassType) bar.instance_vars.should be_empty end it "infers type from custom array literal" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo def initialize end def <<(v) end end class Bar def initialize @x = Foo{1, 2, 3} end def x @x end end Bar.new.x CRYSTAL end it "infers type from custom generic array literal" do assert_type(<<-CRYSTAL) { generic_class "Foo", int32 } class Foo(T) def initialize end def <<(v) end end class Bar def initialize @x = Foo{1, 2, 3} end def x @x end end Bar.new.x CRYSTAL end it "infers type from custom hash literal" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo def initialize end def []=(k, v) end end class Bar def initialize @x = Foo{1 => 2} end def x @x end end Bar.new.x CRYSTAL end it "infers type from custom generic hash literal" do assert_type(<<-CRYSTAL) { generic_class "Foo", int32, string } class Foo(K, V) def initialize end def []=(k, v) end end class Bar def initialize @x = Foo{1 => "foo"} end def x @x end end Bar.new.x CRYSTAL end it "infers type from custom array literal in generic" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo def initialize end def <<(v) end end class Bar(T) def initialize @x = Foo{1, 2, 3} end def x @x end end Bar(Int32).new.x CRYSTAL end it "infers type from custom hash literal in generic" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo def initialize end def []=(k, v) end end class Bar(T) def initialize @x = Foo{1 => 2} end def x @x end end Bar(Int32).new.x CRYSTAL end it "says can't infer type if only nil was assigned" do assert_error <<-CRYSTAL, "instance variable @x of Foo was inferred to be Nil, but Nil alone provides no information" class Foo def initialize @x = nil end def x @x end end Foo.new.x CRYSTAL end it "says can't infer type if only nil was assigned, in generic type" do assert_error <<-CRYSTAL, "instance variable @x of Foo(T) was inferred to be Nil, but Nil alone provides no information" class Foo(T) def initialize @x = nil end def x @x end end Foo(Int32).new.x CRYSTAL end it "allows nil instance var because it's a generic type" do assert_type(<<-CRYSTAL) { nil_type } class Foo(T) def initialize(@x : T) end def x @x end end Foo.new(nil).x CRYSTAL end it "uses virtual types in fun" do assert_type(<<-CRYSTAL) { proc_of(types["Node"].virtual_type, types["Node"].virtual_type) } class Node; end class SubNode < Node; end class Foo def initialize(@x : Node -> Node) end def x @x end end Foo.new(->(x : Node) { x }).x CRYSTAL end it "uses virtual types in union" do assert_type(<<-CRYSTAL) { union_of(types["Node"].virtual_type, int32) } class Node; end class SubNode < Node; end class Foo def initialize(@x : Node | Int32) end def x @x end end Foo.new(1).x CRYSTAL end it "uses virtual types in self" do assert_type(<<-CRYSTAL) { nilable types["Node"].virtual_type } class Node def initialize @x = nil end def initialize(@x : self) end def x @x end end class SubNode < Node; end Node.new.x CRYSTAL end it "infers from Pointer.malloc" do assert_type(<<-CRYSTAL, inject_primitives: true) { pointer_of(int32) } class Foo def initialize @x = Pointer(Int32).malloc(1_u64) end def x @x end end Foo.new.x CRYSTAL end it "infers from Pointer.malloc with two arguments" do assert_type(<<-CRYSTAL) { pointer_of(uint8) } require "prelude" class Foo def initialize @x = Pointer.malloc(10, 1_u8) end def x @x end end Foo.new.x CRYSTAL end it "infers from Pointer.null" do assert_type(<<-CRYSTAL) { pointer_of(int32) } require "prelude" class Foo def initialize @x = Pointer(Int32).null end def x @x end end Foo.new.x CRYSTAL end it "infers from Pointer.malloc in generic type" do assert_type(<<-CRYSTAL, inject_primitives: true) { pointer_of(int32) } class Foo(T) def initialize @x = Pointer(T).malloc(1_u64) end def x @x end end Foo(Int32).new.x CRYSTAL end it "infers from Pointer.null in generic type" do assert_type(<<-CRYSTAL) { pointer_of(int32) } require "prelude" class Foo(T) def initialize @x = Pointer(T).null end def x @x end end Foo(Int32).new.x CRYSTAL end it "infers from Pointer.malloc with two arguments in generic type" do assert_type(<<-CRYSTAL) { pointer_of(uint8) } require "prelude" class Foo(T) def initialize @x = Pointer.malloc(10, 1_u8) end def x @x end end Foo(Int32).new.x CRYSTAL end it "doesn't infer generic type without type argument inside generic" do assert_error <<-CRYSTAL, "can't infer the type parameter T for the generic class Bar(T)" class Bar(T) end class Foo(T) def initialize @bar = Bar.new end def bar @bar end end Foo(Int32).new.bar CRYSTAL end it "doesn't crash on missing var on subclass, with superclass not specifying a type" do assert_error <<-CRYSTAL, "this 'initialize' doesn't initialize instance variable '@x', rendering it nilable" class Foo def initialize(@x) end end class Bar < Foo def initialize end end Bar.new CRYSTAL end it "doesn't complain if not initialized in one initialize, but has initializer (#2465)" do assert_type(<<-CRYSTAL) { int32 } class Foo @x = 1 def initialize(@x) end def initialize end def x @x end end Foo.new.x CRYSTAL end it "can declare type even if included module has a guessed var" do assert_type(<<-CRYSTAL) { union_of int32, float64 } module Moo def foo @x = 1 end end class Foo include Moo @x : Int32 | Float64 def initialize @x = 1.5 end def x @x end end Foo.new.x CRYSTAL end it "doesn't complain if declared type is recursive alias that's nilable" do assert_type(<<-CRYSTAL) { types["Rec"] } class Bar(T) end alias Rec = Int32 | Nil | Bar(Rec) class Foo @x : Rec def x @x end end Foo.new.x CRYSTAL end it "infers from assign to local var (#2467)" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x = x = 1 end def x @x end end Foo.new.x CRYSTAL end it "infers from assign to local var in generic type (#2467)" do assert_type(<<-CRYSTAL) { int32 } class Foo(T) def initialize @x = x = 1 end def x @x end end Foo(Float64).new.x CRYSTAL end it "infers from top-level method that has type annotation" do assert_type(<<-CRYSTAL) { types["Bar"] } class Bar end def bar : Bar Bar.new end class Foo def initialize @bar = ::bar end def bar @bar end end Foo.new.bar CRYSTAL end it "infers from simple top-level method without type annotation" do assert_type(<<-CRYSTAL) { types["Bar"] } class Bar end def bar Bar.new end class Foo def initialize @bar = ::bar end def bar @bar end end Foo.new.bar CRYSTAL end it "infers from class method that has type annotation" do assert_type(<<-CRYSTAL) { types["Bar"] } class Bar def self.bar : Bar Bar.new end end class Foo def initialize @bar = Bar.bar end def bar @bar end end Foo.new.bar CRYSTAL end it "infers from class method that has type annotation, in generic class" do assert_type(<<-CRYSTAL) { types["Bar"] } class Bar def self.bar : Bar Bar.new end end class Foo(T) def initialize @bar = Bar.bar end def bar @bar end end Foo(Int32).new.bar CRYSTAL end it "infers from generic class method that has type annotation" do assert_type(<<-CRYSTAL) { generic_class "Bar", int32 } class Bar(T) def self.bar : self Bar(T).new end end class Foo def initialize @bar = Bar(Int32).bar end def bar @bar end end Foo.new.bar CRYSTAL end it "infers from generic class method that has type annotation, without instantiating" do assert_type(<<-CRYSTAL) { int32 } class Bar(T) def self.bar : Int32 1 end end class Foo def initialize @bar = Bar.bar end def bar @bar end end Foo.new.bar CRYSTAL end it "infers from class method that has type annotation, with overload" do assert_type(<<-CRYSTAL) { types["Bar"] } class Baz end class Bar def self.bar : Baz Baz.new end def self.bar(x) : Bar Bar.new end def self.bar(x) : Baz yield Bar.new end end class Foo def initialize @bar = Bar.bar(1) end def bar @bar end end Foo.new.bar CRYSTAL end it "infers from class method that has type annotation, with multiple overloads matching, all with the same type" do assert_type(<<-CRYSTAL) { types["Bar"] } class Bar def self.bar(x : Int32) : Bar Bar.new end def self.bar(x : String) : Bar Bar.new end end class Foo def initialize(x) @bar = Bar.bar(x) end def bar @bar end end Foo.new(1).bar CRYSTAL end it "infers from multiple class method overloads with same type but different spellings" do assert_type(<<-CRYSTAL) { types["Bar"] } class Bar def self.bar(x : Int32) : Bar Bar.new end def self.bar(x : Float64) : ::Bar Bar.new end def self.bar(x : String) : self Bar.new end end class Foo def initialize(x) @bar = Bar.bar(x) end def bar @bar end end Foo.new(1).bar CRYSTAL end it "infers from new with return type" do assert_type(<<-CRYSTAL) { int32 } class Foo def self.new : Int32 1 end end class Bar def initialize @x = Foo.new end def x @x end end Bar.new.x CRYSTAL end it "infers from new with return type in generic type" do assert_type(<<-CRYSTAL) { int32 } class Foo def self.new : Int32 1 end end class Bar(T) def initialize @x = Foo.new end def x @x end end Bar(Float64).new.x CRYSTAL end it "infers from new with return type returning generic" do assert_type(<<-CRYSTAL) { generic_class "Bar", int32 } class Foo(T) def self.new : Bar(T) Bar(T).new end end class Bar(T) end class Baz def initialize @x = Foo(Int32).new end def x @x end end Baz.new.x CRYSTAL end it "guesses from new on abstract class" do assert_type(<<-CRYSTAL) { types["Bar"] } abstract class Foo def self.new : Bar Bar.new(1) end end class Bar < Foo def initialize(x) end end class Baz def initialize @foo = Foo.new end def foo @foo end end Baz.new.foo CRYSTAL end it "errors on undefined constant" do assert_error <<-CRYSTAL, "undefined constant Bar" class Foo def initialize @x = Bar.new end end Foo.new CRYSTAL end it "infers from class method that invokes new" do assert_type(<<-CRYSTAL) { types["Bar"] } class Foo def initialize @x = Bar.create end def x @x end end class Bar def self.create new end end Foo.new.x CRYSTAL end it "infers from class method that has number literal" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x = Bar.default_num end def x @x end end class Bar def self.default_num 1 end end Foo.new.x CRYSTAL end it "infers from class method that refers to constant" do assert_type(<<-CRYSTAL) { types["Bar"] } class Foo def initialize @x = Bar.default_instance end def x @x end end class Bar DEFAULT = new def self.default_instance DEFAULT end end Foo.new.x CRYSTAL end it "infer from class method with multiple statements and return" do assert_type(<<-CRYSTAL, inject_primitives: true) { nilable int32 } class Foo def initialize @x = Bar.default end def x @x end end class Bar def self.default if 1 == 2 return nil end 1 end end Foo.new.x CRYSTAL end it "doesn't infer from class method with multiple statements and return, on non-easy return" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Foo", inject_primitives: true class Foo def initialize @x = Bar.default end def x @x end end class Bar def self.default if 1 == 2 a = 1 return a end 1 end end Foo.new.x CRYSTAL end it "doesn't infer from class method with multiple statements and return, on non-easy return (2)" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Foo", inject_primitives: true class Foo def initialize @x = Bar.default end def x @x end end class Bar def self.default if 1 == 2 a = 1 return a else 1 end end end Foo.new.x CRYSTAL end it "infer from class method where new is redefined" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x = Bar.default end def x @x end end class Bar def self.default new end def self.new 1 end end Foo.new.x CRYSTAL end it "doesn't crash on recursive method call" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Foo" class Foo def initialize @x = Bar.default end def x @x end end class Bar def self.default Bar.default2 end def self.default2 Bar.default end end Foo.new.x CRYSTAL end it "infers in multiple assign for tuple type (1)" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize @x, @y = Bar.method end def x @x end end class Bar def self.method : {Int32, Bool} {1, true} end end Foo.new.x CRYSTAL end it "says can't infer (#2536)" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@foo' of Foo(Int32)" require "prelude" class Foo(T) def initialize(@arg : T) @foo = [bar] end def initialize(@arg : T) @foo = [bar] yield 3 end def bar 3 end end Foo.new(3).foo CRYSTAL end it "doesn't crash when inferring from new without matches (#2538)" do assert_error <<-CRYSTAL, "wrong number of arguments for 'Foo.new'" class Foo @@default = Foo.new def initialize(@attr) end end Foo.new("aaaa") CRYSTAL end it "infers from method on integer literal, with type annotation" do assert_type(<<-CRYSTAL) { char } struct Int32 def foo : Char 'a' end end class Foo def initialize @x = 1.foo end def x @x end end Foo.new.x CRYSTAL end it "infers from method in generic type, with type annotation" do assert_type(<<-CRYSTAL) { generic_class "Gen", int32 } class Gen(T) def foo : Gen(T) self end end class Foo def initialize @x = Gen(Int32).new.foo end end Foo.new.@x CRYSTAL end it "infers type by removing nil from || left side" do assert_type(<<-CRYSTAL) { int32 } struct Int32 def foo : Int32? 1 end end class Foo def initialize @x = 1.foo || 2 end end Foo.new.@x CRYSTAL end it "infers type from all call matches" do assert_type(<<-CRYSTAL) { union_of int32, char } class Base def foo : Int32 1 end end class Sub1 < Base def foo : Char 'a' end end class Sub2 < Base def foo 1 + 2 end end class Foo def initialize(base : Base) @x = base.foo end end Foo.new(Base.new).@x CRYSTAL end it "guesses inside macro if" do assert_type(<<-CRYSTAL) { int32 } {% if true %} class Foo def initialize @x = 1 end def x @x end end {% end %} Foo.new.x CRYSTAL end it "guesses inside macro expression" do assert_type(<<-CRYSTAL) { int32 } {{ "class Foo; def initialize; @x = 1; end; def x; @x; end; end".id }} Foo.new.x CRYSTAL end it "guesses inside macro for" do assert_type(<<-CRYSTAL) { int32 } {% for name in %w(Foo) %} class {{name.id}} def initialize @x = 1 end def x @x end end {% end %} Foo.new.x CRYSTAL end it "can't infer type from initializer" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Foo" class Foo @x = 1 + 2 def x @x end end Foo.new.x CRYSTAL end it "can't infer type from initializer in non-generic module" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Moo" module Moo @x = 1 + 2 def x @x end end class Foo include Moo end Foo.new.x CRYSTAL end it "can't infer type from initializer in generic module type" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Moo(T)" module Moo(T) @x = 1 + 2 def x @x end end class Foo include Moo(Int32) end Foo.new.x CRYSTAL end it "can't infer type from initializer in generic class type" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Foo(T)" class Foo(T) @x = 1 + 2 def x @x end end Foo(Int32).new.x CRYSTAL end it "infers type from self (#2575)" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo def initialize @x = self end def x @x end end Foo.new.x CRYSTAL end it "infers type from self as virtual type (#2575)" do assert_type(<<-CRYSTAL) { types["Foo"].virtual_type! } class Foo def initialize @x = self end def x @x end end class Bar < Foo end Foo.new.x CRYSTAL end it "declares as named tuple" do assert_type(<<-CRYSTAL) { named_tuple_of({"x": int32, "y": char}) } class Foo @x : NamedTuple(x: Int32, y: Char) def initialize a = {x: 1, y: 'a'} @x = a end def x @x end end Foo.new.x CRYSTAL end it "doesn't complain in second part of #2575" do assert_type(<<-CRYSTAL) { int32 } class Foo @a : Int32 def initialize @a = 5 end def initialize(b) initialize end def a @a end end class Bar < Foo end Bar.new.a CRYSTAL end it "guesses from as.(typeof(...))" do assert_type(<<-CRYSTAL) { int32 } class Foo def initialize(x : Int32) a = 1 @x = a.as(typeof(x)) end def x @x end end Foo.new(1).x CRYSTAL end it "guesses from as.(typeof(...)) in generic type" do assert_type(<<-CRYSTAL) { int32 } class Foo(T) def initialize(x : Int32) a = 1 @x = a.as(typeof(x)) end def x @x end end Foo(Float64).new(1).x CRYSTAL end it "errors if can't find lib call, before erroring on instance var (#2579)" do assert_error <<-CRYSTAL, "undefined fun 'nope' for LibFoo" lib LibFoo end class Foo def initialize LibFoo.nope(out @foo) end end Foo.new CRYSTAL end it "errors when using Class (#2605)" do assert_error <<-CRYSTAL, "can't use Class as the type of instance variable '@class' of Foo, use a more specific type" class Foo def initialize(@class : Class) end end CRYSTAL end it "errors when using Class in generic type" do assert_error <<-CRYSTAL, "can't use Class as the type of instance variable '@class' of Foo(T), use a more specific type" class Foo(T) def initialize(@class : Class) end end CRYSTAL end it "doesn't error when using Class but specifying type" do assert_type(<<-CRYSTAL) { types["Foo"].metaclass } class Foo @x : Foo.class def initialize(@x : Class) end def x @x end end Foo.new(Foo).x CRYSTAL end it "doesn't error when using generic because guessed elsewhere" do assert_type(<<-CRYSTAL) { generic_class "Bar", int32 } class Foo @x = Bar(Int32).new def initialize end def x @x = Bar.new(1) @x end end class Bar(T) def initialize end def initialize(x : T) end end Foo.new.x CRYSTAL end it "doesn't error when using generic in generic type because guessed elsewhere" do assert_type(<<-CRYSTAL) { generic_class "Bar", int32 } class Foo(T) @x = Bar(Int32).new def initialize end def x @x = Bar.new(1) @x end end class Bar(T) def initialize end def initialize(x : T) end end Foo(Int32).new.x CRYSTAL end %w(Object Reference Class).each do |type| it "errors if declaring var in #{type}" do assert_error <<-CRYSTAL, "can't declare instance variables in #{type}" class #{type} @x : Int32? end CRYSTAL end end [ "Value", "Number", "Int", "Float", "Int32", "Tuple(*T)", "NamedTuple(T)", "Enum", "Pointer(T)", "StaticArray(T, N)", "Proc(*T, R)", "Union(*T)", ].each do |type| it "errors if declaring var in #{type}" do assert_error <<-CRYSTAL, "can't declare instance variables in #{type}" struct #{type} @x : Int32? end CRYSTAL end end it "errors if declaring instance variable in module included in Object" do assert_error <<-CRYSTAL, "can't declare instance variables in Object" module Moo @x : Int32? end class Object include Moo end CRYSTAL end it "errors if adds instance variable to Object via guess" do assert_error <<-CRYSTAL, "can't declare instance variables in Object" class Object def foo(@foo : Int32) end end CRYSTAL end it "errors if adds instance variable to Object via guess via included module" do assert_error <<-CRYSTAL, "can't declare instance variables in Object" module Moo def foo(@foo : Int32) end end class Object include Moo end CRYSTAL end it "gives correct error when trying to use Int as an instance variable type" do assert_error <<-CRYSTAL, "can't use Int as the type of an instance variable yet, use a more specific type" class Foo @x : Int end CRYSTAL end it "shouldn't error when accessing instance var in initialized that's always initialized (#2953)" do assert_type(<<-CRYSTAL) { int32 } class Foo @baz = Baz.new def baz @baz end end class Bar < Foo def initialize @baz.x = 2 end end class Baz def initialize @x = 1 end def x=(@x) end def x @x end end Bar.new.baz.x CRYSTAL end # ----------------- # ||| OLD SPECS ||| # vvv vvv it "declares instance var which appears in initialize" do result = assert_type(<<-CRYSTAL) { types["Foo"] } class Foo @x : Int32 def initialize @x = 1 end end Foo.new CRYSTAL mod = result.program foo = mod.types["Foo"].as(NonGenericClassType) foo.instance_vars["@x"].type.should eq(mod.int32) end it "declares instance var of generic class" do assert_type(<<-CRYSTAL class Foo(T) @x : T def initialize(@x) end end Foo(Int32).new(1) CRYSTAL ) do foo = types["Foo"].as(GenericClassType) foo_i32 = foo.instantiate([int32] of TypeVar) foo_i32.lookup_instance_var("@x").type.should eq(int32) foo_i32 end end it "declares instance var of generic class after reopen" do assert_type(<<-CRYSTAL class Foo(T) end f = Foo(Int32).new(1) class Foo(T) @x : T def initialize(@x : T) end end f CRYSTAL ) do foo = types["Foo"].as(GenericClassType) foo_i32 = foo.instantiate([int32] of TypeVar) foo_i32.lookup_instance_var("@x").type.should eq(int32) foo_i32 end end it "declares instance var with initial value" do assert_type(<<-CRYSTAL) { int32 } class Foo @x = 0 def x @x end end Foo.new.x CRYSTAL end it "declares instance var with initial value, with subclass" do assert_type(<<-CRYSTAL) { int32 } class Foo @x = 0 def x @x end end class Bar < Foo def initialize @x = 1 @z = 1 end end Bar.new.x CRYSTAL end it "errors if declaring generic type without type vars" do assert_error <<-CRYSTAL, "can't declare variable of generic non-instantiated type Foo" class Foo(T) end class Baz @x : Foo end CRYSTAL end it "errors when typing an instance variable inside a method" do assert_error <<-CRYSTAL, "declaring the type of an instance variable must be done at the class level" def foo @x : Int32 end foo CRYSTAL end it "declares instance var with union type with a virtual member" do assert_type(<<-CRYSTAL) { nilable types["Parent"].virtual_type! } class Parent; end class Child < Parent; end class Foo @x : Parent? def x @x end end Foo.new.x CRYSTAL end it "declares with `self`" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo @foo : self def initialize @foo = uninitialized self end def foo @foo end end Foo.new.foo CRYSTAL end it "guesses from array literal with of, with subclass" do assert_type(<<-CRYSTAL) { array_of(generic_class("Foo", int32).virtual_type!) } class Foo(T) end class Bar < Foo(Int32) end class Some @some = [] of Foo(Int32) def some @some end end Some.new.some CRYSTAL end it "guesses from hash literal with of, with subclass" do assert_type(<<-CRYSTAL) { hash_of(generic_class("Foo", int32).virtual_type!, generic_class("Foo", int32).virtual_type!) } class Foo(T) end class Bar < Foo(Int32) end class Some @some = {} of Foo(Int32) => Foo(Int32) def some @some end end Some.new.some CRYSTAL end it "guesses from splat (#3149)" do assert_type(<<-CRYSTAL) { generic_class "Args", int32, char } class Args(*T) def initialize(*@args : *T) end end Args.new(1, 'a') CRYSTAL end it "guesses from splat (2) (#3149)" do assert_type(<<-CRYSTAL) { tuple_of([int32, char]) } class Args(*T) def initialize(*@args : *T) end def args @args end end Args.new(1, 'a').args CRYSTAL end it "transfers initializer from generic module to class" do assert_type(<<-CRYSTAL) { int32 } module Moo(T) @x = 1 def x @x end end class Foo include Moo(Int32) end Foo.new.x CRYSTAL end it "transfers initializer from module to generic class" do assert_type(<<-CRYSTAL) { int32 } module Moo @x = 1 def x @x end end class Foo(T) include Moo end Foo(Int32).new.x CRYSTAL end it "doesn't consider self.initialize as initializer (#3239)" do assert_error <<-CRYSTAL, "@instance_vars are not yet allowed in metaclasses: use @@class_vars instead" class Foo def self.initialize @d = 5 end def test @d end end Foo.new.test CRYSTAL end it "doesn't crash on #3580" do assert_error <<-CRYSTAL, "undefined local variable or method" class Hoge @hoge_dir : String = "~/.hoge" ? "~/.hoge" : default_hoge_dir end CRYSTAL end it "is more permissive with macro def initialize" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo @x : Int32 def initialize {% for ivar in @type.instance_vars %} @{{ivar}} = 0 {% end %} end end Foo.new CRYSTAL end it "is more permissive with macro def initialize, bug with named args" do assert_error <<-CRYSTAL, "instance variable '@x' of Foo was not initialized" class Foo @x : Int32 def initialize(**args) {% @type %} end end Foo.new(x: 1) CRYSTAL end it "is more permissive with macro def initialize, other initialize" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo @x : Int32 @y : Int32 def initialize {% for ivar in @type.instance_vars %} @{{ivar}} = 0 {% end %} end def initialize(@x, @y) end end Foo.new CRYSTAL end it "is more permissive with macro def initialize, multiple" do assert_type(<<-CRYSTAL) { int32 } class Foo @x : Int32 def initialize {% begin %} {% @type %} @x = 1 {% end %} end def initialize(x) {% begin %} {% @type %} @x = x {% end %} end def x @x end end Foo.new Foo.new(1).x CRYSTAL end it "errors with macro def but another def doesn't initialize all" do assert_error <<-CRYSTAL, "instance variable '@y' of Foo was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported." class Foo @x : Int32 @y : Int32 def initialize {% for ivar in @type.instance_vars %} @{{ivar}} = 0 {% end %} end def initialize(@x) end end Foo.new CRYSTAL end it "errors if finally not initialized in macro def" do assert_error <<-CRYSTAL, "instance variable '@x' of Foo was not initialized in this 'initialize', rendering it nilable" class Foo @x : Int32 def initialize {% for ivar in @type.instance_vars %} {% end %} end end Foo.new CRYSTAL end it "doesn't error if initializes via super in macro def" do assert_type(<<-CRYSTAL) { types["Bar"] } class Foo def initialize(@x : Int32) end end class Bar < Foo def initialize(x) super {% for ivar in @type.instance_vars %} {% end %} end end Bar.new(1) CRYSTAL end it "doesn't error if uses typeof(@var)" do assert_type(<<-CRYSTAL) { types["Foo"] } struct Int32 def self.zero 0 end end class Foo @x : Int32 def initialize @x = typeof(@x).zero end end Foo.new CRYSTAL end it "doesn't error if not initialized in macro def but outside it" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo @x = 1 def initialize {% @type %} end end Foo.new CRYSTAL end it "doesn't error if inheriting generic instance (#3635)" do assert_type(<<-CRYSTAL) { bool } module Core(T) @a : Bool end class Base(T) include Core(Int32) @a = true end class Foo < Base(String) def a @a end end Foo.new.a CRYSTAL end it "doesn't consider var as nilable if conditionally assigned inside initialize, but has initializer (#3669)" do assert_type(<<-CRYSTAL, inject_primitives: true) { int32 } class Foo @x = 0 def initialize @x = 1 if 1 == 2 end def x @x end end Foo.new.x CRYSTAL end it "types generic instance as virtual type if generic type has subclasses (#3805)" do assert_type(<<-CRYSTAL, inject_primitives: true) { types["Qux"] } class Foo(T) end class Bar(T) < Foo(T) end class Qux def initialize @ptr = Pointer(Foo(Int32)).malloc(1_u64) end def ptr=(ptr) @ptr = ptr end end Bar(Int32).new Qux.new CRYSTAL end it "errors if unknown ivar through macro (#4050)" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@bar' of Foo" class Foo def initialize(**attributes) {% for var in @type.instance_vars %} if arg = attributes[:{{var.name.id}}]? @{{var.name.id}} = arg end {% end %} end end class Bar < Foo def initialize(**attributes) @bar = true super end end Bar.new CRYSTAL end it "can't infer type when using operation on const (#4054)" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@baz' of Foo", inject_primitives: true class Foo BAR = 5 def initialize @baz = BAR + 5 end end Foo.new CRYSTAL end it "instance variables initializers are used in class variables initialized objects (#3988)" do assert_type(<<-CRYSTAL) { int32 } class Foo @@foo = Foo.new @never_nil = 1 def initialize if false @never_nil = 2 end end end Foo.new.@never_nil CRYSTAL end it "allow usage of instance variable initializer from instance variable initializer" do assert_type(<<-CRYSTAL) { tuple_of([int32, int32]) } class Foo @bar = Bar.new @never_nil = 1 def initialize if false @never_nil = 2 end end end class Bar @never_nil = 1 def initialize if false @never_nil = 2 end end end {Foo.new.@never_nil, Bar.new.@never_nil} CRYSTAL end it "resolves unqualified constants in block inside instance var initializer (#14827)" do assert_type(<<-CRYSTAL) { bool } def with_block yield end module A struct B end struct C @data : B = with_block { B.new } end end A::C.new.is_a?(A::C) CRYSTAL end it "errors when assigning instance variable at top level block" do assert_error <<-CRYSTAL, "can't use instance variables at the top level" def foo yield end foo do @foo = 1 end CRYSTAL end it "errors when assigning instance variable at top level control block" do assert_error <<-CRYSTAL, "can't use instance variables at the top level" if true @foo = 1 end CRYSTAL end it "doesn't check call of non-self instance (#4830)" do assert_type(<<-CRYSTAL) { types["Container"] } class Container def initialize(other : Container, x) initialize(other) @foo = "x" end def initialize(other : Container) @foo = other.foo end def initialize(@foo : String, bar) end def foo @foo end end container = Container.new("foo", nil) Container.new(container, "foo2") CRYSTAL end it "errors when assigning instance variable inside nested expression" do assert_error <<-CRYSTAL, "can't use instance variables at the top level" class Foo if true @foo = 1 end end CRYSTAL end it "doesn't find T in generic type that's not the current type (#4460)" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Foo" class Gen(T) def self.new Gen(T).new end end class Foo @x = Gen.new end CRYSTAL end it "doesn't consider instance var as nilable if assigned before self access (#4981)" do assert_type(<<-CRYSTAL) { int32 } def f(x) end class A def initialize @a = 0 f(self) @a = 0 end def a @a end end A.new.a CRYSTAL end it "doesn't combine union of Number and Number subclass (#5073)" do assert_type(<<-CRYSTAL) { generic_class "Gen", union_of(int32, types["A"]) } class Gen(T) end struct A < Number def hash(hasher) hasher end def to_s(io : IO) end end class Foo @foo = Gen(Int32 | A).new end Foo.new.@foo CRYSTAL end it "uses T.new (#4291)" do assert_type(<<-CRYSTAL) { types["Foo"] } class Foo end class Gen(T) @x = T.new def x @x end end Gen(Foo).new.x CRYSTAL end it "can type ivar from module included by generic class (#5281)" do assert_type(<<-CRYSTAL) { types["Baz"] } module Foo def initialize(@x = "foo") end end class Bar(T) include Foo @y = 42 end class Baz < Bar(String); end Baz.new CRYSTAL end it "can type ivar from class inherited by generic class (#5281)" do assert_type(<<-CRYSTAL) { types["Baz"] } class Foo def initialize(@x = "foo") end end class Bar(T) < Foo @y = 42 end class Baz < Bar(String); end Baz.new CRYSTAL end it "can guess the type from splat argument with splatted type" do assert_type(<<-CRYSTAL) { tuple_of([int32]) } class Foo def initialize(*@foo : *{Int32}) end def foo @foo end end Foo.new(1).foo CRYSTAL end it "can guess the type from splat argument with splatted type variable" do assert_type(<<-CRYSTAL) { tuple_of([int32, int32]) } class Foo(T) def initialize(*@foo : *T) end def foo @foo end end Foo.new(1, 2).foo CRYSTAL end it "cannot guess the type from splat argument with not splatted type" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@foo' of Foo" class Foo def initialize(*@foo : Int32) end def foo @foo end end Foo.new(1).foo CRYSTAL end it "can guess the type from double-splat argument with double-splatted type" do assert_type(<<-CRYSTAL) { named_tuple_of({"foo": int32}) } class Foo def initialize(**@foo : **{foo: Int32}) end def foo @foo end end Foo.new(foo: 1).foo CRYSTAL end it "can guess the type from double-splat argument with double-splatted type variable" do assert_type(<<-CRYSTAL) { named_tuple_of({"foo": int32, "bar": int32}) } class Foo(T) def initialize(**@foo : **T) end def foo @foo end end Foo.new(foo: 1, bar: 2).foo CRYSTAL end it "cannot guess the type from double-splat argument with not double-splatted type" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@foo' of Foo" class Foo def initialize(**@foo : Int32) end def foo @foo end end Foo.new(foo: 1).foo CRYSTAL end it "cannot guess type from argument assigned in body" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Foo" class Foo def initialize(x : String) x = 1 @x = x end end Foo.new "foo" CRYSTAL end it "can't infer type of generic method that returns self (#5383)" do assert_error <<-CRYSTAL, "method Gen(T).new must return Gen(T) but it is returning Nil" class Gen(T) def self.new(&block : -> T) : self end end class Foo def initialize @x = Gen.new { 1 } end end Foo.new CRYSTAL end it "guesses virtual array type (1) (#5342)" do assert_type(<<-CRYSTAL) { array_of(array_of(int32).virtual_type).virtual_type } require "prelude" class First(T) < Array(T) end class Second @ary = [[1]] def ary @ary end end Second.new.ary CRYSTAL end it "guesses virtual array type (2) (#5342)" do assert_type(<<-CRYSTAL) { array_of(array_of(int32).virtual_type).virtual_type } require "prelude" class First(T) < Array(T) end class Second @ary = Array { Array { 1 } } def ary @ary end end Second.new.ary CRYSTAL end it "guesses virtual array type (3) (#5342)" do assert_type(<<-CRYSTAL) { array_of(array_of(int32).virtual_type).virtual_type } require "prelude" class First(T) < Array(T) end class Second @ary = [] of Array(Int32) def ary @ary end end Second.new.ary CRYSTAL end it "guesses virtual hash type (1) (#5342)" do assert_type(<<-CRYSTAL) { hash_of(hash_of(int32, int32).virtual_type, int32).virtual_type } require "prelude" class First(K, V) < Hash(K, V) end class Second @hash = { {1 => 2} => 3} def hash @hash end end Second.new.hash CRYSTAL end it "guesses virtual hash type (2) (#5342)" do assert_type(<<-CRYSTAL) { hash_of(hash_of(int32, int32).virtual_type, int32).virtual_type } require "prelude" class First(K, V) < Hash(K, V) end class Second @hash = Hash { Hash { 1 => 2 } => 3 } def hash @hash end end Second.new.hash CRYSTAL end it "guesses virtual array type (3) (#5342)" do assert_type(<<-CRYSTAL) { hash_of(hash_of(int32, int32).virtual_type, int32).virtual_type } require "prelude" class First(K, V) < Hash(K, V) end class Second @hash = {} of Hash(Int32, Int32) => Int32 def hash @hash end end Second.new.hash CRYSTAL end it "doesn't solve instance var initializer in instance context (1) (#5876)" do assert_error <<-CRYSTAL, "undefined local variable or method 'bar'" class Foo @x : Int32 = bar def bar 1 end end Foo.new CRYSTAL end it "doesn't solve instance var initializer in instance context (2) (#5876)" do assert_error <<-CRYSTAL, "undefined local variable or method 'bar'" class Foo(T) @x : T = bar def bar 1 end end Foo(Int32).new CRYSTAL end it "doesn't solve instance var initializer in instance context (3) (#5876)" do assert_error <<-CRYSTAL, "undefined local variable or method 'bar'" module Moo(T) @x : T = bar def bar 1 end end class Foo include Moo(Int32) end Foo.new CRYSTAL end it "solves instance var initializer in metaclass context (#5876)" do assert_type(<<-CRYSTAL) { int32 } class Foo @x : Int32 = bar def self.bar 1 end def x @x end end Foo.new.x CRYSTAL end it "doesn't infer unbound generic type on non-generic call (#6390)" do assert_error <<-CRYSTAL, "can't infer the type parameter T for the generic class Gen(T)" class Gen(T) def self.new(&block) Gen(T).build end def self.build : self end end class Foo def initialize @x = Gen.new { } end end Foo.new CRYSTAL end it "doesn't infer unbound generic type on generic method called from generic's subclass" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Foo" class Gen(T) def self.new(x : T) Gen(T).build end def self.build : self new end end class Foo < Gen(Int32) def initialize @x = Gen.new('a') end end Foo.new CRYSTAL end it "doesn't infer unbound generic type on generic method called from generic's subclass, metaclass context" do assert_error <<-CRYSTAL, "can't infer the type of instance variable '@x' of Foo" class Gen(T) def self.new(x : T) Gen(T).build end def self.build : self new end end class Foo < Gen(Int32) @x = Gen.new('a') end CRYSTAL end it "errors when overriding inherited instance variable with incompatible type" do assert_error <<-CRYSTAL, "instance variable '@a' of A must be Int32, not (Char | Int32)" class A @a = 1 end class B < A @a = 'a' end CRYSTAL end it "accepts overriding inherited instance variable with compatible type" do semantic <<-CRYSTAL class A @a = 1 end class B < A @a = 2 end CRYSTAL end it "looks up return type restriction in defining type, not instantiated type (#11961)" do assert_type(<<-CRYSTAL) { int32 } module Foo(T) def foo : T x = uninitialized T x end end module Bar(T) include Foo(T) end struct Tuple include Bar(Union(*T)) end class Test def initialize @foo = 0 end def test @foo = {@foo, 0}.foo end end Test.new.@foo CRYSTAL end it "looks up self restriction in instantiated type, not defined type" do assert_type(<<-CRYSTAL) { types["Foo2"] } class Foo1 def foo : self self end end class Foo2 < Foo1 end class Bar def initialize @x = Foo2.new end def bar @x = @x.foo end end Bar.new.bar CRYSTAL end it "inferrs Proc(Void) to Proc(Nil)" do assert_type(<<-CRYSTAL) { proc_of(nil_type) } struct Proc def self.new(&block : self) block end end class Foo def initialize @proc = Proc(Void).new { 1 } end end Foo.new.@proc CRYSTAL end describe "instance variable inherited from multiple parents" do context "with compatible type" do it "module and class, with declarations" do result = assert_type(<<-CRYSTAL) { int32 } module M @a : Int32 = 1 end class A @a : Int32 = 2 end class B < A include M end B.new.@a CRYSTAL program = result.program program.types["A"].instance_vars.size.should eq(1) program.types["B"].instance_vars.size.should eq(0) end it "module and class, with declarations (2)" do result = assert_type(<<-CRYSTAL) { int32 } module M @a = 1 end class A include M end class B < A @a = 1 end B.new.@a CRYSTAL program = result.program program.types["A"].instance_vars.size.should eq(1) program.types["B"].instance_vars.size.should eq(0) end it "module and class, with declarations (3)" do result = assert_type(<<-CRYSTAL) { tuple_of [int32, int32] } module M @a = 1 end class A include M end class B < A @a = 1 end class C @a = 1 end class D < C include M end {B.new.@a, D.new.@a} CRYSTAL program = result.program program.types["A"].instance_vars.size.should eq(1) program.types["B"].instance_vars.size.should eq(0) program.types["C"].instance_vars.size.should eq(1) program.types["D"].instance_vars.size.should eq(0) end it "module and class, with definitions" do result = assert_type(<<-CRYSTAL) { int32 } module M @a = 1 end class A @a = 2 end class B < A include M end B.new.@a CRYSTAL program = result.program program.types["A"].instance_vars.size.should eq(1) program.types["B"].instance_vars.size.should eq(0) end it "accepts module and module, with definitions" do semantic <<-CRYSTAL module M @a = 1 end module N @a = 2 end class B include N include M end CRYSTAL end it "accepts module and module, with declarations" do semantic <<-CRYSTAL module M @a : Int32? end module N @a : Int32? end class B include N include M end CRYSTAL end end context "with incompatible type" do it "module and class, with definitions" do assert_error <<-CRYSTAL, "instance variable '@a' of A, with B < A, is already declared as Int32 (trying to re-declare it in B as Char)" module M @a = 'a' end class A @a = 1 end class B < A include M end CRYSTAL end it "module and class, with declarations" do assert_error <<-CRYSTAL, "instance variable '@a' of A, with B < A, is already declared as Int32 (trying to re-declare it in B as Char)" module M @a : Char = 'a' end class A @a : Int32 = 1 end class B < A include M end CRYSTAL end it "errors module and module, with definitions" do assert_error <<-CRYSTAL, "instance variable '@a' of B must be Char, not (Char | Int32)" module M @a = 'c' end module N @a = 1 end class B include N include M end CRYSTAL end it "errors module and module, with declarations" do assert_error <<-CRYSTAL, "instance variable '@a' of B must be Int32, not (Char | Int32)" module M @a : Char = 'c' end module N @a : Int32 = 1 end class B include N include M end CRYSTAL end end end end