interface: add support for type based subclasses
authorSven Verdoolaege <sven.verdoolaege@gmail.com>
Fri, 1 Dec 2017 16:49:17 +0000 (1 17:49 +0100)
committerSven Verdoolaege <sven.verdoolaege@gmail.com>
Thu, 11 Oct 2018 10:30:18 +0000 (11 12:30 +0200)
Some isl types such as isl_ast_node and isl_schedule_node
represent a collection of subclasses that each have their
own corresponding functions.  In C, the user is responsible
for checking the type of the object before calling
the appropriate functions.

In the C++ or Python bindings, the subclasses can instead
be made available as proper subclasses.
Add support for generating these subclasses from
the function that returns the type of the subclass.
In particular, any function with an __isl_subclass annotation
is treated as such a type function.

The generated code is slightly different for C++ and Python.
Since Python is a dynamically-typed language, each object
returned by a C function can be cast directly to the appropriate
subclass.  For this purpose, a __new__ method is introduced
that constructs a Python object of the subclass that corresponds
to the type of the C object.  The __new__ method is overridden
in the subclasses in order to be able to make regular objects
of these subclasses.
The constructor of the superclass is also extended to accept
objects of the (possibly nested) subclasses in order to support
the general casting that is performed when an object is
not of the expected type.
The type checking of the arguments of a method is not changed
by this commit.  In particular, if the first argument is supposed
to be of a type based subclass, then the type check will only
check that it is of the superclass.  This should be safe,
although an exception will be raised through the C level
argument checking if it is not of the correct subclass.

In the C++ bindings, the (static) type of an object returned by
a superclass method is that of the superclass.  This superclass
has two methods for checking the dynamic type and for downcasting
to this type, in particular

bool super::isa<sub>();
and
sub super::as<sub>();

Both methods can only be used for downcasting to an immediate subclass.
The as() method requires the concrete object to be of the expected type.
It will throw an exception if this is not the case.

An alternative would be to let as() return a special value
such as an invalid object when the concrete object is not of
the expected type.  This would be especially convenient
in combination with a conversion of valid/invalid objects to bool as
it would allow constructs of the form

if (auto filter = node.as<isl::schedule_node_filter>()) {
    /* operations on filter node */
}

However, this would be a break from the rule that the (default)
C++ interface never returns an invalid object and in the checked
interface, it would not allow the user to make a distinction
between a conversion failure (which would not be an exceptional result)
and any other failure (which would be an exceptional result).
It was therefore decided to be conservative for now and
to not extend the meaning of "as" in this way.

The names isa and as have been chosen because they are short and
because they allow for a natural reading in English, as in
"if A isa C" and "B is A as C".

The type of an object returned by a subclass method that modifies
the first argument is assumed to be that of the subclass.
This assumption may not apply
to all functions that could potentially be exported.  Further
changes may therefore be required before these functions
can be exported.

Signed-off-by: Sven Verdoolaege <sven.verdoolaege@gmail.com>
cpp/cpp-checked.h.pre
cpp/cpp.h.pre
interface/cpp.cc
interface/cpp.h
interface/cpp_conversion.cc
interface/cpp_conversion.h
interface/generator.cc
interface/generator.h
interface/python.cc
interface/python.h

index 9316718..4d4876e 100644 (file)
@@ -5,6 +5,7 @@
 #include <functional>
 #include <ostream>
 #include <string>
