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