From bcba92512d63f02a6711e0e2307eb395562d8540 Mon Sep 17 00:00:00 2001 From: Gerald Dalley Date: Fri, 19 Jul 2013 13:50:00 -0400 Subject: [PATCH] Fixed IndexNode.is_lvalue 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 | 19 +++++++++++++++---- tests/run/lvalue_refs.pyx | 30 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 tests/run/lvalue_refs.pyx diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 3ad886b..a4a2b1b 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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 index 0000000..7e3b4df --- /dev/null +++ b/tests/run/lvalue_refs.pyx @@ -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 -- 2.7.4