+#include <type_traits>
 
 namespace isl {
 namespace checked {
index f011822..1ac70c5 100644 (file)
@@ -7,6 +7,7 @@
 #include <ostream>
 #include <stdexcept>
 #include <string>
+#include <type_traits>
 
 /* ISL_USE_EXCEPTIONS should be defined to 1 if exceptions are available.
  * gcc and clang define __cpp_exceptions; MSVC and xlC define _CPPUNWIND.
index 1054be7..66e78a4 100644 (file)
@@ -177,7 +177,48 @@ void cpp_generator::print_implementations(ostream &os)
        }
 }
 
+/* If "clazz" is a subclass that is based on a type function,
+ * then introduce a "type" field that holds the value of the type
+ * corresponding to the subclass and make the fields of the class
+ * accessible to the "isa" and "as" methods of the (immediate) superclass.
+ * In particular, "isa" needs access to the type field itself,
+ * while "as" needs access to the private constructor.
+ * In case of the "isa" method, all instances are made friends
+ * to avoid access right confusion.
+ */
+void cpp_generator::print_subclass_type(ostream &os, const isl_class &clazz)
+{
+       std::string cppstring = type2cpp(clazz);
+       std::string super;
+       const char *cppname = cppstring.c_str();
+       const char *supername;
+
+       if (!clazz.is_type_subclass())
+               return;
+
+       super = type2cpp(clazz.superclass_name);
+       supername = super.c_str();
+       osprintf(os, "  template <class T>\n");
+       osprintf(os, "  friend %s %s::isa() const;\n",
+               isl_bool2cpp().c_str(), supername);
+       osprintf(os, "  friend %s %s::as<%s>() const;\n",
+               cppname, supername, cppname);
+       osprintf(os, "  static const auto type = %s;\n",
+               clazz.subclass_name.c_str());
+}
+
 /* Print declarations for class "clazz" to "os".
+ *
+ * If "clazz" is a subclass based on a type function,
+ * then it is made to inherit from the (immediate) superclass and
+ * a "type" attribute is added for use in the "as" and "isa"
+ * methods of the superclass.
+ *
+ * Conversely, if "clazz" is a superclass with a type function,
+ * then declare those "as" and "isa" methods.
+ *
+ * The pointer to the isl object is only added for classes that
+ * are not subclasses, since subclasses refer to the same isl object.
  */
 void cpp_generator::print_class(ostream &os, const isl_class &clazz)
 {
@@ -189,12 +230,19 @@ void cpp_generator::print_class(ostream &os, const isl_class &clazz)
 
        print_class_factory_decl(os, clazz);
        osprintf(os, "\n");
-       osprintf(os, "class %s {\n", cppname);
+       osprintf(os, "class %s ", cppname);
+       if (clazz.is_type_subclass())
+               osprintf(os, ": public %s ",
+                       type2cpp(clazz.superclass_name).c_str());
+       osprintf(os, "{\n");
+       print_subclass_type(os, clazz);
        print_class_factory_decl(os, clazz, "  friend ");
        osprintf(os, "\n");
        osprintf(os, "protected:\n");
-       osprintf(os, "  %s *ptr = nullptr;\n", name);
-       osprintf(os, "\n");
+       if (!clazz.is_type_subclass()) {
+               osprintf(os, "  %s *ptr = nullptr;\n", name);
+               osprintf(os, "\n");
+       }
        print_protected_constructors_decl(os, clazz);
        osprintf(os, "\n");
        osprintf(os, "public:\n");
@@ -203,6 +251,7 @@ void cpp_generator::print_class(ostream &os, const isl_class &clazz)
        print_copy_assignment_decl(os, clazz);
        print_destructor_decl(os, clazz);
        print_ptr_decl(os, clazz);
+       print_downcast_decl(os, clazz);
        print_get_ctx_decl(os);
        osprintf(os, "\n");
        print_methods_decl(os, clazz);
@@ -235,6 +284,10 @@ void cpp_generator::print_class_forward_decl(ostream &os,
  * manage_copy() can be called on any isl raw pointer and the corresponding
  * object is automatically created, without the user having to choose the right
  * isl object type.
+ *
+ * For a subclass based on a type function, no factory functions
+ * are introduced because they share the C object type with
+ * the superclass.
  */
 void cpp_generator::print_class_factory_decl(ostream &os,
        const isl_class &clazz, const std::string &prefix)
@@ -243,6 +296,9 @@ void cpp_generator::print_class_factory_decl(ostream &os,
        std::string cppstring = type2cpp(clazz);
        const char *cppname = cppstring.c_str();
 
+       if (clazz.is_type_subclass())
+               return;
+
        os << prefix;
        osprintf(os, "inline %s manage(__isl_take %s *ptr);\n", cppname, name);
        os << prefix;
@@ -340,16 +396,23 @@ void cpp_generator::print_copy_assignment_decl(ostream &os,
 }
 
 /* Print declaration of destructor for class "clazz" to "os".
+ *
+ * No explicit destructor is needed for type based subclasses.
  */
 void cpp_generator::print_destructor_decl(ostream &os, const isl_class &clazz)
 {
        std::string cppstring = type2cpp(clazz);
        const char *cppname = cppstring.c_str();
 
+       if (clazz.is_type_subclass())
+               return;
+
        osprintf(os, "  inline ~%s();\n", cppname);
 }
 
 /* Print declaration of pointer functions for class "clazz" to "os".
+ * Since type based subclasses share the pointer with their superclass,
+ * they can also reuse these functions from the superclass.
  *
  * To obtain a raw pointer three functions are provided:
  *
@@ -385,6 +448,9 @@ void cpp_generator::print_ptr_decl(ostream &os, const isl_class &clazz)
 {
        const char *name = clazz.name.c_str();
 
+       if (clazz.is_type_subclass())
+               return;
+
        osprintf(os, "  inline __isl_give %s *copy() const &;\n", name);
        osprintf(os, "  inline __isl_give %s *copy() && = delete;\n", name);
        osprintf(os, "  inline __isl_keep %s *get() const;\n", name);
@@ -392,6 +458,56 @@ void cpp_generator::print_ptr_decl(ostream &os, const isl_class &clazz)
        osprintf(os, "  inline bool is_null() const;\n");
 }
 
+/* Print a template declaration with given indentation
+ * for the "isa_type" method that ensures it is only enabled
+ * when called with a template argument
+ * that represents a type that is equal to that
+ * of the return type of the type function of "super".
+ * In particular, "isa_type" gets called from "isa"
+ * with as template argument the type of the "type" field
+ * of the subclass.
+ * The check ensures that this subclass is in fact a direct subclass
+ * of "super".
+ */
+void cpp_generator::print_isa_type_template(ostream &os, int indent,
+       const isl_class &super)
+{
+       osprintf(os, indent,
+               "template <typename T,\n");
+       osprintf(os, indent,
+               "        typename = typename std::enable_if<std::is_same<\n");
+       osprintf(os, indent,
+               "                const decltype(%s(NULL)),\n",
+               super.fn_type->getNameAsString().c_str());
+       osprintf(os, indent,
+               "                const T>::value>::type>\n");
+}
+
+/* Print declarations for the "as" and "isa" methods, if "clazz"
+ * is a superclass with a type function.
+ *
+ * "isa" checks whether an object is of a given subclass type.
+ * "isa_type" does the same, but gets passed the value of the type field
+ * of the subclass as a function argument and the type of this field
+ * as a template argument.
+ * "as" tries to cast an object to a given subclass type, returning
+ * an invalid object if the object is not of the given type.
+ */
+void cpp_generator::print_downcast_decl(ostream &os, const isl_class &clazz)
+{
+       if (!clazz.fn_type)
+               return;
+
+       osprintf(os, "private:\n");
+       print_isa_type_template(os, 2, clazz);
+       osprintf(os, "  inline %s isa_type(T subtype) const;\n",
+               isl_bool2cpp().c_str());
+       osprintf(os, "public:\n");
+       osprintf(os, "  template <class T> inline %s isa() const;\n",
+               isl_bool2cpp().c_str());
+       osprintf(os, "  template <class T> inline T as() const;\n");
+}
+
 /* Print the declaration of the get_ctx method.
  */
 void cpp_generator::print_get_ctx_decl(ostream &os)
@@ -455,6 +571,8 @@ void cpp_generator::print_class_impl(ostream &os, const isl_class &clazz)
        osprintf(os, "\n");
        print_ptr_impl(os, clazz);
        osprintf(os, "\n");
+       if (print_downcast_impl(os, clazz))
+               osprintf(os, "\n");
        print_get_ctx_impl(os, clazz);
        osprintf(os, "\n");
        print_methods_impl(os, clazz);
@@ -487,6 +605,23 @@ static void print_throw_NULL_input(ostream &os)
        print_throw_invalid(os, 4, "NULL input");
 }
 
+/* Print code with the given indentation
+ * for acting on an invalid error with message "msg".
+ * In particular, throw an exception_invalid.
+ * In the checked C++ bindings, isl_die is called instead with the code
+ * in "checked_code".
+ */
+void cpp_generator::print_invalid(ostream &os, int indent, const char *msg,
+       const char *checked_code)
+{
+       if (checked)
+               osprintf(os, indent,
+                       "isl_die(get_ctx().get(), isl_error_invalid, "
+                       "\"%s\", %s);\n", msg, checked_code);
+       else
+               print_throw_invalid(os, indent, msg);
+}
+
 /* Print an operator for inserting objects of "class"
  * into an output stream.
  *
@@ -584,6 +719,10 @@ void cpp_generator::print_check_ptr_end(ostream &os, const char *ptr)
  * in manage_copy.
  * During the copying, isl is made not to print any error message
  * because the error message is included in the exception.
+ *
+ * For a subclass based on a type function, no factory functions
+ * are introduced because they share the C object type with
+ * the superclass.
  */
 void cpp_generator::print_class_factory_impl(ostream &os,
        const isl_class &clazz)
@@ -592,6 +731,9 @@ void cpp_generator::print_class_factory_impl(ostream &os,
        std::string cppstring = type2cpp(clazz);
        const char *cppname = cppstring.c_str();
 
+       if (clazz.is_type_subclass())
+               return;
+
        osprintf(os, "%s manage(__isl_take %s *ptr) {\n", cppname, name);
        print_check_ptr(os, "ptr");
        osprintf(os, "  return %s(ptr);\n", cppname);
@@ -607,6 +749,9 @@ void cpp_generator::print_class_factory_impl(ostream &os,
 }
 
 /* Print implementations of protected constructors for class "clazz" to "os".
+ *
+ * The pointer to the isl object is either initialized directly or
+ * through the (immediate) superclass.
  */
 void cpp_generator::print_protected_constructors_impl(ostream &os,
        const isl_class &clazz)
@@ -614,15 +759,23 @@ void cpp_generator::print_protected_constructors_impl(ostream &os,
        const char *name = clazz.name.c_str();
        std::string cppstring = type2cpp(clazz);
        const char *cppname = cppstring.c_str();
+       bool subclass = clazz.is_type_subclass();
 
        osprintf(os, "%s::%s(__isl_take %s *ptr)\n", cppname, cppname, name);
-       osprintf(os, "    : ptr(ptr) {}\n");
+       if (subclass)
+               osprintf(os, "    : %s(ptr) {}\n",
+                       type2cpp(clazz.superclass_name).c_str());
+       else
+               osprintf(os, "    : ptr(ptr) {}\n");
 }
 
 /* Print implementations of public constructors for class "clazz" to "os".
  *
+ * The pointer to the isl object is either initialized directly or
+ * through the (immediate) superclass.
+ *
  * Throw an exception from the copy constructor if anything went wrong
- * during the copying or if the input is NULL.
+ * during the copying or if the input is NULL, if any copying is performed.
  * During the copying, isl is made not to print any error message
  * because the error message is included in the exception.
  * No exceptions are thrown if checked C++ bindings
@@ -632,16 +785,28 @@ void cpp_generator::print_public_constructors_impl(ostream &os,
        const isl_class &clazz)
 {
        std::string cppstring = type2cpp(clazz);
+       std::string super;
        const char *cppname = cppstring.c_str();
+       bool subclass = clazz.is_type_subclass();
 
+       if (subclass)
+               super = type2cpp(clazz.superclass_name);
        osprintf(os, "%s::%s()\n", cppname, cppname);
-       osprintf(os, "    : ptr(nullptr) {}\n\n");
+       if (subclass)
+               osprintf(os, "    : %s() {}\n\n", super.c_str());
+       else
+               osprintf(os, "    : ptr(nullptr) {}\n\n");
        osprintf(os, "%s::%s(const %s &obj)\n", cppname, cppname, cppname);
-       osprintf(os, "    : ptr(nullptr)\n");
+       if (subclass)
+               osprintf(os, "    : %s(obj)\n", super.c_str());
+       else
+               osprintf(os, "    : ptr(nullptr)\n");
        osprintf(os, "{\n");
-       print_check_ptr_start(os, clazz, "obj.ptr");
-       osprintf(os, "  ptr = obj.copy();\n");
-       print_check_ptr_end(os, "ptr");
+       if (!subclass) {
+               print_check_ptr_start(os, clazz, "obj.ptr");
+               osprintf(os, "  ptr = obj.copy();\n");
+               print_check_ptr_end(os, "ptr");
+       }
        osprintf(os, "}\n");
 }
 
@@ -677,6 +842,8 @@ void cpp_generator::print_copy_assignment_impl(ostream &os,
 }
 
 /* Print implementation of destructor for class "clazz" to "os".
+ *
+ * No explicit destructor is needed for type based subclasses.
  */
 void cpp_generator::print_destructor_impl(ostream &os,
        const isl_class &clazz)
@@ -685,6 +852,9 @@ void cpp_generator::print_destructor_impl(ostream &os,
        std::string cppstring = type2cpp(clazz);
        const char *cppname = cppstring.c_str();
 
+       if (clazz.is_type_subclass())
+               return;
+
        osprintf(os, "%s::~%s() {\n", cppname, cppname);
        osprintf(os, "  if (ptr)\n");
        osprintf(os, "    %s_free(ptr);\n", name);
@@ -692,6 +862,8 @@ void cpp_generator::print_destructor_impl(ostream &os,
 }
 
 /* Print implementation of ptr() functions for class "clazz" to "os".
+ * Since type based subclasses share the pointer with their superclass,
+ * they can also reuse these functions from the superclass.
  */
 void cpp_generator::print_ptr_impl(ostream &os, const isl_class &clazz)
 {
@@ -699,6 +871,9 @@ void cpp_generator::print_ptr_impl(ostream &os, const isl_class &clazz)
        std::string cppstring = type2cpp(clazz);
        const char *cppname = cppstring.c_str();
 
+       if (clazz.is_type_subclass())
+               return;
+
        osprintf(os, "__isl_give %s *%s::copy() const & {\n", name, cppname);
        osprintf(os, "  return %s_copy(ptr);\n", name);
        osprintf(os, "}\n\n");
@@ -715,6 +890,65 @@ void cpp_generator::print_ptr_impl(ostream &os, const isl_class &clazz)
        osprintf(os, "}\n");
 }
 
+/* Print implementations for the "as" and "isa" methods, if "clazz"
+ * is a superclass with a type function.
+ *
+ * "isa" checks whether an object is of a given subclass type.
+ * "isa_type" does the same, but gets passed the value of the type field
+ * of the subclass as a function argument and the type of this field
+ * as a template argument.
+ * "as" casts an object to a given subclass type, erroring out
+ * if the object is not of the given type.
+ *
+ * If the input is an invalid object, then these methods raise
+ * an exception.
+ * If checked bindings are being generated,
+ * then an invalid boolean or object is returned instead.
+ *
+ * Return true if anything was printed.
+ */
+bool cpp_generator::print_downcast_impl(ostream &os, const isl_class &clazz)
+{
+       std::string cppstring = type2cpp(clazz);
+       const char *cppname = cppstring.c_str();
+
+       if (!clazz.fn_type)
+               return false;
+
+       osprintf(os, "template <typename T, typename>\n");
+       osprintf(os, "%s %s::isa_type(T subtype) const\n",
+               isl_bool2cpp().c_str(), cppname);
+       osprintf(os, "{\n");
+       osprintf(os, "  if (is_null())\n");
+       if (checked)
+               osprintf(os, "    return boolean();\n");
+       else
+               print_throw_NULL_input(os);
+       osprintf(os, "  return %s(get()) == subtype;\n",
+               clazz.fn_type->getNameAsString().c_str());
+       osprintf(os, "}\n");
+
+       osprintf(os, "template <class T>\n");
+       osprintf(os, "%s %s::isa() const\n", isl_bool2cpp().c_str(), cppname);
+       osprintf(os, "{\n");
+       osprintf(os, "  return isa_type<decltype(T::type)>(T::type);\n");
+       osprintf(os, "}\n");
+
+       osprintf(os, "template <class T>\n");
+       osprintf(os, "T %s::as() const\n", cppname);
+       osprintf(os, "{\n");
+       if (checked)
+               osprintf(os, " if (isa<T>().is_false())\n");
+       else
+               osprintf(os, " if (!isa<T>())\n");
+       print_invalid(os, 4, "not an object of the requested subtype",
+                   "return T()");
+       osprintf(os, "  return T(copy());\n");
+       osprintf(os, "}\n");
+
+       return true;
+}
+
 /* Print the implementation of the get_ctx method.
  */
 void cpp_generator::print_get_ctx_impl(ostream &os, const isl_class &clazz)
@@ -983,12 +1217,26 @@ void cpp_generator::print_exceptional_execution_check(ostream &os,
        print_throw_last_error(os);
 }
 
+/* Does "fd" modify an object of a subclass based on a type function?
+ */
+static bool is_subclass_mutator(const isl_class &clazz, FunctionDecl *fd)
+{
+       return clazz.is_type_subclass() && generator::is_mutator(clazz, fd);
+}
+
 /* Return the C++ return type of the method corresponding to "fd" in "clazz".
+ *
+ * If "fd" modifies an object of a subclass, then return
+ * the type of this subclass.
+ * Otherwise, return the C++ counterpart of the actual return type.
  */
 std::string cpp_generator::get_return_type(const isl_class &clazz,
        FunctionDecl *fd)
 {
-       return type2cpp(fd->getReturnType());
+       if (is_subclass_mutator(clazz, fd))
+               return type2cpp(clazz);
+       else
+               return type2cpp(fd->getReturnType());
 }
 
 /* Print the return statement of the C++ method corresponding
@@ -1004,15 +1252,23 @@ std::string cpp_generator::get_return_type(const isl_class &clazz,
  * then an isl_bool return type is transformed into a boolean and
  * an isl_stat into a stat since no exceptions can be generated
  * on negative results from the isl function.
+ * If "clazz" is a subclass that is based on a type function and
+ * if the return type corresponds to the superclass data type,
+ * then it is replaced by the subclass data type.
  */
 void cpp_generator::print_method_return(ostream &os, const isl_class &clazz,
        FunctionDecl *method)
 {
        QualType return_type = method->getReturnType();
+       string rettype_str = get_return_type(clazz, method);
+       bool returns_super = is_subclass_mutator(clazz, method);
 
        if (is_isl_type(return_type) ||
                    (checked && is_isl_neg_error(return_type))) {
-               osprintf(os, "  return manage(res);\n");
+               osprintf(os, "  return manage(res)");
+               if (returns_super)
+                       osprintf(os, ".as<%s>()", rettype_str.c_str());
+               osprintf(os, ";\n");
        } else if (is_isl_stat(return_type)) {
                osprintf(os, "  return;\n");
        } else if (is_string(return_type)) {
@@ -1482,10 +1738,11 @@ std::string cpp_generator::rename_method(std::string name)
 }
 
 /* Translate isl class "clazz" to its corresponding C++ type.
+ * Use the name of the type based subclass, if any.
  */
 string cpp_generator::type2cpp(const isl_class &clazz)
 {
-       return type2cpp(clazz.name);
+       return type2cpp(clazz.subclass_name);
 }
 
 /* Translate type string "type_str" to its C++ name counterpart.
index 5428de9..f08395f 100644 (file)
@@ -30,6 +30,7 @@ private:
        void print_forward_declarations(ostream &os);
        void print_declarations(ostream &os);
        void print_class(ostream &os, const isl_class &clazz);
+       void print_subclass_type(ostream &os, const isl_class &clazz);
        void print_class_forward_decl(ostream &os, const isl_class &clazz);
        void print_class_factory_decl(ostream &os, const isl_class &clazz,
                const std::string &prefix = std::string());
@@ -41,6 +42,9 @@ private:
        void print_constructors_decl(ostream &os, const isl_class &clazz);
        void print_destructor_decl(ostream &os, const isl_class &clazz);
        void print_ptr_decl(ostream &os, const isl_class &clazz);
+       void print_isa_type_template(ostream &os, int indent,
+               const isl_class &super);
+       void print_downcast_decl(ostream &os, const isl_class &clazz);
        void print_get_ctx_decl(ostream &os);
        void print_methods_decl(ostream &os, const isl_class &clazz);
        void print_method_group_decl(ostream &os, const isl_class &clazz,
@@ -62,6 +66,7 @@ private:
        void print_copy_assignment_impl(ostream &os, const isl_class &clazz);
        void print_destructor_impl(ostream &os, const isl_class &clazz);
        void print_ptr_impl(ostream &os, const isl_class &clazz);
+       bool print_downcast_impl(ostream &os, const isl_class &clazz);
        void print_get_ctx_impl(ostream &os, const isl_class &clazz);
        void print_methods_impl(ostream &os, const isl_class &clazz);
        void print_method_group_impl(ostream &os, const isl_class &clazz,
@@ -77,6 +82,8 @@ private:
                FunctionDecl *method);
        void print_method_impl(ostream &os, const isl_class &clazz,
                FunctionDecl *method, function_kind kind);
+       void print_invalid(ostream &os, int indent, const char *msg,
+               const char *checked_code);
        void print_stream_insertion(ostream &os, const isl_class &clazz);
        void print_method_param_use(ostream &os, ParmVarDecl *param,
                bool load_from_this_ptr);
index e78aa5e..58294d5 100644 (file)
 #include "cpp.h"
 #include "cpp_conversion.h"
 
+/* If "clazz" describes a subclass of a C type, then print code
+ * for converting an object of the class derived from the C type
+ * to the subclass.  Do this by first converting this class
+ * to the immediate superclass of the subclass and then converting
+ * from this superclass to the subclass.
+ */
+void cpp_conversion_generator::cast(const isl_class &clazz, const char *to)
+{
+       string name = cpp_generator::type2cpp(clazz);
+
+       if (!clazz.is_type_subclass())
+               return;
+
+       cast(classes[clazz.superclass_name], to);
+       printf(".as<%s%s>()", to, name.c_str());
+}
+
 /* Print a function called "function" for converting objects of
  * "clazz" from the "from" bindings to the "to" bindings.
+ * If "clazz" describes a subclass of a C type, then the result
+ * of the conversion between bindings is derived from the C type and
+ * needs to be converted back to the subclass.
  */
 void cpp_conversion_generator::convert(const isl_class &clazz,
        const char *from, const char *to, const char *function)
@@ -23,7 +43,9 @@ void cpp_conversion_generator::convert(const isl_class &clazz,
 
        printf("%s%s %s(%s%s obj) {\n",
                to, name.c_str(), function, from, name.c_str());
-       printf("\t""return %s""manage(obj.copy());\n", to);
+       printf("\t""return %s""manage(obj.copy())", to);
+       cast(clazz, to);
+       printf(";\n");
        printf("}\n");
        printf("\n");
 }
index f70295c..57856b2 100644 (file)
@@ -1,6 +1,7 @@
 #include "generator.h"
 
 class cpp_conversion_generator : public generator {
+       void cast(const isl_class &clazz, const char *to);
        void convert(const isl_class &clazz, const char *from, const char *to,
                const char *function);
        void print(const isl_class &clazz);
index 0a04db6..d9d2643 100644 (file)
@@ -33,6 +33,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <algorithm>
 #include <iostream>
 
 #include <clang/AST/Attr.h>
@@ -103,18 +104,69 @@ FunctionDecl *generator::find_by_name(const string &name, bool required)
        return NULL;
 }
 
+/* Add a subclass derived from "decl" called "sub_name" to the set of classes,
+ * keeping track of the _to_str, _copy and _free functions, if any, separately.
+ * "sub_name" is either the name of the class itself or
+ * the name of a type based subclass.
+ * If the class is a proper subclass, then "super_name" is the name
+ * of its immediate superclass.
+ */
+void generator::add_subclass(RecordDecl *decl, const string &super_name,
+       const string &sub_name)
+{
+       string name = decl->getName();
+
+       classes[sub_name].name = name;
+       classes[sub_name].superclass_name = super_name;
+       classes[sub_name].subclass_name = sub_name;
+       classes[sub_name].type = decl;
+       classes[sub_name].fn_to_str = find_by_name(name + "_to_str", false);
+       classes[sub_name].fn_copy = find_by_name(name + "_copy", true);
+       classes[sub_name].fn_free = find_by_name(name + "_free", true);
+}
+
 /* Add a class derived from "decl" to the set of classes,
  * keeping track of the _to_str, _copy and _free functions, if any, separately.
  */
 void generator::add_class(RecordDecl *decl)
 {
-       string name = decl->getName();
+       return add_subclass(decl, "", decl->getName());
+}
 
-       classes[name].name = name;
-       classes[name].type = decl;
-       classes[name].fn_to_str = find_by_name(name + "_to_str", false);
-       classes[name].fn_copy = find_by_name(name + "_copy", true);
-       classes[name].fn_free = find_by_name(name + "_free", true);
+/* Given a function "fn_type" that returns the subclass type
+ * of a C object, create subclasses for each of the (non-negative)
+ * return values.
+ *
+ * The function "fn_type" is also stored in the superclass,
+ * along with all pairs of type values and subclass names.
+ */
+void generator::add_type_subclasses(FunctionDecl *fn_type)
+{
+       QualType return_type = fn_type->getReturnType();
+       const EnumType *enum_type = return_type->getAs<EnumType>();
+       EnumDecl *decl = enum_type->getDecl();
+       isl_class *c = method2class(fn_type);
+       DeclContext::decl_iterator i;
+
+       c->fn_type = fn_type;
+       for (i = decl->decls_begin(); i != decl->decls_end(); ++i) {
+               EnumConstantDecl *ecd = dyn_cast<EnumConstantDecl>(*i);
+               int val = (int) ecd->getInitVal().getSExtValue();
+               string name = ecd->getNameAsString();
+
+               if (val < 0)
+                       continue;
+               c->type_subclasses[val] = name;
+               add_subclass(c->type, c->subclass_name, name);
+       }
+}
+
+/* Sorting function that places declaration of functions
+ * with a shorter name first.
+ */
+static bool less_name(const FunctionDecl *a, const FunctionDecl *b)
+{
+       return a->getName().size() < b->getName().size();
 }
 
 /* Collect all functions that belong to a certain type, separating
@@ -122,6 +174,11 @@ void generator::add_class(RecordDecl *decl)
  * _copy and _free functions, if any, separately.  If there are any overloaded
  * functions, then they are grouped based on their name after removing the
  * argument type suffix.
+ * Check for functions that describe subclasses before considering
+ * any other functions in order to be able to detect those other
+ * functions as belonging to the subclasses.
+ * Sort the names of the functions based on their lengths
+ * to ensure that nested subclasses are handled later.
  */
 generator::generator(SourceManager &SM, set<RecordDecl *> &exported_types,
        set<FunctionDecl *> exported_functions, set<FunctionDecl *> functions) :
@@ -129,6 +186,8 @@ generator::generator(SourceManager &SM, set<RecordDecl *> &exported_types,
 {
        set<FunctionDecl *>::iterator in;
        set<RecordDecl *>::iterator it;
+       vector<FunctionDecl *> type_subclasses;
+       vector<FunctionDecl *>::iterator iv;
 
        for (in = functions.begin(); in != functions.end(); ++in) {
                FunctionDecl *decl = *in;
@@ -140,9 +199,23 @@ generator::generator(SourceManager &SM, set<RecordDecl *> &exported_types,
 
        for (in = exported_functions.begin(); in != exported_functions.end();
             ++in) {
+               if (is_subclass(*in))
+                       type_subclasses.push_back(*in);
+       }
+       std::sort(type_subclasses.begin(), type_subclasses.end(), &less_name);
+       for (iv = type_subclasses.begin(); iv != type_subclasses.end(); ++iv) {
+               add_type_subclasses(*iv);
+       }
+
+       for (in = exported_functions.begin(); in != exported_functions.end();
+            ++in) {
                FunctionDecl *method = *in;
-               isl_class *c = method2class(method);
+               isl_class *c;
+
+               if (is_subclass(method))
+                       continue;
 
+               c = method2class(method);
                if (!c)
                        continue;
                if (is_constructor(method)) {
@@ -200,6 +273,13 @@ std::vector<string> generator::find_superclasses(Decl *decl)
        return super;
 }
 
+/* Is "decl" marked as describing subclasses?
+ */
+bool generator::is_subclass(FunctionDecl *decl)
+{
+       return find_superclasses(decl).size() > 0;
+}
+
 /* Is decl marked as being part of an overloaded method?
  */
 bool generator::is_overload(Decl *decl)
index 920982d..d3f04c5 100644 (file)
@@ -12,27 +12,40 @@ using namespace clang;
 
 /* isl_class collects all constructors and methods for an isl "class".
  * "name" is the name of the class.
+ * If this object describes a subclass of a C type, then
+ * "subclass_name" is the name of that subclass and "superclass_name"
+ * is the name of the immediate superclass of that subclass.  Otherwise,
+ * "subclass_name" is equal to "name" and "superclass_name" is undefined.
  * "type" is the declaration that introduces the type.
  * "methods" contains the set of methods, grouped by method name.
  * "fn_to_str" is a reference to the *_to_str method of this class, if any.
  * "fn_copy" is a reference to the *_copy method of this class, if any.
  * "fn_free" is a reference to the *_free method of this class, if any.
+ * "fn_type" is a reference to a function that described subclasses, if any.
+ * If "fn_type" is set, then "type_subclasses" maps the values returned
+ * by that function to the names of the corresponding subclasses.
  */
 struct isl_class {
        string name;
+       string superclass_name;
+       string subclass_name;
        RecordDecl *type;
        set<FunctionDecl *> constructors;
        map<string, set<FunctionDecl *> > methods;
+       map<int, string> type_subclasses;
+       FunctionDecl *fn_type;
        FunctionDecl *fn_to_str;
        FunctionDecl *fn_copy;
        FunctionDecl *fn_free;
 
+       /* Is this class a subclass based on a type function? */
+       bool is_type_subclass() const { return name != subclass_name; }
        /* Return name of "fd" without type suffix, if any. */
        static string name_without_type_suffix(FunctionDecl *fd);
        /* Extract the method name corresponding to "fd". */
        string method_name(FunctionDecl *fd) const {
                string m_name = name_without_type_suffix(fd);
-               return m_name.substr(name.length() + 1);
+               return m_name.substr(subclass_name.length() + 1);
        }
 };
 
@@ -53,7 +66,10 @@ public:
        virtual ~generator() {};
 
 protected:
+       void add_subclass(RecordDecl *decl, const string &name,
+               const string &sub_name);
        void add_class(RecordDecl *decl);
+       void add_type_subclasses(FunctionDecl *method);
        isl_class *method2class(FunctionDecl *fd);
        bool callback_takes_argument(ParmVarDecl *param, int pos);
        FunctionDecl *find_by_name(const string &name, bool required);
@@ -61,6 +77,7 @@ public:
        static void die(const char *msg) __attribute__((noreturn));
        static void die(string msg) __attribute__((noreturn));
        static vector<string> find_superclasses(Decl *decl);
+       static bool is_subclass(FunctionDecl *decl);
        static bool is_overload(Decl *decl);
        static bool is_constructor(Decl *decl);
        static bool takes(Decl *decl);
index 90b1756..9ecee19 100644 (file)
@@ -499,9 +499,34 @@ void python_generator::print_constructor(const isl_class &clazz,
        printf("            return\n");
 }
 
+/* If "clazz" has a type function describing subclasses,
+ * then add constructors that allow each of these subclasses
+ * to be treated as an object to the superclass.
+ */
+void python_generator::print_upcast_constructors(const isl_class &clazz)
+{
+       map<int, string>::const_iterator i;
+
+       if (!clazz.fn_type)
+               return;
+
+       for (i = clazz.type_subclasses.begin();
+            i != clazz.type_subclasses.end(); ++i) {
+               printf("        if len(args) == 1 and "
+                                               "isinstance(args[0], %s):\n",
+                        type2python(i->second).c_str());
+               printf("            self.ctx = args[0].ctx\n");
+               printf("            self.ptr = isl.%s_copy(args[0].ptr)\n",
+                       clazz.name.c_str());
+               printf("            return\n");
+       }
+}
+
 /* Print the header of the class "name" with superclasses "super".
  * The order of the superclasses is the opposite of the order
  * in which the corresponding annotations appear in the source code.
+ * If "clazz" is a subclass derived from a type function,
+ * then the immediate superclass is recorded in "clazz" itself.
  */
 void python_generator::print_class_header(const isl_class &clazz,
        const string &name, const vector<string> &super)
@@ -515,6 +540,8 @@ void python_generator::print_class_header(const isl_class &clazz,
                        printf("%s", type2python(super[i]).c_str());
                }
                printf(")");
+       } else if (clazz.is_type_subclass()) {
+               printf("(%s)", type2python(clazz.superclass_name).c_str());
        } else {
                printf("(object)");
        }
@@ -580,6 +607,41 @@ void python_generator::print_method_type(FunctionDecl *fd)
        print_argtypes(fd);
 }
 
+/* If "clazz" has a type function describing subclasses or
+ * if it is one of those type subclasses, then print a __new__ method.
+ *
+ * In the superclass, the __new__ method constructs an object
+ * of the subclass type specified by the type function.
+ * In the subclass, the __new__ method reverts to the original behavior.
+ */
+void python_generator::print_new(const isl_class &clazz,
+       const string &python_name)
+{
+       if (!clazz.fn_type && !clazz.is_type_subclass())
+               return;
+
+       printf("    def __new__(cls, *args, **keywords):\n");
+
+       if (clazz.fn_type) {
+               map<int, string>::const_iterator i;
+
+               printf("        if \"ptr\" in keywords:\n");
+               printf("            type = isl.%s(keywords[\"ptr\"])\n",
+                       clazz.fn_type->getNameAsString().c_str());
+
+               for (i = clazz.type_subclasses.begin();
+                    i != clazz.type_subclasses.end(); ++i) {
+                       printf("            if type == %d:\n", i->first);
+                       printf("                return %s(**keywords)\n",
+                               type2python(i->second).c_str());
+               }
+               printf("            raise\n");
+       }
+
+       printf("        return super(%s, cls).__new__(cls)\n",
+               python_name.c_str());
+}
+
 /* Print declarations for methods printing the class representation,
  * provided there is a corresponding *_to_str function.
  *
@@ -622,7 +684,7 @@ void python_generator::print_representation(const isl_class &clazz,
  *
  * To be able to call C functions it is necessary to explicitly set their
  * argument and result types.  Do this for all exported constructors and
- * methods, as well as for the *_to_str method, if it exists.
+ * methods, as well as for the *_to_str and the type function, if they exist.
  * Assuming each exported class has a *_copy and a *_free method,
  * also unconditionally set the type of such methods.
  */
@@ -643,6 +705,8 @@ void python_generator::print_method_types(const isl_class &clazz)
        print_method_type(clazz.fn_free);
        if (clazz.fn_to_str)
                print_method_type(clazz.fn_to_str);
+       if (clazz.fn_type)
+               print_method_type(clazz.fn_type);
 }
 
 /* Print out the definition of this isl_class.
@@ -651,8 +715,8 @@ void python_generator::print_method_types(const isl_class &clazz)
  * If it is, we make sure those superclasses are printed out first.
  *
  * Then we print a constructor with several cases, one for constructing
- * a Python object from a return value and one for each function that
- * was marked as a constructor.
+ * a Python object from a return value, one for each function that
+ * was marked as a constructor and for each type based subclass.
  *
  * Next, we print out some common methods and the methods corresponding
  * to functions that are not marked as constructors.
@@ -663,7 +727,7 @@ void python_generator::print_method_types(const isl_class &clazz)
  */
 void python_generator::print(const isl_class &clazz)
 {
-       string p_name = type2python(clazz.name);
+       string p_name = type2python(clazz.subclass_name);
        set<FunctionDecl *>::const_iterator in;
        map<string, set<FunctionDecl *> >::const_iterator it;
        vector<string> super = find_superclasses(clazz.type);
@@ -671,7 +735,9 @@ void python_generator::print(const isl_class &clazz)
        for (unsigned i = 0; i < super.size(); ++i)
                if (done.find(super[i]) == done.end())
                        print(classes[super[i]]);
-       done.insert(clazz.name);
+       if (clazz.is_type_subclass() && done.find(clazz.name) == done.end())
+               print(classes[clazz.name]);
+       done.insert(clazz.subclass_name);
 
        printf("\n");
        print_class_header(clazz, p_name, super);
@@ -685,11 +751,13 @@ void python_generator::print(const isl_class &clazz)
        for (in = clazz.constructors.begin(); in != clazz.constructors.end();
                ++in)
                print_constructor(clazz, *in);
+       print_upcast_constructors(clazz);
        printf("        raise Error\n");
        printf("    def __del__(self):\n");
        printf("        if hasattr(self, 'ptr'):\n");
        printf("            isl.%s_free(self.ptr)\n", clazz.name.c_str());
 
+       print_new(clazz, p_name);
        print_representation(clazz, p_name);
 
        for (it = clazz.methods.begin(); it != clazz.methods.end(); ++it)
index 27d8889..e2db4eb 100644 (file)
@@ -32,6 +32,9 @@ private:
        void print_restype(FunctionDecl *fd);
        void print(map<string, isl_class> &classes, set<string> &done);
        void print_constructor(const isl_class &clazz, FunctionDecl *method);
+       void print_upcast_constructors(const isl_class &clazz);
+       void print_new(const isl_class &clazz,
+               const string &python_name);
        void print_representation(const isl_class &clazz,
                const string &python_name);
        void print_method_type(FunctionDecl *fd);