diff --git a/bootstrap/crystal/codegen.cr b/bootstrap/crystal/codegen.cr index c20e9ef8f4e54427ff5eda48ee80e9adf0c4a39a..7e5880d6d6aaa4eb83e445d753c55b7e1a143be1 100644 --- a/bootstrap/crystal/codegen.cr +++ b/bootstrap/crystal/codegen.cr @@ -1737,7 +1737,12 @@ module Crystal end end - if node.name == "super" + if !node.target_defs || node.target_def.owner.try &.is_subclass_of?(@mod.value) + if node_obj = node.obj + owner = node_obj.type? + end + owner ||= node.scope + elsif node.name == "super" owner = node.scope else owner = node.target_def.owner @@ -1778,6 +1783,8 @@ module Crystal end end + return if node.args.any?(&.yields?) && block_breaks? + if block = node.block @block_context << BlockContext.new(block, @vars, @type, @return_block, @return_block_table_blocks, @return_block_table_values, @return_type, @return_union) @vars = {} of String => LLVMVar diff --git a/bootstrap/spec/crystal/codegen/block_spec.cr b/bootstrap/spec/crystal/codegen/block_spec.cr index 06e8541f93c7b347057d17715fdf859d702cb37d..3e5a4055d10da2c04c9af604c0546b01aa1d1f11 100755 --- a/bootstrap/spec/crystal/codegen/block_spec.cr +++ b/bootstrap/spec/crystal/codegen/block_spec.cr @@ -184,524 +184,527 @@ describe "Code gen: block" do ").to_i.should eq(1) end - # it "return from yielder function" do - # run(%q( - # def foo - # yield - # return 1 - # end - - # foo { } - # 2 - # )).to_i.should eq(2) - # end - - # it "return from block" do - # run(%q( - # def foo - # yield - # end - - # def bar - # foo { return 1 } - # 2 - # end - - # bar - # )).to_i.should eq(1) - # end - - # it "return from yielder function (2)" do - # run(%q( - # def foo - # yield - # return 1 if true - # return 2 - # end - - # def bar - # foo {} - # end - - # bar - # )).to_i.should eq(1) - # end - - # it "union value of yielder function" do - # run(%q( - # def foo - # yield - # a = 1.1 - # a = 1 - # a - # end - - # foo {}.to_i - # )).to_i.should eq(1) - # end - - # it "allow return from function called from yielder function" do - # run(%q( - # def foo - # return 2 - # end - - # def bar - # yield - # foo - # 1 - # end - - # bar {} - # )).to_i.should eq(1) - # end - - # it "" do - # run(%q( - # def foo - # yield - # true ? return 1 : return 1.1 - # end - - # foo {}.to_i - # )).to_i.should eq(1) - # end - - # it "return from block that always returns from function that always yields inside if block" do - # run(%q( - # def bar - # yield - # 2 - # end - - # def foo - # if true - # bar { return 1 } - # else - # 0 - # end - # end - - # foo - # )).to_i.should eq(1) - # end - - # it "return from block that always returns from function that conditionally yields" do - # run(%q( - # def bar - # if true - # yield - # end - # end - - # def foo - # bar { return 1 } - # 2 - # end - - # foo - # )).to_i.should eq(1) - # end - - # it "call block from dispatch" do - # run(%q( - # def bar(y) - # yield y - # end - - # def foo - # x = 1.1 - # x = 1 - # bar(x) { |z| z } - # end - - # foo.to_i - # )).to_i.should eq(1) - # end - - # it "call block from dispatch and use local vars" do - # run(%q( - # def bar(y) - # yield y - # end - - # def foo - # total = 0 - # x = 1.5 - # bar(x) { |z| total += z } - # x = 1 - # bar(x) { |z| total += z } - # x = 1.5 - # bar(x) { |z| total += z } - # total - # end - - # foo.to_i - # )).to_i.should eq(4) - # end - - # it "break without value returns nil" do - # run(%q( - # require "nil" - - # def foo - # yield - # 1 - # end - - # x = foo do - # break if true - # end - - # x.nil? - # )).to_b.should be_true - # end - - # it "break block with yielder inside while" do - # run(%q( - # require "int" - # a = 0 - # 10.times do - # a += 1 - # break if a > 5 - # end - # a - # )).to_i.should eq(6) - # end - - # it "break from block returns from yielder" do - # run(%q( - # def foo - # yield - # yield - # end - - # a = 0 - # foo { a += 1; break } - # a - # )).to_i.should eq(1) - # end - - # it "break from block with value" do - # run(%q( - # def foo - # while true - # yield - # a = 3 - # end - # end - - # foo do - # break 1 - # end - # )).to_i.should eq(1) - # end - - # it "break from block with value" do - # run(%q( - # require "nil" - - # def foo - # while true - # yield - # a = 3 - # end - # end - - # def bar - # foo do - # return 1 - # end - # end - - # bar.to_i - # )).to_i.should eq(1) - # end - - # it "doesn't codegen after while that always yields and breaks" do - # run(%q( - # def foo - # while true - # yield - # end - # 1 - # end - - # foo do - # break 2 - # end - # )).to_i.should eq(2) - # end - - # pending "break from block with value" do - # run(%q( - # require "int" - # 10.times { break 20 } - # )).to_i.should eq(20) - # end - - # it "doesn't codegen call if arg yields and always breaks" do - # run(%q( - # require "nil" - - # def foo - # 1 + yield - # end - - # foo { break 2 }.to_i - # )).to_i.should eq(2) - # end - - # it "codegens nested return" do - # run(%q( - # def bar - # yield - # a = 1 - # end - - # def foo - # bar { yield } - # end - - # def z - # foo { return 2 } - # end - - # z - # )).to_i.should eq(2) - # end - - # it "codegens nested break" do - # run(%q( - # def bar - # yield - # a = 1 - # end - - # def foo - # bar { yield } - # end - - # foo { break 2 } - # )).to_i.should eq(2) - # end - - # it "codegens call with block with call with arg that yields" do - # run(%q( - # def bar - # yield - # a = 2 - # end - - # def foo - # bar { 1 + yield } - # end - - # foo { break 3 } - # )).to_i.should eq(3) - # end - - # it "can break without value from yielder that returns nilable" do - # run(%q( - # require "nil" - # require "reference" - - # def foo - # yield - # "" - # end - - # a = foo do - # break - # end - - # a.nil? - # )).to_b.should be_true - # end - - # it "break with value from yielder that returns a nilable" do - # run(%q( - # require "nil" - # require "reference" - - # def foo - # yield - # "" - # end - - # a = foo do - # break if false - # break "" - # end - - # a.nil? - # )).to_b.should be_false - # end - - # it "can use self inside a block called from dispatch" do - # run(%q( - # require "nil" - - # class Foo - # def do; yield; end - # end - # class Bar < Foo - # end - - - # class Int - # def foo - # x = Foo.new - # x = Bar.new - # x.do { $x = self } - # end - # end - - # 123.foo - # $x.to_i - # )).to_i.should eq(123) - # end - - # it "return from block called from dispatch" do - # run(%q( - # class Foo - # def do; yield; end - # end - # class Bar < Foo - # end - - # def foo - # x = Foo.new - # x = Bar.new - # x.do { return 1 } - # 0 - # end - - # foo - # )).to_i.should eq(1) - # end - - # it "breaks from while in function called from block" do - # run(%q( - # def foo - # yield - # end - - # def bar - # while true - # break 1 - # end - # 2 - # end - - # foo do - # bar - # end - # )).to_i.should eq(2) - # end - - # it "allows modifying yielded value (with literal)" do - # run(%q( - # def foo - # yield 1 - # end - - # foo { |x| x = 2; x } - # )).to_i.should eq(2) - # end - - # it "allows modifying yielded value (with variable)" do - # run(%q( - # def foo - # a = 1 - # yield a - # a - # end - - # foo { |x| x = 2; x } - # )).to_i.should eq(1) - # end - - # it "it yields nil from another call" do - # run(%q( - # def foo(key, default) - # foo(key) { default } - # end - - # def foo(key) - # if !(true) - # return yield key - # end - # yield key - # end - - # foo(1, nil) - # )) - # end - - # it "allows yield from dispatch call" do - # run(%q( - # def foo(x : Value) - # yield 1 - # end - - # def foo(x : Int) - # yield 2 - # end - - # def bar - # a = 1; a = 1.1 - # foo(a) do |i| - # yield i - # end - # end - - # x = 0 - # bar { |i| x = i } - # x - # )).to_i.should eq(1) - # end - - # it "block with nilable type" do - # run(%q( - # class Foo - # def foo - # yield 1 - # end - # end - - # class Bar - # def foo - # yield 2 - # Reference.new - # end - # end - - # a = Foo.new || Bar.new - # a.foo {} - # )) - # end - - # it "block with nilable type 2" do - # run(%q( - # class Foo - # def foo - # yield 1 - # nil - # end - # end - - # class Bar - # def foo - # yield 2 - # Reference.new - # end - # end - - # a = Foo.new || Bar.new - # a.foo {} - # )) - # end + it "return from yielder function" do + run(" + def foo + yield + return 1 + end + + foo { } + 2 + ").to_i.should eq(2) + end + + it "return from block" do + run(" + def foo + yield + end + + def bar + foo { return 1 } + 2 + end + + bar + ").to_i.should eq(1) + end + + it "return from yielder function (2)" do + run(" + def foo + yield + return 1 if true + return 2 + end + + def bar + foo {} + end + + bar + ").to_i.should eq(1) + end + + it "union value of yielder function" do + run(" + def foo + yield + a = 1.1 + a = 1 + a + end + + foo {}.to_i + ").to_i.should eq(1) + end + + it "allow return from function called from yielder function" do + run(" + def foo + return 2 + end + + def bar + yield + foo + 1 + end + + bar {} + ").to_i.should eq(1) + end + + it "" do + run(" + def foo + yield + true ? return 1 : return 1.1 + end + + foo {}.to_i + ").to_i.should eq(1) + end + + it "return from block that always returns from function that always yields inside if block" do + run(" + def bar + yield + 2 + end + + def foo + if true + bar { return 1 } + else + 0 + end + end + + foo + ").to_i.should eq(1) + end + + it "return from block that always returns from function that conditionally yields" do + run(" + def bar + if true + yield + end + end + + def foo + bar { return 1 } + 2 + end + + foo + ").to_i.should eq(1) + end + + it "call block from dispatch" do + run(" + def bar(y) + yield y + end + + def foo + x = 1.1 + x = 1 + bar(x) { |z| z } + end + + foo.to_i + ").to_i.should eq(1) + end + + it "call block from dispatch and use local vars" do + run(" + def bar(y) + yield y + end + + def foo + total = 0 + x = 1.5 + bar(x) { |z| total += z } + x = 1 + bar(x) { |z| total += z } + x = 1.5 + bar(x) { |z| total += z } + total + end + + foo.to_i + ").to_i.should eq(4) + end + + it "break without value returns nil" do + run(" + require \"nil\" + require \"value\" + + def foo + yield + 1 + end + + x = foo do + break if 1 == 1 + end + + x.nil? + ").to_b.should be_true + end + + it "break block with yielder inside while" do + run(" + require \"int\" + a = 0 + 10.times do + a += 1 + break if a > 5 + end + a + ").to_i.should eq(6) + end + + it "break from block returns from yielder" do + run(" + def foo + yield + yield + end + + a = 0 + foo { a += 1; break } + a + ").to_i.should eq(1) + end + + it "break from block with value" do + run(" + def foo + while true + yield + a = 3 + end + end + + foo do + break 1 + end + ").to_i.should eq(1) + end + + it "break from block with value" do + run(" + require \"nil\" + + def foo + while true + yield + a = 3 + end + end + + def bar + foo do + return 1 + end + end + + bar.to_i + ").to_i.should eq(1) + end + + it "doesn't codegen after while that always yields and breaks" do + run(" + def foo + while true + yield + end + 1 + end + + foo do + break 2 + end + ").to_i.should eq(2) + end + + pending "break from block with value" do + run(" + require \"int\" + 10.times { break 20 } + ").to_i.should eq(20) + end + + it "doesn't codegen call if arg yields and always breaks" do + run(" + require \"nil\" + + def foo + 1 + yield + end + + foo { break 2 }.to_i + ").to_i.should eq(2) + end + + it "codegens nested return" do + run(" + def bar + yield + a = 1 + end + + def foo + bar { yield } + end + + def z + foo { return 2 } + end + + z + ").to_i.should eq(2) + end + + it "codegens nested break" do + run(" + def bar + yield + a = 1 + end + + def foo + bar { yield } + end + + foo { break 2 } + ").to_i.should eq(2) + end + + it "codegens call with block with call with arg that yields" do + run(" + def bar + yield + a = 2 + end + + def foo + bar { 1 + yield } + end + + foo { break 3 } + ").to_i.should eq(3) + end + + it "can break without value from yielder that returns nilable" do + run(" + require \"nil\" + require \"reference\" + + def foo + yield + \"\" + end + + a = foo do + break + end + + a.nil? + ").to_b.should be_true + end + + it "break with value from yielder that returns a nilable" do + run(" + require \"nil\" + require \"reference\" + + def foo + yield + \"\" + end + + a = foo do + break if false + break \"\" + end + + a.nil? + ").to_b.should be_false + end + + it "can use self inside a block called from dispatch" do + run(" + require \"nil\" + + class Foo + def do; yield; end + end + class Bar < Foo + end + + + class Int + def foo + x = Foo.new + x = Bar.new + x.do { $x = self } + end + end + + 123.foo + $x.to_i + ").to_i.should eq(123) + end + + it "return from block called from dispatch" do + run(" + class Foo + def do; yield; end + end + class Bar < Foo + end + + def foo + x = Foo.new + x = Bar.new + x.do { return 1 } + 0 + end + + foo + ").to_i.should eq(1) + end + + it "breaks from while in function called from block" do + run(" + def foo + yield + end + + def bar + while true + break 1 + end + 2 + end + + foo do + bar + end + ").to_i.should eq(2) + end + + it "allows modifying yielded value (with literal)" do + run(" + def foo + yield 1 + end + + foo { |x| x = 2; x } + ").to_i.should eq(2) + end + + it "allows modifying yielded value (with variable)" do + run(" + def foo + a = 1 + yield a + a + end + + foo { |x| x = 2; x } + ").to_i.should eq(1) + end + + it "it yields nil from another call" do + run(" + require \"bool\" + + def foo(key, default) + foo(key) { default } + end + + def foo(key) + if !(true) + return yield key + end + yield key + end + + foo(1, nil) + ") + end + + it "allows yield from dispatch call" do + run(" + def foo(x : Value) + yield 1 + end + + def foo(x : Int) + yield 2 + end + + def bar + a = 1; a = 1.1 + foo(a) do |i| + yield i + end + end + + x = 0 + bar { |i| x = i } + x + ").to_i.should eq(1) + end + + it "block with nilable type" do + run(" + class Foo + def foo + yield 1 + end + end + + class Bar + def foo + yield 2 + Reference.new + end + end + + a = Foo.new || Bar.new + a.foo {} + ") + end + + it "block with nilable type 2" do + run(" + class Foo + def foo + yield 1 + nil + end + end + + class Bar + def foo + yield 2 + Reference.new + end + end + + a = Foo.new || Bar.new + a.foo {} + ") + end it "allows yields with less arguments than in block" do run(" @@ -723,167 +726,168 @@ describe "Code gen: block" do ").to_i.should eq(1) end - # it "codegens block with nilable type with return" do - # run(%q( - # def foo - # if yield - # return Reference.new - # end - # nil - # end - - # foo { false } - # )) - # end - - # it "codegens block with union with return" do - # run(%q( - # def foo - # yield - - # return 1 if 1 == 2 - - # nil - # end - - # foo { } - # )) - # end - - # it "codegens if with call with block (ssa issue)" do - # run(%q( - # def bar - # yield - # end - - # def foo - # if 1 == 2 - # bar do - # x = 1 - # end - # else - # 3 - # end - # end - - # foo - # )).to_i.should eq(3) - # end - - # it "codegens block with return and yield and no return" do - # run(%q( - # lib C - # fun exit : NoReturn - # end - - # def foo(key) - # foo(key) { C.exit } - # end - - # def foo(key) - # if 1 == 1 - # return 2 - # end - # yield - # end - - # foo 1 - # )).to_i.should eq(2) - # end - - # it "codegens while/break inside block" do - # run(%q( - # def foo - # yield - # end - - # foo do - # while true - # break - # end - # 1 - # end - # )).to_i.should eq(1) - # end - - # it "codegens block with union arg" do - # run(%q( - # class Number - # def abs - # self - # end - # end - - # class Foo(T) - # def initialize(x : T) - # @x = x - # end - - # def each - # yield @x - # end - # end - - # a = Foo.new(1) || Foo.new(1.5) - # a.each do |x| - # x.abs - # end.to_i - # )).to_i.should eq(1) - # end - - # it "codegens block with hierarchy type arg" do - # run(%q( - # class Var(T) - # def initialize(x : T) - # @x = x - # end - - # def each - # yield @x - # end - # end - - # class Foo - # def bar - # 1 - # end - # end - - # class Bar < Foo - # def bar - # 2 - # end - # end - - # a = Var.new(Foo.new) || Var.new(Bar.new) - # a.each do |x| - # x.bar - # end - # )).to_i.should eq(1) - # end - - # it "codegens call with blocks of different type without args" do - # run(%q( - # def foo - # yield - # end - - # foo { 1.1 } - # foo { 1 } - # )).to_i.should eq(1) - # end - - # it "codegens dispatch with block and break" do - # run(%q( - # require "prelude" - - # a = [1, 2, 3] || [1.5] - # n = 0 - # a.each do |x| - # break if x > 2 - # n += x - # end - # n.to_i - # )).to_i.should eq(3) - # end + it "codegens block with nilable type with return" do + run(" + def foo + if yield + return Reference.new + end + nil + end + + foo { false } + ") + end + + it "codegens block with union with return" do + run(" + def foo + yield + + return 1 if 1 == 2 + + nil + end + + x = foo { } + 1 + ") + end + + it "codegens if with call with block (ssa issue)" do + run(" + def bar + yield + end + + def foo + if 1 == 2 + bar do + x = 1 + end + else + 3 + end + end + + foo + ").to_i.should eq(3) + end + + it "codegens block with return and yield and no return" do + run(" + lib C + fun exit : NoReturn + end + + def foo(key) + foo(key) { C.exit } + end + + def foo(key) + if 1 == 1 + return 2 + end + yield + end + + foo 1 + ").to_i.should eq(2) + end + + it "codegens while/break inside block" do + run(" + def foo + yield + end + + foo do + while true + break + end + 1 + end + ").to_i.should eq(1) + end + + it "codegens block with union arg" do + run(" + class Number + def abs + self + end + end + + class Foo(T) + def initialize(x : T) + @x = x + end + + def each + yield @x + end + end + + a = Foo.new(1) || Foo.new(1.5) + a.each do |x| + x.abs + end.to_i + ").to_i.should eq(1) + end + + it "codegens block with hierarchy type arg" do + run(" + class Var(T) + def initialize(x : T) + @x = x + end + + def each + yield @x + end + end + + class Foo + def bar + 1 + end + end + + class Bar < Foo + def bar + 2 + end + end + + a = Var.new(Foo.new) || Var.new(Bar.new) + a.each do |x| + x.bar + end + ").to_i.should eq(1) + end + + it "codegens call with blocks of different type without args" do + run(" + def foo + yield + end + + foo { 1.1 } + foo { 1 } + ").to_i.should eq(1) + end + + pending "codegens dispatch with block and break" do + run(" + require \"bootstrap\" + + a = [1, 2, 3] || [1.5] + n = 0 + a.each do |x| + break if x > 2 + n += x + end + n.to_i + ").to_i.should eq(3) + end end diff --git a/bootstrap/spec/crystal/codegen/c_enum_spec.cr b/bootstrap/spec/crystal/codegen/c_enum_spec.cr new file mode 100644 index 0000000000000000000000000000000000000000..ae4757b5b860fa6c924b2585efd19325215aeb26 --- /dev/null +++ b/bootstrap/spec/crystal/codegen/c_enum_spec.cr @@ -0,0 +1,22 @@ +#!/usr/bin/env bin/crystal -run +require "../../spec_helper" + +CodeGenEnumString = "lib Foo; enum Bar; X, Y, Z = 10, W; end end" + +describe "Code gen: enum" do + it "codegens enum value" do + run("#{CodeGenEnumString}; Foo::Bar::X").to_i.should eq(0) + end + + it "codegens enum value 2" do + run("#{CodeGenEnumString}; Foo::Bar::Y").to_i.should eq(1) + end + + it "codegens enum value 3" do + run("#{CodeGenEnumString}; Foo::Bar::Z").to_i.should eq(10) + end + + it "codegens enum value 4" do + run("#{CodeGenEnumString}; Foo::Bar::W").to_i.should eq(11) + end +end diff --git a/bootstrap/spec/crystal/codegen/c_struct_spec.cr b/bootstrap/spec/crystal/codegen/c_struct_spec.cr new file mode 100644 index 0000000000000000000000000000000000000000..2ef8ded208d35a43f796b7565491da201874a317 --- /dev/null +++ b/bootstrap/spec/crystal/codegen/c_struct_spec.cr @@ -0,0 +1,97 @@ +#!/usr/bin/env bin/crystal -run +require "../../spec_helper" + +CodeGenStructString = "lib Foo; struct Bar; x : Int32; y : Float32; end; end" + +describe "Code gen: struct" do + it "codegens struct property default value" do + run("#{CodeGenStructString}; bar = Foo::Bar.new; bar.x").to_i.should eq(0) + end + + it "codegens struct property setter" do + run("#{CodeGenStructString}; bar = Foo::Bar.new; bar.y = 2.5_f32; bar.y").to_f32.should eq(2.5) + end + + it "codegens struct property setter" do + run("#{CodeGenStructString}; bar = Foo::Bar.new; p = bar.ptr; p.value.y = 2.5_f32; bar.y").to_f32.should eq(2.5) + end + + it "codegens set struct value with constant" do + run("#{CodeGenStructString}; CONST = 1; bar = Foo::Bar.new; bar.x = CONST; bar.x").to_i.should eq(1) + end + + it "codegens union inside struct" do + run(" + lib Foo + union Bar + x : Int32 + y : Int64 + end + + struct Baz + lala : Bar + end + end + + a = Foo::Baz.new + a.lala.x = 10 + a.lala.x + ").to_i.should eq(10) + end + + it "codegens struct get inside struct" do + run(" + lib C + struct Bar + y : Int32 + end + + struct Foo + x : Int32 + bar : Bar + end + end + + foo = C::Foo.new + (foo.ptr.as(Int32) + 1_i64).value = 2 + + foo.bar.y + ").to_i.should eq(2) + end + + it "codegens struct set inside struct" do + run(" + lib C + struct Bar + y : Int32 + end + + struct Foo + x : Int32 + bar : Bar + end + end + + foo = C::Foo.new + bar = C::Bar.new + bar.y = 2 + foo.bar = bar + + foo.bar.y + ").to_i.should eq(2) + end + + it "codegens pointer malloc of struct" do + run(" + lib C + struct Foo + x : Int32 + end + end + + p = Pointer(C::Foo).malloc(1_u64) + p.value.x = 1 + p.value.x + ").to_i.should eq(1) + end +end diff --git a/bootstrap/spec/crystal/codegen/c_union_spec.cr b/bootstrap/spec/crystal/codegen/c_union_spec.cr new file mode 100644 index 0000000000000000000000000000000000000000..5370809396ebf55831ec5ea134a298cd25740376 --- /dev/null +++ b/bootstrap/spec/crystal/codegen/c_union_spec.cr @@ -0,0 +1,44 @@ +#!/usr/bin/env bin/crystal -run +require "../../spec_helper" + +CodeGenUnionString = "lib Foo; union Bar; x : Int32; y : Int64; z : Float32; end; end" + +describe "Code gen: c union" do + it "codegens union property default value" do + run("#{CodeGenUnionString}; bar = Foo::Bar.new; bar.x").to_i.should eq(0) + end + + it "codegens union property default value 2" do + run("#{CodeGenUnionString}; bar = Foo::Bar.new; bar.z").to_f32.should eq(0) + end + + it "codegens union property setter 1" do + run("#{CodeGenUnionString}; bar = Foo::Bar.new; bar.x = 42; bar.x").to_i.should eq(42) + end + + it "codegens union property setter 2" do + run("#{CodeGenUnionString}; bar = Foo::Bar.new; bar.z = 42.0_f32; bar.z").to_f32.should eq(42.0) + end + + it "codegens struct inside union" do + run(" + lib Foo + struct Baz + lele : Int64 + lala : Int32 + end + + union Bar + x : Int32 + y : Int64 + z : Baz + end + end + + a = Foo::Bar.new + a.z = Foo::Baz.new + a.z.lala = 10 + a.z.lala + ").to_i.should eq(10) + end +end diff --git a/bootstrap/spec/crystal/codegen/case_spec.cr b/bootstrap/spec/crystal/codegen/case_spec.cr new file mode 100644 index 0000000000000000000000000000000000000000..de1b9fd2a9d349a93c86d91d4432e21bc5fbfd84 --- /dev/null +++ b/bootstrap/spec/crystal/codegen/case_spec.cr @@ -0,0 +1,73 @@ +#!/usr/bin/env bin/crystal -run +require "../../spec_helper" + +describe "Code gen: case" do + it "codegens case with one condition" do + run("require \"object\"; case 1; when 1; 2; else; 3; end").to_i.should eq(2) + end + + it "codegens case with two conditions" do + run("require \"object\"; case 1; when 0, 1; 2; else; 3; end").to_i.should eq(2) + end + + it "codegens case with else" do + run("require \"object\"; case 1; when 0; 2; else; 3; end").to_i.should eq(3) + end + + it "codegens case that always returns" do + run(" + require \"object\" + def foo + if true + case 0 + when 1; return 2 + else return 3 + end + end + 4 + end + + foo + ").to_i.should eq(3) + end + + it "codegens case when cond is a call" do + run(" + require \"object\" + + $a = 0 + + def foo + $a += 1 + end + + case foo + when 2 + 1 + when 1 + 2 + else + 3 + end + ").to_i.should eq(2) + end + + it "codegens case with class" do + run(" + require \"nil\" + class Int32 + def foo + self + end + end + + a = -1 || 'a' + case a + when Int32 + a.foo + when Char + a.ord + end.to_i + ").to_i.should eq(-1) + end +end diff --git a/bootstrap/spec/crystal/codegen/class_spec.cr b/bootstrap/spec/crystal/codegen/class_spec.cr index d3b51c8cd84b5f9fed178bfb7b117feaef127d30..7276f7bcce1e979d8bfe7938794431791f9a6d37 100755 --- a/bootstrap/spec/crystal/codegen/class_spec.cr +++ b/bootstrap/spec/crystal/codegen/class_spec.cr @@ -37,4 +37,134 @@ describe "Code gen: class" do it "codegens byte size of Int32" do run("Int32.byte_size").to_i.should eq(4) end + + it "codegens recursive type" do + run(" + class Foo + def next=(@next) + end + end + + f = Foo.new + f.next = f + ") + end + + it "codegens method call of instance var" do + run(" + class List + def initialize + @last = 0 + end + + def foo + @last = 1 + @last.to_f + end + end + + l = List.new + l.foo + ").to_f64.should eq(1.0) + end + + it "codegens new which calls initialize" do + run(" + class Foo + def initialize(value) + @value = value + end + + def value + @value + end + end + + f = Foo.new 1 + f.value + ").to_i.should eq(1) + end + + it "codegens method from another method without obj and accesses instance vars" do + run(" + class Foo + def foo + bar + end + + def bar + @a = 1 + end + end + + f = Foo.new + f.foo + ").to_i.should eq(1) + end + + it "codegens virtual call that calls another method" do + run(" + class Foo + def foo + foo2 + end + + def foo2 + 1 + end + end + + class Bar < Foo + end + + Bar.new.foo + ").to_i.should eq(1) + end + + it "codgens virtual method of generic class" do + run(" + require \"char\" + + class Object + def foo + bar + end + + def bar + 'a' + end + end + + class Foo(T) + def bar + 1 + end + end + + Foo(Int32).new.foo.to_i + ").to_i.should eq(1) + end + + it "changes instance variable in method (ssa bug)" do + run(" + class Foo + def initialize + @var = 0 + end + + def foo + @var = 1 + bar + @var + end + + def bar + @var = 2 + end + end + + foo = Foo.new + foo.foo + ").to_i.should eq(2) + end end diff --git a/bootstrap/spec/crystal/codegen/class_var_spec.cr b/bootstrap/spec/crystal/codegen/class_var_spec.cr new file mode 100644 index 0000000000000000000000000000000000000000..dad1361a47cbdc43276a51788864952b88813278 --- /dev/null +++ b/bootstrap/spec/crystal/codegen/class_var_spec.cr @@ -0,0 +1,75 @@ +#!/usr/bin/env bin/crystal -run +require "../../spec_helper" + +describe "Codegen: class var" do + it "codegens class var" do + run(" + class Foo + @@foo = 1 + + def self.foo + @@foo + end + end + + Foo.foo + ").to_i.should eq(1) + end + + it "codegens class var as nil" do + run(" + require \"nil\" + + class Foo + def self.foo + @@foo + end + end + + Foo.foo.to_i + ").to_i.should eq(0) + end + + it "codegens class var inside instance method" do + run(" + class Foo + @@foo = 1 + + def foo + @@foo + end + end + + Foo.new.foo + ").to_i.should eq(1) + end + + it "codegens class var as nil if assigned for the first time inside method" do + run(" + require \"nil\" + + class Foo + def self.foo + @@foo = 1 + @@foo + end + end + + Foo.foo.to_i + ").to_i.should eq(1) + end + + it "codegens class var of program" do + run(" + @@foo = 1 + @@foo + ").to_i.should eq(1) + end + + it "codegens class var of program as nil" do + run(" + require \"nil\" + @@foo.to_i + ").to_i.should eq(0) + end +end diff --git a/bootstrap/spec/crystal/codegen/const_spec.cr b/bootstrap/spec/crystal/codegen/const_spec.cr index a26023d77768c322eab60cd0d22589626f4e345f..9537bfe1967f1a36d1e2cc3f4627849401bfc6cc 100644 --- a/bootstrap/spec/crystal/codegen/const_spec.cr +++ b/bootstrap/spec/crystal/codegen/const_spec.cr @@ -2,6 +2,122 @@ require "../../spec_helper" describe "Codegen: const" do + it "define a constant" do + run("A = 1; A").to_i.should eq(1) + end + + it "support nested constant" do + run("class B; A = 1; end; B::A").to_i.should eq(1) + end + + it "support constant inside a def" do + run(" + class Foo + A = 1 + + def foo + A + end + end + + Foo.new.foo + ").to_i.should eq(1) + end + + it "finds nearest constant first" do + run(" + A = 1 + + class Foo + A = 2.5_f32 + + def foo + A + end + end + + Foo.new.foo + ").to_f32.should eq(2.5) + end + + it "allows constants with same name" do + run(" + A = 1 + + class Foo + A = 2.5_f32 + + def foo + A + end + end + + A + Foo.new.foo + ").to_f32.should eq(2.5) + end + + it "constants with expression" do + run(" + A = 1 + 1 + A + ").to_i.should eq(2) + end + + it "finds global constant" do + run(" + A = 1 + + class Foo + def foo + A + end + end + + Foo.new.foo + ").to_i.should eq(1) + end + + it "define a constant in lib" do + run("lib Foo; A = 1; end; Foo::A").to_i.should eq(1) + end + + it "invokes block in const" do + run("require \"bootstrap\"; A = [\"1\"].map { |x| x.to_i }; A[0]").to_i.should eq(1) + end + + it "declare constants in right order" do + run("A = 1 + 1; B = true ? A : 0; B").to_i.should eq(2) + end + + it "uses correct types lookup" do + run("module A; class B; def foo; 1; end; end; C = B.new; end; def foo; A::C.foo; end; foo").to_i.should eq(1) + end + + it "codegens variable assignment in const" do + run(" + class Foo + def initialize(@x) + end + + def x + @x + end + end + + A = begin + f = Foo.new(1) + f + end + + def foo + A.x + end + + foo + ").to_i.should eq(1) + end + it "declaring var" do run(" BAR = begin diff --git a/bootstrap/spec/crystal/codegen/def_spec.cr b/bootstrap/spec/crystal/codegen/def_spec.cr index b70743767e76f3302fcb63a2838ef468896fd77d..75976ca879aad10e727a63a912a5c3323e284e58 100755 --- a/bootstrap/spec/crystal/codegen/def_spec.cr +++ b/bootstrap/spec/crystal/codegen/def_spec.cr @@ -2,36 +2,269 @@ require "../../spec_helper" describe "Code gen: def" do - it "codegens def" do + it "codegens empty def" do + run("def foo; end; foo") + end + + it "codegens call without args" do + run("def foo; 1; end; 2; foo").to_i.should eq(1) + end + + it "call functions defined in any order" do + run("def foo; bar; end; def bar; 1; end; foo").to_i.should eq(1) + end + + it "codegens call with args" do + run("def foo(x); x; end; foo 1").to_i.should eq(1) + end + + it "call external function 'putchar'" do + run("require \"io\"; C.putchar '\\0'").to_i.should eq(0) + end + + it "uses self" do + run("class Int; def foo; self + 1; end; end; 3.foo").to_i.should eq(4) + end + + it "uses var after external" do + run("require \"io\"; a = 1; C.putchar '\\0'; a").to_i.should eq(1) + end + + it "allows to change argument values" do + run("def foo(x); x = 1; x; end; foo(2)").to_i.should eq(1) + end + + it "runs empty def" do + run("def foo; end; foo") + end + + it "builds infinite recursive function" do + node = parse "def foo; foo; end; foo" + result = infer_type node + result.program.build result.node + end + + it "unifies all calls to same def" do run(" - def foo + require \"bootstrap\" + + def raise(msg) + nil + end + + class Hash2 + def initialize + @buckets = [[1]] + end + + def []=(key, value) + bucket.push value + end + + def [](key) + bucket[0] + end + + def bucket + @buckets[0] + end + end + + hash = Hash2.new + hash[1] = 2 + hash[1] + ").to_i.should eq(1) + end + + it "codegens recursive type with union" do + run(" + class A + def next=(n) + @next = n + end + + def next + @next + end + end + + a = A.allocate + a.next = A.allocate + a = a.next + ") + end + + it "codegens with related types" do + run(" + class A + def next=(n) + @next = n + end + + def next + @next + end + end + + class B + def next=(n) + @next = n + end + + def next + @next + end + end + + def foo(x, y) + if n = x.next + n.next = y + end + end + + a = A.allocate + a.next = B.allocate + + foo(a, B.allocate) + + c = A.allocate + c.next = B.allocate + + foo(c, c.next) + ") + end + + it "codegens and doesn't break if obj is int and there's a mutation" do + run(" + require \"bootstrap\" + + class Int + def baz(x) + end + end + + elems = [1] + elems[0].baz [1] + ") + end + + it "codegens with and witout default arguments" do + run(" + def foo(x = 1) + x + 1 + end + + foo(2) + foo + ").to_i.should eq(5) + end + + it "codegens with and witout many default arguments" do + run(" + def foo(x = 1, y = 2, z = 3) + x + y + z + end + + foo + foo(9) + foo(3, 4) + foo(6, 3, 1) + ").to_i.should eq(40) + end + + it "codegens with interesting default argument" do + run(" + class Foo + def foo(x = self.bar) + x + 1 + end + + def bar + 1 + end + end + + f = Foo.new + + f.foo(2) + f.foo + ").to_i.should eq(5) + end + + it "codegens dispatch on static method" do + run(" + def Object.foo(x) 1 end - foo + a = 1 + a = 1.5 + Object.foo(a) ").to_i.should eq(1) end - it "codegens def with argument" do + it "use target def type as return type" do run(" - def foo(x) - x + require \"nil\" + require \"value\" + require \"object\" + + def foo + if false + return 0 + end end - foo(1) - ").to_i.should eq(1) + foo.nil? ? 1 : 0 + ").to_i.should eq(1) + end + + it "codegens recursive nasty code" do + run(" + class Foo + def initialize(x) + end + end + + class Bar + def initialize(x) + end + end + + class Box + def set(elem) + @elem = elem + end + + def get + @elem + end + end + + def foo + exps = Box.new + sub = foo + t = Foo.new(sub) || Bar.new(sub) + exps.set t + exps.get || 1 + end + + false && foo + ") end - it "codegens class def" do + it "looks up matches in super classes and merges them with subclasses" do run(" class Foo - def self.foo + def foo(other) 1 end end - Foo.foo - ").to_i.should eq(1) + class Bar < Foo + def foo(other : Int) + 2 + end + end + + bar1 = Bar.new + bar1.foo(1 || 1.5) + ").to_i.should eq(2) end it "codegens def which changes type of arg" do diff --git a/bootstrap/spec/crystal/codegen/global_spec.cr b/bootstrap/spec/crystal/codegen/global_spec.cr new file mode 100644 index 0000000000000000000000000000000000000000..75e4c0b46b164d5f44d6e55a9a32ac517b271eca --- /dev/null +++ b/bootstrap/spec/crystal/codegen/global_spec.cr @@ -0,0 +1,20 @@ +#!/usr/bin/env bin/crystal -run +require "../../spec_helper" + +describe "Code gen: global" do + it "codegens global" do + run("$foo = 1; def foo; $foo = 2; end; foo; $foo").to_i.should eq(2) + end + + it "codegens global with union" do + run("$foo = 1; def foo; $foo = 2.5_f32; end; foo; $foo.to_f").to_f64.should eq(2.5) + end + + it "codegens global when not initialized" do + run("require \"nil\"; $foo.to_i").to_i.should eq(0) + end + + it "codegens global when not initialized" do + run("require \"nil\"; def foo; $foo = 2 if 1 == 2; end; foo; $foo.to_i").to_i.should eq(0) + end +end diff --git a/bootstrap/spec/crystal/type_inference/block_spec.cr b/bootstrap/spec/crystal/type_inference/block_spec.cr index 214a06ab4ec7111754c0d07f7c32824544e2b754..652e504bed1da99efa641359a8138ab39f7d150d 100755 --- a/bootstrap/spec/crystal/type_inference/block_spec.cr +++ b/bootstrap/spec/crystal/type_inference/block_spec.cr @@ -73,34 +73,34 @@ describe "Block inference" do ") { int32 } end - # it "infer type with union" do - # assert_type(%q( - # require "int" - # require "pointer" - # require "array" - # a = [1] || [1.1] - # a.each { |x| x } - # )) { union_of(array_of(int32), array_of(float64)) } - # end - - # it "break from block without value" do - # assert_type(%q( - # def foo; yield; end - - # foo do - # break - # end - # )) { self.nil } - # end - - # it "break without value has nil type" do - # assert_type(%q( - # def foo; yield; 1; end - # foo do - # break if false - # end - # )) { union_of(self.nil, int32) } - # end + it "infer type with union" do + assert_type(" + require \"int\" + require \"pointer\" + require \"array\" + a = [1] || [1.1] + a.each { |x| x } + ") { union_of(array_of(int32), array_of(float64)) } + end + + it "break from block without value" do + assert_type(" + def foo; yield; end + + foo do + break + end + ") { |mod| mod.nil } + end + + it "break without value has nil type" do + assert_type(" + def foo; yield; 1; end + foo do + break if false + end + ") { |mod| union_of(mod.nil, int32) } + end it "infers type of block before call" do result = assert_type(" @@ -324,45 +324,45 @@ describe "Block inference" do ") { |mod| mod.nil } end - # it "preserves type filters in block" do - # assert_type(%q( - # class Foo - # def bar - # 'a' - # end - # end - - # def foo - # yield 1 - # end - - # a = Foo.new || nil - # if a - # foo do |x| - # a.bar - # end - # else - # 'b' - # end - # )) { char } - # end - - # it "checks block type with hierarchy type" do - # assert_type(%q( - # require "prelude" - - # class A - # end - - # class B < A - # end - - # a = [] of A - # a << B.new - - # a.map { |x| x.to_s } - - # 1 - # )) { int32 } - # end + it "preserves type filters in block" do + assert_type(" + class Foo + def bar + 'a' + end + end + + def foo + yield 1 + end + + a = Foo.new || nil + if a + foo do |x| + a.bar + end + else + 'b' + end + ") { char } + end + + it "checks block type with hierarchy type" do + assert_type(" + require \"bootstrap\" + + class A + end + + class B < A + end + + a = [] of A + a << B.new + + a.map { |x| x.to_s } + + 1 + ") { int32 } + end end diff --git a/spec/codegen/block_spec.rb b/spec/codegen/block_spec.rb index c2524dadc88fca9b1084fab4f09e389ceb23d047..335f065171bc6a810e5b29139b04f8b641f0c559 100644 --- a/spec/codegen/block_spec.rb +++ b/spec/codegen/block_spec.rb @@ -349,7 +349,7 @@ describe 'Code gen: block' do end x = foo do - break if true + break if 1 == 1 end x.nil?