Skip to content

Commit

Permalink
Showing 5 changed files with 65 additions and 23 deletions.
28 changes: 28 additions & 0 deletions spec/compiler/type_inference/class_var_spec.cr
Original file line number Diff line number Diff line change
@@ -431,4 +431,32 @@ describe "Type inference: class var" do
Foo.x
)) { int32 }
end

it "doesn't error if accessing class variable before defined (#2941)" do
assert_type(%(
class Bar
@@x : Baz = Foo.x
def self.x
@@x
end
end
class Foo
@@x = Baz.new
def self.x
@@x
end
end
class Baz
def y
1
end
end
Bar.x.y
)) { int32 }
end
end
16 changes: 16 additions & 0 deletions spec/compiler/type_inference/uninitialized_spec.cr
Original file line number Diff line number Diff line change
@@ -88,6 +88,22 @@ describe "Type inference: uninitialized" do
)) { int32 }
end

it "can use uninitialized with class type (#2940)" do
assert_type(%(
class Foo(U)
def initialize
@x = uninitialized U
end
def x
@x
end
end
Foo(Int32.class).new.x
)) { int32.metaclass }
end

%w(Object Value Reference Number Int Float Struct Class Enum).each do |type|
it "disallows declaring var of type #{type}" do
assert_error %(
12 changes: 0 additions & 12 deletions src/compiler/crystal/semantic/base_type_visitor.cr
Original file line number Diff line number Diff line change
@@ -895,18 +895,6 @@ module Crystal
call_convention
end

def check_declare_var_type(node)
type = node.declared_type.type.instance_type

if type.is_a?(GenericClassType)
node.raise "can't declare variable of generic non-instantiated type #{type}"
end

Crystal.check_type_allowed_in_generics(node, type, "can't use #{type} as a Proc argument type")

type
end

def check_declare_var_type(node, declared_type, variable_kind)
type = declared_type.instance_type

Original file line number Diff line number Diff line change
@@ -37,6 +37,14 @@ module Crystal
simple_vars, complex_vars = class_var_initializers.partition &.node.simple_literal?
class_var_initializers = simple_vars + complex_vars

# Next assign their initializer, so we know which are initialized
# and shouldn't raise an error when trying to accessing them
# before they are defined
class_var_initializers.each do |initializer|
class_var = initializer.owner.class_vars[initializer.name]?
class_var.initializer = initializer if class_var
end

# Now type them
class_var_initializers.each do |initializer|
owner = initializer.owner
24 changes: 13 additions & 11 deletions src/compiler/crystal/semantic/main_visitor.cr
Original file line number Diff line number Diff line change
@@ -210,9 +210,11 @@ module Crystal
var.raise "variable '#{var.name}' already declared"
end

@in_type_args += 1
node.declared_type.accept self
@in_type_args -= 1

var_type = check_declare_var_type node
var_type = check_declare_var_type node, node.declared_type.type, "a variable"
var.type = var_type

meta_var = @meta_vars[var.name] ||= new_meta_var(var.name)
@@ -229,16 +231,15 @@ module Crystal
when InstanceVar
type = scope? || current_type
if @untyped_def
@in_type_args += 1
node.declared_type.accept self
@in_type_args -= 1

var_type = check_declare_var_type node

ivar = lookup_instance_var var
ivar.type = var_type
var.type = var_type
check_declare_var_type node, node.declared_type.type, "an instance variable"
ivar = lookup_instance_var(var, type)

if @is_initialize
@vars[var.name] = MetaVar.new(var.name, var_type)
@vars[var.name] = MetaVar.new(var.name, ivar.type)
end
else
# Already handled in a previous visitor
@@ -248,11 +249,12 @@ module Crystal

case type
when NonGenericClassType
@in_type_args += 1
node.declared_type.accept self
var_type = check_declare_var_type node
type.declare_instance_var(var.name, var_type)
@in_type_args -= 1
check_declare_var_type node, node.declared_type.type, "an instance variable"
when GenericClassType
type.declare_instance_var(var.name, node.declared_type)
# OK
when GenericClassInstanceType
# OK
else
@@ -433,7 +435,7 @@ module Crystal
raise_recursive_dependency node, var
end

if first_time_accessing_meta_type_var?(var)
if !var.initializer && first_time_accessing_meta_type_var?(var)
var_type = var.type?
if var_type && !var_type.includes_type?(mod.nil)
node.raise "class variable '#{var.name}' of #{var.owner} is read here before it was initialized, rendering it nilable, but its type is #{var_type}"

0 comments on commit 56b1890

Please sign in to comment.