require "../../../spec_helper" describe "Code gen: C ABI" do it "passes struct less than 64 bits (for real)" do test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(3)) struct s { char x; short y; }; int foo(struct s a) { return a.x + a.y; } C lib LibFoo struct Struct x : Int8 y : Int16 end fun foo(s : Struct) : Int32 end s = LibFoo::Struct.new x: 1_i8, y: 2_i16 LibFoo.foo(s) CRYSTAL end it "passes struct between 64 and 128 bits (for real)" do test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(3)) struct s { long long x; short y; }; long long foo(struct s a) { return a.x + a.y; } C lib LibFoo struct Struct x : Int64 y : Int16 end fun foo(s : Struct) : Int64 end s = LibFoo::Struct.new x: 1_i64, y: 2_i16 LibFoo.foo(s) CRYSTAL end it "passes struct bigger than 128 bits (for real)" do test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(6)) struct s { long long x; long long y; char z; }; long long foo(struct s a) { return a.x + a.y + a.z; } C lib LibFoo struct Struct x : Int64 y : Int64 z : Int8 end fun foo(s : Struct) : Int64 end s = LibFoo::Struct.new x: 1_i64, y: 2_i64, z: 3_i8 LibFoo.foo(s) CRYSTAL end it "passes struct after many other args (for real)" do test_c(<<-C, <<-CRYSTAL, &.to_string.should eq("28")) struct s { long long x, y; }; long long foo(long long a, long long b, long long c, long long d, long long e, struct s v) { return a + b + c + d + e + v.x + v.y; } C lib LibFoo struct S x : Int64 y : Int64 end fun foo(a : Int64, b : Int64, c : Int64, d : Int64, e : Int64, v : S) : Int64 end v = LibFoo::S.new(x: 6, y: 7) LibFoo.foo(1, 2, 3, 4, 5, v) CRYSTAL end it "passes struct after many other args when returning a large struct (sret return type)" do test_c(<<-C, <<-CRYSTAL, &.to_string.should eq("[6, 7, 10]")) struct s { long long x, y; }; struct t { long long x, y, z; }; struct t foo(long long a, long long b, long long c, long long d, struct s v) { return (struct t){ v.x, v.y, a + b + c + d }; } C lib LibFoo struct S x : Int64 y : Int64 end struct T x : Int64 y : Int64 z : Int64 end fun foo(a : Int64, b : Int64, c : Int64, d : Int64, v : S) : T end v = LibFoo::S.new(x: 6, y: 7) w = LibFoo.foo(1, 2, 3, 4, v) [w.x, w.y, w.z] CRYSTAL end it "returns struct less than 64 bits (for real)" do test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(3)) struct s { char x; short y; }; struct s foo() { struct s a = {1, 2}; return a; } C lib LibFoo struct Struct x : Int8 y : Int16 end fun foo : Struct end str = LibFoo.foo str.x.to_i + str.y.to_i CRYSTAL end it "returns struct between 64 and 128 bits (for real)" do test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(3)) struct s { long long x; short y; }; struct s foo() { struct s a = {1, 2}; return a; } C lib LibFoo struct Struct x : Int64 y : Int16 end fun foo : Struct end str = LibFoo.foo (str.x + str.y).to_i32 CRYSTAL end it "returns struct bigger than 128 bits with sret" do test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(6)) struct s { long long x; long long y; char z; }; struct s foo(int z) { struct s a = {1, 2, z}; return a; } C lib LibFoo struct Struct x : Int64 y : Int64 z : Int8 end fun foo(w : Int32) : Struct end str = LibFoo.foo(3) (str.x + str.y + str.z).to_i32 CRYSTAL end it "accepts large struct in a callback (for real)" do test_c(<<-C, <<-CRYSTAL, &.to_string.should eq("6")) struct s { long long x, y, z; }; void ccaller(void (*func)(struct s)) { struct s v = {1, 2, 3}; func(v); } C lib LibFoo struct S x : Int64 y : Int64 z : Int64 end fun ccaller(func : (S) ->) end module Global class_property x = 0i64 end fun callback(v : LibFoo::S) Global.x = v.x &+ v.y &+ v.z end LibFoo.ccaller(->callback) Global.x CRYSTAL end it "promotes variadic args (float to double)" do test_c(<<-C, <<-CRYSTAL, &.to_f64.should eq(1.0)) #include double foo(int n, ...) { va_list args; va_start(args, n); return va_arg(args, double); } C lib LibFoo fun foo(n : Int32, ...) : Float64 end LibFoo.foo(1, 1.0_f32) CRYSTAL end [{"i8", -123}, {"u8", 255}, {"i16", -123}, {"u16", 65535}, ].each do |int_kind, int_value| it "promotes variadic args (#{int_kind} to i32) (#9742)" do test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(int_value)) #include int foo(int n, ...) { va_list args; va_start(args, n); return va_arg(args, int); } C lib LibFoo fun foo(n : Int32, ...) : Int32 end LibFoo.foo(1, #{int_value}_#{int_kind}) CRYSTAL end end end