Skip to content

Commit 842548e

Browse files
makenowjustRX14
authored andcommittedSep 1, 2017
Allow assignment to class var when fit this class var types of ancestors (#4871)
Fix #4869
1 parent 4d9aed0 commit 842548e

File tree

2 files changed

+32
-7
lines changed

2 files changed

+32
-7
lines changed
 

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

+18
Original file line numberDiff line numberDiff line change
@@ -497,4 +497,22 @@ describe "Semantic: class var" do
497497
),
498498
"class variable '@@foo' of Foo is not nilable (it's Int32) so it must have an initializer"
499499
end
500+
501+
it "can assign to class variable if this type can be up-casted to ancestors class variable type (#4869)" do
502+
assert_type(%(
503+
class Foo
504+
@@x : Int32?
505+
506+
def self.x
507+
@@x
508+
end
509+
end
510+
511+
class Bar < Foo
512+
@@x = 42
513+
end
514+
515+
Bar.x
516+
)) { nilable(int32) }
517+
end
500518
end

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

+14-7
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ struct Crystal::TypeDeclarationProcessor
135135
# Process class variables
136136
type_guess_visitor.class_vars.each do |owner, vars|
137137
vars.each do |name, info|
138-
declare_meta_type_var(owner.class_vars, owner, name, info)
138+
# No need to freeze its type because it is frozen by check_class_var_errors
139+
declare_meta_type_var(owner.class_vars, owner, name, info, freeze_type: false)
139140
end
140141
end
141142

@@ -155,7 +156,7 @@ struct Crystal::TypeDeclarationProcessor
155156
{node, self}
156157
end
157158

158-
private def declare_meta_type_var(vars, owner, name, type : Type, location : Location? = nil, instance_var = false)
159+
private def declare_meta_type_var(vars, owner, name, type : Type, location : Location? = nil, instance_var = false, freeze_type = true)
159160
if instance_var && location && !owner.allows_instance_vars?
160161
raise_cant_declare_instance_var(owner, location)
161162
end
@@ -174,24 +175,24 @@ struct Crystal::TypeDeclarationProcessor
174175
var.owner = owner
175176
var.type = type
176177
var.bind_to(var)
177-
var.freeze_type = type
178+
var.freeze_type = type if freeze_type
178179
var.location = location
179180
vars[name] = var
180181
var
181182
end
182183

183-
private def declare_meta_type_var(vars, owner, name, info : TypeGuessVisitor::TypeInfo)
184+
private def declare_meta_type_var(vars, owner, name, info : TypeGuessVisitor::TypeInfo, freeze_type = true)
184185
type = info.type
185186
type = Type.merge!(type, @program.nil) unless info.outside_def
186-
declare_meta_type_var(vars, owner, name, type)
187+
declare_meta_type_var(vars, owner, name, type, freeze_type: freeze_type)
187188
end
188189

189-
private def declare_meta_type_var(vars, owner, name, info : TypeDeclarationWithLocation, instance_var = false, check_nilable = true)
190+
private def declare_meta_type_var(vars, owner, name, info : TypeDeclarationWithLocation, instance_var = false, check_nilable = true, freeze_type = true)
190191
if instance_var && !owner.allows_instance_vars?
191192
raise_cant_declare_instance_var(owner, info.location)
192193
end
193194

194-
var = declare_meta_type_var(vars, owner, name, info.type.as(Type), info.location)
195+
var = declare_meta_type_var(vars, owner, name, info.type.as(Type), info.location, freeze_type: freeze_type)
195196
var.location = info.location
196197

197198
# Check if var is uninitialized
@@ -646,10 +647,16 @@ struct Crystal::TypeDeclarationProcessor
646647
ancestor_class_var = ancestor.lookup_class_var?(name)
647648
next unless ancestor_class_var
648649

650+
if owner_class_var.type.implements?(ancestor_class_var.type)
651+
owner_class_var.type = ancestor_class_var.type
652+
end
653+
649654
if owner_class_var.type != ancestor_class_var.type
650655
raise TypeException.new("class variable '#{name}' of #{owner} is already defined as #{ancestor_class_var.type} in #{ancestor}", info.location)
651656
end
652657
end
658+
659+
owner_class_var.freeze_type = owner_class_var.type
653660
end
654661
end
655662
end

0 commit comments

Comments
 (0)