Commit 472423b7 authored by Stefan Behnel's avatar Stefan Behnel

optimise frozenset(it) into a call to PyFrozenSet_New(it)

parent 54adfe2d
......@@ -1986,47 +1986,64 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
is_temp = node.is_temp
)
PyObject_AsDouble_func_type = PyrexTypes.CFuncType(
PyrexTypes.c_double_type, [
PyrexTypes.CFuncTypeArg("obj", PyrexTypes.py_object_type, None),
],
exception_value = "((double)-1)",
exception_check = True)
PySet_New_func_type = PyrexTypes.CFuncType(
Builtin.set_type, [
PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None)
])
def _handle_simple_function_set(self, node, function, pos_args):
if len(pos_args) == 1:
if pos_args[0].is_sequence_constructor:
# We can optimise set([x,y,z]) safely into a set literal,
# but only if we create all items before adding them -
# adding an item may raise an exception if it is not
# hashable, but creating the later items may have
# side-effects.
args = []
temps = []
for arg in pos_args[0].args:
if not arg.is_simple():
arg = UtilNodes.LetRefNode(arg)
temps.append(arg)
args.append(arg)
result = ExprNodes.SetNode(node.pos, is_temp=1, args=args)
for temp in temps[::-1]:
result = UtilNodes.EvalWithTempExprNode(temp, result)
return result
else:
# PySet_New(it) is better than a generic Python call to set(it)
return ExprNodes.PythonCapiCallNode(
node.pos, "PySet_New",
self.PySet_New_func_type,
args=pos_args,
is_temp=node.is_temp,
utility_code=UtilityCode.load_cached('pyset_compat', 'Builtins.c'),
py_name="set")
return node
if len(pos_args) != 1:
return node
if pos_args[0].is_sequence_constructor:
# We can optimise set([x,y,z]) safely into a set literal,
# but only if we create all items before adding them -
# adding an item may raise an exception if it is not
# hashable, but creating the later items may have
# side-effects.
args = []
temps = []
for arg in pos_args[0].args:
if not arg.is_simple():
arg = UtilNodes.LetRefNode(arg)
temps.append(arg)
args.append(arg)
result = ExprNodes.SetNode(node.pos, is_temp=1, args=args)
for temp in temps[::-1]:
result = UtilNodes.EvalWithTempExprNode(temp, result)
return result
else:
# PySet_New(it) is better than a generic Python call to set(it)
return ExprNodes.PythonCapiCallNode(
node.pos, "PySet_New",
self.PySet_New_func_type,
args=pos_args,
is_temp=node.is_temp,
utility_code=UtilityCode.load_cached('pyset_compat', 'Builtins.c'),
py_name="set")
PyFrozenSet_New_func_type = PyrexTypes.CFuncType(
Builtin.frozenset_type, [
PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None)
])
def _handle_simple_function_frozenset(self, node, function, pos_args):
if len(pos_args) != 1:
return node
# PyFrozenSet_New(it) is better than a generic Python call to frozenset(it)
return ExprNodes.PythonCapiCallNode(
node.pos, "PyFrozenSet_New",
self.PyFrozenSet_New_func_type,
args=pos_args,
is_temp=node.is_temp,
utility_code=UtilityCode.load_cached('pyset_compat', 'Builtins.c'),
py_name="frozenset")
PyObject_AsDouble_func_type = PyrexTypes.CFuncType(
PyrexTypes.c_double_type, [
PyrexTypes.CFuncTypeArg("obj", PyrexTypes.py_object_type, None),
],
exception_value = "((double)-1)",
exception_check = True)
def _handle_simple_function_float(self, node, function, pos_args):
"""Transform float() into either a C type cast or a faster C
......
# Py2.3 doesn't have the 'set' builtin type, but Cython does :)
_set = set
_frozenset = frozenset
cimport cython
def cython_set():
"""
>>> cython_set() is _set
......@@ -12,6 +14,16 @@ def cython_set():
assert set is cython.set
return cython.set
def cython_frozenset():
"""
>>> cython_frozenset() is _frozenset
True
"""
assert frozenset is cython.frozenset
return cython.frozenset
def cython_set_override():
"""
>>> cython_set_override() is _set
......@@ -20,6 +32,16 @@ def cython_set_override():
set = 1
return cython.set
def cython_frozenset_override():
"""
>>> cython_frozenset_override() is _frozenset
True
"""
frozenset = 1
return cython.frozenset
def test_set_literal():
"""
>>> type(test_set_literal()) is _set
......@@ -30,6 +52,7 @@ def test_set_literal():
cdef set s1 = {1,'a',1,'b','a'}
return s1
def test_set_add():
"""
>>> type(test_set_add()) is _set
......@@ -45,6 +68,7 @@ def test_set_add():
s1.add((1,2))
return s1
def test_set_clear():
"""
>>> type(test_set_clear()) is _set
......@@ -57,6 +81,7 @@ def test_set_clear():
s1.clear()
return s1
def test_set_clear_None():
"""
>>> test_set_clear_None()
......@@ -66,6 +91,7 @@ def test_set_clear_None():
cdef set s1 = None
s1.clear()
def test_set_list_comp():
"""
>>> type(test_set_list_comp()) is _set
......@@ -77,6 +103,19 @@ def test_set_list_comp():
s1 = set([i%3 for i in range(5)])
return s1
def test_frozenset_list_comp():
"""
>>> type(test_frozenset_list_comp()) is _frozenset
True
>>> sorted(test_frozenset_list_comp())
[0, 1, 2]
"""
cdef frozenset s1
s1 = frozenset([i%3 for i in range(5)])
return s1
def test_set_pop():
"""
>>> type(test_set_pop()) is _set
......@@ -90,6 +129,7 @@ def test_set_pop():
two = s1.pop()
return s1
@cython.test_fail_if_path_exists("//SimpleCallNode//NameNode")
def test_object_pop(s):
"""
......@@ -101,6 +141,7 @@ def test_object_pop(s):
"""
return s.pop()
def test_set_discard():
"""
>>> type(test_set_discard()) is _set
......@@ -117,6 +158,7 @@ def test_set_discard():
s1.discard(3)
return s1
def test_set_sideeffect_unhashable_failure():
"""
>>> test_set_sideeffect_unhashable_failure()
......@@ -135,8 +177,31 @@ def test_set_sideeffect_unhashable_failure():
else: assert False, "expected exception not raised"
return L
def test_frozenset_sideeffect_unhashable_failure():
"""
>>> test_frozenset_sideeffect_unhashable_failure()
[2, 4, 5]
"""
L = []
def sideeffect(x):
L.append(x)
return x
def unhashable_value(x):
L.append(x)
return set()
try:
s = frozenset([1,sideeffect(2),3,unhashable_value(4),sideeffect(5)])
except TypeError: pass
else: assert False, "expected exception not raised"
return L
@cython.test_assert_path_exists("//SetNode")
@cython.test_fail_if_path_exists("//SimpleCallNode")
@cython.test_fail_if_path_exists(
"//SimpleCallNode",
"//PythonCapiCallNode"
)
def test_set_of_list():
"""
>>> s = test_set_of_list()
......@@ -147,6 +212,20 @@ def test_set_of_list():
"""
return set([1, 2, 3])
@cython.test_assert_path_exists("//PythonCapiCallNode")
@cython.test_fail_if_path_exists("//SetNode")
def test_frozenset_of_list():
"""
>>> s = test_frozenset_of_list()
>>> isinstance(s, _frozenset)
True
>>> sorted(s)
[1, 2, 3]
"""
return frozenset([1, 2, 3])
@cython.test_assert_path_exists("//SetNode")
@cython.test_fail_if_path_exists("//SimpleCallNode")
def test_set_of_tuple():
......@@ -159,6 +238,52 @@ def test_set_of_tuple():
"""
return set((1, 2, 3))
@cython.test_assert_path_exists("//PythonCapiCallNode")
@cython.test_fail_if_path_exists("//SetNode")
def test_frozenset_of_tuple():
"""
>>> s = test_frozenset_of_tuple()
>>> isinstance(s, _frozenset)
True
>>> sorted(s)
[1, 2, 3]
"""
return frozenset((1, 2, 3))
@cython.test_assert_path_exists("//PythonCapiCallNode")
@cython.test_fail_if_path_exists(
"//SimpleCallNode",
"//SetNode"
)
def test_set_of_iterable(x):
"""
>>> s = test_set_of_iterable([1, 2, 3])
>>> isinstance(s, _set)
True
>>> sorted(s)
[1, 2, 3]
"""
return set(x)
@cython.test_assert_path_exists("//PythonCapiCallNode")
@cython.test_fail_if_path_exists(
"//SimpleCallNode",
"//SetNode"
)
def test_frozenset_of_iterable(x):
"""
>>> s = test_frozenset_of_iterable([1, 2, 3])
>>> isinstance(s, _frozenset)
True
>>> sorted(s)
[1, 2, 3]
"""
return frozenset(x)
def sorted(it):
# Py3 can't compare different types
chars = []
......
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