Partial merge of trunk progress. Some tests still fail.
[cython.git] / Cython / Compiler / Optimize.py
index f70a68e3eda58b335c28e9c40699fefcd4bc59be..8552645777555d48887ed6a2c6fb2fc92d336b07 100644 (file)
@@ -1,3 +1,10 @@
+
+import cython
+from cython import set
+cython.declare(UtilityCode=object, EncodedString=object, BytesLiteral=object,
+               Nodes=object, ExprNodes=object, PyrexTypes=object, Builtin=object,
+               UtilNodes=object, Naming=object)
+
 import Nodes
 import ExprNodes
 import PyrexTypes
@@ -17,14 +24,14 @@ from ParseTreeTransforms import SkipDeclarations
 import codecs
 
 try:
-    reduce
-except NameError:
+    from __builtin__ import reduce
+except ImportError:
     from functools import reduce
 
 try:
-    set
-except NameError:
-    from sets import Set as set
+    from __builtin__ import basestring
+except ImportError:
+    basestring = str # Python 3
 
 class FakePythonEnv(object):
     "A fake environment for creating type test nodes etc."
@@ -67,12 +74,13 @@ class IterationTransform(Visitor.VisitorTransform):
     PyDict_Next_name = EncodedString("PyDict_Next")
 
     PyDict_Next_entry = Symtab.Entry(
-        PyDict_Next_name, PyDict_Next_name, PyDict_Next_func_type)
+        name = PyDict_Next_name, cname = PyDict_Next_name, type = PyDict_Next_func_type)
 
     visit_Node = Visitor.VisitorTransform.recurse_to_children
 
     def visit_ModuleNode(self, node):
         self.current_scope = node.scope
+        self.module_scope = node.scope
         self.visitchildren(node)
         return node
 
@@ -82,17 +90,17 @@ class IterationTransform(Visitor.VisitorTransform):
         self.visitchildren(node)
         self.current_scope = oldscope
         return node
-    
+
     def visit_PrimaryCmpNode(self, node):
         if node.is_ptr_contains():
-        
+
             # for t in operand2:
             #     if operand1 == t:
             #         res = True
             #         break
             # else:
             #     res = False
-            
+
             pos = node.pos
             res_handle = UtilNodes.TempHandle(PyrexTypes.c_bint_type)
             res = res_handle.ref(pos)
@@ -106,7 +114,7 @@ class IterationTransform(Visitor.VisitorTransform):
             cmp_node = ExprNodes.PrimaryCmpNode(
                 pos, operator=u'==', operand1=node.operand1, operand2=target)
             if_body = Nodes.StatListNode(
-                pos, 
+                pos,
                 stats = [Nodes.SingleAssignmentNode(pos, lhs=result_ref, rhs=ExprNodes.BoolNode(pos, value=1)),
                          Nodes.BreakStatNode(pos)])
             if_node = Nodes.IfStatNode(
@@ -125,7 +133,7 @@ class IterationTransform(Visitor.VisitorTransform):
             for_loop.analyse_expressions(self.current_scope)
             for_loop = self(for_loop)
             new_node = UtilNodes.TempResultFromStatNode(result_ref, for_loop)
-            
+
             if node.operator == 'not_in':
                 new_node = ExprNodes.NotNode(pos, operand=new_node)
             return new_node
@@ -137,7 +145,7 @@ class IterationTransform(Visitor.VisitorTransform):
     def visit_ForInStatNode(self, node):
         self.visitchildren(node)
         return self._optimise_for_loop(node)
-    
+
     def _optimise_for_loop(self, node):
         iterator = node.iterator.sequence
         if iterator.type is Builtin.dict_type:
@@ -168,12 +176,13 @@ class IterationTransform(Visitor.VisitorTransform):
             dict_obj = function.obj
             method = function.attribute
 
+            is_py3 = self.module_scope.context.language_level >= 3
             keys = values = False
-            if method == 'iterkeys':
+            if method == 'iterkeys' or (is_py3 and method == 'keys'):
                 keys = True
-            elif method == 'itervalues':
+            elif method == 'itervalues' or (is_py3 and method == 'values'):
                 values = True
-            elif method == 'iteritems':
+            elif method == 'iteritems' or (is_py3 and method == 'items'):
                 keys = values = True
             else:
                 return node
@@ -366,6 +375,9 @@ class IterationTransform(Visitor.VisitorTransform):
                 base=counter_temp,
                 type=Builtin.bytes_type,
                 is_temp=1)
