Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: ca5540941808
Choose a base ref
...
head repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: c25e705cb60a
Choose a head ref
  • 2 commits
  • 23 files changed
  • 1 contributor

Commits on Jul 25, 2016

  1. Compiler: unify CStructOrUnionType with NonGenericClassType.

    This simplifies the code and also will make it easier to have regular Crystal
    structs be marked as extern structs or unions.
    
    Also: fixed reading/writing union instance vars (inspect now works on C union types)
    Ary Borenszweig committed Jul 25, 2016
    Copy the full SHA
    6f83d17 View commit details
  2. Random: doc fix for rand(Int)

    Ary Borenszweig committed Jul 25, 2016
    Copy the full SHA
    c25e705 View commit details
23 changes: 22 additions & 1 deletion spec/compiler/codegen/c_union_spec.cr
Original file line number Diff line number Diff line change
@@ -95,7 +95,7 @@ describe "Code gen: c union" do
color = LibNVG::Color.new
color.to_s
)).to_string.should eq("LibNVG::Color()")
)).to_string.should eq("LibNVG::Color(@array=0)")
end

it "automatically converts numeric type in field assignment" do
@@ -193,4 +193,25 @@ describe "Code gen: c union" do
sizeof(LibFoo::Bar)
)).to_i.should eq(6)
end

