rename PYREX_WITHOUT_ASSERTIONS -> CYTHON_WITHOUT_ASSERTIONS
[cython.git] / Cython / Compiler / Nodes.py
index 899eaee1bffdf7afa0d8939013a8d97d6bd3b5a7..10706766af77d56a8a5bec6fdcb42a4d885937ea 100644 (file)
@@ -3,15 +3,17 @@
 #   Pyrex - Parse tree nodes
 #
 
-import sys, os, time, copy
+import cython
+from cython import set
+cython.declare(sys=object, os=object, time=object, copy=object,
+               Builtin=object, error=object, warning=object, Naming=object, PyrexTypes=object,
+               py_object_type=object, ModuleScope=object, LocalScope=object, ClosureScope=object, \
+               StructOrUnionScope=object, PyClassScope=object, CClassScope=object,
+               CppClassScope=object, UtilityCode=object, EncodedString=object,
+               absolute_path_length=cython.Py_ssize_t)
 
-try:
-    set
-except NameError:
-    # Python 2.3
-    from sets import Set as set
+import sys, os, time, copy
 
-import Code
 import Builtin
 from Errors import error, warning, InternalError
 import Naming
@@ -241,7 +243,7 @@ class Node(object):
         if encountered is None:
             encountered = set()
         if id(self) in encountered:
-            return "<%s (%d) -- already output>" % (self.__class__.__name__, id(self))
+            return "<%s (0x%x) -- already output>" % (self.__class__.__name__, id(self))
         encountered.add(id(self))
         
         def dump_child(x, level):
@@ -253,12 +255,12 @@ class Node(object):
                 return repr(x)
             
         
-        attrs = [(key, value) for key, value in self.__dict__.iteritems() if key not in filter_out]
+        attrs = [(key, value) for key, value in self.__dict__.items() if key not in filter_out]
         if len(attrs) == 0:
-            return "<%s (%d)>" % (self.__class__.__name__, id(self))
+            return "<%s (0x%x)>" % (self.__class__.__name__, id(self))
         else:
             indent = "  " * level
-            res = "<%s (%d)\n" % (self.__class__.__name__, id(self))
+            res = "<%s (0x%x)\n" % (self.__class__.__name__, id(self))
             for key, value in attrs:
                 res += "%s  %s: %s\n" % (indent, key, dump_child(value, level + 1))
             res += "%s>" % indent
@@ -534,8 +536,9 @@ class CFuncDeclaratorNode(CDeclaratorNode):
         if nonempty:
             nonempty -= 1
         func_type_args = []
-        for arg_node in self.args:
-            name_declarator, type = arg_node.analyse(env, nonempty = nonempty)
+        for i, arg_node in enumerate(self.args):
+            name_declarator, type = arg_node.analyse(env, nonempty = nonempty,
+                                                     is_self_arg = (i == 0 and env.is_c_class_scope))
             name = name_declarator.name
             if name_declarator.cname:
                 error(self.pos, 
@@ -591,6 +594,7 @@ class CFuncDeclaratorNode(CDeclaratorNode):
                             "Exception value must be a Python exception or cdef function with no arguments.")
                     exc_val = self.exception_value
                 else:
+                    self.exception_value = self.exception_value.coerce_to(return_type, env)
                     if self.exception_value.analyse_const_expression(env):
                         exc_val = self.exception_value.get_constant_c_result_code()
                         if exc_val is None:
@@ -649,8 +653,9 @@ class CArgDeclNode(Node):
     default_value = None
     annotation = None
 
-    def analyse(self, env, nonempty = 0):
-        #print "CArgDeclNode.analyse: is_self_arg =", self.is_self_arg ###
+    def analyse(self, env, nonempty = 0, is_self_arg = False):
+        if is_self_arg:
+            self.base_type.is_self_arg = self.is_self_arg = True
         if self.type is None:
             # The parser may missinterpret names as types...
             # We fix that here.
@@ -834,7 +839,11 @@ class TemplatedTypeNode(CBaseTypeNode):
             else:
                 template_types = []
                 for template_node in self.positional_args:
-                    template_types.append(template_node.analyse_as_type(env))
+                    type = template_node.analyse_as_type(env)
+                    if type is None:
+                        error(template_node.pos, "unknown type in template argument")
+                        return error_type
+                    template_types.append(type)
                 self.type = base_type.specialize_here(self.pos, template_types)
         
         elif base_type.is_pyobject:
@@ -851,7 +860,7 @@ class TemplatedTypeNode(CBaseTypeNode):
             if sys.version_info[0] < 3:
                 # Py 2.x enforces byte strings as keyword arguments ...
                 options = dict([ (name.encode('ASCII'), value)
-                                 for name, value in options.iteritems() ])
+                                 for name, value in options.items() ])
 
             self.type = PyrexTypes.BufferType(base_type, **options)
         
@@ -895,13 +904,11 @@ class CVarDefNode(StatNode):
     #  declarators   [CDeclaratorNode]
     #  in_pxd        boolean
     #  api           boolean
-    #  properties    [entry]
 
     #  decorators    [cython.locals(...)] or None 
     #  directive_locals { string : NameNode } locals defined by cython.locals(...)
 
     child_attrs = ["base_type", "declarators"]
