Skip to content

Commit

Permalink
Showing 6 changed files with 386 additions and 3 deletions.
145 changes: 145 additions & 0 deletions spec/compiler/codegen/extern_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
require "../../spec_helper"

describe "Codegen: extern struct" do
it "declares extern struct with no constructor" do
run(%(
@[Extern]
struct Foo
@x = uninitialized Int32
def x
@x
end
end
Foo.new.x
)).to_i.should eq(0)
end

it "declares extern struct with no constructor, assigns var" do
run(%(
@[Extern]
struct Foo
@x = uninitialized Int32
def x=(@x)
end
def x
@x
end
end
foo = Foo.new
foo.x = 10
foo.x
)).to_i.should eq(10)
end

it "declares extern union with no constructor" do
run(%(
@[Extern(union: true)]
struct Foo
@x = uninitialized Int32
@y = uninitialized Float32
def x=(@x)
end
def x
@x
end
def y=(@y)
end
end
foo = Foo.new
foo.x = 1
foo.y = 1.5_f32
foo.x
)).to_i.should eq(1069547520)
end

it "declares extern struct, sets and gets insance var" do

This comment has been minimized.

Copy link
@Sija

Sija Sep 15, 2016

Contributor

insance -> instance

run(%(
@[Extern]
struct Foo
@y = uninitialized Float64
@x = uninitialized Int32
def foo
@x = 42
@x
end
end
Foo.new.foo
)).to_i.should eq(42)
end

it "declares extern union, sets and gets insance var" do

This comment has been minimized.

Copy link
@Sija

Sija Sep 15, 2016

Contributor

ditto

run(%(
@[Extern(union: true)]
struct Foo
@x = uninitialized Int32
@y = uninitialized Float32
def foo
@x = 1
@y = 1.5_f32
@x
end
end
Foo.new.foo
)).to_i.should eq(1069547520)
end

it "sets callback on extern struct" do
run(%(
require "prelude"
@[Extern]
struct Foo
@x = uninitialized -> Int32
def set
@x = ->{ 42 }
end
def get
@x.call
end
end
foo = Foo.new
foo.set
foo.get
)).to_i.should eq(42)
end

it "sets callback on extern union" do
run(%(
require "prelude"
@[Extern(union: true)]
struct Foo
@y = uninitialized Float64
@x = uninitialized -> Int32
def set
@x = ->{ 42 }
end
def get
@x.call
end
end
foo = Foo.new
foo.set
foo.get
)).to_i.should eq(42)
end
end
172 changes: 172 additions & 0 deletions spec/compiler/semantic/extern_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
require "../../spec_helper"

describe "Semantic: extern struct" do
it "declares extern struct with no constructor" do
assert_type(%(
@[Extern]
struct Foo
@x = uninitialized Int32
def x
@x
end
end
Foo.new.x
)) { int32 }
end

it "declares with constructor" do
assert_type(%(
@[Extern]
struct Foo
@x = uninitialized Int32
def initialize(@x)
end
def foo
@x
end
end
Foo.new(1).foo
)) { int32 }
end

it "overrides getter" do
assert_type(%(
@[Extern]
struct Foo
@x = uninitialized Int32
def x
'a'
end
end
Foo.new.x
)) { char }
end

it "can be passed to C fun" do
assert_type(%(
@[Extern]
struct Foo
@x = uninitialized Int32
end
lib LibFoo
fun foo(x : Foo) : Float64
end
LibFoo.foo(Foo.new)
)) { float64 }
end

it "can include module" do
assert_type(%(
module Moo
@x = uninitialized Int32
def x
@x
end
end
@[Extern]
struct Foo
include Moo
end
Foo.new.x
)) { int32 }
end

it "errors if using non-primitive for field type" do
assert_error %(
class Bar
end
@[Extern]
struct Foo
@x = uninitialized Bar
end
),
"only primitive types, pointers, structs, unions, enums and tuples are allowed in extern struct declarations"
end

it "errors if using non-primitive for field type via module" do
assert_error %(
class Bar
end
module Moo
@x = uninitialized Bar
end
@[Extern]
struct Foo
include Moo
end
),
"only primitive types, pointers, structs, unions, enums and tuples are allowed in extern struct declarations"
end

it "errors if using non-primitive type in constructor" do
assert_error %(
class Bar
end
@[Extern]
struct Foo
def initialize
@x = Bar.new
end
end
),
"only primitive types, pointers, structs, unions, enums and tuples are allowed in extern struct declarations"
end

it "declares extern union with no constructor" do
assert_type(%(
@[Extern(union: true)]
struct Foo
@x = uninitialized Int32
def x
@x
end
end
Foo.new.x
)) { int32 }
end

it "can use extern struct in lib" do
assert_type(%(
@[Extern]
struct Foo
end
lib LibFoo
fun foo(x : Foo) : Foo
end
foo = Foo.new
LibFoo.foo(foo)
)) { types["Foo"] }
end

it "can new with named args" do
assert_type(%(
@[Extern]
struct A
def initialize(@x : Int32)
end
end
A.new(x: 6)
)) { types["A"] }
end
end
2 changes: 1 addition & 1 deletion src/compiler/crystal/semantic/main_visitor.cr
Original file line number Diff line number Diff line change
@@ -1340,7 +1340,7 @@ module Crystal
when ProcInstanceType
return special_proc_type_new_call(node, instance_type)
when .extern?
if named_args = node.named_args
if instance_type.namespace.is_a?(LibType) && (named_args = node.named_args)
return special_c_struct_or_union_new_with_named_args(node, instance_type, named_args)
end
end
2 changes: 1 addition & 1 deletion src/compiler/crystal/semantic/semantic_visitor.cr
Original file line number Diff line number Diff line change
@@ -395,7 +395,7 @@ abstract class Crystal::SemanticVisitor < Crystal::Visitor
def check_allowed_in_lib(node, type = node.type.instance_type)
unless type.allowed_in_lib?
msg = String.build do |msg|
msg << "only primitive types, pointers, structs, unions, enums and tuples are allowed in lib declarations"
msg << "only primitive types, pointers, structs, unions, enums and tuples are allowed in lib declarations, not #{type}"
msg << " (did you mean LibC::Int?)" if type == @program.int
msg << " (did you mean LibC::Float?)" if type == @program.float
end
Loading

0 comments on commit dcfd903

Please sign in to comment.