it "reads union instance var" do
run(%(
lib LibFoo
union Foo
char : Char
int : Int32
end
end
struct LibFoo::Foo
def read_int
@int
end
end
foo = LibFoo::Foo.new
foo.int = 42
foo.read_int
)).to_i.should eq(42)
end
end
22 changes: 11 additions & 11 deletions spec/compiler/parser/parser_spec.cr
Original file line number Diff line number Diff line change
@@ -670,12 +670,12 @@ describe "Parser" do
it_parses "lib LibC; type A = B*; end", LibDef.new("LibC", [TypeDef.new("A", "B".path.pointer_of)] of ASTNode)
it_parses "lib LibC; type A = B**; end", LibDef.new("LibC", [TypeDef.new("A", "B".path.pointer_of.pointer_of)] of ASTNode)
it_parses "lib LibC; type A = B.class; end", LibDef.new("LibC", [TypeDef.new("A", Metaclass.new("B".path))] of ASTNode)
it_parses "lib LibC; struct Foo; end end", LibDef.new("LibC", [StructDef.new("Foo")] of ASTNode)
it_parses "lib LibC; struct Foo; x : Int; y : Float; end end", LibDef.new("LibC", [StructDef.new("Foo", [Arg.new("x", restriction: "Int".path), Arg.new("y", restriction: "Float".path)] of ASTNode)] of ASTNode)
it_parses "lib LibC; struct Foo; x : Int*; end end", LibDef.new("LibC", [StructDef.new("Foo", Expressions.from(Arg.new("x", restriction: "Int".path.pointer_of)))] of ASTNode)
it_parses "lib LibC; struct Foo; x : Int**; end end", LibDef.new("LibC", [StructDef.new("Foo", Expressions.from(Arg.new("x", restriction: "Int".path.pointer_of.pointer_of)))] of ASTNode)
it_parses "lib LibC; struct Foo; x, y, z : Int; end end", LibDef.new("LibC", [StructDef.new("Foo", [Arg.new("x", restriction: "Int".path), Arg.new("y", restriction: "Int".path), Arg.new("z", restriction: "Int".path)] of ASTNode)] of ASTNode)
it_parses "lib LibC; union Foo; end end", LibDef.new("LibC", [UnionDef.new("Foo")] of ASTNode)
it_parses "lib LibC; struct Foo; end end", LibDef.new("LibC", [CStructOrUnionDef.new("Foo")] of ASTNode)
it_parses "lib LibC; struct Foo; x : Int; y : Float; end end", LibDef.new("LibC", [CStructOrUnionDef.new("Foo", [Arg.new("x", restriction: "Int".path), Arg.new("y", restriction: "Float".path)] of ASTNode)] of ASTNode)
it_parses "lib LibC; struct Foo; x : Int*; end end", LibDef.new("LibC", [CStructOrUnionDef.new("Foo", Expressions.from(Arg.new("x", restriction: "Int".path.pointer_of)))] of ASTNode)
it_parses "lib LibC; struct Foo; x : Int**; end end", LibDef.new("LibC", [CStructOrUnionDef.new("Foo", Expressions.from(Arg.new("x", restriction: "Int".path.pointer_of.pointer_of)))] of ASTNode)
it_parses "lib LibC; struct Foo; x, y, z : Int; end end", LibDef.new("LibC", [CStructOrUnionDef.new("Foo", [Arg.new("x", restriction: "Int".path), Arg.new("y", restriction: "Int".path), Arg.new("z", restriction: "Int".path)] of ASTNode)] of ASTNode)
it_parses "lib LibC; union Foo; end end", LibDef.new("LibC", [CStructOrUnionDef.new("Foo", union: true)] of ASTNode)
it_parses "lib LibC; enum Foo; A\nB, C\nD = 1; end end", LibDef.new("LibC", [EnumDef.new("Foo".path, [Arg.new("A"), Arg.new("B"), Arg.new("C"), Arg.new("D", 1.int32)] of ASTNode)] of ASTNode)
it_parses "lib LibC; enum Foo; A = 1, B; end end", LibDef.new("LibC", [EnumDef.new("Foo".path, [Arg.new("A", 1.int32), Arg.new("B")] of ASTNode)] of ASTNode)
it_parses "lib LibC; Foo = 1; end", LibDef.new("LibC", [Assign.new("Foo".path, 1.int32)] of ASTNode)
@@ -687,9 +687,9 @@ describe "Parser" do
it_parses "lib LibC\n$errno : B, C -> D\nend", LibDef.new("LibC", [ExternalVar.new("errno", ProcNotation.new(["B".path, "C".path] of ASTNode, "D".path))] of ASTNode)
it_parses "lib LibC\n$errno = Foo : Int32\nend", LibDef.new("LibC", [ExternalVar.new("errno", "Int32".path, "Foo")] of ASTNode)
it_parses "lib LibC\nalias Foo = Bar\nend", LibDef.new("LibC", [Alias.new("Foo", "Bar".path)] of ASTNode)
it_parses "lib LibC; struct Foo; ifdef cond; a : Int32; else; b : Float64; end; end; end", LibDef.new("LibC", [StructDef.new("Foo", IfDef.new("cond".var, Arg.new("a", restriction: "Int32".path), Arg.new("b", restriction: "Float64".path)))] of ASTNode)
it_parses "lib LibC\nstruct Foo\nifdef cond\na : Int32\nelse\nb : Float64\nend\nend\nend", LibDef.new("LibC", [StructDef.new("Foo", IfDef.new("cond".var, Arg.new("a", restriction: "Int32".path), Arg.new("b", restriction: "Float64".path)))] of ASTNode)
it_parses "lib LibC; struct Foo; include Bar; end; end", LibDef.new("LibC", [StructDef.new("Foo", Include.new("Bar".path))] of ASTNode)
it_parses "lib LibC; struct Foo; ifdef cond; a : Int32; else; b : Float64; end; end; end", LibDef.new("LibC", [CStructOrUnionDef.new("Foo", IfDef.new("cond".var, Arg.new("a", restriction: "Int32".path), Arg.new("b", restriction: "Float64".path)))] of ASTNode)
it_parses "lib LibC\nstruct Foo\nifdef cond\na : Int32\nelse\nb : Float64\nend\nend\nend", LibDef.new("LibC", [CStructOrUnionDef.new("Foo", IfDef.new("cond".var, Arg.new("a", restriction: "Int32".path), Arg.new("b", restriction: "Float64".path)))] of ASTNode)
it_parses "lib LibC; struct Foo; include Bar; end; end", LibDef.new("LibC", [CStructOrUnionDef.new("Foo", Include.new("Bar".path))] of ASTNode)

it_parses "lib LibC\nifdef foo\ntype A = B\nend\nend", LibDef.new("LibC", [IfDef.new("foo".var, TypeDef.new("A", "B".path))] of ASTNode)

@@ -698,8 +698,8 @@ describe "Parser" do
it_parses "lib LibC; {{ 1 }}; end", LibDef.new("LibC", body: [MacroExpression.new(1.int32)] of ASTNode)
it_parses "lib LibC; {% if 1 %}2{% end %}; end", LibDef.new("LibC", body: [MacroIf.new(1.int32, MacroLiteral.new("2"))] of ASTNode)

