Skip to content

Commit

Permalink
Compiler: evaluate instance var initializers at the metaclass level
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite committed Jul 20, 2018
1 parent 6ab9c60 commit 9ae5664
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 6 deletions.
38 changes: 37 additions & 1 deletion spec/compiler/codegen/class_spec.cr
Expand Up @@ -646,7 +646,7 @@ describe "Code gen: class" do
it "doesn't crash on instance variable assigned a proc, and never instantiated (#923)" do
codegen(%(
class Klass
def f(arg)
def self.f(arg)
end
@a : Proc(String, Nil) = ->f(String)
Expand Down Expand Up @@ -1031,4 +1031,40 @@ describe "Code gen: class" do
foo.x
), inject_primitives: false).to_i.should eq(1)
end

it "runs instance variable initializer at the class level" do
run(%(
class Foo
@x : Int32 = bar
def self.bar
42
end
def x
@x
end
end
Foo.new.x
)).to_i.should eq(42)
end

it "runs instance variable initializer at the class level, for generic type" do
run(%(
class Foo(T)
@x : T = bar
def self.bar
42
end
def x
@x
end
end
Foo(Int32).new.x
)).to_i.should eq(42)
end
end
67 changes: 67 additions & 0 deletions spec/compiler/semantic/instance_var_spec.cr
Expand Up @@ -5042,4 +5042,71 @@ describe "Semantic: instance var" do
Second.new.hash
)) { hash_of(hash_of(int32, int32).virtual_type, int32).virtual_type }
end

it "doesn't solve instance var initializer in instance context (1) (#5876)" do
assert_error %(
class Foo
@x : Int32 = bar
def bar
1
end
end
Foo.new
),
"undefined local variable or method 'bar'"
end

it "doesn't solve instance var initializer in instance context (2) (#5876)" do
assert_error %(
class Foo(T)
@x : T = bar
def bar
1
end
end
Foo(Int32).new
),
"undefined local variable or method 'bar'"
end

it "doesn't solve instance var initializer in instance context (3) (#5876)" do
assert_error %(
module Moo(T)
@x : T = bar
def bar
1
end
end
class Foo
include Moo(Int32)
end
Foo.new
),
"undefined local variable or method 'bar'"
end

it "solves instance var initializer in metaclass context (#5876)" do
assert_type(%(
class Foo
@x : Int32 = bar
def self.bar
1
end
def x
@x
end
end
Foo.new.x
)) { int32 }
end
end
3 changes: 1 addition & 2 deletions src/compiler/crystal/codegen/codegen.cr
Expand Up @@ -1831,9 +1831,8 @@ module Crystal
with_cloned_context do
# Instance var initializers must run with "self"
# properly set up to the type being allocated
context.type = real_type
context.type = real_type.metaclass
context.vars = LLVMVars.new
context.vars["self"] = LLVMVar.new(type_ptr, real_type)
alloca_vars init.meta_vars

accept init.value
Expand Down
Expand Up @@ -103,7 +103,7 @@ class Crystal::InstanceVarsInitializerVisitor < Crystal::SemanticVisitor
end

ivar_visitor = MainVisitor.new(program, meta_vars: i.meta_vars)
ivar_visitor.scope = scope
ivar_visitor.scope = scope.metaclass
value.accept ivar_visitor
end
end
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/crystal/types.cr
Expand Up @@ -945,7 +945,7 @@ module Crystal
if !meta_vars && !self.is_a?(GenericType)
meta_vars = MetaVars.new
visitor = MainVisitor.new(program, vars: meta_vars, meta_vars: meta_vars)
visitor.scope = self
visitor.scope = self.metaclass
value = value.clone
value.accept visitor
end
Expand Down Expand Up @@ -1460,7 +1460,7 @@ module Crystal
def run_instance_var_initializer(initializer, instance : GenericClassInstanceType | NonGenericClassType)
meta_vars = MetaVars.new
visitor = MainVisitor.new(program, vars: meta_vars, meta_vars: meta_vars)
visitor.scope = instance
visitor.scope = instance.metaclass
value = initializer.value.clone
value.accept visitor
instance_var = instance.lookup_instance_var(initializer.name)
Expand Down

0 comments on commit 9ae5664

Please sign in to comment.