-    properties = ()
     
     decorators = None
     directive_locals = {}
@@ -917,7 +924,6 @@ class CVarDefNode(StatNode):
         # a property; made in AnalyseDeclarationsTransform).
         if (dest_scope.is_c_class_scope
             and self.visibility in ('public', 'readonly')):
-            self.properties = []
             need_property = True
         else:
             need_property = False
@@ -945,14 +951,13 @@ class CVarDefNode(StatNode):
                     entry.directive_locals = self.directive_locals
             else:
                 if self.directive_locals:
-                    s.error("Decorators can only be followed by functions")
+                    error(self.pos, "Decorators can only be followed by functions")
                 if self.in_pxd and self.visibility != 'extern':
                     error(self.pos, 
                         "Only 'extern' C variable declaration allowed in .pxd file")
                 entry = dest_scope.declare_var(name, type, declarator.pos,
                             cname = cname, visibility = visibility, is_cdef = 1)
-                if need_property:
-                    self.properties.append(entry)
+                entry.needs_property = need_property
     
 
 class CStructOrUnionDefNode(StatNode):
@@ -1143,11 +1148,13 @@ class FuncDefNode(StatNode, BlockNode):
     #  #filename        string        C name of filename string const
     #  entry           Symtab.Entry
     #  needs_closure   boolean        Whether or not this function has inner functions/classes/yield
+    #  needs_outer_scope boolean      Whether or not this function requires outer scope
     #  directive_locals { string : NameNode } locals defined by cython.locals(...)
     
     py_func = None
     assmt = None
     needs_closure = False
+    needs_outer_scope = False
     modifiers = []
     
     def analyse_default_values(self, env):
@@ -1174,7 +1181,7 @@ class FuncDefNode(StatNode, BlockNode):
     def create_local_scope(self, env):
         genv = env
         while genv.is_py_class_scope or genv.is_c_class_scope:
-            genv = env.outer_scope
+            genv = genv.outer_scope
         if self.needs_closure:
             lenv = ClosureScope(name=self.entry.name,
                                 outer_scope = genv,
@@ -1195,7 +1202,7 @@ class FuncDefNode(StatNode, BlockNode):
         import Buffer
 
         lenv = self.local_scope
-        if lenv.is_closure_scope:
+        if lenv.is_closure_scope and not lenv.is_passthrough:
             outer_scope_cname = "%s->%s" % (Naming.cur_scope_cname,
                                             Naming.outer_scope_cname)
         else:
@@ -1215,6 +1222,15 @@ class FuncDefNode(StatNode, BlockNode):
         if is_buffer_slot:
             if 'cython_unused' not in self.modifiers:
                 self.modifiers = self.modifiers + ['cython_unused']
+
+        preprocessor_guard = None
+        if self.entry.is_special and not is_buffer_slot:
+            slot = TypeSlots.method_name_to_slot.get(self.entry.name)
+            if slot:
+                preprocessor_guard = slot.preprocessor_guard_code()
+                if (self.entry.name == '__long__' and
+                    not self.entry.scope.lookup_here('__int__')):
+                    preprocessor_guard = None
         
         profile = code.globalstate.directives['profile']
         if profile:
@@ -1231,6 +1247,10 @@ class FuncDefNode(StatNode, BlockNode):
         self.generate_cached_builtins_decls(lenv, code)
         # ----- Function header
         code.putln("")
+
+        if preprocessor_guard:
+            code.putln(preprocessor_guard)
+
         with_pymethdef = self.needs_assignment_synthesis(env, code)
         if self.py_func:
             self.py_func.generate_function_header(code, 
@@ -1239,11 +1259,18 @@ class FuncDefNode(StatNode, BlockNode):
         self.generate_function_header(code,
             with_pymethdef = with_pymethdef)
         # ----- Local variable declarations
-        if lenv.is_closure_scope:
+        # Find function scope
+        cenv = env
+        while cenv.is_py_class_scope or cenv.is_c_class_scope:
+            cenv = cenv.outer_scope
+        if self.needs_closure:
             code.put(lenv.scope_class.type.declaration_code(Naming.cur_scope_cname))
             code.putln(";")
-        elif env.is_closure_scope:
-            code.put(env.scope_class.type.declaration_code(Naming.outer_scope_cname))
+        elif self.needs_outer_scope:
+            if lenv.is_passthrough:
+                code.put(lenv.scope_class.type.declaration_code(Naming.cur_scope_cname))
+                code.putln(";")
+            code.put(cenv.scope_class.type.declaration_code(Naming.outer_scope_cname))
             code.putln(";")
         self.generate_argument_declarations(lenv, code)
         for entry in lenv.var_entries:
@@ -1267,7 +1294,9 @@ class FuncDefNode(StatNode, BlockNode):
         acquire_gil = self.acquire_gil
         if acquire_gil:
             env.use_utility_code(force_init_threads_utility_code)
+            code.putln("#ifdef WITH_THREAD")
             code.putln("PyGILState_STATE _save = PyGILState_Ensure();")
+            code.putln("#endif")
         # ----- set up refnanny
         if not lenv.nogil:
             code.put_setup_refcount_context(self.entry.name)
@@ -1292,14 +1321,16 @@ class FuncDefNode(StatNode, BlockNode):
             code.putln("}")
             code.put_gotref(Naming.cur_scope_cname)
             # Note that it is unsafe to decref the scope at this point.
-        if env.is_closure_scope:
+        if self.needs_outer_scope:
             code.putln("%s = (%s)%s;" % (
                             outer_scope_cname,
-                            env.scope_class.type.declaration_code(''),
+                            cenv.scope_class.type.declaration_code(''),
                             Naming.self_cname))
-            if self.needs_closure:
+            if lenv.is_passthrough:
+                code.putln("%s = %s;" % (Naming.cur_scope_cname, outer_scope_cname));
+            elif self.needs_closure:
                 # inner closures own a reference to their outer parent
-                code.put_incref(outer_scope_cname, env.scope_class.type)
+                code.put_incref(outer_scope_cname, cenv.scope_class.type)
                 code.put_giveref(outer_scope_cname)
         # ----- Trace function call
         if profile:
@@ -1452,12 +1483,18 @@ class FuncDefNode(StatNode, BlockNode):
             code.put_finish_refcount_context()
         
         if acquire_gil:
+            code.putln("#ifdef WITH_THREAD")
             code.putln("PyGILState_Release(_save);")
+            code.putln("#endif")
 
         if not self.return_type.is_void:
             code.putln("return %s;" % Naming.retval_cname)
             
         code.putln("}")
+
+        if preprocessor_guard:
+            code.putln("#endif /*!(%s)*/" % preprocessor_guard)
+
         # ----- Go back and insert temp variable declarations
         tempvardecl_code.put_temp_declarations(code.funcstate)
         # ----- Python version
@@ -1579,7 +1616,7 @@ class CFuncDefNode(FuncDefNode):
     def analyse_declarations(self, env):
         self.directive_locals.update(env.directives['locals'])
         base_type = self.base_type.analyse(env)
-        # The 2 here is because we need both function and argument names. 
+        # The 2 here is because we need both function and argument names.
         name_declarator, type = self.declarator.analyse(base_type, env, nonempty = 2 * (self.body is not None))
         if not type.is_cfunction:
             error(self.pos, 
@@ -1886,13 +1923,16 @@ class DefNode(FuncDefNode):
                                               is_overridable = True)
             cfunc = CVarDefNode(self.pos, type=cfunc_type)
         else:
+            if scope is None:
+                scope = cfunc.scope
             cfunc_type = cfunc.type
             if len(self.args) != len(cfunc_type.args) or cfunc_type.has_varargs:
                 error(self.pos, "wrong number of arguments")
                 error(cfunc.pos, "previous declaration here")
-            for formal_arg, type_arg in zip(self.args, cfunc_type.args):
-                name_declarator, type = formal_arg.analyse(cfunc.scope, nonempty=1)
-                if type is None or type is PyrexTypes.py_object_type or formal_arg.is_self:
+            for i, (formal_arg, type_arg) in enumerate(zip(self.args, cfunc_type.args)):
+                name_declarator, type = formal_arg.analyse(scope, nonempty=1,
+                                                           is_self_arg = (i == 0 and scope.is_c_class_scope))
+                if type is None or type is PyrexTypes.py_object_type:
                     formal_arg.type = type_arg.type
                     formal_arg.name_declarator = name_declarator
         import ExprNodes
@@ -1938,6 +1978,9 @@ class DefNode(FuncDefNode):
             # staticmethod() was overridden - not much we can do here ...
             self.is_staticmethod = False
 
+        if self.name == '__new__':
+            self.is_staticmethod = 1
+
         self.analyse_argument_types(env)
         if self.name == '<lambda>':
             self.declare_lambda_function(env)
@@ -2106,6 +2149,11 @@ class DefNode(FuncDefNode):
             entry.doc = embed_position(self.pos, self.doc)
             entry.doc_cname = \
                 Naming.funcdoc_prefix + prefix + name
+            if entry.is_special:
+                if entry.name in TypeSlots.invisible or not entry.doc or (entry.name in '__getattr__' and env.directives['fast_getattr']):
+                    entry.wrapperbase_cname = None
+                else:
+                    entry.wrapperbase_cname = Naming.wrapperbase_prefix + prefix + name
         else:
             entry.doc = None
 
@@ -2176,16 +2224,21 @@ class DefNode(FuncDefNode):
 
     def synthesize_assignment_node(self, env):
         import ExprNodes
-        if env.is_py_class_scope:
-            rhs = ExprNodes.UnboundMethodNode(self.pos, 
-                function = ExprNodes.PyCFunctionNode(self.pos,
-                    pymethdef_cname = self.entry.pymethdef_cname))
-        elif env.is_closure_scope:
+        genv = env
+        while genv.is_py_class_scope or genv.is_c_class_scope:
+            genv = genv.outer_scope
+
+        if genv.is_closure_scope:
             rhs = ExprNodes.InnerFunctionNode(
                 self.pos, pymethdef_cname = self.entry.pymethdef_cname)
         else:
             rhs = ExprNodes.PyCFunctionNode(
                 self.pos, pymethdef_cname = self.entry.pymethdef_cname, binding = env.directives['binding'])
+
+        if env.is_py_class_scope:
+            if not self.is_staticmethod and not self.is_classmethod:
+                rhs.binding = True
+
         self.assmt = SingleAssignmentNode(self.pos,
             lhs = ExprNodes.NameNode(self.pos, name = self.name),
             rhs = rhs)
@@ -2220,10 +2273,8 @@ class DefNode(FuncDefNode):
         if proto_only:
             return
         if (Options.docstrings and self.entry.doc and
-            (not self.entry.is_special or
-             self.entry.signature.method_flags()) and
-            not self.entry.scope.is_property_scope
-            ):
+                not self.entry.scope.is_property_scope and
+                (not self.entry.is_special or self.entry.wrapperbase_cname)):
             docstr = self.entry.doc
             if docstr.is_unicode:
                 docstr = docstr.utf8encode()
@@ -2231,11 +2282,14 @@ class DefNode(FuncDefNode):
                 'static char %s[] = "%s";' % (
                     self.entry.doc_cname,
                     split_string_literal(escape_byte_string(docstr))))