it_parses "lib LibC; struct Foo; {{ 1 }}; end; end", LibDef.new("LibC", body: StructDef.new("Foo", Expressions.from([MacroExpression.new(1.int32)] of ASTNode)))
it_parses "lib LibC; struct Foo; {% if 1 %}2{% end %}; end; end", LibDef.new("LibC", body: StructDef.new("Foo", Expressions.from([MacroIf.new(1.int32, MacroLiteral.new("2"))] of ASTNode)))
it_parses "lib LibC; struct Foo; {{ 1 }}; end; end", LibDef.new("LibC", body: CStructOrUnionDef.new("Foo", Expressions.from([MacroExpression.new(1.int32)] of ASTNode)))
it_parses "lib LibC; struct Foo; {% if 1 %}2{% end %}; end; end", LibDef.new("LibC", body: CStructOrUnionDef.new("Foo", Expressions.from([MacroIf.new(1.int32, MacroLiteral.new("2"))] of ASTNode)))

it_parses "1 .. 2", RangeLiteral.new(1.int32, 2.int32, false)
it_parses "1 ... 2", RangeLiteral.new(1.int32, 2.int32, true)
10 changes: 6 additions & 4 deletions spec/compiler/semantic/c_struct_spec.cr
Original file line number Diff line number Diff line change
@@ -5,7 +5,9 @@ describe "Semantic: struct" do
result = assert_type("lib LibFoo; struct Bar; x : Int32; y : Float64; end; end; LibFoo::Bar") { types["LibFoo"].types["Bar"].metaclass }
mod = result.program

bar = mod.types["LibFoo"].types["Bar"].as(CStructType)
bar = mod.types["LibFoo"].types["Bar"].as(NonGenericClassType)
bar.extern?.should be_true
bar.extern_union?.should be_false
bar.instance_vars["@x"].type.should eq(mod.int32)
bar.instance_vars["@y"].type.should eq(mod.float64)
end
@@ -60,7 +62,7 @@ describe "Semantic: struct" do

it "errors on struct if no field" do
assert_error "lib LibFoo; struct Bar; x : Int32; end; end; f = LibFoo::Bar.new; f.y = 'a'",
"struct LibFoo::Bar has no field 'y'"
"undefined method 'y=' for LibFoo::Bar"
end

it "errors on struct setter if different type" do
@@ -255,8 +257,8 @@ describe "Semantic: struct" do
end
end
))
foo_struct = result.program.types["LibFoo"].types["Struct"].as(CStructType)
foo_struct.packed.should be_true
foo_struct = result.program.types["LibFoo"].types["Struct"].as(NonGenericClassType)
foo_struct.packed?.should be_true
end

it "errors on empty c struct (#633)" do
4 changes: 3 additions & 1 deletion spec/compiler/semantic/c_union_spec.cr
Original file line number Diff line number Diff line change
@@ -4,7 +4,9 @@ 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(CUnionType)
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
6 changes: 5 additions & 1 deletion src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
@@ -1067,7 +1067,7 @@ module Crystal
ivar = type.lookup_instance_var_with_owner(name).instance_var
ivar_ptr = instance_var_ptr type, name, value
@last = downcast ivar_ptr, node_type, ivar.type, false
if type.is_a?(CStructOrUnionType)
if type.extern?
# When reading the instance variable of a C struct or union
# we need to convert C functions to Crystal procs. This
# can happen for example in Struct#to_s, where all fields
@@ -1963,6 +1963,10 @@ module Crystal
end

def instance_var_ptr(type, name, pointer)
if type.extern_union?
return union_field_ptr(type.instance_vars[name].type, pointer)
end

index = type.index_of_instance_var(name)

unless type.struct?
128 changes: 32 additions & 96 deletions src/compiler/crystal/codegen/llvm_typer.cr
Original file line number Diff line number Diff line change
@@ -116,14 +116,6 @@ module Crystal
PROC_TYPE
end

private def create_llvm_type(type : CStructType, wants_size)
llvm_struct_type(type, wants_size)
end

private def create_llvm_type(type : CUnionType, wants_size)
llvm_struct_type(type, wants_size)
end

private def create_llvm_type(type : InstanceVarContainer, wants_size)
final_type = llvm_struct_type(type, wants_size)
unless type.struct?
@@ -284,22 +276,40 @@ module Crystal
llvm_type(type, wants_size)
end

