Skip to content

Commit a82ef1b

Browse files
author
Ary Borenszweig
committedMar 4, 2017
Compiler: declare all instance vars initializers before typing them. Fixes #3988
1 parent f1f3892 commit a82ef1b

File tree

3 files changed

+77
-16
lines changed

3 files changed

+77
-16
lines changed
 

Diff for: ‎spec/compiler/semantic/instance_var_spec.cr

+45
Original file line numberDiff line numberDiff line change
@@ -4640,4 +4640,49 @@ describe "Semantic: instance var" do
46404640
),
46414641
"Can't infer the type of instance variable '@baz' of Foo"
46424642
end
4643+
4644+
it "instance variables initializers are used in class variables initialized objects (#3988)" do
4645+
assert_type(%(
4646+
class Foo
4647+
@@foo = Foo.new
4648+
4649+
@never_nil = 1
4650+
4651+
def initialize
4652+
if false
4653+
@never_nil = 2
4654+
end
4655+
end
4656+
end
4657+
4658+
Foo.new.@never_nil
4659+
)) { int32 }
4660+
end
4661+
4662+
it "allow usage of instance variable initializer from instance variable initializer" do
4663+
assert_type(%(
4664+
class Foo
4665+
@bar = Bar.new
4666+
@never_nil = 1
4667+
4668+
def initialize
4669+
if false
4670+
@never_nil = 2
4671+
end
4672+
end
4673+
end
4674+
4675+
class Bar
4676+
@never_nil = 1
4677+
4678+
def initialize
4679+
if false
4680+
@never_nil = 2
4681+
end
4682+
end
4683+
end
4684+
4685+
{Foo.new.@never_nil, Bar.new.@never_nil}
4686+
)) { tuple_of([int32, int32]) }
4687+
end
46434688
end

Diff for: ‎src/compiler/crystal/semantic.cr

+6-5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ class Crystal::Program
2121
def semantic(node : ASTNode, cleanup = true) : ASTNode
2222
node, processor = top_level_semantic(node)
2323

24+
Crystal.timing("Semantic (ivars initializers)", @wants_stats) do
25+
visitor = InstanceVarsInitializerVisitor.new(self)
26+
visit_with_finished_hooks(node, visitor)
27+
visitor.finish
28+
end
29+
2430
Crystal.timing("Semantic (cvars initializers)", @wants_stats) do
2531
visit_class_vars_initializers(node)
2632
end
@@ -29,11 +35,6 @@ class Crystal::Program
2935
# give an error otherwise
3036
processor.check_non_nilable_class_vars_without_initializers
3137

32-
Crystal.timing("Semantic (ivars initializers)", @wants_stats) do
33-
visitor = InstanceVarsInitializerVisitor.new(self)
34-
visit_with_finished_hooks(node, visitor)
35-
end
36-
3738
result = Crystal.timing("Semantic (main)", @wants_stats) do
3839
visit_main(node, process_finished_hooks: true, cleanup: cleanup)
3940
end

Diff for: ‎src/compiler/crystal/semantic/instance_vars_initializer_visitor.cr

+26-11
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ require "./semantic_visitor"
2626
# end
2727
# ```
2828
class Crystal::InstanceVarsInitializerVisitor < Crystal::SemanticVisitor
29+
record Initializer, scope : Type, target : InstanceVar, value : ASTNode, meta_vars : MetaVars
30+
getter initializers = [] of Initializer
31+
2932
def visit_any(node)
3033
case node
3134
when Assign
@@ -60,21 +63,33 @@ class Crystal::InstanceVarsInitializerVisitor < Crystal::SemanticVisitor
6063
when Program, FileModule
6164
node.raise "can't use instance variables at the top level"
6265
when ClassType, NonGenericModuleType, GenericModuleType
63-
meta_vars = MetaVars.new
64-
ivar_visitor = MainVisitor.new(program, meta_vars: meta_vars)
65-
ivar_visitor.scope = current_type
66+
initializers << Initializer.new(current_type, target, value, MetaVars.new)
67+
node.type = @program.nil
68+
return
69+
end
70+
end
6671

67-
unless current_type.is_a?(GenericType)
68-
value.accept ivar_visitor
72+
def finish
73+
# First declare them, so when we type all of them we will have
74+
# the info of which instance vars have initializers (so they are not nil)
75+
initializers.each do |i|
76+
scope = i.scope
77+
unless scope.lookup_instance_var?(i.target.name)
78+
program.undefined_instance_variable(i.target, scope, nil)
6979
end
7080

71-
unless current_type.lookup_instance_var?(target.name)
72-
ivar_visitor.undefined_instance_variable(current_type, target)
73-
end
81+
scope.add_instance_var_initializer(i.target.name, i.value, scope.is_a?(GenericType) ? nil : i.meta_vars)
82+
end
7483

75-
current_type.add_instance_var_initializer(target.name, value, current_type.is_a?(GenericType) ? nil : meta_vars)
76-
node.type = @program.nil
77-
return
84+
# Now type them
85+
initializers.each do |i|
86+
scope = i.scope
87+
88+
unless scope.is_a?(GenericType)
89+
ivar_visitor = MainVisitor.new(program, meta_vars: i.meta_vars)
90+
ivar_visitor.scope = scope
91+
i.value.accept ivar_visitor
92+
end
7893
end
7994
end
8095
end

0 commit comments

Comments
 (0)
Please sign in to comment.