Note: the transformation described here should eventually be replaced by a design documented in runtime/mercury_typeclass_info.h.
Representation of a typeclass_info: The typeclass_info is represented in two parts (the typeclass_info itself, and a base_typeclass_info), in a similar fashion to the type_info being represented in two parts (the type_info and the type_ctor_info).
The base_typeclass_info contains:
The typeclass_info contains:
The base_typeclass_info is produced statically, and there is one for each instance declaration. For each constraint on the instance declaration, the corresponding typeclass_info is stored in the second part.
For example for the following program:
:- typeclass foo(T) where [...]. :- instance foo(int) where [...]. :- instance foo(list(T)) <= foo(T) where [...].The typeclass_info for foo(int) is:
:- pred p(T1) <= foo(T1). :- pred q(T2, T3) <= foo(T2), bar(T3). :- pred r(T4, T5) <= foo(T4). p(X) :- q([X], 0), r(1, 0).We add an extra argument for each type class constraint, and one argument for each unconstrained type variable.
:- pred p(typeclass_info(foo(T1)), T1). :- pred q(typeclass_info(foo(T2)), typeclass_info(bar(T3)), T2, T3). :- pred r(typeclass_info(foo(T4)), type_info(T5), T4, T5).We transform the body of p to this:
p(TypeClassInfoT1, X) :- BaseTypeClassInfoT2 = base_typeclass_info( 1, 1, 0, 1, n5, (ie. the number of methods) ... ... (The methods for the foo class from the list ... instance) ... ), TypeClassInfoT2 = typeclass_info( BaseTypeClassInfoT2, TypeClassInfoT1, <type_info for list(T1)>), BaseTypeClassInfoT3 = base_typeclass_info( 0, 0, 0, (presuming bar has no superclasses) 1, ... ... (The methods for the bar class from the int ... instance) ... ), TypeClassInfoT3 = typeclass_info( BaseTypeClassInfoT3, <type_info for int>), q(TypeClassInfoT2, TypeClassInfoT3, [X], 0), BaseTypeClassInfoT4 = baseclass_type_info( 0, 0, 0, 1, ... ... (The methods for the foo class from the int ... instance) ... ), TypeClassInfoT4 = typeclass_info( BaseTypeClassInfoT4, <type_info for int>), r(TypeClassInfoT4, <type_info for int>, X, 0).
For the C backends, this is what we actually do. As a result, duplicate instance declarations will result in a link error for a multiply defined symbol if linking is done statically. (With dynamic linking, multiply defined symbols don't seem to cause any warnings or errors on the platforms we use, unless both definitions occur in the same shared library or both occur in the main program.) Note that the names of the global variables do in fact have module names in them, but they are the names of the modules that declare the type class and that declare the type constructors occuring in the argument types. The name of the module that contains the instance declaration need not be among these names.
For the Java backend, the data structures we generate must all be module qualified with the name of the module which generates them. If two modules contain duplicate instance declarations, we cannot catch that fact at link time. We could catch them at runtime, by having each module register its base_typeclass_infos at module initialization time, and detecting duplicate registrations. However, we currently have no such mechanism in place.