Fixed IndexNode.is_lvalue
authorGerald Dalley <dalleyg@deshaw.com>
Fri, 19 Jul 2013 17:50:00 +0000 (13:50 -0400)
committerGerald Dalley <dalleyg@deshaw.com>
Fri, 19 Jul 2013 18:27:36 +0000 (14:27 -0400)
In C/C++, almost all index operator expressions return lvalues. We now
allow anything that doesn't look like it resolves to assigning to a
whole array object to be considered an lvalue.

Before this commit, using the index operator on containers that return
references would crash the Cython compiler.  run/tests/lvalue_refs.pyx
contains an example of code that previously crashed the compiler.

Author: Gerald Dalley

Cython/Compiler/ExprNodes.py
tests/run/lvalue_refs.pyx [new file with mode: 0644]

index 3ad886b..a4a2b1b 100755 (executable)
@@ -3193,11 +3193,22 @@ class IndexNode(ExprNode):
         return self.base.check_const_addr() and self.index.check_const()
 
     def is_lvalue(self):
-        base_type = self.base.type
-        if self.type.is_ptr or self.type.is_array:
-            return not base_type.base_type.is_array
-        else:
+        # NOTE: references currently have both is_reference and is_ptr
+        # set.  Since pointers and references have different lvalue
+        # rules, we must be careful to separate the two.
+        if self.type.is_reference:
+            if self.type.ref_base_type.is_array:
+                # fixed-sized arrays aren't l-values
+                return False
+        elif self.type.is_ptr:
+            # non-const pointers can always be reassigned
             return True
+        elif self.type.is_array:
+            # fixed-sized arrays aren't l-values
+            return False
+        # Just about everything else returned by the index operator
+        # can be an lvalue.
+        return True
 
     def calculate_result_code(self):
         if self.is_buffer_access:
diff --git a/tests/run/lvalue_refs.pyx b/tests/run/lvalue_refs.pyx
new file mode 100644 (file)
index 0000000..7e3b4df
--- /dev/null
@@ -0,0 +1,30 @@
+# tag: cpp
+
+from libcpp.vector cimport vector
+
+__doc__ = u"""
+   >>> test_lvalue_ref_assignment()
+"""
+
+ctypedef double*  dp
+ctypedef double** dpp
+
+cdef void foo(vector[dpp] &bar, vector[vector[dp]] &baz) nogil:
+    bar[0] = &baz[0][0]
+
+def test_lvalue_ref_assignment():
+    cdef vector[dpp]        bar
+    cdef vector[vector[dp]] baz
+    cdef vector[double]     data
+    cdef dp                 bongle = &data[0]
+
+    bar.resize(1)
+    bar[0] = NULL
+    baz.resize(1)
+    baz[0].resize(1)
+    baz[0][0] = bongle
+
+    foo(bar, baz)
+
+    assert bar[0] == &baz[0][0]
+    assert bar[0][0] == bongle