cython-devel
changeset 1851:7c9b5a80ccfe
ticket #186: generate more efficient code when tuple-unpacking a typed tuple
| author | Stefan Behnel <scoder@users.berlios.de> |
|---|---|
| date | Fri Mar 13 23:42:09 2009 +0100 (2 years ago) |
| parents | 519f0fb67849 |
| children | cd739394ceb2 |
| files | Cython/Compiler/ExprNodes.py tests/run/tupleassign.pyx |
line diff
1.1 --- a/Cython/Compiler/ExprNodes.py Fri Mar 13 23:24:36 2009 +0100
1.2 +++ b/Cython/Compiler/ExprNodes.py Fri Mar 13 23:42:09 2009 +0100
1.3 @@ -3014,9 +3014,13 @@
1.4 # allocates the temps in a rather hacky way -- the assignment
1.5 # is evaluated twice, within each if-block.
1.6
1.7 - code.putln(
1.8 - "if (PyTuple_CheckExact(%s) && PyTuple_GET_SIZE(%s) == %s) {" % (
1.9 - rhs.py_result(),
1.10 + if rhs.type is tuple_type:
1.11 + tuple_check = "likely(%s != Py_None)"
1.12 + else:
1.13 + tuple_check = "PyTuple_CheckExact(%s)"
1.14 + code.putln(
1.15 + "if (%s && likely(PyTuple_GET_SIZE(%s) == %s)) {" % (
1.16 + tuple_check % rhs.py_result(),
1.17 rhs.py_result(),
1.18 len(self.args)))
1.19 code.putln("PyObject* tuple = %s;" % rhs.py_result())
1.20 @@ -3037,37 +3041,55 @@
1.21
1.22 code.putln("} else {")
1.23
1.24 - code.putln(
1.25 - "%s = PyObject_GetIter(%s); %s" % (
1.26 - self.iterator.result(),
1.27 - rhs.py_result(),
1.28 - code.error_goto_if_null(self.iterator.result(), self.pos)))
1.29 - code.put_gotref(self.iterator.py_result())
1.30 - rhs.generate_disposal_code(code)
1.31 - for i in range(len(self.args)):
1.32 - item = self.unpacked_items[i]
1.33 - unpack_code = "__Pyx_UnpackItem(%s, %d)" % (
1.34 - self.iterator.py_result(), i)
1.35 - code.putln(
1.36 - "%s = %s; %s" % (
1.37 - item.result(),
1.38 - typecast(item.ctype(), py_object_type, unpack_code),
1.39 - code.error_goto_if_null(item.result(), self.pos)))
1.40 - code.put_gotref(item.py_result())
1.41 - value_node = self.coerced_unpacked_items[i]
1.42 - value_node.generate_evaluation_code(code)
1.43 - code.put_error_if_neg(self.pos,
1.44 - "__Pyx_EndUnpack(%s)" % (
1.45 - self.iterator.py_result()))
1.46 - if debug_disposal_code:
1.47 - print("UnpackNode.generate_assignment_code:")
1.48 - print("...generating disposal code for %s" % self.iterator)
1.49 - self.iterator.generate_disposal_code(code)
1.50 - self.iterator.free_temps(code)
1.51 -
1.52 - for i in range(len(self.args)):
1.53 - self.args[i].generate_assignment_code(
1.54 - self.coerced_unpacked_items[i], code)
1.55 + if rhs.type is tuple_type:
1.56 + code.globalstate.use_utility_code(raise_none_iter_error_utility_code)
1.57 + code.putln("if (%s == Py_None) {" %
1.58 + rhs.py_result())
1.59 + code.putln("__Pyx_RaiseNoneNotIterableError();")
1.60 + code.putln("} else if (PyTuple_GET_SIZE(%s) < %s) {" % (
1.61 + rhs.py_result(), len(self.args)))
1.62 + code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
1.63 + code.putln("__Pyx_RaiseNeedMoreValuesError(PyTuple_GET_SIZE(%s));" %
1.64 + rhs.py_result());
1.65 + code.putln("} else {")
1.66 + code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
1.67 + code.putln("__Pyx_RaiseTooManyValuesError();");
1.68 + code.putln("}")
1.69 + code.putln(
1.70 + code.error_goto(self.pos))
1.71 + else:
1.72 + code.putln(
1.73 + "%s = PyObject_GetIter(%s); %s" % (
1.74 + self.iterator.result(),
1.75 + rhs.py_result(),
1.76 + code.error_goto_if_null(self.iterator.result(), self.pos)))
1.77 + code.put_gotref(self.iterator.py_result())
1.78 + rhs.generate_disposal_code(code)
1.79 + for i in range(len(self.args)):
1.80 + item = self.unpacked_items[i]
1.81 + unpack_code = "__Pyx_UnpackItem(%s, %d)" % (
1.82 + self.iterator.py_result(), i)
1.83 + code.putln(
1.84 + "%s = %s; %s" % (
1.85 + item.result(),
1.86 + typecast(item.ctype(), py_object_type, unpack_code),
1.87 + code.error_goto_if_null(item.result(), self.pos)))
1.88 + code.put_gotref(item.py_result())
1.89 + value_node = self.coerced_unpacked_items[i]
1.90 + value_node.generate_evaluation_code(code)
1.91 + code.put_error_if_neg(self.pos,
1.92 + "__Pyx_EndUnpack(%s)" % (
1.93 + self.iterator.py_result()))
1.94 + if debug_disposal_code:
1.95 + print("UnpackNode.generate_assignment_code:")
1.96 + print("...generating disposal code for %s" % self.iterator)
1.97 + self.iterator.generate_disposal_code(code)
1.98 + self.iterator.free_temps(code)
1.99 +
1.100 + for i in range(len(self.args)):
1.101 + self.args[i].generate_assignment_code(
1.102 + self.coerced_unpacked_items[i], code)
1.103 +
1.104 code.putln("}")
1.105 rhs.free_temps(code)
1.106
1.107 @@ -5257,43 +5279,6 @@
1.108
1.109 #------------------------------------------------------------------------------------
1.110
1.111 -unpacking_utility_code = UtilityCode(
1.112 -proto = """
1.113 -static PyObject *__Pyx_UnpackItem(PyObject *, Py_ssize_t index); /*proto*/
1.114 -static int __Pyx_EndUnpack(PyObject *); /*proto*/
1.115 -""",
1.116 -impl = """
1.117 -static PyObject *__Pyx_UnpackItem(PyObject *iter, Py_ssize_t index) {
1.118 - PyObject *item;
1.119 - if (!(item = PyIter_Next(iter))) {
1.120 - if (!PyErr_Occurred()) {
1.121 - PyErr_Format(PyExc_ValueError,
1.122 - #if PY_VERSION_HEX < 0x02050000
1.123 - "need more than %d values to unpack", (int)index);
1.124 - #else
1.125 - "need more than %zd values to unpack", index);
1.126 - #endif
1.127 - }
1.128 - }
1.129 - return item;
1.130 -}
1.131 -
1.132 -static int __Pyx_EndUnpack(PyObject *iter) {
1.133 - PyObject *item;
1.134 - if ((item = PyIter_Next(iter))) {
1.135 - Py_DECREF(item);
1.136 - PyErr_SetString(PyExc_ValueError, "too many values to unpack");
1.137 - return -1;
1.138 - }
1.139 - else if (!PyErr_Occurred())
1.140 - return 0;
1.141 - else
1.142 - return -1;
1.143 -}
1.144 -""")
1.145 -
1.146 -#------------------------------------------------------------------------------------
1.147 -
1.148 type_test_utility_code = UtilityCode(
1.149 proto = """
1.150 static int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type); /*proto*/
1.151 @@ -5531,18 +5516,89 @@
1.152 proto = """
1.153 static INLINE void __Pyx_RaiseNoneAttributeError(const char* attrname);
1.154 """,
1.155 -impl = """
1.156 +impl = '''
1.157 static INLINE void __Pyx_RaiseNoneAttributeError(const char* attrname) {
1.158 PyErr_Format(PyExc_AttributeError, "'NoneType' object has no attribute '%s'", attrname);
1.159 }
1.160 -""")
1.161 +''')
1.162
1.163 raise_noneindex_error_utility_code = UtilityCode(
1.164 proto = """
1.165 static INLINE void __Pyx_RaiseNoneIndexingError(void);
1.166 """,
1.167 -impl = """
1.168 +impl = '''
1.169 static INLINE void __Pyx_RaiseNoneIndexingError(void) {
1.170 PyErr_SetString(PyExc_TypeError, "'NoneType' object is unsubscriptable");
1.171 }
1.172 -""")
1.173 +''')
1.174 +
1.175 +raise_none_iter_error_utility_code = UtilityCode(
1.176 +proto = """
1.177 +static INLINE void __Pyx_RaiseNoneNotIterableError(void);
1.178 +""",
1.179 +impl = '''
1.180 +static INLINE void __Pyx_RaiseNoneNotIterableError(void) {
1.181 + PyErr_SetString(PyExc_TypeError, "'NoneType' object is iterable");
1.182 +}
1.183 +''')
1.184 +
1.185 +raise_too_many_values_to_unpack = UtilityCode(
1.186 +proto = """
1.187 +static INLINE void __Pyx_RaiseTooManyValuesError(void);
1.188 +""",
1.189 +impl = '''
1.190 +static INLINE void __Pyx_RaiseTooManyValuesError(void) {
1.191 + PyErr_SetString(PyExc_ValueError, "too many values to unpack");
1.192 +}
1.193 +''')
1.194 +
1.195 +raise_need_more_values_to_unpack = UtilityCode(
1.196 +proto = """
1.197 +static INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index);
1.198 +""",
1.199 +impl = '''
1.200 +static INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index) {
1.201 + PyErr_Format(PyExc_ValueError,
1.202 + #if PY_VERSION_HEX < 0x02050000
1.203 + "need more than %d value%s to unpack", (int)index,
1.204 + #else
1.205 + "need more than %zd value%s to unpack", index,
1.206 + #endif
1.207 + (index == 1) ? "" : "s");
1.208 +}
1.209 +''')
1.210 +
1.211 +#------------------------------------------------------------------------------------
1.212 +
1.213 +unpacking_utility_code = UtilityCode(
1.214 +proto = """
1.215 +static PyObject *__Pyx_UnpackItem(PyObject *, Py_ssize_t index); /*proto*/
1.216 +static int __Pyx_EndUnpack(PyObject *); /*proto*/
1.217 +""",
1.218 +impl = """
1.219 +static PyObject *__Pyx_UnpackItem(PyObject *iter, Py_ssize_t index) {
1.220 + PyObject *item;
1.221 + if (!(item = PyIter_Next(iter))) {
1.222 + if (!PyErr_Occurred()) {
1.223 + __Pyx_RaiseNeedMoreValuesError(index);
1.224 + }
1.225 + }
1.226 + return item;
1.227 +}
1.228 +
1.229 +static int __Pyx_EndUnpack(PyObject *iter) {
1.230 + PyObject *item;
1.231 + if ((item = PyIter_Next(iter))) {
1.232 + Py_DECREF(item);
1.233 + __Pyx_RaiseTooManyValuesError();
1.234 + return -1;
1.235 + }
1.236 + else if (!PyErr_Occurred())
1.237 + return 0;
1.238 + else
1.239 + return -1;
1.240 +}
1.241 +""",
1.242 +requires = [raise_need_more_values_to_unpack,
1.243 + raise_too_many_values_to_unpack]
1.244 +)
2.1 --- a/tests/run/tupleassign.pyx Fri Mar 13 23:24:36 2009 +0100
2.2 +++ b/tests/run/tupleassign.pyx Fri Mar 13 23:42:09 2009 +0100
2.3 @@ -3,6 +3,8 @@
2.4 (1, 2, 3)
2.5 >>> assign3(t)
2.6 (1, 2, 3)
2.7 +>>> assign3_typed(t)
2.8 +(1, 2, 3)
2.9 >>> assign3_int(l)
2.10 (1, 2, 3)
2.11 >>> assign3_mixed1(l)
2.12 @@ -12,6 +14,40 @@
2.13 >>> assign3_mixed3(l)
2.14 (1, 2, 3)
2.15
2.16 +>>> assign3_typed(l)
2.17 +Traceback (most recent call last):
2.18 +TypeError: Argument 't' has incorrect type (expected tuple, got list)
2.19 +
2.20 +>>> a,b,c = (1,) # doctest: +ELLIPSIS
2.21 +Traceback (most recent call last):
2.22 +ValueError: ...
2.23 +>>> assign3((1,))
2.24 +Traceback (most recent call last):
2.25 +ValueError: need more than 1 value to unpack
2.26 +>>> assign3_typed((1,))
2.27 +Traceback (most recent call last):
2.28 +ValueError: need more than 1 value to unpack
2.29 +
2.30 +>>> a,b,c = (1,2) # doctest: +ELLIPSIS
2.31 +Traceback (most recent call last):
2.32 +ValueError: ...
2.33 +>>> assign3((1,2))
2.34 +Traceback (most recent call last):
2.35 +ValueError: need more than 2 values to unpack
2.36 +>>> assign3_typed((1,2))
2.37 +Traceback (most recent call last):
2.38 +ValueError: need more than 2 values to unpack
2.39 +
2.40 +>>> a,b,c = (1,2,3,4)
2.41 +Traceback (most recent call last):
2.42 +ValueError: too many values to unpack
2.43 +>>> assign3((1,2,3,4))
2.44 +Traceback (most recent call last):
2.45 +ValueError: too many values to unpack
2.46 +>>> assign3_typed((1,2,3,4))
2.47 +Traceback (most recent call last):
2.48 +ValueError: too many values to unpack
2.49 +
2.50 >>> a,b = 99,98
2.51 >>> a,b = t
2.52 Traceback (most recent call last):
2.53 @@ -47,6 +83,10 @@
2.54 a,b,c = t
2.55 return (a,b,c)
2.56
2.57 +def assign3_typed(tuple t):
2.58 + a,b,c = t
2.59 + return (a,b,c)
2.60 +
2.61 def assign3_int(t):
2.62 cdef int a,b,c
2.63 a,b,c = t
