require "../../spec_helper"

describe "Semantic: c union" do
  it "types c union" do
    result = assert_type("lib LibFoo; union Bar; x : Int32; y : Float64; end; end; LibFoo::Bar") { types["LibFoo"].types["Bar"].metaclass }
    mod = result.program
    bar = mod.types["LibFoo"].types["Bar"].as(NonGenericClassType)
    bar.extern?.should be_true
    bar.extern_union?.should be_true
    bar.instance_vars["@x"].type.should eq(mod.int32)
    bar.instance_vars["@y"].type.should eq(mod.float64)
  end

  it "types Union#new" do
    assert_type("lib LibFoo; union Bar; x : Int32; y : Float64; end; end; LibFoo::Bar.new") do
      types["LibFoo"].types["Bar"]
    end
  end

  it "types union setter" do
    assert_type("lib LibFoo; union Bar; x : Int32; y : Float64; end; end; bar = LibFoo::Bar.new; bar.x = 1") { int32 }
  end

  it "types union getter" do
    assert_type("lib LibFoo; union Bar; x : Int32; y : Float64; end; end; bar = LibFoo::Bar.new; bar.x") { int32 }
  end

  it "types union setter via pointer" do
    assert_type("lib LibFoo; union Bar; x : Int32; y : Float64; end; end; bar = Pointer(LibFoo::Bar).malloc(1_u64); bar.value.x = 1", inject_primitives: true) { int32 }
  end

  it "types union getter via pointer" do
    assert_type("lib LibFoo; union Bar; x : Int32; y : Float64; end; end; bar = Pointer(LibFoo::Bar).malloc(1_u64); bar.value.x", inject_primitives: true) { int32 }
  end

  it "errors if setting closure" do
    assert_error <<-CRYSTAL, "can't set closure as C union member"
      lib LibFoo
        union Bar
          x : ->
        end
      end

      a = 1

      bar = LibFoo::Bar.new
      bar.x = -> { a }
      CRYSTAL
  end

  it "errors on empty c union (#633)" do
    assert_error <<-CRYSTAL, "empty unions are disallowed"
      lib LibFoo
        union Struct
        end
      end
      CRYSTAL
  end

  it "errors if using void in union field type" do
    assert_error <<-CRYSTAL, "can't use Void as a union field type"
      lib LibFoo
        union Struct
          x : Void
        end
      end
      CRYSTAL
  end

  it "errors if using void via typedef in union field type" do
    assert_error <<-CRYSTAL, "can't use Void as a union field type"
      lib LibFoo
        type MyVoid = Void

        union Struct
          x : MyVoid
        end
      end
      CRYSTAL
  end
end
