Commit 3fa4d801 authored by Stefan Behnel's avatar Stefan Behnel

ticket #186: generate more efficient code when tuple-unpacking a typed tuple

parent 2a912f00
...@@ -3014,9 +3014,13 @@ class SequenceNode(NewTempExprNode): ...@@ -3014,9 +3014,13 @@ class SequenceNode(NewTempExprNode):
# allocates the temps in a rather hacky way -- the assignment # allocates the temps in a rather hacky way -- the assignment
# is evaluated twice, within each if-block. # is evaluated twice, within each if-block.
if rhs.type is tuple_type:
tuple_check = "likely(%s != Py_None)"
else:
tuple_check = "PyTuple_CheckExact(%s)"
code.putln( code.putln(
"if (PyTuple_CheckExact(%s) && PyTuple_GET_SIZE(%s) == %s) {" % ( "if (%s && likely(PyTuple_GET_SIZE(%s) == %s)) {" % (
rhs.py_result(), tuple_check % rhs.py_result(),
rhs.py_result(), rhs.py_result(),
len(self.args))) len(self.args)))
code.putln("PyObject* tuple = %s;" % rhs.py_result()) code.putln("PyObject* tuple = %s;" % rhs.py_result())
...@@ -3037,6 +3041,23 @@ class SequenceNode(NewTempExprNode): ...@@ -3037,6 +3041,23 @@ class SequenceNode(NewTempExprNode):
code.putln("} else {") code.putln("} else {")
if rhs.type is tuple_type:
code.globalstate.use_utility_code(raise_none_iter_error_utility_code)
code.putln("if (%s == Py_None) {" %
rhs.py_result())
code.putln("__Pyx_RaiseNoneNotIterableError();")
code.putln("} else if (PyTuple_GET_SIZE(%s) < %s) {" % (
rhs.py_result(), len(self.args)))
code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
code.putln("__Pyx_RaiseNeedMoreValuesError(PyTuple_GET_SIZE(%s));" %
rhs.py_result());
code.putln("} else {")
code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
code.putln("__Pyx_RaiseTooManyValuesError();");
code.putln("}")
code.putln(
code.error_goto(self.pos))
else:
code.putln( code.putln(
"%s = PyObject_GetIter(%s); %s" % ( "%s = PyObject_GetIter(%s); %s" % (
self.iterator.result(), self.iterator.result(),
...@@ -3068,6 +3089,7 @@ class SequenceNode(NewTempExprNode): ...@@ -3068,6 +3089,7 @@ class SequenceNode(NewTempExprNode):
for i in range(len(self.args)): for i in range(len(self.args)):
self.args[i].generate_assignment_code( self.args[i].generate_assignment_code(
self.coerced_unpacked_items[i], code) self.coerced_unpacked_items[i], code)
code.putln("}") code.putln("}")
rhs.free_temps(code) rhs.free_temps(code)
...@@ -5257,43 +5279,6 @@ bad: ...@@ -5257,43 +5279,6 @@ bad:
#------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------
unpacking_utility_code = UtilityCode(
proto = """
static PyObject *__Pyx_UnpackItem(PyObject *, Py_ssize_t index); /*proto*/
static int __Pyx_EndUnpack(PyObject *); /*proto*/
""",
impl = """
static PyObject *__Pyx_UnpackItem(PyObject *iter, Py_ssize_t index) {
PyObject *item;
if (!(item = PyIter_Next(iter))) {
if (!PyErr_Occurred()) {
PyErr_Format(PyExc_ValueError,
#if PY_VERSION_HEX < 0x02050000
"need more than %d values to unpack", (int)index);
#else
"need more than %zd values to unpack", index);
#endif
}
}
return item;
}
static int __Pyx_EndUnpack(PyObject *iter) {
PyObject *item;
if ((item = PyIter_Next(iter))) {
Py_DECREF(item);
PyErr_SetString(PyExc_ValueError, "too many values to unpack");
return -1;
}
else if (!PyErr_Occurred())
return 0;
else
return -1;
}
""")
#------------------------------------------------------------------------------------
type_test_utility_code = UtilityCode( type_test_utility_code = UtilityCode(
proto = """ proto = """
static int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type); /*proto*/ static int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type); /*proto*/
...@@ -5531,18 +5516,89 @@ raise_noneattr_error_utility_code = UtilityCode( ...@@ -5531,18 +5516,89 @@ raise_noneattr_error_utility_code = UtilityCode(
proto = """ proto = """
static INLINE void __Pyx_RaiseNoneAttributeError(const char* attrname); static INLINE void __Pyx_RaiseNoneAttributeError(const char* attrname);
""", """,
impl = """ impl = '''
static INLINE void __Pyx_RaiseNoneAttributeError(const char* attrname) { static INLINE void __Pyx_RaiseNoneAttributeError(const char* attrname) {
PyErr_Format(PyExc_AttributeError, "'NoneType' object has no attribute '%s'", attrname); PyErr_Format(PyExc_AttributeError, "'NoneType' object has no attribute '%s'", attrname);
} }
""") ''')
raise_noneindex_error_utility_code = UtilityCode( raise_noneindex_error_utility_code = UtilityCode(
proto = """ proto = """
static INLINE void __Pyx_RaiseNoneIndexingError(void); static INLINE void __Pyx_RaiseNoneIndexingError(void);
""", """,
impl = """ impl = '''
static INLINE void __Pyx_RaiseNoneIndexingError(void) { static INLINE void __Pyx_RaiseNoneIndexingError(void) {
PyErr_SetString(PyExc_TypeError, "'NoneType' object is unsubscriptable"); PyErr_SetString(PyExc_TypeError, "'NoneType' object is unsubscriptable");
} }
""") ''')
raise_none_iter_error_utility_code = UtilityCode(
proto = """
static INLINE void __Pyx_RaiseNoneNotIterableError(void);
""",
impl = '''
static INLINE void __Pyx_RaiseNoneNotIterableError(void) {
PyErr_SetString(PyExc_TypeError, "'NoneType' object is iterable");
}
''')
raise_too_many_values_to_unpack = UtilityCode(
proto = """
static INLINE void __Pyx_RaiseTooManyValuesError(void);
""",
impl = '''
static INLINE void __Pyx_RaiseTooManyValuesError(void) {
PyErr_SetString(PyExc_ValueError, "too many values to unpack");
}
''')
raise_need_more_values_to_unpack = UtilityCode(
proto = """
static INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index);
""",
impl = '''
static INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index) {
PyErr_Format(PyExc_ValueError,
#if PY_VERSION_HEX < 0x02050000
"need more than %d value%s to unpack", (int)index,
#else
"need more than %zd value%s to unpack", index,
#endif
(index == 1) ? "" : "s");
}
''')
#------------------------------------------------------------------------------------
unpacking_utility_code = UtilityCode(
proto = """
static PyObject *__Pyx_UnpackItem(PyObject *, Py_ssize_t index); /*proto*/
static int __Pyx_EndUnpack(PyObject *); /*proto*/
""",
impl = """
static PyObject *__Pyx_UnpackItem(PyObject *iter, Py_ssize_t index) {
PyObject *item;
if (!(item = PyIter_Next(iter))) {
if (!PyErr_Occurred()) {
__Pyx_RaiseNeedMoreValuesError(index);
}
}
return item;
}
static int __Pyx_EndUnpack(PyObject *iter) {
PyObject *item;
if ((item = PyIter_Next(iter))) {
Py_DECREF(item);
__Pyx_RaiseTooManyValuesError();
return -1;
}
else if (!PyErr_Occurred())
return 0;
else
return -1;
}
""",
requires = [raise_need_more_values_to_unpack,
raise_too_many_values_to_unpack]
)
...@@ -3,6 +3,8 @@ __doc__ = u""" ...@@ -3,6 +3,8 @@ __doc__ = u"""
(1, 2, 3) (1, 2, 3)
>>> assign3(t) >>> assign3(t)
(1, 2, 3) (1, 2, 3)
>>> assign3_typed(t)
(1, 2, 3)
>>> assign3_int(l) >>> assign3_int(l)
(1, 2, 3) (1, 2, 3)
>>> assign3_mixed1(l) >>> assign3_mixed1(l)
...@@ -12,6 +14,40 @@ __doc__ = u""" ...@@ -12,6 +14,40 @@ __doc__ = u"""
>>> assign3_mixed3(l) >>> assign3_mixed3(l)
(1, 2, 3) (1, 2, 3)
>>> assign3_typed(l)
Traceback (most recent call last):
TypeError: Argument 't' has incorrect type (expected tuple, got list)
>>> a,b,c = (1,) # doctest: +ELLIPSIS
Traceback (most recent call last):
ValueError: ...
>>> assign3((1,))
Traceback (most recent call last):
ValueError: need more than 1 value to unpack
>>> assign3_typed((1,))
Traceback (most recent call last):
ValueError: need more than 1 value to unpack
>>> a,b,c = (1,2) # doctest: +ELLIPSIS
Traceback (most recent call last):
ValueError: ...
>>> assign3((1,2))
Traceback (most recent call last):
ValueError: need more than 2 values to unpack
>>> assign3_typed((1,2))
Traceback (most recent call last):
ValueError: need more than 2 values to unpack
>>> a,b,c = (1,2,3,4)
Traceback (most recent call last):
ValueError: too many values to unpack
>>> assign3((1,2,3,4))
Traceback (most recent call last):
ValueError: too many values to unpack
>>> assign3_typed((1,2,3,4))
Traceback (most recent call last):
ValueError: too many values to unpack
>>> a,b = 99,98 >>> a,b = 99,98
>>> a,b = t >>> a,b = t
Traceback (most recent call last): Traceback (most recent call last):
...@@ -47,6 +83,10 @@ def assign3(t): ...@@ -47,6 +83,10 @@ def assign3(t):
a,b,c = t a,b,c = t
return (a,b,c) return (a,b,c)
def assign3_typed(tuple t):
a,b,c = t
return (a,b,c)
def assign3_int(t): def assign3_int(t):
cdef int a,b,c cdef int a,b,c
a,b,c = t a,b,c = t
......
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