Commit 80359387 authored by Stefan Behnel's avatar Stefan Behnel

optimise MyType.__new__(MyType) into a tp_new() slot call

parent 7138b1d2
......@@ -7,6 +7,7 @@ import UtilNodes
import TypeSlots
import Symtab
import Options
import Naming
from Code import UtilityCode
from StringEncoding import EncodedString, BytesLiteral
......@@ -851,7 +852,10 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
method_handler = self._find_handler(
"method_%s_%s" % (type_name, function.attribute), kwargs)
if method_handler is None:
return node
method_handler = self._find_handler(
"methodany_%s" % function.attribute, kwargs)
if method_handler is None:
return node
if self_arg is not None:
arg_list = [self_arg] + list(arg_list)
if kwargs:
......@@ -1030,6 +1034,55 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
)
return node
### special methods
def _handle_simple_methodany___new__(self, node, args, is_unbound_method):
"""Replace 'exttype.__new__(exttype)' by a call to exttype->tp_new()
"""
obj = node.function.obj
if not is_unbound_method or len(args) != 1:
return node
type_arg = args[0]
if not obj.is_name or not type_arg.is_name:
# play safe
return node
if obj.type != Builtin.type_type or type_arg.type != Builtin.type_type:
# not a known type, play safe
return node
if not type_arg.type_entry or not obj.type_entry:
if obj.name != type_arg.name:
return node
# otherwise, we know it's a type and we know it's the same
# type for both - that should do
elif type_arg.type_entry != obj.type_entry:
# different types - do what CPython does at runtime
error(type_arg.pos, "%s.__new__(%s) is not safe, use %s.__new__()" %
(obj.type_entry.name, type_arg.type_entry.name,
type_arg.type_entry.name))
return node
return_type = None
if obj.type_entry:
return_type = obj.type_entry.type
if return_type is None and type_arg.type_entry:
return_type = type_arg.type_entry.type
if return_type is None:
return_type = PyrexTypes.py_object_type
# FIXME: we could potentially look up the actual tp_new C method
# of the extension type and call that instead of the generic slot
func_type = PyrexTypes.CFuncType(
return_type, [
PyrexTypes.CFuncTypeArg("type", PyrexTypes.py_object_type, None)
])
return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_tp_new", func_type,
args = args,
utility_code = tpnew_utility_code,
is_temp = node.is_temp
)
### methods of builtin types
PyObject_Append_func_type = PyrexTypes.CFuncType(
......@@ -1317,6 +1370,16 @@ static INLINE PyObject* __Pyx_Type(PyObject* o) {
)
tpnew_utility_code = UtilityCode(
proto = """
static INLINE PyObject* __Pyx_tp_new(PyObject* type_obj) {
return (PyObject*) (((PyTypeObject*)(type_obj))->tp_new(
(PyTypeObject*)(type_obj), %(TUPLE)s, NULL));
}
""" % {'TUPLE' : Naming.empty_tuple}
)
class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
"""Calculate the result of constant expressions to store it in
``expr_node.constant_result``, and replace trivial cases by their
......
cdef class MyType:
def __init__(self):
print "INIT"
cdef class MySubType(MyType):
def __init__(self):
print "INIT"
cdef class MyOtherType:
def __init__(self):
print "INIT"
def make_new():
m = MyType.__new__(MyType)
m = MyOtherType.__new__(MyOtherType)
return m
def make_new_error():
m = MySubType.__new__(MyType)
m = MyOtherType.__new__(MyType)
m = MyOtherType.__new__(MySubType)
return m
_ERRORS = """
20:32: MySubType.__new__(MyType) is not safe, use MyType.__new__()
21:34: MyOtherType.__new__(MyType) is not safe, use MyType.__new__()
22:37: MyOtherType.__new__(MySubType) is not safe, use MySubType.__new__()
"""
cimport cython
cdef class MyType:
def __init__(self):
print "INIT"
cdef class MySubType(MyType):
def __init__(self):
print "INIT"
class MyClass(object):
def __init__(self):
print "INIT"
class MyTypeSubClass(MyType):
def __init__(self):
print "INIT"
# only this can be safely optimised:
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
def make_new():
"""
>>> isinstance(make_new(), MyType)
True
"""
m = MyType.__new__(MyType)
return m
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
def make_new_builtin():
"""
>>> isinstance(make_new_builtin(), tuple)
True
"""
m = dict.__new__(dict)
m = list.__new__(list)
m = tuple.__new__(tuple)
return m
# these cannot:
@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
@cython.test_fail_if_path_exists('//PythonCapiCallNode')
def make_new_pyclass():
"""
>>> isinstance(make_new_pyclass(), MyTypeSubClass)
True
"""
m = MyClass.__new__(MyClass)
m = MyTypeSubClass.__new__(MyTypeSubClass)
return m
@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
@cython.test_fail_if_path_exists('//PythonCapiCallNode')
def make_new_args(type t1=None, type t2=None):
"""
>>> isinstance(make_new_args(), MyType)
True
>>> isinstance(make_new_args(MyType), MyType)
True
>>> isinstance(make_new_args(MyType, MyType), MyType)
True
>>> isinstance(make_new_args(MyType, MySubType), MySubType)
Traceback (most recent call last):
TypeError: tp_new.MyType.__new__(tp_new.MySubType) is not safe, use tp_new.MySubType.__new__()
>>> isinstance(make_new_args(MySubType, MyType), MyType)
Traceback (most recent call last):
TypeError: tp_new.MySubType.__new__(tp_new.MyType): tp_new.MyType is not a subtype of tp_new.MySubType
"""
if t1 is None:
t1 = MyType
if t2 is None:
t2 = MyType
m = t1.__new__(t2)
return m
@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
@cython.test_fail_if_path_exists('//PythonCapiCallNode')
def make_new_none(type t1=None, type t2=None):
"""
>>> isinstance(make_new_none(), MyType)
Traceback (most recent call last):
TypeError: object.__new__(X): X is not a type object (NoneType)
"""
m = t1.__new__(t2)
return m
@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
@cython.test_fail_if_path_exists('//PythonCapiCallNode')
def make_new_none_typed(tuple t1=None, tuple t2=None):
"""
>>> isinstance(make_new_none(), MyType)
Traceback (most recent call last):
TypeError: object.__new__(X): X is not a type object (NoneType)
"""
m = t1.__new__(t2)
return m
@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
@cython.test_fail_if_path_exists('//PythonCapiCallNode')
def make_new_untyped(t):
"""
>>> make_new_untyped(None)
Traceback (most recent call last):
TypeError: object.__new__(X): X is not a type object (NoneType)
"""
m = t.__new__(t)
return m
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment