From 9c265fded272eeba9418602dfce0e593208c376c Mon Sep 17 00:00:00 2001
From: Ary Borenszweig <aborenszweig@manas.com.ar>
Date: Mon, 17 Mar 2014 08:26:14 -0300
Subject: [PATCH] Fixed type lookup: types were looked up from "self", but they
 should be looked up from the matching def.

---
 spec/compiler/codegen/const_spec.cr           | 18 ++++++++++++++-
 spec/compiler/type_inference/module_spec.cr   | 22 +++++++++++++++++++
 src/compiler/crystal/type_inference.cr        | 11 ++++++----
 src/compiler/crystal/type_inference/call.cr   |  7 +++---
 .../crystal/type_inference/type_lookup.cr     |  9 ++++++++
 .../type_inference/type_visitor_helper.cr     |  2 +-
 6 files changed, 59 insertions(+), 10 deletions(-)

diff --git a/spec/compiler/codegen/const_spec.cr b/spec/compiler/codegen/const_spec.cr
index a674256fa3..3ab20f1440 100644
--- a/spec/compiler/codegen/const_spec.cr
+++ b/spec/compiler/codegen/const_spec.cr
@@ -91,7 +91,23 @@ describe "Codegen: const" do
   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)
+    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
diff --git a/spec/compiler/type_inference/module_spec.cr b/spec/compiler/type_inference/module_spec.cr
index a62e669204..9760626eea 100755
--- a/spec/compiler/type_inference/module_spec.cr
+++ b/spec/compiler/type_inference/module_spec.cr
@@ -361,4 +361,26 @@ describe "Type inference: module" do
       end
       ", "cyclic include detected"
   end
+
+  it "finds types close to included module" do
+    assert_type("
+      module Foo
+        class T
+        end
+
+        def foo
+          T
+        end
+      end
+
+      class Bar
+        class T
+        end
+
+        include Foo
+      end
+
+      Bar.new.foo
+      ") { types["Foo"].types["T"].metaclass }
+  end
 end
diff --git a/src/compiler/crystal/type_inference.cr b/src/compiler/crystal/type_inference.cr
index d41b626981..3b354b6fc6 100644
--- a/src/compiler/crystal/type_inference.cr
+++ b/src/compiler/crystal/type_inference.cr
@@ -21,6 +21,7 @@ module Crystal
     getter! untyped_def
     getter block
     getter vars
+    property type_lookup
     property in_fun_literal
 
     def initialize(@mod, @vars = {} of String => Var, @scope = nil, @parent = nil, @call = nil, @owner = nil, @untyped_def = nil, @typed_def = nil, @arg_types = nil, @free_vars = nil, @yield_vars = nil, @type_filter_stack = [nil] of Hash(String, TypeFilter)?)
@@ -268,7 +269,7 @@ module Crystal
 
       target.bind_to value
 
-      current_type.types[target.names.first] = Const.new(@mod, current_type, target.names.first, value, @types.clone, @scope)
+      current_type.types[target.names.first] = Const.new(@mod, current_type, target.names.first, value, @types.dup, @scope)
 
       node.type = @mod.nil
     end
@@ -383,6 +384,7 @@ module Crystal
       pushing_type_filters do
         block_visitor = TypeVisitor.new(mod, block_vars, (node.scope || @scope), @parent, @call, @owner, @untyped_def, @typed_def, @arg_types, @free_vars, @yield_vars, @type_filter_stack)
         block_visitor.block = node
+        block_visitor.type_lookup = type_lookup
         node.body.accept block_visitor
       end
 
@@ -408,6 +410,7 @@ module Crystal
 
       block_visitor = TypeVisitor.new(mod, fun_vars, @scope, @parent, @call, @owner, node.def, node.def, @arg_types, @free_vars, @yield_vars, @type_filter_stack)
       block_visitor.in_fun_literal = true
+      block_visitor.type_lookup = type_lookup
       node.def.body.accept block_visitor
 
       false
@@ -695,10 +698,10 @@ module Crystal
       case type
       when Const
         unless type.value.type?
-          old_types, old_scope, old_vars = @types, @scope, @vars
-          @types, @scope, @vars = type.scope_types, type.scope, ({} of String => Var)
+          old_types, old_scope, old_vars, old_type_lookup = @types, @scope, @vars, @type_lookup
+          @types, @scope, @vars, @type_lookup = type.scope_types, type.scope, ({} of String => Var), nil
           type.value.accept self
-          @types, @scope, @vars = old_types, old_scope, old_vars
+          @types, @scope, @vars, @type_lookup = old_types, old_scope, old_vars, old_type_lookup
         end
         node.target_const = type
         node.bind_to type.value
diff --git a/src/compiler/crystal/type_inference/call.cr b/src/compiler/crystal/type_inference/call.cr
index 80b82a463d..83e51deb38 100644
--- a/src/compiler/crystal/type_inference/call.cr
+++ b/src/compiler/crystal/type_inference/call.cr
@@ -147,13 +147,12 @@ module Crystal
         match_owner = match.owner
         typed_def = match_owner.lookup_def_instance(match.def.object_id, lookup_arg_types, block_type) if use_cache
         unless typed_def
-          prepared_typed_def = prepare_typed_def_with_args(match.def, match_owner, lookup_self_type, match.arg_types, fun_literal)
-          typed_def = prepared_typed_def.typed_def
-          typed_def_args = prepared_typed_def.args
+          typed_def, typed_def_args = prepare_typed_def_with_args(match.def, match_owner, lookup_self_type, match.arg_types, fun_literal)
           match_owner.add_def_instance(match.def.object_id, lookup_arg_types, block_type, typed_def) if use_cache
           if typed_def.body
             bubbling_exception do
               visitor = TypeVisitor.new(mod, typed_def_args, lookup_self_type, parent_visitor, self, owner, match.def, typed_def, match.arg_types, match.free_vars, yield_vars)
+              visitor.type_lookup = match.type_lookup
               typed_def.body.accept visitor
             end
           end
@@ -756,7 +755,7 @@ module Crystal
         args[var.name] = var
       end
 
-      PreparedTypedDef.new(typed_def, args)
+      {typed_def, args}
     end
   end
 end
diff --git a/src/compiler/crystal/type_inference/type_lookup.cr b/src/compiler/crystal/type_inference/type_lookup.cr
index 8497de7655..73a5bf3e15 100644
--- a/src/compiler/crystal/type_inference/type_lookup.cr
+++ b/src/compiler/crystal/type_inference/type_lookup.cr
@@ -87,5 +87,14 @@ module Crystal
       @type = @root
       false
     end
+
+    def visit(node : TypeOf)
+      visitor = TypeVisitor.new(@root.program, {"self" => Var.new("self", @root.instance_type)})
+      node.expressions.each do |exp|
+        exp.accept visitor
+      end
+      @type = @root.program.type_merge(node.expressions.map &.type)
+      false
+    end
   end
 end
diff --git a/src/compiler/crystal/type_inference/type_visitor_helper.cr b/src/compiler/crystal/type_inference/type_visitor_helper.cr
index 82e3719a7b..d9bd1626a2 100644
--- a/src/compiler/crystal/type_inference/type_visitor_helper.cr
+++ b/src/compiler/crystal/type_inference/type_visitor_helper.cr
@@ -411,7 +411,7 @@ module Crystal
           target_type = type.not_nil!.lookup_type(node.names[1 .. -1])
         end
       else
-        base_lookup = node.global ? mod : (@scope || @types.last)
+        base_lookup = node.global ? mod : (@type_lookup || @scope || @types.last)
         target_type = base_lookup.lookup_type node
 
         unless target_type
-- 
GitLab