Commit 90c52acc authored by Stefan Behnel's avatar Stefan Behnel

Merge branch 'master' of git+ssh://github.com/cython/cython

parents 3d5523b5 b585cf51
......@@ -59,6 +59,16 @@ Other changes
-------------
0.23.5 (2015-xx-yy)
===================
Bugs fixed
----------
* Fix prange() to behave identically to range(). The end condition was
miscalculated when the range was not exactly divisible by the step.
0.23.4 (2015-10-10)
===================
......
......@@ -398,7 +398,7 @@ def fully_qualified_name(filename):
@cached_function
def parse_dependencies(source_filename):
# Actual parsing is way to slow, so we use regular expressions.
# Actual parsing is way too slow, so we use regular expressions.
# The only catch is that we must strip comments and string
# literals ahead of time.
fh = Utils.open_source_file(source_filename, error_handling='ignore')
......@@ -435,6 +435,8 @@ class DependencyTree(object):
self._transitive_cache = {}
def parse_dependencies(self, source_filename):
if path_exists(source_filename):
source_filename = os.path.normpath(source_filename)
return parse_dependencies(source_filename)
@cached_method
......
......@@ -9625,8 +9625,7 @@ class TypecastNode(ExprNode):
return CoerceIntToBytesNode(self.operand, env)
elif self.operand.type.can_coerce_to_pyobject(env):
self.result_ctype = py_object_type
base_type = self.base_type.analyse(env)
self.operand = self.operand.coerce_to(base_type, env)
self.operand = self.operand.coerce_to(self.type, env)
else:
if self.operand.type.is_ptr:
if not (self.operand.type.base_type.is_void or self.operand.type.base_type.is_struct):
......
......@@ -8186,8 +8186,8 @@ class ParallelStatNode(StatNode, ParallelNode):
code.set_all_labels(self.old_loop_labels + (self.old_return_label,
self.old_error_label))
def end_parallel_control_flow_block(self, code,
break_=False, continue_=False):
def end_parallel_control_flow_block(
self, code, break_=False, continue_=False, return_=False):
"""
This ends the parallel control flow block and based on how the parallel
section was exited, takes the corresponding action. The break_ and
......@@ -8244,6 +8244,7 @@ class ParallelStatNode(StatNode, ParallelNode):
code.put(" case 2: ")
code.put_goto(code.break_label)
if return_:
code.put(" case 3: ")
code.put_goto(code.return_label)
......@@ -8323,10 +8324,12 @@ class ParallelWithBlockNode(ParallelStatNode):
continue_ = code.label_used(code.continue_label)
break_ = code.label_used(code.break_label)
return_ = code.label_used(code.return_label)
self.restore_labels(code)
self.end_parallel_control_flow_block(code, break_=break_,
continue_=continue_)
continue_=continue_,
return_=return_)
self.release_closure_privates(code)
......@@ -8528,6 +8531,7 @@ class ParallelRangeNode(ParallelStatNode):
# the start, stop , step, temps and target cnames
fmt_dict = {
'target': target_index_cname,
'target_type': self.target.type.empty_declaration_code()
}
# Setup start, stop and step, allocating temps if needed
......@@ -8556,7 +8560,7 @@ class ParallelRangeNode(ParallelStatNode):
self.control_flow_var_code_point = code.insertion_point()
# Note: nsteps is private in an outer scope if present
code.putln("%(nsteps)s = (%(stop)s - %(start)s) / %(step)s;" % fmt_dict)
code.putln("%(nsteps)s = (%(stop)s - %(start)s + %(step)s - %(step)s/abs(%(step)s)) / %(step)s;" % fmt_dict)
# The target iteration variable might not be initialized, do it only if
# we are executing at least 1 iteration, otherwise we should leave the
......@@ -8670,7 +8674,7 @@ class ParallelRangeNode(ParallelStatNode):
# at least it doesn't spoil indentation
code.begin_block()
code.putln("%(target)s = %(start)s + %(step)s * %(i)s;" % fmt_dict)
code.putln("%(target)s = (%(target_type)s)(%(start)s + %(step)s * %(i)s);" % fmt_dict)
self.initialize_privates_to_nan(code, exclude=self.target.entry)
if self.is_parallel:
......
......@@ -2703,11 +2703,13 @@ class TransformBuiltinMethods(EnvTransform):
node.function.pos, operand1=node.args[0], operand2=node.args[1])
elif function == u'cast':
if len(node.args) != 2:
error(node.function.pos, u"cast() takes exactly two arguments")
error(node.function.pos,
u"cast() takes exactly two arguments and an optional typecheck keyword")
else:
type = node.args[0].analyse_as_type(self.current_env())
if type:
node = ExprNodes.TypecastNode(node.function.pos, type=type, operand=node.args[1])
node = ExprNodes.TypecastNode(
node.function.pos, type=type, operand=node.args[1], typecheck=False)
else:
error(node.args[0].pos, "Not a type")
elif function == u'sizeof':
......@@ -2753,6 +2755,26 @@ class TransformBuiltinMethods(EnvTransform):
return self._inject_super(node, func_name)
return node
def visit_GeneralCallNode(self, node):
function = node.function.as_cython_attribute()
if function:
args = node.positional_args.args
kwargs = node.keyword_args.compile_time_value(None)
if function == u'cast':
if (len(args) != 2 or len(kwargs) > 1 or
(len(kwargs) == 1 and 'typecheck' not in kwargs)):
error(node.function.pos,
u"cast() takes exactly two arguments and an optional typecheck keyword")
else:
type = args[0].analyse_as_type(self.current_env())
if type:
typecheck = kwargs.get('typecheck', False)
node = ExprNodes.TypecastNode(
node.function.pos, type=type, operand=args[1], typecheck=typecheck)
else:
error(args[0].pos, "Not a type")
return node
class ReplaceFusedTypeChecks(VisitorTransform):
"""
......
......@@ -148,7 +148,9 @@ def cmod(a, b):
# Emulated language constructs
def cast(type, *args):
def cast(type, *args, **kwargs):
kwargs.pop('typecheck', None)
assert not kwargs
if hasattr(type, '__call__'):
return type(*args)
else:
......
......@@ -287,6 +287,15 @@ Further Cython functions and declarations
T = cython.typedef(cython.p_int) # ctypedef int* T
* ``cast`` will (unsafely) reinterpret an expression type. ``cython.cast(T, t)``
is equivalent to ``<T>t``. The first attribute must be a type, the second is
the expression to cast. Specifying the optional keyword argument
``typecheck=True`` has the semantics of ``<T?>t``.
::
t1 = cython.cast(T, t)
t2 = cython.cast(T, t, typecheck=True)
.. _magic_attributes_pxd:
......
......@@ -49,6 +49,26 @@ def test_cast(x):
n = cython.cast(cython.int, x)
return n
@cython.locals(as_list=list)
def test_cast_object(x, typecheck):
"""
>>> test_cast_object([1, 2, 3], True)
[1, 2, 3]
>>> test_cast_object([1, 2, 3], False)
[1, 2, 3]
>>> test_cast_object((1, 2, 3), True)
Traceback (most recent call last):
...
TypeError: Expected list, got tuple
>>> test_cast_object((1, 2, 3), False)
(1, 2, 3)
"""
if typecheck:
as_list = cython.cast(list, x, typecheck=True)
else:
as_list = cython.cast(list, x, typecheck=False)
return as_list
@cython.locals(x=cython.int, y=cython.p_int)
def test_address(x):
"""
......
......@@ -46,6 +46,34 @@ def test_descending_prange():
return sum
def test_prange_matches_range(int start, int stop, int step):
"""
>>> test_prange_matches_range(0, 8, 3)
>>> test_prange_matches_range(0, 9, 3)
>>> test_prange_matches_range(0, 10, 3)
>>> test_prange_matches_range(0, 10, -3)
>>> test_prange_matches_range(0, -10, -3)
>>> test_prange_matches_range(1, -10, -3)
>>> test_prange_matches_range(2, -10, -3)
>>> test_prange_matches_range(3, -10, -3)
"""
cdef int i, range_last, prange_last
prange_set = set()
for i in prange(start, stop, step, nogil=True, num_threads=3):
prange_last = i
with gil:
prange_set.add(i)
range_set = set(range(start, stop, step))
assert range_set == prange_set, "missing: %s extra %s" % (sorted(range_set-prange_set), sorted(prange_set - range_set))
for ii in range(start, stop, step):
range_last = ii
if range_set:
assert prange_last == i
assert range_last == prange_last
def test_propagation():
"""
>>> test_propagation()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment