@@ -117,7 +117,7 @@ def create_generic_type(constant, name)
117
117
# the generic class `Foo[Bar]` is still a `Foo`. That is:
118
118
# `Foo[Bar].new.is_a?(Foo)` should be true, which isn't the case
119
119
# if we just clone the class. But subclassing works just fine.
120
- create_safe_subclass ( constant )
120
+ create_safe_subclass ( constant , name )
121
121
else
122
122
# This can only be a module and it is fine to just clone modules
123
123
# since they can't have instances and will not have `is_a?` relationships.
@@ -151,21 +151,30 @@ def create_generic_type(constant, name)
151
151
generic_type
152
152
end
153
153
154
- sig { params ( constant : T ::Class [ T . anything ] ) . returns ( T ::Class [ T . anything ] ) }
155
- def create_safe_subclass ( constant )
154
+ sig { params ( constant : T ::Class [ T . anything ] , name : String ) . returns ( T ::Class [ T . anything ] ) }
155
+ def create_safe_subclass ( constant , name )
156
156
# Lookup the "inherited" class method
157
157
inherited_method = constant . method ( :inherited )
158
158
# and the module that defines it
159
159
owner = inherited_method . owner
160
160
161
- # If no one has overriden the inherited method yet, just subclass
161
+ # If no one has overridden the inherited method yet, just subclass
162
162
return Class . new ( constant ) if Class == owner
163
163
164
+ # Capture this Hash locally, to mutate it in the `inherited` callback below.
165
+ generic_instances = @generic_instances
166
+
164
167
begin
165
168
# Otherwise, some inherited method could be preventing us
166
169
# from creating subclasses, so let's override it and rescue
167
- owner . send ( :define_method , :inherited ) do |s |
168
- inherited_method . call ( s )
170
+ owner . send ( :define_method , :inherited ) do |new_subclass |
171
+ # Register this new subclass ASAP, to prevent re-entry into the `create_safe_subclass` code-path.
172
+ # This can happen if the sig of the original `.inherited` method references the generic type itself.
173
+ generic_instances [ name ] ||= new_subclass
174
+
175
+ # Call the original `.inherited` method, but rescue any errors that might be raised,
176
+ # which would have otherwise prevented our subclass from being created.
177
+ inherited_method . call ( new_subclass )
169
178
rescue
170
179
# Ignoring errors
171
180
end
0 commit comments