+        elif node.target.type.is_ptr and not node.target.type.assignable_from(ptr_type.base_type):
+            # Allow iteration with pointer target to avoid copy.
+            target_value = counter_temp
         else:
             target_value = ExprNodes.IndexNode(
                 node.target.pos,
@@ -681,9 +693,9 @@ class IterationTransform(Visitor.VisitorTransform):
 
 class SwitchTransform(Visitor.VisitorTransform):
     """
-    This transformation tries to turn long if statements into C switch statements. 
+    This transformation tries to turn long if statements into C switch statements.
     The requirement is that every clause be an (or of) var == value, where the var
-    is common among all clauses and both var and value are ints. 
+    is common among all clauses and both var and value are ints.
     """
     NO_MATCH = (None, None, None)
 
@@ -700,28 +712,25 @@ class SwitchTransform(Visitor.VisitorTransform):
                 break
 
         if isinstance(cond, ExprNodes.PrimaryCmpNode):
-            if cond.cascade is None and not cond.is_python_comparison():
+            if cond.cascade is not None:
+                return self.NO_MATCH
+            elif cond.is_c_string_contains() and \
+                   isinstance(cond.operand2, (ExprNodes.UnicodeNode, ExprNodes.BytesNode)):
+                not_in = cond.operator == 'not_in'
+                if not_in and not allow_not_in:
+                    return self.NO_MATCH
+                if isinstance(cond.operand2, ExprNodes.UnicodeNode) and \
+                       cond.operand2.contains_surrogates():
+                    # dealing with surrogates leads to different
+                    # behaviour on wide and narrow Unicode
+                    # platforms => refuse to optimise this case
+                    return self.NO_MATCH
+                return not_in, cond.operand1, self.extract_in_string_conditions(cond.operand2)
+            elif not cond.is_python_comparison():
                 if cond.operator == '==':
                     not_in = False
                 elif allow_not_in and cond.operator == '!=':
                     not_in = True
-                elif cond.is_c_string_contains() and \
-                         isinstance(cond.operand2, (ExprNodes.UnicodeNode, ExprNodes.BytesNode)):
-                    not_in = cond.operator == 'not_in'
-                    if not_in and not allow_not_in:
-                        return self.NO_MATCH
-                    if isinstance(cond.operand2, ExprNodes.UnicodeNode) and \
-                           cond.operand2.contains_surrogates():
-                        # dealing with surrogates leads to different
-                        # behaviour on wide and narrow Unicode
-                        # platforms => refuse to optimise this case
-                        return self.NO_MATCH
-                    # this looks somewhat silly, but it does the right
-                    # checks for NameNode and AttributeNode
-                    if is_common_value(cond.operand1, cond.operand1):
-                        return not_in, cond.operand1, self.extract_in_string_conditions(cond.operand2)
-                    else:
-                        return self.NO_MATCH
                 else:
                     return self.NO_MATCH
                 # this looks somewhat silly, but it does the right
@@ -750,7 +759,7 @@ class SwitchTransform(Visitor.VisitorTransform):
 
     def extract_in_string_conditions(self, string_literal):
         if isinstance(string_literal, ExprNodes.UnicodeNode):
-            charvals = map(ord, set(string_literal.value))
+            charvals = list(map(ord, set(string_literal.value)))
             charvals.sort()
             return [ ExprNodes.IntNode(string_literal.pos, value=str(charval),
                                        constant_result=charval)
@@ -886,14 +895,14 @@ class SwitchTransform(Visitor.VisitorTransform):
         return UtilNodes.TempResultFromStatNode(result_ref, switch_node)
 
     visit_Node = Visitor.VisitorTransform.recurse_to_children
-                              
+
 
 class FlattenInListTransform(Visitor.VisitorTransform, SkipDeclarations):
     """
     This transformation flattens "x in [val1, ..., valn]" into a sequential list
-    of comparisons. 
+    of comparisons.
     """
-    
+
     def visit_PrimaryCmpNode(self, node):
         self.visitchildren(node)
         if node.cascade is not None:
@@ -921,7 +930,15 @@ class FlattenInListTransform(Visitor.VisitorTransform, SkipDeclarations):
         conds = []
         temps = []
         for arg in args:
-            if not arg.is_simple():
+            try:
+                # Trial optimisation to avoid redundant temp
+                # assignments.  However, since is_simple() is meant to
+                # be called after type analysis, we ignore any errors
+                # and just play safe in that case.
+                is_simple_arg = arg.is_simple()
+            except Exception:
+                is_simple_arg = False
+            if not is_simple_arg:
                 # must evaluate all non-simple RHS before doing the comparisons
                 arg = UtilNodes.LetRefNode(arg)
                 temps.append(arg)
@@ -932,12 +949,12 @@ class FlattenInListTransform(Visitor.VisitorTransform, SkipDeclarations):
                                 operand2 = arg,
                                 cascade = None)
             conds.append(ExprNodes.TypecastNode(
-                                pos = node.pos, 
+                                pos = node.pos,
                                 operand = cond,
                                 type = PyrexTypes.c_bint_type))
         def concat(left, right):
             return ExprNodes.BoolBinopNode(
-                                pos = node.pos, 
+                                pos = node.pos,
                                 operator = conjunction,
                                 operand1 = left,
                                 operand2 = right)
@@ -1002,7 +1019,7 @@ class DropRefcountingTransform(Visitor.VisitorTransform):
                 if not index_id:
                     return node
                 rindices.append(index_id)
-            
+
             if set(lindices) != set(rindices):
                 return node
             if len(set(lindices)) != len(right_indices):
@@ -1104,8 +1121,9 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
     def _function_is_builtin_name(self, function):
         if not function.is_name:
             return False
-        entry = self.current_env().lookup(function.name)
-        if entry and getattr(entry, 'scope', None) is not Builtin.builtin_scope:
+        env = self.current_env()
+        entry = env.lookup(function.name)
+        if entry is not env.builtin_scope().lookup_here(function.name):
             return False
         # if entry is None, it's at least an undeclared name, so likely builtin
         return True
@@ -1160,11 +1178,12 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
             self.yield_nodes = []
 
         visit_Node = Visitor.TreeVisitor.visitchildren
-        def visit_YieldExprNode(self, node):
+        # XXX: disable inlining while it's not back supported
+        def __visit_YieldExprNode(self, node):
             self.yield_nodes.append(node)
             self.visitchildren(node)
 
-        def visit_ExprStatNode(self, node):
+        def __visit_ExprStatNode(self, node):
             self.visitchildren(node)
             if node.expr in self.yield_nodes:
                 self.yield_stat_nodes[node.expr] = node
@@ -1281,20 +1300,79 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
             gen_expr_node.pos, loop = loop_node, result_node = result_ref,
             expr_scope = gen_expr_node.expr_scope, orig_func = is_any and 'any' or 'all')
 
