require "../../spec_helper"

describe "Semantic: ReferenceStorage" do
  it "errors if T is a struct type" do
    assert_error <<-CRYSTAL, "Can't instantiate ReferenceStorage(T) with T = Foo (T must be a reference type)"
      @[Primitive(:ReferenceStorageType)]
      struct ReferenceStorage(T) < Value
      end

      struct Foo
        @x = 1
      end

      ReferenceStorage(Foo)
      CRYSTAL
  end

  it "errors if T is a value type" do
    assert_error <<-CRYSTAL, "Can't instantiate ReferenceStorage(T) with T = Int32 (T must be a reference type)"
      @[Primitive(:ReferenceStorageType)]
      struct ReferenceStorage(T) < Value
      end

      ReferenceStorage(Int32)
      CRYSTAL
  end

  it "errors if T is a union type" do
    assert_error <<-CRYSTAL, "Can't instantiate ReferenceStorage(T) with T = (Bar | Foo) (T must be a reference type)"
      @[Primitive(:ReferenceStorageType)]
      struct ReferenceStorage(T) < Value
      end

      class Foo
      end

      class Bar
      end

      ReferenceStorage(Foo | Bar)
      CRYSTAL
  end

  it "errors if T is a nilable type" do
    assert_error <<-CRYSTAL, "Can't instantiate ReferenceStorage(T) with T = (Foo | Nil) (T must be a reference type)"
      @[Primitive(:ReferenceStorageType)]
      struct ReferenceStorage(T) < Value
      end

      class Foo
      end

      ReferenceStorage(Foo?)
      CRYSTAL
  end

  it "allows a different name" do
    assert_type(<<-CRYSTAL) { types["Foo"].metaclass }
      @[Primitive(:ReferenceStorageType)]
      struct MyRef(U) < Value
        def u
          U
        end
      end

      class Foo
      end

      MyRef(Foo).new.u
      CRYSTAL
  end

  it "adds ReferenceStorage to Value.subclasses once (#15677)" do
    assert_type(<<-CRYSTAL) { bool }
      @[Primitive(:ReferenceStorageType)]
      struct ReferenceStorage(T) < Value
      end

      {{ Value.subclasses.select(&.<=(ReferenceStorage)).size == 1 ? true : nil }}
      CRYSTAL
  end
end
