compile time/runtime checks for array slice assignments
authorStefan Behnel <scoder@users.berlios.de>
Fri, 14 Nov 2008 18:20:43 +0000 (19:20 +0100)
committerStefan Behnel <scoder@users.berlios.de>
Fri, 14 Nov 2008 18:20:43 +0000 (19:20 +0100)
Cython/Compiler/ExprNodes.py
tests/run/arrayassign.pyx

index 74276592f9e536b92ec84ff81f98c88a2eae3bef..3d15ca9036a1121d514cf759a58a026f0dc50302 100644 (file)
@@ -1830,6 +1830,7 @@ class SliceIndexNode(ExprNode):
             if rhs.type.is_array:
                 # FIXME: we should check both array sizes here
                 array_length = rhs.type.size
+                self.generate_slice_guard_code(code, array_length)
             else:
                 # FIXME: fix the array size according to start/stop
                 array_length = self.base.type.size
@@ -1852,6 +1853,54 @@ class SliceIndexNode(ExprNode):
                 self.start_code(),
                 self.stop_code()))
         self.generate_subexpr_disposal_code(code)
+
+    def generate_slice_guard_code(self, code, target_size):
+        if not self.base.type.is_array:
+            return
+        slice_size = self.base.type.size
+        start = stop = None
+        if self.stop:
+            stop = self.stop.result()
+            try:
+                stop = int(stop)
+                if stop > 0:
+                    slice_size = stop
+                else:
+                    slice_size = self.base.type.size + stop
+                stop = None
+            except ValueError:
+                pass
+        if self.start:
+            start = self.start.result()
+            try:
+                start = int(start)
+                if start < 0:
+                    start = self.base.type.size + start
+                slice_size -= start
+                start = None
+            except ValueError:
+                pass
+        check = None
+        if slice_size < 0:
+            if target_size > 0:
+                error(self.pos, "Assignment to empty slice.")
+        elif start is None and stop is None:
+            # we know the exact slice length
+            if target_size != slice_size:
+                error(self.pos, "Assignment to slice of wrong length, expected %d, got %d" % (
+                        slice_size, target_size))
+        elif start is not None:
+            if stop is None:
+                stop = slice_size
+            check = "(%s)-(%s)" % (stop, start)
+        else: # stop is not None:
+            check = stop
+        if check:
+            code.putln("if (unlikely((%s) != %d)) {" % (check, target_size))
+            code.putln('PyErr_Format(PyExc_ValueError, "Assignment to slice of wrong length, expected %%d, got %%d", %d, (%s));' % (
+                        target_size, check))
+            code.putln(code.error_goto(self.pos))
+            code.putln("}")
     
     def start_code(self):
         if self.start:
index b9018a42ce9f685f13712e4eb7a2e0bd6c87f82c..e0734979541d0fd0340f565dfe007ec7a62755f8 100644 (file)
@@ -8,12 +8,44 @@ __doc__ = u"""
 >>> test_literal_list_slice_start_end()
 (1, 2, 3, 4, 5)
 
->>> test_literal_list_slice_start_param(2)
+>>> test_literal_list_slice_start_param(4)
 (1, 2, 3, 4, 5)
->>> test_literal_list_slice_end_param(4)
+>>> test_literal_list_slice_start_param(3)
+Traceback (most recent call last):
+ValueError: Assignment to slice of wrong length, expected 5, got 6
+>>> test_literal_list_slice_start_param(5)
+Traceback (most recent call last):
+ValueError: Assignment to slice of wrong length, expected 5, got 4
+
+>>> test_literal_list_slice_end_param(5)
 (1, 2, 3, 4, 5)
+>>> test_literal_list_slice_end_param(4)
+Traceback (most recent call last):
+ValueError: Assignment to slice of wrong length, expected 5, got 4
+>>> test_literal_list_slice_end_param(6)
+Traceback (most recent call last):
+ValueError: Assignment to slice of wrong length, expected 5, got 6
+
 >>> test_literal_list_slice_start_end_param(2,7)
 (1, 2, 3, 4, 5)
+>>> test_literal_list_slice_start_end_param(3,7)
+Traceback (most recent call last):
+ValueError: Assignment to slice of wrong length, expected 5, got 4
+>>> test_literal_list_slice_start_end_param(1,7)
+Traceback (most recent call last):
+ValueError: Assignment to slice of wrong length, expected 5, got 6
+>>> test_literal_list_slice_start_end_param(2,6)
+Traceback (most recent call last):
+ValueError: Assignment to slice of wrong length, expected 5, got 4
+>>> test_literal_list_slice_start_end_param(2,8)
+Traceback (most recent call last):
+ValueError: Assignment to slice of wrong length, expected 5, got 6
+>>> test_literal_list_slice_start_end_param(3,6)
+Traceback (most recent call last):
+ValueError: Assignment to slice of wrong length, expected 5, got 3
+>>> test_literal_list_slice_start_end_param(1,8)
+Traceback (most recent call last):
+ValueError: Assignment to slice of wrong length, expected 5, got 7
 
 >>> test_ptr_literal_list_slice_all()
 (1, 2, 3, 4, 5)
@@ -53,7 +85,7 @@ def test_literal_list_slice_start_end():
 def test_literal_list_slice_start_param(s):
     cdef int a[9] # = [9,8,7,6,5,4,3,2,1]
     a[s:] = [1,2,3,4,5]
-    return (a[2], a[3], a[4], a[5], a[6])
+    return (a[4], a[5], a[6], a[7], a[8])
 #    return a[s:]
 
 def test_literal_list_slice_end_param(e):