python: ensure Python 3 can derive method resolution order
commit4f79e34c6d99f06f007ea8f817f43f147f05647c
authorTobias Grosser <tobias@grosser.es>
Mon, 21 Nov 2016 13:40:49 +0000 (21 14:40 +0100)
committerSven Verdoolaege <skimo@kotnet.org>
Fri, 2 Dec 2016 10:24:27 +0000 (2 11:24 +0100)
treea154dc8d6676c768830ce120ae49f4073ed4f39b
parent8264710bfe7d270a5b3ec7448492f1135f694d41
python: ensure Python 3 can derive method resolution order

Python 3 removes the original Python method resolution algorithm used
for multiple inheritance and replaces it with a new algorithm that
ensures "monotonicity", this means it ensures that the local method
resolution order remains unchanged even when local hierarchies are used
as part of larger class hierarchies. To ensure this property, certain
class hierarchies which do not preserve monotonicity are refused by
python 3.

The order of the superclasses derived from the annotations
in include/isl/aff_type.h does not preserve monotonicity.
Python 3 reports this through the following error:

    class pw_aff(union_pw_aff, multi_pw_aff, pw_multi_aff):
TypeError: Cannot create a consistent method resolution
order (MRO) for bases multi_pw_aff, union_pw_multi_aff, pw_multi_aff

The main problem is that there is no linearization of the classes
in the isl_aff hierarchy that extends all the partial orders
of superclasses.  Fix this issue by defining a linearization
of the classes and deriving the partial orders from this linearization.
The linearization is considered from most specific to least specific,
meaning that each class should appear after all of its subclasses.

There are three types of subclass relationships in the isl_aff hierarchy.
- an application of one of the three type constructors (multi,
  piecewise and union) yields a type that is more general
  than the base type.  For example, isl_aff is a subclass of isl_multi_aff
  and, similarly, isl_pw_aff is a subclass of isl_multi_pw_aff.
- an application of the type constructors preserves the subclass
  relationship.  For example, since isl_aff is a subclass of isl_multi_aff,
  then so is isl_pw_aff a subclass of isl_pw_multi_aff.
- isl_pw_multi_aff is marked as a subclass of isl_multi_pw_aff.
  This is not entirely correct because an isl_multi_pw_aff cannot
  represent a partially defined 0-tuple, but for tuples with at least
  one element, this is the correct order since the elements
  of an isl_multi_pw_aff can be defined over different domains,
  while the elements of an isl_pw_multi_aff are all defined over
  the same domain.
  Along the same lines, isl_union_pw_multi_aff could be considered
  a subclass of isl_multi_union_pw_aff.  Even though this subclass
  relationship is not explicitly marked in the current code base,
  the linearization should not prevent this relationship from being
  added in the future.

A valid linearization is obtained by starting from the most
specific type (isl_aff) and then successively applying one
of the type constructors in a specific order to one of
the types derived so far, preferring base types that appear
earlier in the linearization, and adding the result to the end of the list.
Adding derived types after their base types covers the first class
of subclass relationships.
Applying a given type constructor to the types in their linearized
order covers the second class of relationships.  For example,
since isl_aff is a subclass of isl_multi_aff, it appears
before isl_multi_aff and so the piecewise constructor is applied
to isl_aff before it is applied to isl_multi_aff.

The third class of relationships is handled by the choice
of the order in which the type constructors are considered.
That is, whenever the list is extended, it is extended
through the application of the first constructor in the list
that generates a new type.
It should be noted that the union constructor can only be applied
to the result of an application of the piecewise constructors.
It therefore does not make sense to consider the union constructor
before the piecewise constructor.  This leaves three possible
orderings of the constructors: (multi, piecewise, union),
(piecewise, multi, union) and (piecewise, union, multi).
The third one ensures that both isl_multi_pw_aff is derived after
isl_pw_multi_aff and isl_multi_union_pw_aff is derived after
isl_union_pw_multi_aff.

The resulting linearization is as follows:

a pa upa ma pma upma mpa mupa

The order of the superclasses of a class is then
the corresponding subsequence of this list.

Note that the order of this superclasses is the opposite
of the order in which the corresponding annotations appear
in the header file.  In particular, the least general
extension appears closest to the annotated type.

To implement this new linearization, pw_multi_aff is moved before
multi_pw_aff and union_pw_multi_aff before multi_pw_aff.  Both reorderings
make sense by themselves as they move the more concrete class
closer to the annotated type.

Signed-off-by: Tobias Grosser <tobias@grosser.es>
Signed-off-by: Sven Verdoolaege <skimo@kotnet.org>
include/isl/aff_type.h
interface/python.cc