To add support for generic types to the type system, there can be now additional information added to a type, which are specifying, if a type has parameters.
TypeNode's have a new field "type_parameters" with a hashtable. The keys in the hashtable are the names of the type parameters in strings. And the values are containing the minimum type, that instances of the generic type need to specify.
Whenever someone wants to instanciate such a type, there is a new type registered in the background for the specific combination of parameter values. These types are final and instantiable instead of abstract.
I called this hard-coding of the type parameter "implementing", and the type parameters in the abstract generic types are "required", because they require a type or from it derived types as the parameter. And these requirements are then implemented by the other final types.
So now there is also information per parameter, about if it is "required" or "implemented". The automatically created types turn all parameters into "implemented" ones, with the type that they implement as the parameter value.
You can also have types with some parameters as "required" and some as "implemented". This is useful, if a type derives from a generic type (which copies all required and implemented parameters over), but implements only a few parameters, or if it implements all parameters and adds a new "required" one (which would make it again a generic type).
To make for example different "implementations" of the same generic type "compatible", if the parameters are derived from each other, there are also changes to g_type_is_a(). It checks now if the parents of both types are derived from each other, and for each "generic" and "implemented" parameter, if there is a corresponding "implemented" parameter, that meets the requirements.
Because of that all parameters need to be kept in all derived types, even if they are long before already "implemented". So you can use a parameter name only once.
To get the type id of an "implementation" of a generic type, you can use g_type_get_implemented_type (). This takes the generic type, and a list of parameter name, value arguments and returns the resulting type id. You can then use it for type checking or instantiating.
To create a new generic type, you can use g_type_add_required_type_parameter(). this just adds a required parameter to the type. The same is g_type_implement_type_parameter() doing with implemented parameters. This can be useful, when you "implement" parameters in a derived type, but which isn't only a instance of the type. Important here is only, that the implemented type must be "required" in the parent type, and that it is not allowed to turn a "implemented" into a "required" parameter again.
Everything else is up to the type. You can create gobject, but also fundamental generic types. For gobject there are two useful macros for G_DEFINE_TYPE_WITH_CODE(). I have also made two example classes for demonstration.
This is completely experimental, and don't expect it to actually run.
I am also very unsure about the naming. You know probably better terms.
Other things I haven't looked at yet are ref/unref of type_data and bindings.