-    def _handle_simple_function_sum(self, node, pos_args):
-        """Transform sum(genexpr) into an equivalent inlined aggregation loop.
+    def _handle_simple_function_sorted(self, node, pos_args):
+        """Transform sorted(genexpr) into [listcomp].sort().  CPython
+        just reads the iterable into a list and calls .sort() on it.
+        Expanding the iterable in a listcomp is still faster.
         """
-        if len(pos_args) not in (1,2):
+        if len(pos_args) != 1:
             return node
         if not isinstance(pos_args[0], ExprNodes.GeneratorExpressionNode):
             return node
         gen_expr_node = pos_args[0]
         loop_node = gen_expr_node.loop
-
         yield_expression, yield_stat_node = self._find_single_yield_expression(loop_node)
         if yield_expression is None:
             return node
 
+        result_node = UtilNodes.ResultRefNode(
+            pos = loop_node.pos, type = Builtin.list_type, may_hold_none=False)
+
+        target = ExprNodes.ListNode(node.pos, args = [])
+        append_node = ExprNodes.ComprehensionAppendNode(
+            yield_expression.pos, expr = yield_expression,
+            target = ExprNodes.CloneNode(target))
+
+        Visitor.recursively_replace_node(loop_node, yield_stat_node, append_node)
+
+        listcomp_node = ExprNodes.ComprehensionNode(
+            gen_expr_node.pos, loop = loop_node, target = target,
+            append = append_node, type = Builtin.list_type,
+            expr_scope = gen_expr_node.expr_scope,
+            has_local_scope = True)
+        listcomp_assign_node = Nodes.SingleAssignmentNode(
+            node.pos, lhs = result_node, rhs = listcomp_node, first = True)
+
+        sort_method = ExprNodes.AttributeNode(
+            node.pos, obj = result_node, attribute = EncodedString('sort'),
+            # entry ? type ?
+            needs_none_check = False)
+        sort_node = Nodes.ExprStatNode(
+            node.pos, expr = ExprNodes.SimpleCallNode(
+                node.pos, function = sort_method, args = []))
+
+        sort_node.analyse_declarations(self.current_env())
+
+        return UtilNodes.TempResultFromStatNode(
+            result_node,
+            Nodes.StatListNode(node.pos, stats = [ listcomp_assign_node, sort_node ]))
+
+    def _handle_simple_function_sum(self, node, pos_args):
+        """Transform sum(genexpr) into an equivalent inlined aggregation loop.
+        """
+        if len(pos_args) not in (1,2):
+            return node
+        if not isinstance(pos_args[0], (ExprNodes.GeneratorExpressionNode,
+                                        ExprNodes.ComprehensionNode)):
+            return node
+        gen_expr_node = pos_args[0]
+        loop_node = gen_expr_node.loop
+
+        if isinstance(gen_expr_node, ExprNodes.GeneratorExpressionNode):
+            yield_expression, yield_stat_node = self._find_single_yield_expression(loop_node)
+            if yield_expression is None:
+                return node
+        else: # ComprehensionNode
+            yield_stat_node = gen_expr_node.append
+            yield_expression = yield_stat_node.expr
+            try:
+                if not yield_expression.is_literal or not yield_expression.type.is_int:
+                    return node
+            except AttributeError:
+                return node # in case we don't have a type yet
+            # special case: old Py2 backwards compatible "sum([int_const for ...])"
+            # can safely be unpacked into a genexpr
+
         if len(pos_args) == 1:
             start = ExprNodes.IntNode(node.pos, value='0', constant_result=0)
         else:
@@ -1322,7 +1400,8 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
 
         return ExprNodes.InlinedGeneratorExpressionNode(
             gen_expr_node.pos, loop = exec_code, result_node = result_ref,
-            expr_scope = gen_expr_node.expr_scope, orig_func = 'sum')
+            expr_scope = gen_expr_node.expr_scope, orig_func = 'sum',
+            has_local_scope = gen_expr_node.has_local_scope)
 
     def _handle_simple_function_min(self, node, pos_args):
         return self._optimise_min_max(node, pos_args, '<')
@@ -1337,7 +1416,7 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
             # leave this to Python
             return node
 
-        cascaded_nodes = map(UtilNodes.ResultRefNode, args[1:])
+        cascaded_nodes = list(map(UtilNodes.ResultRefNode, args[1:]))
 
         last_result = args[0]
         for arg_node in cascaded_nodes:
@@ -1533,6 +1612,15 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             return node.operand
         return node
 
+    def visit_ExprStatNode(self, node):
+        """
+        Drop useless coercions.
+        """
+        self.visitchildren(node)
+        if isinstance(node.expr, ExprNodes.CoerceToPyTypeNode):
+            node.expr = node.expr.arg
+        return node
+
     def visit_CoerceToBooleanNode(self, node):
         """Drop redundant conversion nodes after tree changes.
         """
@@ -1658,8 +1746,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             # into a C function call (defined in the builtin scope)
             if not function.entry:
                 return node
-            is_builtin = function.entry.is_builtin \
-                         or getattr(function.entry, 'scope', None) is Builtin.builtin_scope
+            is_builtin = function.entry.is_builtin or \
+                         function.entry is self.current_env().builtin_scope().lookup_here(function.name)
             if not is_builtin:
                 return node
             function_handler = self._find_handler(
@@ -1781,7 +1869,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
         # Note: this requires the float() function to be typed as
         # returning a C 'double'
         if len(pos_args) == 0:
-            return ExprNode.FloatNode(
+            return ExprNodes.FloatNode(
                 node, value="0.0", constant_result=0.0
                 ).coerce_to(Builtin.float_type, self.current_env())
         elif len(pos_args) != 1:
@@ -1814,8 +1902,12 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             self._error_wrong_arg_count('bool', node, pos_args, '0 or 1')
             return node
         else:
-            return pos_args[0].coerce_to_boolean(
-                self.current_env()).coerce_to_pyobject(self.current_env())
+            # => !!<bint>(x)  to make sure it's exactly 0 or 1
+            operand = pos_args[0].coerce_to_boolean(self.current_env())
+            operand = ExprNodes.NotNode(node.pos, operand = operand)
+            operand = ExprNodes.NotNode(node.pos, operand = operand)
+            # coerce back to Python object as that's the result we are expecting
+            return operand.coerce_to_pyobject(self.current_env())
 
     ### builtin functions
 
@@ -1865,7 +1957,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
                 node.pos, cfunc_name, self.PyObject_Size_func_type,
                 args = [arg],
                 is_temp = node.is_temp)
-        elif arg.type is PyrexTypes.c_py_unicode_type:
+        elif arg.type.is_unicode_char:
             return ExprNodes.IntNode(node.pos, value='1', constant_result=1,
                                      type=node.type)
         else:
@@ -1915,22 +2007,29 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
         test_nodes = []
         env = self.current_env()
         for test_type_node in types:
-            if not test_type_node.entry:
-                return node
-            entry = env.lookup(test_type_node.entry.name)
-            if not entry or not entry.type or not entry.type.is_builtin_type:
-                return node
-            type_check_function = entry.type.type_check_function(exact=False)
-            if not type_check_function:
-                return node
-            if type_check_function not in tests:
+            builtin_type = None
+            if isinstance(test_type_node, ExprNodes.NameNode):
+                if test_type_node.entry:
+                    entry = env.lookup(test_type_node.entry.name)
+                    if entry and entry.type and entry.type.is_builtin_type:
+                        builtin_type = entry.type
+            if builtin_type and builtin_type is not Builtin.type_type:
+                type_check_function = entry.type.type_check_function(exact=False)
+                if type_check_function in tests:
+                    continue
                 tests.append(type_check_function)
-                test_nodes.append(
-                    ExprNodes.PythonCapiCallNode(
-                        test_type_node.pos, type_check_function, self.Py_type_check_func_type,
-                        args = [arg],
-                        is_temp = True,
-                        ))
+                type_check_args = [arg]
+            elif test_type_node.type is Builtin.type_type:
+                type_check_function = '__Pyx_TypeCheck'
+                type_check_args = [arg, test_type_node]
+            else:
+                return node
+            test_nodes.append(
+                ExprNodes.PythonCapiCallNode(
+                    test_type_node.pos, type_check_function, self.Py_type_check_func_type,
+                    args = type_check_args,
+                    is_temp = True,
+                    ))
 
         def join_with_or(a,b, make_binop_node=ExprNodes.binop_node):
             or_node = make_binop_node(node.pos, 'or', a, b)
@@ -1950,7 +2049,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             return node
         arg = pos_args[0]
         if isinstance(arg, ExprNodes.CoerceToPyTypeNode):
-            if arg.arg.type is PyrexTypes.c_py_unicode_type:
+            if arg.arg.type.is_unicode_char:
                 return arg.arg.coerce_to(node.type, self.current_env())
         return node
 
@@ -2058,30 +2157,13 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
                         is_temp = node.is_temp,
                         utility_code = pop_index_utility_code
                         )
-                
+
         return node
 
     _handle_simple_method_list_pop = _handle_simple_method_object_pop
 
-    PyList_Append_func_type = PyrexTypes.CFuncType(
-        PyrexTypes.c_int_type, [
-            PyrexTypes.CFuncTypeArg("list", PyrexTypes.py_object_type, None),
-            PyrexTypes.CFuncTypeArg("item", PyrexTypes.py_object_type, None),
-            ],
-        exception_value = "-1")
-
-    def _handle_simple_method_list_append(self, node, args, is_unbound_method):
-        """Call PyList_Append() instead of l.append().
-        """
-        if len(args) != 2:
-            self._error_wrong_arg_count('list.append', node, args, 2)
-            return node
-        return self._substitute_method_call(
-            node, "PyList_Append", self.PyList_Append_func_type,
-            'append', is_unbound_method, args)
-
     single_param_func_type = PyrexTypes.CFuncType(
-        PyrexTypes.c_int_type, [
+        PyrexTypes.c_returncode_type, [
             PyrexTypes.CFuncTypeArg("obj", PyrexTypes.py_object_type, None),
             ],
         exception_value = "-1")
@@ -2093,17 +2175,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             return node
         return self._substitute_method_call(
             node, "PyList_Sort", self.single_param_func_type,
-            'sort', is_unbound_method, args)
-
-    def _handle_simple_method_list_reverse(self, node, args, is_unbound_method):
-        """Call PyList_Reverse() instead of l.reverse().
-        """
-        if len(args) != 1:
-            self._error_wrong_arg_count('list.reverse', node, args, 1)
-            return node
-        return self._substitute_method_call(
-            node, "PyList_Reverse", self.single_param_func_type,
-            'reverse', is_unbound_method, args)
+            'sort', is_unbound_method, args).coerce_to(node.type, self.current_env)
 
     Pyx_PyDict_GetItem_func_type = PyrexTypes.CFuncType(
         PyrexTypes.py_object_type, [
@@ -2132,7 +2204,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
 
     PyUnicode_uchar_predicate_func_type = PyrexTypes.CFuncType(
         PyrexTypes.c_bint_type, [
-            PyrexTypes.CFuncTypeArg("uchar", PyrexTypes.c_py_unicode_type, None),
+            PyrexTypes.CFuncTypeArg("uchar", PyrexTypes.c_py_ucs4_type, None),
             ])
 
     def _inject_unicode_predicate(self, node, args, is_unbound_method):
@@ -2140,7 +2212,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             return node
         ustring = args[0]
         if not isinstance(ustring, ExprNodes.CoerceToPyTypeNode) or \
-               ustring.arg.type is not PyrexTypes.c_py_unicode_type:
+               not ustring.arg.type.is_unicode_char:
             return node
         uchar = ustring.arg
         method_name = node.function.attribute
@@ -2170,8 +2242,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
     _handle_simple_method_unicode_isupper   = _inject_unicode_predicate
 
     PyUnicode_uchar_conversion_func_type = PyrexTypes.CFuncType(
-        PyrexTypes.c_py_unicode_type, [
-            PyrexTypes.CFuncTypeArg("uchar", PyrexTypes.c_py_unicode_type, None),
+        PyrexTypes.c_py_ucs4_type, [
+            PyrexTypes.CFuncTypeArg("uchar", PyrexTypes.c_py_ucs4_type, None),
             ])
 
     def _inject_unicode_character_conversion(self, node, args, is_unbound_method):
@@ -2179,7 +2251,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             return node
         ustring = args[0]
         if not isinstance(ustring, ExprNodes.CoerceToPyTypeNode) or \
-               ustring.arg.type is not PyrexTypes.c_py_unicode_type:
+               not ustring.arg.type.is_unicode_char:
             return node
         uchar = ustring.arg
         method_name = node.function.attribute
@@ -2214,24 +2286,6 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             node, "PyUnicode_Splitlines", self.PyUnicode_Splitlines_func_type,
             'splitlines', is_unbound_method, args)
 
-    PyUnicode_Join_func_type = PyrexTypes.CFuncType(
-        Builtin.unicode_type, [
-            PyrexTypes.CFuncTypeArg("sep", Builtin.unicode_type, None),
-            PyrexTypes.CFuncTypeArg("iterable", PyrexTypes.py_object_type, None),
-            ])
-
-    def _handle_simple_method_unicode_join(self, node, args, is_unbound_method):
-        """Replace unicode.join(...) by a direct call to the
-        corresponding C-API function.
-        """
-        if len(args) != 2:
-            self._error_wrong_arg_count('unicode.join', node, args, 2)
-            return node
-
-        return self._substitute_method_call(
-            node, "PyUnicode_Join", self.PyUnicode_Join_func_type,
-            'join', is_unbound_method, args)
-
     PyUnicode_Split_func_type = PyrexTypes.CFuncType(
         Builtin.list_type, [
             PyrexTypes.CFuncTypeArg("str", Builtin.unicode_type, None),
@@ -2669,10 +2723,18 @@ py_unicode_istitle_utility_code = UtilityCode(
 # Py_UNICODE_ISTITLE() doesn't match unicode.istitle() as the latter
 # additionally allows character that comply with Py_UNICODE_ISUPPER()
 proto = '''
+#if PY_VERSION_HEX < 0x030200A2
 static CYTHON_INLINE int __Pyx_Py_UNICODE_ISTITLE(Py_UNICODE uchar); /* proto */
+#else
+static CYTHON_INLINE int __Pyx_Py_UNICODE_ISTITLE(Py_UCS4 uchar); /* proto */
+#endif
 ''',
 impl = '''
+#if PY_VERSION_HEX < 0x030200A2
 static CYTHON_INLINE int __Pyx_Py_UNICODE_ISTITLE(Py_UNICODE uchar) {
+#else
+static CYTHON_INLINE int __Pyx_Py_UNICODE_ISTITLE(Py_UCS4 uchar) {
+#endif
     return Py_UNICODE_ISTITLE(uchar) || Py_UNICODE_ISUPPER(uchar);
 }
 ''')
@@ -2908,6 +2970,17 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
     """Calculate the result of constant expressions to store it in
     ``expr_node.constant_result``, and replace trivial cases by their
     constant result.
