From 57bf99a1648a27ebe460bd227a05dde8d186840a Mon Sep 17 00:00:00 2001
From: Ary Borenszweig <aborenszweig@manas.com.ar>
Date: Thu, 18 Sep 2014 09:25:18 -0300
Subject: [PATCH] Fixed #210: Error calling a proc with an argument of module
 type

---
 spec/compiler/codegen/fun_spec.cr          | 10 +++++
 spec/compiler/codegen/module_spec.cr       | 43 ++++++++++++++++++++++
 src/compiler/crystal/codegen/cast.cr       |  4 ++
 src/compiler/crystal/codegen/primitives.cr |  8 +++-
 4 files changed, 64 insertions(+), 1 deletion(-)

diff --git a/spec/compiler/codegen/fun_spec.cr b/spec/compiler/codegen/fun_spec.cr
index 96af3f79b4..7879c04cd1 100644
--- a/spec/compiler/codegen/fun_spec.cr
+++ b/spec/compiler/codegen/fun_spec.cr
@@ -475,4 +475,14 @@ describe "Code gen: fun" do
       foo { }
       )).to_i.should eq(1)
   end
+
+  it "codegens fun with union type that returns itself" do
+    run(%(
+      a = 1 || 1.5
+
+      foo = ->(x : Int32 | Float64) { x }
+      foo.call(a)
+      foo.call(a).to_i
+      )).to_i.should eq(1)
+  end
 end
diff --git a/spec/compiler/codegen/module_spec.cr b/spec/compiler/codegen/module_spec.cr
index 524833461b..0f56411381 100644
--- a/spec/compiler/codegen/module_spec.cr
+++ b/spec/compiler/codegen/module_spec.cr
@@ -184,4 +184,47 @@ describe "Code gen: module" do
       p.value.foo
       )).to_i.should eq(2)
   end
+
+  it "declares proc with module type" do
+    run(%(
+      module Moo
+        def moo
+          1
+        end
+      end
+
+      class Foo
+        include Moo
+      end
+
+      class Bar
+        include Moo
+      end
+
+      foo = ->(x : Moo) { x.moo }
+      foo.call(Bar.new)
+      )).to_i.should eq(1)
+  end
+
+  it "declares proc with module type and invoke it with two different types that return themselves" do
+    build(%(
+      module Moo
+        def moo
+          1
+        end
+      end
+
+      class Foo
+        include Moo
+      end
+
+      struct Bar
+        include Moo
+      end
+
+      foo = ->(x : Moo) { x }
+      foo.call(Foo.new)
+      foo.call(Bar.new)
+      ))
+  end
 end
diff --git a/src/compiler/crystal/codegen/cast.cr b/src/compiler/crystal/codegen/cast.cr
index 0907b97628..8cb604aa41 100644
--- a/src/compiler/crystal/codegen/cast.cr
+++ b/src/compiler/crystal/codegen/cast.cr
@@ -344,6 +344,10 @@ class Crystal::CodeGenVisitor < Crystal::Visitor
     value
   end
 
+  def upcast_distinct(value, to_type : NonGenericModuleType, from_type : Type)
+    upcast_distinct value, to_type.including_types.not_nil!, from_type
+  end
+
   def upcast_distinct(value, to_type : Type, from_type : Type)
     raise "Bug: trying to upcast #{to_type} <- #{from_type}"
   end
diff --git a/src/compiler/crystal/codegen/primitives.cr b/src/compiler/crystal/codegen/primitives.cr
index eef4e420c6..c48f66bfa3 100644
--- a/src/compiler/crystal/codegen/primitives.cr
+++ b/src/compiler/crystal/codegen/primitives.cr
@@ -434,7 +434,10 @@ class Crystal::CodeGenVisitor < Crystal::Visitor
     ctx_is_null = equal? ctx_ptr, LLVM.null(LLVM::VoidPointer)
     cond ctx_is_null, ctx_is_null_block, ctx_is_not_null_block
 
-    Phi.open(self, node, true) do |phi|
+    old_needs_value = @needs_value
+    @needs_value = true
+
+    phi_value = Phi.open(self, node, @needs_value) do |phi|
       position_at_end ctx_is_null_block
       real_fun_ptr = bit_cast fun_ptr, llvm_fun_type(context.type)
       value = codegen_call_or_invoke(node, target_def, nil, real_fun_ptr, args, true, target_def.type, false, fun_type)
@@ -446,6 +449,9 @@ class Crystal::CodeGenVisitor < Crystal::Visitor
       value = codegen_call_or_invoke(node, target_def, nil, real_fun_ptr, args, true, target_def.type, true, fun_type)
       phi.add value, node.type, true
     end
+
+    old_needs_value = @needs_value
+    phi_value
   end
 
   def codegen_primitive_fun_closure(node, target_def, call_args)
-- 
GitLab