+            if self.entry.is_special:
+                code.putln(
+                    "struct wrapperbase %s;" % self.entry.wrapperbase_cname)
         if with_pymethdef:
             code.put(
                 "static PyMethodDef %s = " % 
                     self.entry.pymethdef_cname)
-            code.put_pymethoddef(self.entry, ";")
+            code.put_pymethoddef(self.entry, ";", allow_skip=False)
         code.putln("%s {" % header)
 
     def generate_argument_declarations(self, env, code):
@@ -2589,24 +2643,21 @@ class DefNode(FuncDefNode):
         all_args = tuple(positional_args) + tuple(kw_only_args)
         max_args = len(all_args)
 
-        default_args = []
-        for i, arg in enumerate(all_args):
-            if arg.default and arg.type.is_pyobject:
-                default_value = arg.calculate_default_value_code(code)
-                if arg.type is not PyrexTypes.py_object_type:
-                    default_value = "(PyObject*)"+default_value
-                default_args.append((i, default_value))
-
         code.putln("Py_ssize_t kw_args = PyDict_Size(%s);" %
                    Naming.kwds_cname)
-        # it looks funny to separate the init-to-0 from setting the
-        # default value, but C89 needs this
+        # the 'values' array collects borrowed references to arguments
+        # before doing any type coercion etc.
         code.putln("PyObject* values[%d] = {%s};" % (
             max_args, ','.join('0'*max_args)))
-        for i, default_value in default_args:
-            code.putln('values[%d] = %s;' % (i, default_value))
 
-        # parse the tuple and check that it's not too long
+        # assign borrowed Python default values to the values array,
+        # so that they can be overwritten by received arguments below
+        for i, arg in enumerate(all_args):
+            if arg.default and arg.type.is_pyobject:
+                default_value = arg.calculate_default_value_code(code)
+                code.putln('values[%d] = %s;' % (i, arg.type.as_pyobject(default_value)))
+
+        # parse the args tuple and check that it's not too long
         code.putln('switch (PyTuple_GET_SIZE(%s)) {' % Naming.args_cname)
         if self.star_arg:
             code.putln('default:')
@@ -2643,10 +2694,10 @@ class DefNode(FuncDefNode):
                     if arg.kw_only:
                         # handled separately below
                         continue
-                    code.putln('if (kw_args > %d) {' % num_required_args)
+                    code.putln('if (kw_args > 0) {')
                     code.putln('PyObject* value = PyDict_GetItem(%s, %s);' % (
                         Naming.kwds_cname, pystring_cname))
-                    code.putln('if (unlikely(value)) { values[%d] = value; kw_args--; }' % i)
+                    code.putln('if (value) { values[%d] = value; kw_args--; }' % i)
                     code.putln('}')
                 else:
                     num_required_args -= 1
@@ -2899,35 +2950,71 @@ class PyClassDefNode(ClassDefNode):
     #
     #  The following subnodes are constructed internally:
     #
-    #  dict     DictNode   Class dictionary
+    #  dict     DictNode   Class dictionary or Py3 namespace
     #  classobj ClassNode  Class object
     #  target   NameNode   Variable to assign class object to
 
-    child_attrs = ["body", "dict", "classobj", "target"]
+    child_attrs = ["body", "dict", "metaclass", "mkw", "bases", "classobj", "target"]
     decorators = None
+    py3_style_class = False # Python3 style class (bases+kwargs)
     
-    def __init__(self, pos, name, bases, doc, body, decorators = None):
+    def __init__(self, pos, name, bases, doc, body, decorators = None,
+                 keyword_args = None, starstar_arg = None):
         StatNode.__init__(self, pos)
         self.name = name
         self.doc = doc
         self.body = body
         self.decorators = decorators
         import ExprNodes
-        self.dict = ExprNodes.DictNode(pos, key_value_pairs = [])
         if self.doc and Options.docstrings:
             doc = embed_position(self.pos, self.doc)
-            # FIXME: correct string node?
             doc_node = ExprNodes.StringNode(pos, value = doc)
         else:
             doc_node = None
