Skip to content

Commit ecda856

Browse files
author
Ary Borenszweig
committedFeb 9, 2017
Compiler: always resolve T in main code as non-virtual. Fixes #3989
1 parent b0b4497 commit ecda856

File tree

3 files changed

+74
-3
lines changed

3 files changed

+74
-3
lines changed
 

‎spec/compiler/semantic/generic_class_spec.cr

+48
Original file line numberDiff line numberDiff line change
@@ -994,4 +994,52 @@ describe "Semantic: generic class" do
994994
),
995995
"private method 'new' called"
996996
end
997+
998+
it "never types Path as virtual outside generic type parameter (#3989)" do
999+
assert_type(%(
1000+
class Base
1001+
end
1002+
1003+
class Derived < Base
1004+
def initialize(x : Int32)
1005+
end
1006+
end
1007+
1008+
class Generic(T)
1009+
def initialize
1010+
T.new
1011+
end
1012+
1013+
def t
1014+
T
1015+
end
1016+
end
1017+
1018+
Generic(Base).new.t
1019+
)) { types["Base"].metaclass }
1020+
end
1021+
1022+
it "never types Generic as virtual outside generic type parameter (#3989)" do
1023+
assert_type(%(
1024+
class Base(T)
1025+
end
1026+
1027+
class Derived(T) < Base(T)
1028+
def initialize(x : Int32)
1029+
end
1030+
end
1031+
1032+
class Generic(T)
1033+
def initialize
1034+
T.new
1035+
end
1036+
1037+
def t
1038+
T
1039+
end
1040+
end
1041+
1042+
Generic(Base(Int32)).new.t
1043+
)) { generic_class("Base", int32).metaclass }
1044+
end
9971045
end

‎src/compiler/crystal/semantic/main_visitor.cr

+11-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,17 @@ module Crystal
166166
node.target_const = type
167167
node.bind_to type.value
168168
when Type
169-
node.type = check_type_in_type_args(type.remove_alias_if_simple)
169+
# We devirtualize the type because in an expression like
170+
#
171+
# T.new
172+
#
173+
# even if T is a virtual type that resulted from a generic
174+
# type argument, creating an instance or invoking methods
175+
# on the type itself don't need to resolve virtually.
176+
#
177+
# It's different if from a virtual type we do `v.class.new`
178+
# because the class could be any in the hierarchy.
179+
node.type = check_type_in_type_args(type.remove_alias_if_simple).devirtualize
170180
when ASTNode
171181
type.accept self unless type.type?
172182
node.syntax_replacement = type

‎src/compiler/crystal/types.cr

+15-2
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,17 @@ module Crystal
226226
false
227227
end
228228

229+
# Returns the non-virtual type of a given type
230+
# (returns self if self is already non-virtual)
229231
def devirtualize
230-
self.is_a?(VirtualTypeLookup) ? self.base_type : self
232+
case self
233+
when VirtualType
234+
self.base_type
235+
when VirtualMetaclassType
236+
self.base_type.metaclass
237+
else
238+
self
239+
end
231240
end
232241

233242
def implements?(other_type : Type)
@@ -2796,6 +2805,7 @@ module Crystal
27962805
include InstanceVarContainer
27972806
include ClassVarContainer
27982807

2808+
# Given `Foo+`, this returns `Foo`.
27992809
getter base_type : Type
28002810

28012811
def initialize(program, @base_type)
@@ -2898,7 +2908,10 @@ module Crystal
28982908
instance_type.leaf?
28992909
end
29002910

2901-
delegate base_type, lookup_first_def, to: instance_type
2911+
# Given `Foo+:Class` returns `Foo` (not `Foo:Class`)
2912+
delegate base_type, to: instance_type
2913+
2914+
delegate lookup_first_def, to: instance_type.metaclass
29022915

29032916
def each_concrete_type
29042917
instance_type.subtypes.each do |type|

0 commit comments

Comments
 (0)
Please sign in to comment.