File tree 3 files changed +74
-3
lines changed
3 files changed +74
-3
lines changed Original file line number Diff line number Diff line change @@ -994,4 +994,52 @@ describe "Semantic: generic class" do
994
994
) ,
995
995
" private method 'new' called"
996
996
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
997
1045
end
Original file line number Diff line number Diff line change @@ -166,7 +166,17 @@ module Crystal
166
166
node.target_const = type
167
167
node.bind_to type .value
168
168
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
170
180
when ASTNode
171
181
type .accept self unless type .type?
172
182
node.syntax_replacement = type
Original file line number Diff line number Diff line change @@ -226,8 +226,17 @@ module Crystal
226
226
false
227
227
end
228
228
229
+ # Returns the non-virtual type of a given type
230
+ # (returns self if self is already non-virtual)
229
231
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
231
240
end
232
241
233
242
def implements ?(other_type : Type )
@@ -2796,6 +2805,7 @@ module Crystal
2796
2805
include InstanceVarContainer
2797
2806
include ClassVarContainer
2798
2807
2808
+ # Given `Foo+`, this returns `Foo`.
2799
2809
getter base_type : Type
2800
2810
2801
2811
def initialize (program, @base_type )
@@ -2898,7 +2908,10 @@ module Crystal
2898
2908
instance_type.leaf?
2899
2909
end
2900
2910
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
2902
2915
2903
2916
def each_concrete_type
2904
2917
instance_type.subtypes.each do |type |
You can’t perform that action at this time.
0 commit comments