-        self.classobj = ExprNodes.ClassNode(pos, name = name,
-            bases = bases, dict = self.dict, doc = doc_node)
+        if keyword_args or starstar_arg:
+            self.py3_style_class = True
+            self.bases = bases
+            self.metaclass = None
+            if keyword_args and not starstar_arg:
+                for i, item in list(enumerate(keyword_args.key_value_pairs))[::-1]:
+                    if item.key.value == 'metaclass':
+                        if self.metaclass is not None:
+                            error(item.pos, "keyword argument 'metaclass' passed multiple times")
+                        # special case: we already know the metaclass,
+                        # so we don't need to do the "build kwargs,
+                        # find metaclass" dance at runtime
+                        self.metaclass = item.value
+                        del keyword_args.key_value_pairs[i]
+            if starstar_arg or (keyword_args and keyword_args.key_value_pairs):
+                self.mkw = ExprNodes.KeywordArgsNode(
+                    pos, keyword_args = keyword_args, starstar_arg = starstar_arg)
+            else:
+                self.mkw = ExprNodes.NullNode(pos)
+            if self.metaclass is None:
+                self.metaclass = ExprNodes.PyClassMetaclassNode(
+                    pos, mkw = self.mkw, bases = self.bases)
+            self.dict = ExprNodes.PyClassNamespaceNode(pos, name = name,
+                        doc = doc_node, metaclass = self.metaclass, bases = self.bases,
+                        mkw = self.mkw)
+            self.classobj = ExprNodes.Py3ClassNode(pos, name = name,
+                    bases = self.bases, dict = self.dict, doc = doc_node,
+                    metaclass = self.metaclass, mkw = self.mkw)
+        else:
+            self.dict = ExprNodes.DictNode(pos, key_value_pairs = [])
+            self.metaclass = None
+            self.mkw = None
+            self.bases = None
+            self.classobj = ExprNodes.ClassNode(pos, name = name,
+                    bases = bases, dict = self.dict, doc = doc_node)
         self.target = ExprNodes.NameNode(pos, name = name)
         
     def as_cclass(self):
         """
         Return this node as if it were declared as an extension class
         """
+        if self.py3_style_class:
+            error(self.classobj.pos, "Python3 style class could not be represented as C class")
+            return
         bases = self.classobj.bases.args
         if len(bases) == 0:
             base_class_name = None
@@ -2965,8 +3052,8 @@ class PyClassDefNode(ClassDefNode):
         
     def create_scope(self, env):
         genv = env
-        while env.is_py_class_scope or env.is_c_class_scope:
-            env = env.outer_scope
+        while genv.is_py_class_scope or genv.is_c_class_scope:
+            genv = genv.outer_scope
         cenv = self.scope = PyClassScope(name = self.name, outer_scope = genv)
         return cenv
     
@@ -2978,6 +3065,10 @@ class PyClassDefNode(ClassDefNode):
         self.body.analyse_declarations(cenv)
     
     def analyse_expressions(self, env):
+        if self.py3_style_class:
+            self.bases.analyse_expressions(env)
+            self.metaclass.analyse_expressions(env)
+            self.mkw.analyse_expressions(env)
         self.dict.analyse_expressions(env)
         self.classobj.analyse_expressions(env)
         genv = env.global_scope()
@@ -2991,16 +3082,27 @@ class PyClassDefNode(ClassDefNode):
     def generate_execution_code(self, code):
         code.pyclass_stack.append(self)
         cenv = self.scope
+        if self.py3_style_class:
+            self.bases.generate_evaluation_code(code)
+            self.mkw.generate_evaluation_code(code)
+            self.metaclass.generate_evaluation_code(code)
         self.dict.generate_evaluation_code(code)
+        cenv.namespace_cname = cenv.class_obj_cname = self.dict.result()
+        self.body.generate_execution_code(code)
         self.classobj.generate_evaluation_code(code)
         cenv.namespace_cname = cenv.class_obj_cname = self.classobj.result()
-        self.body.generate_execution_code(code)
         self.target.generate_assignment_code(self.classobj, code)
         self.dict.generate_disposal_code(code)
         self.dict.free_temps(code)
+        if self.py3_style_class:
+            self.mkw.generate_disposal_code(code)
+            self.mkw.free_temps(code)
+            self.metaclass.generate_disposal_code(code)
+            self.metaclass.free_temps(code)
+            self.bases.generate_disposal_code(code)
+            self.bases.free_temps(code)
         code.pyclass_stack.pop()
 
-
 class CClassDefNode(ClassDefNode):
     #  An extension type definition.
     #
@@ -3080,7 +3182,12 @@ class CClassDefNode(ClassDefNode):
                     elif not base_class_entry.type.is_extension_type:
                         error(self.pos, "'%s' is not an extension type" % self.base_class_name)
                     elif not base_class_entry.type.is_complete():
-                        error(self.pos, "Base class '%s' is incomplete" % self.base_class_name)
+                        error(self.pos, "Base class '%s' of type '%s' is incomplete" % (
+                            self.base_class_name, self.class_name))
+                    elif base_class_entry.type.scope and base_class_entry.type.scope.directives and \
+                             base_class_entry.type.scope.directives['final']:
+                        error(self.pos, "Base class '%s' of type '%s' is final" % (
+                            self.base_class_name, self.class_name))
                     else:
                         self.base_type = base_class_entry.type
         has_body = self.body is not None
@@ -3110,7 +3217,7 @@ class CClassDefNode(ClassDefNode):
             api = self.api,
             buffer_defaults = buffer_defaults)
         if home_scope is not env and self.visibility == 'extern':
