module Tapioca::Runtime::GenericTypeRegistry

def create_generic_type(constant, name)

def create_generic_type(constant, name)
  generic_type = case constant
  when Class
    # For classes, we want to create a subclass, so that an instance of
    # the generic class `Foo[Bar]` is still a `Foo`. That is:
    # `Foo[Bar].new.is_a?(Foo)` should be true, which isn't the case
    # if we just clone the class. But subclassing works just fine.
    create_safe_subclass(constant)
  else
    # This can only be a module and it is fine to just clone modules
    # since they can't have instances and will not have `is_a?` relationships.
    # Moreover, we never `include`/`extend` any generic modules into the
    # ancestor tree, so this doesn't become a problem with checking the
    # instance of a class being `is_a?` of a module type.
    constant.clone
  end
  # Let's set the `name` and `to_s` methods to return the proper generic name
  name_proc = -> { name }
  generic_type.define_singleton_method(:name, name_proc)
  generic_type.define_singleton_method(:to_s, name_proc)
  override_type = GenericType.new(generic_type, constant)
  override_type_proc = -> { override_type }
  generic_type.define_singleton_method(:__tapioca_override_type, override_type_proc)
  # We need to define a `<=` method on the cloned constant, so that Sorbet
  # can do covariance/contravariance checks on the type variables.
  #
  # Normally, we would be doing proper covariance/contravariance checks here, but
  # that is not necessary, since we are not implementing a runtime type checker
  # here. It is just enough for the checks to pass, so that we can serialize the
  # signatures, assuming the sigs were well-formed.
  #
  # So we act like all subtype checks pass.
  generic_type.define_singleton_method(:<=) { |_| true }
  # Return the generic type we created
  generic_type
end

def create_safe_subclass(constant)

def create_safe_subclass(constant)
  # Lookup the "inherited" class method
  inherited_method = constant.method(:inherited)
  # and the module that defines it
  owner = inherited_method.owner
  # If no one has overridden the inherited method yet, just subclass
  return Class.new(constant) if Class == owner
  begin
    # Otherwise, some inherited method could be preventing us
    # from creating subclasses, so let's override it and rescue
    owner.send(:define_method, :inherited) do |s|
      inherited_method.call(s)
    rescue
      # Ignoring errors
    end
    # return a subclass
    Class.new(constant)
  ensure
    # Reinstate the original inherited method back.
    owner.send(:define_method, :inherited, inherited_method)
  end
end

def generic_type_instance?(instance)

def generic_type_instance?(instance)
  @generic_instances.values.any? { |generic_type| generic_type === instance }
end

def lookup_or_initialize_type_variables(constant)

def lookup_or_initialize_type_variables(constant)
  @type_variables[constant] ||= []
end

def lookup_type_variables(constant)

def lookup_type_variables(constant)
  @type_variables[constant]
end

def register_type(constant, types)

def register_type(constant, types)
  # Build the name of the instantiated generic type,
  # something like `"Foo[X, Y, Z]"`
  type_list = types.map { |type| T::Utils.coerce(type).name }.join(", ")
  name = "#{Reflection.name_of(constant)}[#{type_list}]"
  # Create a generic type with an overridden `name`
  # method that returns the name we constructed above.
  #
  # Also, we try to memoize the generic type based on the name, so that
  # we don't have to keep recreating them all the time.
  @generic_instances[name] ||= create_generic_type(constant, name)
end

def register_type_variable(constant, type_variable)

def register_type_variable(constant, type_variable)
  type_variables = lookup_or_initialize_type_variables(constant)
  type_variables << type_variable
end