private def create_llvm_struct_type(type : CStructType, wants_size)
LLVM::Type.struct(type.llvm_name, type.packed) do |a_struct|
private def create_llvm_struct_type(type : InstanceVarContainer, wants_size)
if type.extern_union?
return create_llvm_c_union_struct_type(type, wants_size)
end

LLVM::Type.struct(type.llvm_name, type.packed?) do |a_struct|
if wants_size
@wants_size_struct_cache[type] = a_struct
else
@struct_cache[type] = a_struct
end

ivars = type.all_instance_vars
ivars_size = ivars.size
ivars_size += 1 unless type.struct?

element_types = Array(LLVM::Type).new(ivars_size)
element_types.push LLVM::Int32 unless type.struct? # For the type id

@types_being_computed.add(type)
types = type.instance_vars.map { |name, var| llvm_embedded_c_type(var.type, wants_size).as(LLVM::Type) }
ivars.each do |name, ivar|
if type.extern?
element_types.push llvm_embedded_c_type(ivar.type, wants_size)
else
element_types.push llvm_embedded_type(ivar.type, wants_size)
end
end
@types_being_computed.delete(type)
types

element_types
end
end

private def create_llvm_struct_type(type : CUnionType, wants_size)
private def create_llvm_c_union_struct_type(type, wants_size)
LLVM::Type.struct(type.llvm_name) do |a_struct|
if wants_size
@wants_size_struct_cache[type] = a_struct
@@ -341,42 +351,6 @@ module Crystal
end
end

private def create_llvm_struct_type(type : InstanceVarContainer, wants_size)
LLVM::Type.struct(type.llvm_name) do |a_struct|
if wants_size
@wants_size_struct_cache[type] = a_struct
else
@struct_cache[type] = a_struct
end

ivars = type.all_instance_vars
ivars_size = ivars.size

unless type.struct?
ivars_size += 1
end

element_types = Array(LLVM::Type).new(ivars_size)

unless type.struct?
element_types.push LLVM::Int32 # For the type id
end

@types_being_computed.add(type)
ivars.each do |name, ivar|
if ivar_type = ivar.type?
element_types.push llvm_embedded_type(ivar_type, wants_size)
else
# This is for untyped fields: we don't really care how to represent them in memory.
element_types.push LLVM::Int8
end
end
@types_being_computed.delete(type)

element_types
end
end

private def create_llvm_struct_type(type : Type, wants_size)
raise "Bug: called llvm_struct_type for #{type}"
end
@@ -394,49 +368,15 @@ module Crystal
end

def llvm_embedded_type(type, wants_size = false)
llvm_embedded_type_impl(type.remove_indirection, wants_size)
end

private def llvm_embedded_type_impl(type : CStructType, wants_size)
llvm_struct_type(type, wants_size)
end

private def llvm_embedded_type_impl(type : CUnionType, wants_size)
llvm_struct_type(type, wants_size)
end

private def llvm_embedded_type_impl(type : ProcInstanceType, wants_size)
llvm_type(type, wants_size)
end

private def llvm_embedded_type_impl(type : PointerInstanceType, wants_size)
llvm_type(type, wants_size)
end

private def llvm_embedded_type_impl(type : InstanceVarContainer, wants_size)
if type.struct?
llvm_struct_type(type, wants_size)
type = type.remove_indirection
case type
when NoReturnType, VoidType
LLVM::Int8
else
llvm_type(type, wants_size)
end
end

private def llvm_embedded_type_impl(type : StaticArrayInstanceType, wants_size)
llvm_type(type, wants_size)
end

private def llvm_embedded_type_impl(type : NoReturnType, wants_size)
LLVM::Int8
end

private def llvm_embedded_type_impl(type : VoidType, wants_size)
LLVM::Int8
end

private def llvm_embedded_type_impl(type, wants_size)
llvm_type(type, wants_size)
end

def llvm_embedded_c_type(type : ProcInstanceType, wants_size = false)
proc_type(type)
end
@@ -453,20 +393,16 @@ module Crystal
proc_type(type.proc_type)
end

def llvm_c_type(type : CStructOrUnionType)
llvm_struct_type(type)
end

def llvm_c_type(type : TupleInstanceType)
llvm_struct_type(type)
end

def llvm_c_type(type)
llvm_arg_type(type)
end

def llvm_c_return_type(type : CStructType)
llvm_type(type)
if type.extern?
llvm_struct_type(type)
else
llvm_arg_type(type)
end
end

def llvm_c_return_type(type : NilType)
Loading