-            env.add_imported_entry(self.class_name, self.entry, pos)
+            env.add_imported_entry(self.class_name, self.entry, self.pos)
         self.scope = scope = self.entry.type.scope
         if scope is not None:
             scope.directives = env.directives
@@ -3278,7 +3385,7 @@ class SingleAssignmentNode(AssignmentNode):
                 
                 if func_name in ['declare', 'typedef']:
                     if len(args) > 2 or kwds is not None:
-                        error(rhs.pos, "Can only declare one type at a time.")
+                        error(self.rhs.pos, "Can only declare one type at a time.")
                         return
                     type = args[0].analyse_as_type(env)
                     if type is None:
@@ -3309,7 +3416,7 @@ class SingleAssignmentNode(AssignmentNode):
                 elif func_name in ['struct', 'union']:
                     self.declaration_only = True
                     if len(args) > 0 or kwds is None:
-                        error(rhs.pos, "Struct or union members must be given by name.")
+                        error(self.rhs.pos, "Struct or union members must be given by name.")
                         return
                     members = []
                     for member, type_node in kwds.key_value_pairs:
@@ -3478,132 +3585,41 @@ class InPlaceAssignmentNode(AssignmentNode):
     #  (it must be a NameNode, AttributeNode, or IndexNode).     
     
     child_attrs = ["lhs", "rhs"]
-    dup = None
 
     def analyse_declarations(self, env):
         self.lhs.analyse_target_declaration(env)
         
     def analyse_types(self, env):
-        self.dup = self.create_dup_node(env) # re-assigns lhs to a shallow copy
         self.rhs.analyse_types(env)
         self.lhs.analyse_target_types(env)
-        import ExprNodes
-        if self.lhs.type.is_pyobject:
-            self.rhs = self.rhs.coerce_to_pyobject(env)
-        elif self.rhs.type.is_pyobject:
-            self.rhs = self.rhs.coerce_to(self.lhs.type, env)
-        if self.lhs.type.is_pyobject:
-            self.result_value_temp = ExprNodes.PyTempNode(self.pos, env)
-            self.result_value = self.result_value_temp.coerce_to(self.lhs.type, env)
-        
+
     def generate_execution_code(self, code):
         import ExprNodes
         self.rhs.generate_evaluation_code(code)
-        self.dup.generate_subexpr_evaluation_code(code)
-        if self.dup.is_temp:
-            self.dup.allocate_temp_result(code)
-        # self.dup.generate_result_code is run only if it is not buffer access
-        if self.operator == "**":
-            extra = ", Py_None"
-        else:
-            extra = ""
-        if self.lhs.type.is_pyobject:
-            if isinstance(self.lhs, ExprNodes.IndexNode) and self.lhs.is_buffer_access:
+        self.lhs.generate_subexpr_evaluation_code(code)
+        c_op = self.operator
+        if c_op == "//":
+            c_op = "/"
+        elif c_op == "**":
+            error(self.pos, "No C inplace power operator")
+        if isinstance(self.lhs, ExprNodes.IndexNode) and self.lhs.is_buffer_access:
+            if self.lhs.type.is_pyobject:
                 error(self.pos, "In-place operators not allowed on object buffers in this release.")
-            self.dup.generate_result_code(code)
-            self.result_value_temp.allocate(code)
-            code.putln(
-                "%s = %s(%s, %s%s); %s" % (
-                    self.result_value.result(), 
-                    self.py_operation_function(), 
-                    self.dup.py_result(),
-                    self.rhs.py_result(),
-                    extra,
-                    code.error_goto_if_null(self.result_value.py_result(), self.pos)))
-            code.put_gotref(self.result_value.py_result())
-            self.result_value.generate_evaluation_code(code) # May be a type check...
-            self.rhs.generate_disposal_code(code)
-            self.rhs.free_temps(code)
-            self.dup.generate_disposal_code(code)
-            self.dup.free_temps(code)
-            self.lhs.generate_assignment_code(self.result_value, code)
-            self.result_value_temp.release(code)
-        else: 
-            c_op = self.operator
-            if c_op == "//":
-                c_op = "/"
-            elif c_op == "**":
-                error(self.pos, "No C inplace power operator")
-            elif self.lhs.type.is_complex:
-                error(self.pos, "Inplace operators not implemented for complex types.")
-                
-            # have to do assignment directly to avoid side-effects
-            if isinstance(self.lhs, ExprNodes.IndexNode) and self.lhs.is_buffer_access:
-                self.lhs.generate_buffer_setitem_code(self.rhs, code, c_op)
-            else:
-                self.dup.generate_result_code(code)
-                code.putln("%s %s= %s;" % (self.lhs.result(), c_op, self.rhs.result()) )
-            self.rhs.generate_disposal_code(code)
-            self.rhs.free_temps(code)
-        if self.dup.is_temp:
-            self.dup.generate_subexpr_disposal_code(code)
-            self.dup.free_subexpr_temps(code)
-            
-    def create_dup_node(self, env): 
-        import ExprNodes
-        self.dup = self.lhs
-        self.dup.analyse_types(env)
-        if isinstance(self.lhs, ExprNodes.NameNode):
-            target_lhs = ExprNodes.NameNode(self.dup.pos,
-                                            name = self.dup.name,
-                                            is_temp = self.dup.is_temp,
-                                            entry = self.dup.entry)
-        elif isinstance(self.lhs, ExprNodes.AttributeNode):
-            target_lhs = ExprNodes.AttributeNode(self.dup.pos,
-                                                 obj = ExprNodes.CloneNode(self.lhs.obj),
-                                                 attribute = self.dup.attribute,
-                                                 is_temp = self.dup.is_temp)
-        elif isinstance(self.lhs, ExprNodes.IndexNode):
-            if self.lhs.index:
-                index = ExprNodes.CloneNode(self.lhs.index)
-            else:
-                index = None
-            if self.lhs.indices:
-                indices = [ExprNodes.CloneNode(x) for x in self.lhs.indices]
-            else:
-                indices = []
-            target_lhs = ExprNodes.IndexNode(self.dup.pos,
-                                             base = ExprNodes.CloneNode(self.dup.base),
-                                             index = index,
-                                             indices = indices,
-                                             is_temp = self.dup.is_temp)
+            if c_op in ('/', '%') and self.lhs.type.is_int and not code.directives['cdivision']:
+                error(self.pos, "In-place non-c divide operators not allowed on int buffers.")
+            self.lhs.generate_buffer_setitem_code(self.rhs, code, c_op)
         else:
