@@ -117,7 +117,7 @@ def create_generic_type(constant, name)
117117 # the generic class `Foo[Bar]` is still a `Foo`. That is:
118118 # `Foo[Bar].new.is_a?(Foo)` should be true, which isn't the case
119119 # if we just clone the class. But subclassing works just fine.
120- create_safe_subclass ( constant )
120+ create_safe_subclass ( constant , name )
121121 else
122122 # This can only be a module and it is fine to just clone modules
123123 # since they can't have instances and will not have `is_a?` relationships.
@@ -151,21 +151,30 @@ def create_generic_type(constant, name)
151151 generic_type
152152 end
153153
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 )
156156 # Lookup the "inherited" class method
157157 inherited_method = constant . method ( :inherited )
158158 # and the module that defines it
159159 owner = inherited_method . owner
160160
161- # If no one has overriden the inherited method yet, just subclass
161+ # If no one has overridden the inherited method yet, just subclass
162162 return Class . new ( constant ) if Class == owner
163163
164+ # Capture this Hash locally, to mutate it in the `inherited` callback below.
165+ generic_instances = @generic_instances
166+
164167 begin
165168 # Otherwise, some inherited method could be preventing us
166169 # 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 )
169178 rescue
170179 # Ignoring errors
171180 end
0 commit comments