+
+    General rules:
+
+    - We calculate float constants to make them available to the
+      compiler, but we do not aggregate them into a single literal
+      node to prevent any loss of precision.
+
+    - We recursively calculate constants from non-literal nodes to
+      make them available to the compiler, but we only aggregate
+      literal nodes at each step.  Non-literal nodes are never merged
+      into a single node.
     """
     def _calculate_const(self, node):
         if node.constant_result is not ExprNodes.constant_value_not_set:
@@ -2919,7 +2992,7 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
 
         # check if all children are constant
         children = self.visitchildren(node)
-        for child_result in children.itervalues():
+        for child_result in children.values():
             if type(child_result) is list:
                 for child in child_result:
                     if getattr(child, 'constant_result', not_a_constant) is not_a_constant:
@@ -2940,8 +3013,8 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
             import traceback, sys
             traceback.print_exc(file=sys.stdout)
 
-    NODE_TYPE_ORDER = (ExprNodes.CharNode, ExprNodes.IntNode,
-                       ExprNodes.LongNode, ExprNodes.FloatNode)
+    NODE_TYPE_ORDER = [ExprNodes.CharNode, ExprNodes.IntNode,
+                       ExprNodes.LongNode, ExprNodes.FloatNode]
 
     def _widest_node_class(self, *nodes):
         try:
@@ -2954,14 +3027,49 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
         self._calculate_const(node)
         return node
 
+    def visit_UnopNode(self, node):
+        self._calculate_const(node)
+        if node.constant_result is ExprNodes.not_a_constant:
+            return node
+        if not node.operand.is_literal:
+            return node
+        if isinstance(node.operand, ExprNodes.BoolNode):
+            return ExprNodes.IntNode(node.pos, value = str(node.constant_result),
+                                     type = PyrexTypes.c_int_type,
+                                     constant_result = node.constant_result)
+        if node.operator == '+':
+            return self._handle_UnaryPlusNode(node)
+        elif node.operator == '-':
+            return self._handle_UnaryMinusNode(node)
+        return node
+
+    def _handle_UnaryMinusNode(self, node):
+        if isinstance(node.operand, ExprNodes.LongNode):
+            return ExprNodes.LongNode(node.pos, value = '-' + node.operand.value,
+                                      constant_result = node.constant_result)
+        if isinstance(node.operand, ExprNodes.FloatNode):
+            # this is a safe operation
+            return ExprNodes.FloatNode(node.pos, value = '-' + node.operand.value,
+                                       constant_result = node.constant_result)
+        node_type = node.operand.type
+        if node_type.is_int and node_type.signed or \
+               isinstance(node.operand, ExprNodes.IntNode) and node_type.is_pyobject:
+            return ExprNodes.IntNode(node.pos, value = '-' + node.operand.value,
+                                     type = node_type,
+                                     longness = node.operand.longness,
+                                     constant_result = node.constant_result)
+        return node
+
+    def _handle_UnaryPlusNode(self, node):
+        if node.constant_result == node.operand.constant_result:
+            return node.operand
+        return node
+
     def visit_BoolBinopNode(self, node):
         self._calculate_const(node)
         if node.constant_result is ExprNodes.not_a_constant:
             return node
         if not node.operand1.is_literal or not node.operand2.is_literal:
-            # We calculate other constants to make them available to
-            # the compiler, but we only aggregate constant nodes
-            # recursively, so non-const nodes are straight out.
             return node
 
         if node.constant_result == node.operand1.constant_result and node.operand1.is_literal:
@@ -2977,48 +3085,49 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
         if node.constant_result is ExprNodes.not_a_constant:
             return node
         if isinstance(node.constant_result, float):
-            # We calculate float constants to make them available to
-            # the compiler, but we do not aggregate them into a
-            # constant node to prevent any loss of precision.
             return node
-        if not node.operand1.is_literal or not node.operand2.is_literal:
-            # We calculate other constants to make them available to
-            # the compiler, but we only aggregate constant nodes
-            # recursively, so non-const nodes are straight out.
+        operand1, operand2 = node.operand1, node.operand2
+        if not operand1.is_literal or not operand2.is_literal:
             return node
 
         # now inject a new constant node with the calculated value
         try:
-            type1, type2 = node.operand1.type, node.operand2.type
+            type1, type2 = operand1.type, operand2.type
             if type1 is None or type2 is None:
                 return node
         except AttributeError:
             return node
 
-        if type1 is type2:
-            new_node = node.operand1
-        else:
+        if type1.is_numeric and type2.is_numeric:
             widest_type = PyrexTypes.widest_numeric_type(type1, type2)
-            if type(node.operand1) is type(node.operand2):
-                new_node = node.operand1
-                new_node.type = widest_type
-            elif type1 is widest_type:
-                new_node = node.operand1
-            elif type2 is widest_type:
-                new_node = node.operand2
+        else:
+            widest_type = PyrexTypes.py_object_type
+        target_class = self._widest_node_class(operand1, operand2)
+        if target_class is None:
+            return node
+        elif target_class is ExprNodes.IntNode:
+            unsigned = getattr(operand1, 'unsigned', '') and \
+                       getattr(operand2, 'unsigned', '')
+            longness = "LL"[:max(len(getattr(operand1, 'longness', '')),
+                                 len(getattr(operand2, 'longness', '')))]
+            new_node = ExprNodes.IntNode(pos=node.pos,
+                                         unsigned = unsigned, longness = longness,
+                                         value = str(node.constant_result),
+                                         constant_result = node.constant_result)
+            # IntNode is smart about the type it chooses, so we just
+            # make sure we were not smarter this time
+            if widest_type.is_pyobject or new_node.type.is_pyobject:
+                new_node.type = PyrexTypes.py_object_type
             else:
-                target_class = self._widest_node_class(
-                    node.operand1, node.operand2)
-                if target_class is None:
-                    return node
-                new_node = target_class(pos=node.pos, type = widest_type)
-
-        new_node.constant_result = node.constant_result
-        if isinstance(node, ExprNodes.BoolNode):
-            new_node.value = node.constant_result
+                new_node.type = PyrexTypes.widest_numeric_type(widest_type, new_node.type)
         else:
-            new_node.value = str(node.constant_result)
-        #new_node = new_node.coerce_to(node.type, self.current_scope)
+            if isinstance(node, ExprNodes.BoolNode):
+                node_value = node.constant_result
+            else:
+                node_value = str(node.constant_result)
+            new_node = target_class(pos=node.pos, type = widest_type,
+                                    value = node_value,
+                                    constant_result = node.constant_result)
         return new_node
 
     def visit_PrimaryCmpNode(self, node):
@@ -3029,6 +3138,15 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
         return ExprNodes.BoolNode(node.pos, value=bool_result,
                                   constant_result=bool_result)
 
+    def visit_CondExprNode(self, node):
+        self._calculate_const(node)
+        if node.test.constant_result is ExprNodes.not_a_constant:
+            return node
+        if node.test.constant_result:
+            return node.true_val
+        else:
+            return node.false_val
+
     def visit_IfStatNode(self, node):
         self.visitchildren(node)
         # eliminate dead code based on constant condition results
@@ -3058,10 +3176,10 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
 class FinalOptimizePhase(Visitor.CythonTransform):
     """
     This visitor handles several commuting optimizations, and is run
-    just before the C code generation phase. 
-    
-    The optimizations currently implemented in this class are: 
-        - eliminate None assignment and refcounting for first assignment. 
+    just before the C code generation phase.
+
+    The optimizations currently implemented in this class are:
+        - eliminate None assignment and refcounting for first assignment.
         - isinstance -> typecheck for cdef types
         - eliminate checks for None and/or types that became redundant after tree changes
     """