-            assert False, "Unsupported node: %s" % type(self.lhs)
-        self.lhs = target_lhs
-        return self.dup
-    
-    def py_operation_function(self):
-        return self.py_functions[self.operator]
-
-    py_functions = {
-        "|":        "PyNumber_InPlaceOr",
-        "^":        "PyNumber_InPlaceXor",
-        "&":        "PyNumber_InPlaceAnd",
-        "+":        "PyNumber_InPlaceAdd",
-        "-":        "PyNumber_InPlaceSubtract",
-        "*":        "PyNumber_InPlaceMultiply",
-        "/":        "__Pyx_PyNumber_InPlaceDivide",
-        "%":        "PyNumber_InPlaceRemainder",
-        "<<":        "PyNumber_InPlaceLshift",
-        ">>":        "PyNumber_InPlaceRshift",
-        "**":        "PyNumber_InPlacePower",
-        "//":        "PyNumber_InPlaceFloorDivide",
-    }
+            # C++
+            # TODO: make sure overload is declared
+            code.putln("%s %s= %s;" % (self.lhs.result(), c_op, self.rhs.result()))
+        self.lhs.generate_subexpr_disposal_code(code)
+        self.lhs.free_subexpr_temps(code)
+        self.rhs.generate_disposal_code(code)
+        self.rhs.free_temps(code)
 
     def annotate(self, code):
         self.lhs.annotate(code)
         self.rhs.annotate(code)
-        self.dup.annotate(code)
     
     def create_binop_node(self):
         import ExprNodes
@@ -3984,7 +4000,7 @@ class AssertStatNode(StatNode):
     gil_message = "Raising exception"
     
     def generate_execution_code(self, code):
-        code.putln("#ifndef PYREX_WITHOUT_ASSERTIONS")
+        code.putln("#ifndef CYTHON_WITHOUT_ASSERTIONS")
         self.cond.generate_evaluation_code(code)
         code.putln(
             "if (unlikely(!%s)) {" %
@@ -4083,7 +4099,6 @@ class IfClauseNode(Node):
         self.body.analyse_control_flow(env)
         
     def analyse_declarations(self, env):
-        self.condition.analyse_declarations(env)
         self.body.analyse_declarations(env)
     
     def analyse_expressions(self, env):
@@ -4153,6 +4168,7 @@ class SwitchStatNode(StatNode):
     child_attrs = ['test', 'cases', 'else_clause']
     
     def generate_execution_code(self, code):
+        self.test.generate_evaluation_code(code)
         code.putln("switch (%s) {" % self.test.result())
         for case in self.cases:
             case.generate_execution_code(code)
@@ -4701,7 +4717,7 @@ class TryExceptStatNode(StatNode):
 class ExceptClauseNode(Node):
     #  Part of try ... except statement.
     #
-    #  pattern        ExprNode
+    #  pattern        [ExprNode]
     #  target         ExprNode or None
     #  body           StatNode
     #  excinfo_target NameNode or None   optional target for exception info
@@ -4731,8 +4747,10 @@ class ExceptClauseNode(Node):
         genv = env.global_scope()
         self.function_name = env.qualified_name
         if self.pattern:
-            self.pattern.analyse_expressions(env)
-            self.pattern = self.pattern.coerce_to_pyobject(env)
+            # normalise/unpack self.pattern into a list
+            for i, pattern in enumerate(self.pattern):
+                pattern.analyse_expressions(env)
+                self.pattern[i] = pattern.coerce_to_pyobject(env)
 
         if self.target:
             self.exc_value = ExprNodes.ExcValueNode(self.pos, env)
@@ -4749,15 +4767,17 @@ class ExceptClauseNode(Node):
     def generate_handling_code(self, code, end_label):
         code.mark_pos(self.pos)
         if self.pattern:
-            self.pattern.generate_evaluation_code(code)
-            
+            exc_tests = []
+            for pattern in self.pattern:
+                pattern.generate_evaluation_code(code)
+                exc_tests.append("PyErr_ExceptionMatches(%s)" % pattern.py_result())
+
             match_flag = code.funcstate.allocate_temp(PyrexTypes.c_int_type, False)
             code.putln(
-                "%s = PyErr_ExceptionMatches(%s);" % (
-                    match_flag,
-                    self.pattern.py_result()))
-            self.pattern.generate_disposal_code(code)
-            self.pattern.free_temps(code)
+                "%s = %s;" % (match_flag, ' || '.join(exc_tests)))
+            for pattern in self.pattern:
+                pattern.generate_disposal_code(code)
+                pattern.free_temps(code)
             code.putln(
                 "if (%s) {" %
                     match_flag)
