generate the expected (fast) code for isinstance() checks on builtin types
authorStefan Behnel <scoder@users.berlios.de>
Tue, 20 Jul 2010 22:45:26 +0000 (00:45 +0200)
committerStefan Behnel <scoder@users.berlios.de>
Tue, 20 Jul 2010 22:45:26 +0000 (00:45 +0200)
Cython/Compiler/Optimize.py
tests/run/isinstance.pyx

index 9e661aa6467e60b56104f429ed399b5d8ce9fb56..f25f401a7707235c9ab950aa174a9594cbc47320 100644 (file)
@@ -1839,6 +1839,59 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             is_temp = False)
         return ExprNodes.CastNode(node, PyrexTypes.py_object_type)
 
+    Py_type_check_func_type = PyrexTypes.CFuncType(
+        PyrexTypes.c_bint_type, [
+            PyrexTypes.CFuncTypeArg("arg", PyrexTypes.py_object_type, None)
+            ])
+
+    def _handle_simple_function_isinstance(self, node, pos_args):
+        """Replace isinstance() checks against builtin types by the
+        corresponding C-API call.
+        """
+        if len(pos_args) != 2:
+            return node
+        arg, types = pos_args
+        temp = None
+        if isinstance(types, ExprNodes.TupleNode):
+            types = types.args
+            arg = temp = UtilNodes.ResultRefNode(arg)
+        elif types.type is Builtin.type_type:
+            types = [types]
+        else:
+            return node
+
+        tests = []
+        test_nodes = []
+        env = self.current_env()
+        for test_type_node in types:
+            if not test_type_node.entry:
+                return node
+            entry = env.lookup(test_type_node.entry.name)
+            if not entry or not entry.type or not entry.type.is_builtin_type:
+                return node
+            type_check_function = entry.type.type_check_function(exact=False)
+            if not type_check_function:
+                return node
+            if type_check_function not in tests:
+                tests.append(type_check_function)
+                test_nodes.append(
+                    ExprNodes.PythonCapiCallNode(
+                        test_type_node.pos, type_check_function, self.Py_type_check_func_type,
+                        args = [arg],
+                        is_temp = True,
+                        ))
+
+        def join_with_or(a,b, make_binop_node=ExprNodes.binop_node):
+            or_node = make_binop_node(node.pos, 'or', a, b)
+            or_node.type = PyrexTypes.c_bint_type
+            or_node.is_temp = True
+            return or_node
+
+        test_node = reduce(join_with_or, test_nodes).coerce_to(node.type, env)
+        if temp is not None:
+            test_node = UtilNodes.EvalWithTempExprNode(temp, test_node)
+        return test_node
+
     ### special methods
 
     Pyx_tp_new_func_type = PyrexTypes.CFuncType(
index 4b36aa4e3af168a61eb3c49fc18d1f8c3720ac0c..75a9f5366d72c5ac0c0f77054f3cb7ef1c4ad65c 100644 (file)
@@ -1,9 +1,32 @@
+
+cimport cython
+
 cdef class A:
     pass
 
-def test_all():
+@cython.test_assert_path_exists('//SimpleCallNode//SimpleCallNode')
+@cython.test_fail_if_path_exists('//SimpleCallNode//PythonCapiCallNode',
+                                 '//PythonCapiCallNode//SimpleCallNode')
+def test_non_optimised():
     """
-    >>> test_all()
+    >>> test_non_optimised()
+    True
+    """
+    # Non-optimized
+    cdef object foo = A
+    assert isinstance(A(), foo)
+    assert isinstance(0, (int, long))
+    assert not isinstance(u"xyz", (int, long))
+    assert isinstance(complex(), complex)  # FIXME: this should be optimised, too!
+    return True
+
+@cython.test_assert_path_exists('//PythonCapiCallNode',
+                                '//PythonCapiCallNode//SimpleCallNode')
+@cython.test_fail_if_path_exists('//SimpleCallNode//SimpleCallNode',
+                                 '//SimpleCallNode//PythonCapiCallNode')
+def test_optimised():
+    """
+    >>> test_optimised()
     True
     """
     new_type = type('a',(),{})
@@ -14,23 +37,39 @@ def test_all():
     assert isinstance(int(), int)
     assert isinstance(long(), long)
     assert isinstance(float(), float)
-    assert isinstance(complex(), complex)
     assert isinstance(bytes(), bytes)
     assert isinstance(str(), str)
     assert isinstance(unicode(), unicode)
     assert isinstance(tuple(), tuple)
     assert isinstance(list(), list)
     assert isinstance(dict(), dict)
-#    if py_ver > (2, 3):
-#        assert isinstance(set(), set)
+    assert isinstance(set(), set)
     assert isinstance(slice(0), slice)
+    assert not isinstance(u"foo", int)
     assert isinstance(A, type)
+    return True
+
+@cython.test_assert_path_exists('//PythonCapiCallNode')
+@cython.test_fail_if_path_exists('//SimpleCallNode//SimpleCallNode',
+                                 '//SimpleCallNode//PythonCapiCallNode',
+                                 '//TupleNode//NameNode')
+def test_optimised_tuple():
+    """
+    >>> test_optimised_tuple()
+    True
+    """
+    assert isinstance(bool(),  (bool, int, long, float, bytes, str, unicode, tuple, list, dict, set, slice))
+    assert isinstance(int(),   (bool, int, long, float, bytes, str, unicode, tuple, list, dict, set, slice))
+    assert isinstance(list(),  (bool, int, long, float, bytes, str, unicode, tuple, list, dict, set, slice))
+    return True
+
+@cython.test_assert_path_exists('//SimpleCallNode//SimpleCallNode')
+@cython.test_fail_if_path_exists('//SimpleCallNode//PythonCapiCallNode',
+                                 '//PythonCapiCallNode//SimpleCallNode')
+def test_custom():
+    """
+    >>> test_custom()
+    True
+    """
     assert isinstance(A(), A)
-    assert not isinstance(u"foo", int)
-    
-    # Non-optimized
-    cdef object foo = A
-    assert isinstance(A(), foo)
-    assert isinstance(0, (int, long))
-    assert not isinstance(u"xyz", (int, long))
     return True