@@ -4836,7 +4856,8 @@ class ExceptClauseNode(Node):
         
     def annotate(self, code):
         if self.pattern:
-            self.pattern.annotate(code)
+            for pattern in self.pattern:
+                pattern.annotate(code)
         if self.target:
             self.target.annotate(code)
         self.body.annotate(code)
@@ -4906,6 +4927,7 @@ class TryFinallyStatNode(StatNode):
         code.putln(
             "}")
         temps_to_clean_up = code.funcstate.all_free_managed_temps()
+        code.mark_pos(self.finally_clause.pos)
         code.putln(
             "/*finally:*/ {")
         cases_used = []
@@ -5054,10 +5076,15 @@ class GILStatNode(TryFinallyStatNode):
 
     def generate_execution_code(self, code):
         code.mark_pos(self.pos)
+        code.putln("{")
         if self.state == 'gil':
-            code.putln("{ PyGILState_STATE _save = PyGILState_Ensure();")
+            code.putln("#ifdef WITH_THREAD")
+            code.putln("PyGILState_STATE _save = PyGILState_Ensure();")
+            code.putln("#endif")
         else:
-            code.putln("{ PyThreadState *_save;")
+            code.putln("#ifdef WITH_THREAD")
+            code.putln("PyThreadState *_save;")
+            code.putln("#endif")
             code.putln("Py_UNBLOCK_THREADS")
         TryFinallyStatNode.generate_execution_code(self, code)
         code.putln("}")
@@ -5075,7 +5102,9 @@ class GILExitNode(StatNode):
 
     def generate_execution_code(self, code):
         if self.state == 'gil':
-            code.putln("PyGILState_Release();")
+            code.putln("#ifdef WITH_THREAD")
+            code.putln("PyGILState_Release(_save);")
+            code.putln("#endif")
         else:
             code.putln("Py_BLOCK_THREADS")
 
@@ -5216,17 +5245,26 @@ class FromImportStatNode(StatNode):
                         break
             else:
                 entry =  env.lookup(target.name)
-                if (entry.is_type and 
-                    entry.type.name == name and
-                    entry.type.module_name == self.module.module_name.value):
-                    continue # already cimported
+                # check whether or not entry is already cimported
+                if (entry.is_type and entry.type.name == name
+                    and hasattr(entry.type, 'module_name')):
+                    if entry.type.module_name == self.module.module_name.value:
+                        # cimported with absolute name
+                        continue
+                    try:
+                        # cimported with relative name
+                        module = env.find_module(self.module.module_name.value,
+                                                 pos=None)
+                        if entry.type.module_name == module.qualified_name:
+                            continue
+                    except AttributeError:
+                        pass 
                 target.analyse_target_expression(env, None)
                 if target.type is py_object_type:
                     coerced_item = None
                 else:
                     coerced_item = self.item.coerce_to(target.type, env)
-                self.interned_items.append(
-                    (name, target, coerced_item))
+                self.interned_items.append((name, target, coerced_item))
     
     def generate_execution_code(self, code):
         self.module.generate_evaluation_code(code)
@@ -6159,10 +6197,10 @@ static int __Pyx_SetVtable(PyObject *dict, void *vtable); /*proto*/
 """,
 impl = """
 static int __Pyx_SetVtable(PyObject *dict, void *vtable) {
-#if PY_VERSION_HEX < 0x03010000
-    PyObject *ob = PyCObject_FromVoidPtr(vtable, 0);
-#else
+#if PY_VERSION_HEX >= 0x02070000 && !(PY_MAJOR_VERSION==3&&PY_MINOR_VERSION==0)
     PyObject *ob = PyCapsule_New(vtable, 0, 0);
+#else
+    PyObject *ob = PyCObject_FromVoidPtr(vtable, 0);
 #endif
     if (!ob)
         goto bad;
@@ -6187,10 +6225,10 @@ static int __Pyx_GetVtable(PyObject *dict, void *vtabptr) {
     PyObject *ob = PyMapping_GetItemString(dict, (char *)"__pyx_vtable__");
     if (!ob)
         goto bad;
-#if PY_VERSION_HEX < 0x03010000
-    *(void **)vtabptr = PyCObject_AsVoidPtr(ob);
-#else
+#if PY_VERSION_HEX >= 0x02070000 && !(PY_MAJOR_VERSION==3&&PY_MINOR_VERSION==0)
     *(void **)vtabptr = PyCapsule_GetPointer(ob, 0);
+#else
+    *(void **)vtabptr = PyCObject_AsVoidPtr(ob);
 #endif
     if (!*(void **)vtabptr)
         goto bad;