big C++ mergeback
authorRobert Bradshaw <robertwb@math.washington.edu>
Tue, 15 Dec 2009 11:54:36 +0000 (03:54 -0800)
committerRobert Bradshaw <robertwb@math.washington.edu>
Tue, 15 Dec 2009 11:54:36 +0000 (03:54 -0800)
12 files changed:
1  2 
Cython/Compiler/ExprNodes.py
Cython/Compiler/Main.py
Cython/Compiler/Nodes.py
Cython/Compiler/Options.py
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/Parsing.pxd
Cython/Compiler/Parsing.py
Cython/Compiler/PyrexTypes.py
Cython/Compiler/Symtab.py
Cython/Compiler/Visitor.py
tests/bugs.txt
tests/errors/e_del.pyx

index eac472378cd352bd1e96889c4430c66e3ab40187,d2789833eef8cd1f593f5f288f6b6dfcc4845ed1..f42ab943c7029890a9f1326c8cdeeec8ef23476e
mode 100755,100644..100755
@@@ -1073,44 -1039,8 +1039,42 @@@ class ImagNode(AtomicExprNode)
                      float(self.value),
                      code.error_goto_if_null(self.result(), self.pos)))
              code.put_gotref(self.py_result())
-         else:
-             self.c99_complex = code.globalstate.directives['c99_complex']
          
  
 +class NewExprNode(AtomicExprNode):
 +
 +    # C++ new statement
 +    #
 +    # cppclass              string               c++ class to create
 +    # template_parameters   None or [ExprNode]   temlate parameters, if any
 +    
 +    def analyse_types(self, env):
 +        entry = env.lookup(self.cppclass)
 +        if entry is None or not entry.is_cpp_class:
 +            error(self.pos, "new operator can only be applied to a C++ class")
 +            return
 +        self.cpp_check(env)
 +        if self.template_parameters is not None:
 +            template_types = [v.analyse_as_type(env) for v in self.template_parameters]
 +            type = entry.type.specialize_here(self.pos, template_types)
 +        else:
 +            type = entry.type
 +        constructor = type.scope.lookup(u'<init>')
 +        if constructor is None:
 +            return_type = PyrexTypes.CFuncType(type, [])
 +            return_type = PyrexTypes.CPtrType(return_type)
 +            type.scope.declare_cfunction(u'<init>', return_type, self.pos)
 +            constructor = type.scope.lookup(u'<init>')
 +        self.class_type = type
 +        self.entry = constructor
 +        self.type = constructor.type
 +   
 +    def generate_result_code(self, code):
 +        pass
 +   
 +    def calculate_result_code(self):
 +        return "new " + self.class_type.declaration_code("")
 +
  
  class NameNode(AtomicExprNode):
      #  Reference to a local or global variable name.
          entry = self.entry
          type = entry.type
          self.type = type
-         if entry.is_pyglobal or entry.is_builtin:
-             assert type.is_pyobject, "Python global or builtin not a Python object"
-             self.interned_cname = self.entry.interned_cname = \
-                 env.intern_identifier(self.entry.name)
  
      def check_identifier_kind(self):
-         #print "NameNode.check_identifier_kind:", self.entry.name ###
-         #print self.entry.__dict__ ###
+         # Check that this is an appropriate kind of name for use in an
+         # expression.  Also finds the variable entry associated with
+         # an extension type.
          entry = self.entry
-         #entry.used = 1
+         if entry.is_type and entry.type.is_extension_type:
+             self.type_entry = entry
          if not (entry.is_const or entry.is_variable 
 -            or entry.is_builtin or entry.is_cfunction):
 +            or entry.is_builtin or entry.is_cfunction
 +            or entry.is_cpp_class):
                  if self.entry.as_variable:
                      self.entry = self.entry.as_variable
                  else:
@@@ -1737,21 -1765,22 +1800,34 @@@ class IndexNode(ExprNode)
      def analyse_as_type(self, env):
          base_type = self.base.analyse_as_type(env)
          if base_type and not base_type.is_pyobject:
 -            return PyrexTypes.CArrayType(base_type, int(self.index.compile_time_value(env)))
 +            if base_type.is_cpp_class:
 +                if isinstance(self.index, TupleExprNode):
 +                    template_values = self.index.args
 +                else:
 +                    template_values = [self.index]
 +                import Nodes
 +                type_node = Nodes.TemplatedTypeNode(
 +                    pos = self.pos, 
 +                    positional_args = template_values, 
 +                    keyword_args = None)
 +                return type_node.analyse(env, base_type = base_type)
 +            else:
 +                return PyrexTypes.CArrayType(base_type, int(self.index.compile_time_value(env)))
          return None
      
+     def type_dependencies(self, env):
+         return self.base.type_dependencies(env)
+     
+     def infer_type(self, env):
+         if isinstance(self.base, (StringNode, UnicodeNode)): # FIXME: BytesNode?
+             return py_object_type
+         base_type = self.base.infer_type(env)
+         if base_type.is_ptr or base_type.is_array:
+             return base_type.base_type
+         else:
+             # TODO: Handle buffers (hopefully without too much redundancy).
+             return py_object_type
+     
      def analyse_types(self, env):
          self.analyse_base_and_index_types(env, getting = 1)
      
@@@ -2435,6 -2526,26 +2572,9 @@@ class SimpleCallNode(CallNode)
          max_nargs = len(func_type.args)
          expected_nargs = max_nargs - func_type.optional_arg_count
          actual_nargs = len(self.args)
 -        if actual_nargs < expected_nargs \
 -            or (not func_type.has_varargs and actual_nargs > max_nargs):
 -                expected_str = str(expected_nargs)
 -                if func_type.has_varargs:
 -                    expected_str = "at least " + expected_str
 -                elif func_type.optional_arg_count:
 -                    if actual_nargs < max_nargs:
 -                        expected_str = "at least " + expected_str
 -                    else:
 -                        expected_str = "at most " + str(max_nargs)
 -                error(self.pos, 
 -                    "Call with wrong number of arguments (expected %s, got %s)"
 -                        % (expected_str, actual_nargs))
 -                self.args = None
 -                self.type = PyrexTypes.error_type
 -                self.result_code = "<error>"
 -                return
+         if func_type.optional_arg_count and expected_nargs != actual_nargs:
+             self.has_optional_args = 1
+             self.is_temp = 1
          # Coerce arguments
          for i in range(min(max_nargs, actual_nargs)):
              formal_type = func_type.args[i].type
@@@ -3764,10 -4079,7 +4111,9 @@@ class UnopNode(ExprNode)
          if self.is_py_operation():
              self.coerce_operand_to_pyobject(env)
              self.type = py_object_type
-             self.gil_check(env)
              self.is_temp = 1
 +        elif self.is_cpp_operation():
 +            self.analyse_cpp_operation(env)
          else:
              self.analyse_c_operation(env)
      
      def is_py_operation(self):
          return self.operand.type.is_pyobject
  
+     def nogil_check(self, env):
+         if self.is_py_operation():
+             self.gil_error()
++
 +    def is_cpp_operation(self):
 +        return self.operand.type.is_cpp_class
 +    
      def coerce_operand_to_pyobject(self, env):
          self.operand = self.operand.coerce_to_pyobject(env)
      
@@@ -4193,29 -4549,26 +4601,38 @@@ class BinopNode(ExprNode)
          if self.is_py_operation():
              self.coerce_operands_to_pyobjects(env)
              self.type = py_object_type
-             self.gil_check(env)
              self.is_temp = 1
-             if Options.incref_local_binop and self.operand1.type.is_pyobject:
-                 self.operand1 = self.operand1.coerce_to_temp(env)
 +        elif self.is_cpp_operation():
 +            self.analyse_cpp_operation(env)
          else:
              self.analyse_c_operation(env)
      
      def is_py_operation(self):
-         return (self.operand1.type.is_pyobject 
-             or self.operand2.type.is_pyobject)
+         return self.is_py_operation_types(self.operand1.type, self.operand2.type)
      
 +    def is_cpp_operation(self):
 +        type1 = self.operand1.type
 +        type2 = self.operand2.type
 +        if type1.is_ptr:
 +            type1 = type1.base_type
 +        if type2.is_ptr:
 +            type2 = type2.base_type
 +        return (type1.is_cpp_class
 +            or type2.is_cpp_class)
 +    
+     def is_py_operation_types(self, type1, type2):
+         return type1.is_pyobject or type2.is_pyobject
+     def result_type(self, type1, type2):
+         if self.is_py_operation_types(type1, type2):
+             return py_object_type
+         else:
+             return self.compute_c_result_type(type1, type2)
+     def nogil_check(self, env):
+         if self.is_py_operation():
+             self.gil_error()
+         
      def coerce_operands_to_pyobjects(self, env):
          self.operand1 = self.operand1.coerce_to_pyobject(env)
          self.operand2 = self.operand2.coerce_to_pyobject(env)
@@@ -4809,53 -5162,125 +5254,134 @@@ class CmpNode(object)
                  result = result and cascade.compile_time_value(operand2, denv)
          return result
  
-     def is_python_comparison(self):
-         return (self.has_python_operands()
-             or (self.cascade and self.cascade.is_python_comparison())
-             or self.operator in ('in', 'not_in'))
 +    def is_cpp_comparison(self):
 +        type1 = self.operand1.type
 +        type2 = self.operand2.type
 +        if type1.is_ptr:
 +            type1 = type1.base_type
 +        if type2.is_ptr:
 +            type2 = type2.base_type
 +        return type1.is_cpp_class or type2.is_cpp_class
 +
+     def find_common_int_type(self, env, op, operand1, operand2):
+         # type1 != type2 and at least one of the types is not a C int
+         type1 = operand1.type
+         type2 = operand2.type
+         type1_can_be_int = False
+         type2_can_be_int = False
+         if isinstance(operand1, (StringNode, BytesNode)) \
+                and operand1.can_coerce_to_char_literal():
+             type1_can_be_int = True
+         if isinstance(operand2, (StringNode, BytesNode)) \
+                  and operand2.can_coerce_to_char_literal():
+             type2_can_be_int = True
+         if type1.is_int:
+             if type2_can_be_int:
+                 return type1
+         elif type2.is_int:
+             if type1_can_be_int:
+                 return type2
+         elif type1_can_be_int:
+             if type2_can_be_int:
+                 return PyrexTypes.c_uchar_type
  
-     def is_python_result(self):
-         return ((self.has_python_operands() and self.operator not in ('is', 'is_not', 'in', 'not_in'))
-             or (self.cascade and self.cascade.is_python_result()))
+         return None
  
-     def check_types(self, env, operand1, op, operand2):
-         if operand1.type.is_complex or operand2.type.is_complex:
-             if op not in ('==', '!='):
-                 error(self.pos, "complex types unordered")
-             common_type = PyrexTypes.widest_numeric_type(operand1.type, operand2.type)
-             self.operand1 = operand1.coerce_to(common_type, env)
-             self.operand2 = operand2.coerce_to(common_type, env)
-         elif not self.types_okay(operand1, op, operand2):
-             error(self.pos, "Invalid types for '%s' (%s, %s)" %
-                 (self.operator, operand1.type, operand2.type))
-     
-     def types_okay(self, operand1, op, operand2):
+     def find_common_type(self, env, op, operand1, common_type=None):
+         operand2 = self.operand2
          type1 = operand1.type
          type2 = operand2.type
-         if type1.is_error or type2.is_error:
-             return 1
-         if type1.is_pyobject: # type2 will be, too
-             return 1
-         elif type1.is_ptr or type1.is_array:
-             return type1.is_null_ptr or type2.is_null_ptr \
-                 or ((type2.is_ptr or type2.is_array)
-                     and type1.base_type.same_as(type2.base_type))
-         elif ((type1.is_numeric and type2.is_numeric
-                     or type1.is_enum and (type1 is type2 or type2.is_int)
-                     or type1.is_int and type2.is_enum)
-                 and op not in ('is', 'is_not')):
-             return 1
+         new_common_type = None
+         # catch general errors
+         if type1 == str_type and (type2.is_string or type2 in (bytes_type, unicode_type)) or \
+                type2 == str_type and (type1.is_string or type1 in (bytes_type, unicode_type)):
+             error(self.pos, "Comparisons between bytes/unicode and str are not portable to Python 3")
+             new_common_type = error_type
+         # try to use numeric comparisons where possible
+         elif type1.is_complex or type2.is_complex:
+             if op not in ('==', '!='):
+                 error(self.pos, "complex types are unordered")
+                 new_common_type = error_type
+             if type1.is_pyobject:
+                 new_common_type = type1
+             elif type2.is_pyobject:
+                 new_common_type = type2
+             else:
+                 new_common_type = PyrexTypes.widest_numeric_type(type1, type2)
+         elif type1.is_numeric and type2.is_numeric:
+             new_common_type = PyrexTypes.widest_numeric_type(type1, type2)
+         elif common_type is None or not common_type.is_pyobject:
+             new_common_type = self.find_common_int_type(env, op, operand1, operand2)
+         if new_common_type is None:
+             # fall back to generic type compatibility tests
+             if type1 == type2:
+                 new_common_type = type1
+             elif type1.is_pyobject or type2.is_pyobject:
+                 if type2.is_numeric or type2.is_string:
+                     if operand2.check_for_coercion_error(type1):
+                         new_common_type = error_type
+                     else:
+                         new_common_type = py_object_type
+                 elif type1.is_numeric or type1.is_string:
+                     if operand1.check_for_coercion_error(type2):
+                         new_common_type = error_type
+                     else:
+                         new_common_type = py_object_type
+                 elif py_object_type.assignable_from(type1) and py_object_type.assignable_from(type2):
+                     new_common_type = py_object_type
+                 else:
+                     # one Python type and one non-Python type, not assignable
+                     self.invalid_types_error(operand1, op, operand2)
+                     new_common_type = error_type
+             elif type1.assignable_from(type2):
+                 new_common_type = type1
+             elif type2.assignable_from(type1):
+                 new_common_type = type2
+             else:
+                 # C types that we couldn't handle up to here are an error
+                 self.invalid_types_error(operand1, op, operand2)
+                 new_common_type = error_type
+         # recursively merge types
+         if common_type is None or new_common_type.is_error:
+             common_type = new_common_type
          else:
-             return type1.is_cfunction and type1.is_cfunction and type1 == type2
+             # we could do a lot better by splitting the comparison
+             # into a non-Python part and a Python part, but this is
+             # safer for now
+             common_type = PyrexTypes.spanning_type(common_type, new_common_type)
+         if self.cascade:
+             common_type = self.cascade.find_common_type(env, self.operator, operand2, common_type)
+         return common_type
+     def invalid_types_error(self, operand1, op, operand2):
+         error(self.pos, "Invalid types for '%s' (%s, %s)" %
+               (op, operand1.type, operand2.type))
+     def is_python_comparison(self):
+         return (self.has_python_operands()
+                 or (self.cascade and self.cascade.is_python_comparison())
+                 or self.operator in ('in', 'not_in'))
+     def coerce_operands_to(self, dst_type, env):
+         operand2 = self.operand2
+         if operand2.type != dst_type:
+             self.operand2 = operand2.coerce_to(dst_type, env)
+         if self.cascade:
+             self.cascade.coerce_operands_to(dst_type, env)
+     def is_python_result(self):
+         return ((self.has_python_operands() and
+                  self.operator not in ('is', 'is_not', 'in', 'not_in'))
+             or (self.cascade and self.cascade.is_python_result()))
  
      def generate_operation_code(self, code, result_code, 
              operand1, op , operand2):
@@@ -4950,16 -5420,21 +5521,24 @@@ class PrimaryCmpNode(ExprNode, CmpNode)
      def analyse_types(self, env):
          self.operand1.analyse_types(env)
          self.operand2.analyse_types(env)
 +        if self.is_cpp_comparison():
 +            self.analyse_cpp_comparison(env)
 +            return
          if self.cascade:
-             self.cascade.analyse_types(env, self.operand2)
-         self.is_pycmp = self.is_python_comparison()
-         if self.is_pycmp:
-             self.coerce_operands_to_pyobjects(env)
-         if self.has_int_operands():
-             self.coerce_chars_to_ints(env)
+             self.cascade.analyse_types(env)
+         if self.operator in ('in', 'not_in'):
+             common_type = py_object_type
+             self.is_pycmp = True
+         else:
+             common_type = self.find_common_type(env, self.operator, self.operand1)
+             self.is_pycmp = common_type.is_pyobject
+         if not common_type.is_error:
+             if self.operand1.type != common_type:
+                 self.operand1 = self.operand1.coerce_to(common_type, env)
+             self.coerce_operands_to(common_type, env)
          if self.cascade:
              self.operand2 = self.operand2.coerce_to_simple(env)
              self.cascade.coerce_cascaded_operands_to_temp(env)
              cdr = cdr.cascade
          if self.is_pycmp or self.cascade:
              self.is_temp = 1
 -
 +    
 +    def analyse_cpp_comparison(self, env):
 +        type1 = self.operand1.type
 +        type2 = self.operand2.type
 +        if type1.is_ptr:
 +            type1 = type1.base_type
 +        if type2.is_ptr:
 +            type2 = type2.base_type
 +        entry = env.lookup(type1.name)
 +        function = entry.type.scope.lookup("operator%s" % self.operator)
 +        if not function:
 +            error(self.pos, "Invalid types for '%s' (%s, %s)" %
 +                (self.operator, type1, type2))
 +            return
 +        entry = PyrexTypes.best_match([self.operand1, self.operand2], function.all_alternatives(), self.pos)
 +        if entry is None:
 +            self.type = PyrexTypes.error_type
 +            self.result_code = "<error>"
 +            return
 +        if (entry.type.is_ptr):
 +            self.type = entry.type.base_type.return_type
 +        else:
 +            self.type = entry.type.return_type
 +    
-     def check_operand_types(self, env):
-         self.check_types(env, 
-             self.operand1, self.operator, self.operand2)
-         if self.cascade:
-             self.cascade.check_operand_types(env, self.operand2)
-     
      def has_python_operands(self):
          return (self.operand1.type.is_pyobject
              or self.operand2.type.is_pyobject)
index 15774e8695c605af34c49b190802f43ed859d0cb,e869e9816ecf90489ca5da23a82aa96a8591512a..4d35ed3c42af31655f03b2f18e158471f423713c
@@@ -59,16 -66,15 +66,17 @@@ class Context(object)
      #  include_directories   [string]
      #  future_directives     [object]
      
-     def __init__(self, include_directories, pragma_overrides, cpp=False):
 -    def __init__(self, include_directories, compiler_directives):
++    def __init__(self, include_directories, compiler_directives, cpp=False):
          #self.modules = {"__builtin__" : BuiltinScope()}
          import Builtin, CythonScope
          self.modules = {"__builtin__" : Builtin.builtin_scope}
          self.modules["cython"] = CythonScope.create_cython_scope(self)
          self.include_directories = include_directories
          self.future_directives = set()
-         self.pragma_overrides = pragma_overrides
+         self.compiler_directives = compiler_directives
 +        self.cpp = cpp
 +
          self.pxds = {} # full name -> node tree
  
          standard_include_path = os.path.abspath(
@@@ -523,7 -549,7 +552,7 @@@ def create_default_resultobj(compilatio
  
  def run_pipeline(source, options, full_module_name = None):
      # Set up context
-     context = Context(options.include_path, options.pragma_overrides, options.cplus)
 -    context = Context(options.include_path, options.compiler_directives)
++    context = Context(options.include_path, options.compiler_directives, options.cplus)
  
      # Set up source object
      cwd = os.getcwd()
index c2fbf48661b1fd18e5ebf9035a87dc96a8e5b03f,e6b00487627784e71f92e67a22e311ad6272dfba..68dd042dc756810a87bc38c581a744a6d5e77edb
@@@ -11,10 -16,11 +17,11 @@@ from Errors import error, warning, Inte
  import Naming
  import PyrexTypes
  import TypeSlots
- from PyrexTypes import py_object_type, error_type, CTypedefType, CFuncType
+ from PyrexTypes import py_object_type, error_type, CFuncType
  from Symtab import ModuleScope, LocalScope, GeneratorLocalScope, \
 -    StructOrUnionScope, PyClassScope, CClassScope
 +    StructOrUnionScope, PyClassScope, CClassScope, CppClassScope
- from Cython.Utils import open_new_file, replace_suffix, UtilityCode
+ from Cython.Utils import open_new_file, replace_suffix
+ from Code import UtilityCode
  from StringEncoding import EncodedString, escape_byte_string, split_docstring
  import Options
  import ControlFlow
@@@ -133,21 -139,10 +140,19 @@@ class Node(object)
      
      gil_message = "Operation"
  
-     def gil_check(self, env):
-         if env.nogil:
-             self.gil_error()
+     nogil_check = None
  
-     def gil_error(self):
+     def gil_error(self, env=None):
          error(self.pos, "%s not allowed without gil" % self.gil_message)
 +    
 +    cpp_message = "Operation"
 +    
 +    def cpp_check(self, env):
 +        if not env.is_cpp():
 +            self.cpp_error()
 +
 +    def cpp_error(self):
 +        error(self.pos, "%s only allowed in c++" % self.cpp_message)
  
      def clone_node(self):
          """Clone the node. This is defined as a shallow copy, except for member lists
@@@ -780,34 -742,24 +773,38 @@@ class TemplatedTypeNode(CBaseTypeNode)
  
      name = None
      
 -    def analyse(self, env, could_be_name = False):
 -        base_type = self.base_type_node.analyse(env)
 +    def analyse(self, env, could_be_name = False, base_type = None):
 +        if base_type is None:
 +            base_type = self.base_type_node.analyse(env)
          if base_type.is_error: return base_type
 -        import Buffer
 +        
 +        if base_type.is_cpp_class:
 +            if len(self.keyword_args.key_value_pairs) != 0:
 +                error(self.pos, "c++ templates cannot take keyword arguments");
 +                self.type = PyrexTypes.error_type
 +            else:
 +                template_types = []
 +                for template_node in self.positional_args:
 +                    template_types.append(template_node.analyse_as_type(env))
 +                self.type = base_type.specialize_here(self.pos, template_types)
 +        
 +        else:
 +        
 +            import Buffer
  
 -        options = Buffer.analyse_buffer_options(
 -            self.pos,
 -            env,
 -            self.positional_args,
 -            self.keyword_args,
 -            base_type.buffer_defaults)
 +            options = Buffer.analyse_buffer_options(
 +                self.pos,
 +                env,
 +                self.positional_args,
 +                self.keyword_args,
 +                base_type.buffer_defaults)
-             
 -        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() ])
++            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() ])
 -        self.type = PyrexTypes.BufferType(base_type, **options)
 +            self.type = PyrexTypes.BufferType(base_type, **options)
-         
          return self.type
  
  class CComplexBaseTypeNode(CBaseTypeNode):
@@@ -3437,16 -3387,15 +3472,21 @@@ class DelStatNode(StatNode)
      def analyse_expressions(self, env):
          for arg in self.args:
              arg.analyse_target_expression(env, None)
 -            if not arg.type.is_pyobject:
 -                error(arg.pos, "Deletion of non-Python object")
 +            if arg.type.is_pyobject:
 +                self.gil_check(env)
 +            elif arg.type.is_ptr and arg.type.base_type.is_cpp_class:
 +                self.cpp_check(env)
 +            elif arg.type.is_cpp_class:
 +                error(arg.pos, "Deletion of non-heap C++ object")
 +            else:
 +                error(arg.pos, "Deletion of non-Python, non-C++ object")
              #arg.release_target_temp(env)
  
+     def nogil_check(self, env):
+         for arg in self.args:
+             if arg.type.is_pyobject:
+                 self.gil_error()
      gil_message = "Deleting Python object"
  
      def generate_execution_code(self, code):
Simple merge
index bd035601cbc78b5fe864a23f83c7d764447adb7e,49762e4a8fef70b23d9ee09ba5c57cdb48df7ec8..6cbb70af25be399855c54048dc7f809d5585d0fb
@@@ -152,10 -154,10 +153,10 @@@ class PostParse(CythonTransform)
      - For __cythonbufferdefaults__ the arguments are checked for
      validity.
  
-     TemplatedTypeNode has its options interpreted:
 -    CBufferAccessTypeNode has its directives interpreted:
++    TemplatedTypeNode has its directives interpreted:
      Any first positional argument goes into the "dtype" attribute,
      any "ndim" keyword argument goes into the "ndim" attribute and
-     so on. Also it is checked that the option combination is valid.
+     so on. Also it is checked that the directive combination is valid.
      - __cythonbufferdefaults__ attributes are parsed and put into the
      type information.
  
Simple merge
index 816429457e578a940e757712c6431d3314bc4d39,d459b4b24139ef5806414dfe74b35d04546b260c..871633b5a5ca8b4bcfa26039575ea82d53d0aa93
@@@ -2589,66 -2613,8 +2678,66 @@@ def p_module(s, pxd, full_module_name)
              repr(s.sy), repr(s.systring)))
      return ModuleNode(pos, doc = doc, body = body,
                        full_module_name = full_module_name,
-                       option_comments = option_comments)
+                       directive_comments = directive_comments)
  
 +def p_cpp_class_definition(s, pos,  ctx):
 +    # s.sy == 'cppclass'
 +    s.next()
 +    module_path = []
 +    class_name = p_ident(s)
 +    cname = p_opt_cname(s)
 +    if cname is None and ctx.namespace is not None:
 +        cname = ctx.namespace + "::" + class_name
 +    if s.sy == '.':
 +        error(pos, "Qualified class name not allowed C++ class")
 +    if s.sy == '[':
 +        s.next()
 +        templates = [p_ident(s)]
 +        while s.sy == ',':
 +            s.next()
 +            templates.append(p_ident(s))
 +        s.expect(']')
 +    else:
 +        templates = None
 +    if s.sy == '(':
 +        s.next()
 +        base_classes = [p_dotted_name(s, False)[2]]
 +        while s.sy == ',':
 +            s.next()
 +            base_classes.append(p_dotted_name(s, False)[2])
 +        s.expect(')')
 +    else:
 +        base_classes = []
 +    if s.sy == '[':
 +        error(s.position(), "Name options not allowed for C++ class")
 +    if s.sy == ':':
 +        s.next()
 +        s.expect('NEWLINE')
 +        s.expect_indent()
 +        attributes = []
 +        body_ctx = Ctx(visibility = ctx.visibility)
 +        body_ctx.templates = templates
 +        while s.sy != 'DEDENT':
 +            if s.sy != 'pass':
 +                attributes.append(
 +                    p_c_func_or_var_declaration(s, s.position(), body_ctx))
 +            else:
 +                s.next()
 +                s.expect_newline("Expected a newline")
 +        s.expect_dedent()
 +    else:
 +        s.expect_newline("Syntax error in C++ class definition")
 +    return Nodes.CppClassNode(pos,
 +        name = class_name,
 +        cname = cname,
 +        base_classes = base_classes,
 +        visibility = ctx.visibility,
 +        in_pxd = ctx.level == 'module_pxd',
 +        attributes = attributes,
 +        templates = templates)
 +
 +
 +
  #----------------------------------------------
  #
  #   Debugging
index 26e4fb09b5ce6629a56291f8ce7c4aa176d2fa38,6d7d608aa8041f1aea325bf662f7e2f2b4b291e2..b0bd832b3740e88d72148758b086bb0c339d5701
mode 100755,100644..100755
@@@ -1287,22 -1570,9 +1633,26 @@@ class CFuncType(CType)
          s = self.declaration_code("(*)", with_calling_convention=False)
          return '(%s)' % s
      
 +    def specialize(self, values):
 +        if self.templates is None:
 +            new_templates = None
 +        else:
 +            new_templates = [v.specialize(values) for v in self.templates]
 +        return CFuncType(self.return_type.specialize(values),
 +                             [arg.specialize(values) for arg in self.args],
 +                             has_varargs = 0,
 +                             exception_value = self.exception_value,
 +                             exception_check = self.exception_check,
 +                             calling_convention = self.calling_convention,
 +                             nogil = self.nogil,
 +                             with_gil = self.with_gil,
 +                             is_overridable = self.is_overridable,
 +                             optional_arg_count = self.optional_arg_count,
 +                             templates = new_templates)
++    
+     def opt_arg_cname(self, arg_name):
+         return self.op_arg_struct.base_type.scope.lookup(arg_name).cname
  
  class CFuncTypeArg(object):
      #  name       string
      
      def declaration_code(self, for_display = 0):
          return self.type.declaration_code(self.cname, for_display)
 +    
 +    def specialize(self, values):
 +        return CFuncTypeArg(self.name, self.type.specialize(values), self.pos, self.cname)
  
+ class StructUtilityCode(object):
+     def __init__(self, type, forward_decl):
+         self.type = type
+         self.header = "static PyObject* %s(%s)" % (type.to_py_function, type.declaration_code('s'))
+         self.forward_decl = forward_decl
+     def __eq__(self, other):
+         return isinstance(other, StructUtilityCode) and self.header == other.header
+     def __hash__(self):
+         return hash(self.header)
+     
+     def put_code(self, output):
+         code = output['utility_code_def']
+         proto = output['utility_code_proto']
+         
+         code.putln("%s {" % self.header)
+         code.putln("PyObject* res;")
+         code.putln("PyObject* member;")
+         code.putln("res = PyDict_New(); if (res == NULL) return NULL;")
+         for member in self.type.scope.var_entries:
+             nameconst_cname = code.get_py_string_const(member.name, identifier=True)
+             code.putln("member = %s(s.%s); if (member == NULL) goto bad;" % (
+                 member.type.to_py_function, member.cname))
+             code.putln("if (PyDict_SetItem(res, %s, member) < 0) goto bad;" % nameconst_cname)
+             code.putln("Py_DECREF(member);")
+         code.putln("return res;")
+         code.putln("bad:")
+         code.putln("Py_XDECREF(member);")
+         code.putln("Py_DECREF(res);")
+         code.putln("return NULL;")
+         code.putln("}")
+         # This is a bit of a hack, we need a forward declaration
+         # due to the way things are ordered in the module...
+         if self.forward_decl:
+             proto.putln(self.type.declaration_code('') + ';')
+         proto.putln(self.header + ";")
+         
  
  class CStructOrUnionType(CType):
      #  name          string
index aa9c51d3f1d1c80fffc93e87270ef86281a15f66,c8fb01c7f1c2d5695a7de3234668894afab10d89..cb5f41a7b5a74c4ed6a1c0f761992c733fefa04c
@@@ -172,7 -172,10 +174,11 @@@ class Entry(object)
          self.type = type
          self.pos = pos
          self.init = init
 +        self.overloaded_alternatives = []
+         self.assignments = []
+     
+     def __repr__(self):
+         return "Entry(name=%s, type=%s)" % (self.name, self.type)
          
      def redeclared(self, pos):
          error(pos, "'%s' does not match previous declaration" % self.name)
@@@ -699,12 -553,9 +569,16 @@@ class Scope(object)
                  return 1
          return 0
      
+     def infer_types(self):
+         from TypeInference import get_type_inferer
+         get_type_inferer().infer_types(self)
++    
 +    def is_cpp(self):
 +        outer = self.outer_scope
 +        if outer is None:
 +            return False
 +        else:
 +            return outer.is_cpp()
  
  class PreImportScope(Scope):
  
@@@ -832,12 -682,8 +705,9 @@@ class ModuleScope(Scope)
      # included_files       [string]           Cython sources included with 'include'
      # pxd_file_loaded      boolean            Corresponding .pxd file has been processed
      # cimported_modules    [ModuleScope]      Modules imported with cimport
-     # new_interned_string_entries [Entry]     New interned strings waiting to be declared
-     # interned_nums        [int/long]         Interned numeric constants
-     # all_pystring_entries [Entry]            Python string consts from all scopes
      # types_imported       {PyrexType : 1}    Set of types for which import code generated
      # has_import_star      boolean            Module contains import *
 +    # cpp                  boolean            Compiling a C++ file
      
      is_module_scope = 1
      has_import_star = 0
          var_entry.is_readonly = 1
          entry.as_variable = var_entry
      
- class LocalScope(Scope):
 +    def is_cpp(self):
 +        return self.cpp
 +        
+     def infer_types(self):
+         from TypeInference import PyObjectTypeInferer
+         PyObjectTypeInferer().infer_types(self)
+         
+ class LocalScope(Scope):    
  
-     def __init__(self, name, outer_scope):
-         Scope.__init__(self, name, outer_scope, outer_scope)
+     def __init__(self, name, outer_scope, parent_scope = None):
+         if parent_scope is None:
+             parent_scope = outer_scope
+         Scope.__init__(self, name, outer_scope, parent_scope)
      
      def mangle(self, prefix, name):
          return prefix + name
@@@ -1608,70 -1418,7 +1481,65 @@@ class CClassScope(ClassScope)
                                         base_entry.visibility, base_entry.func_modifiers)
              entry.is_inherited = 1
              
-     def allocate_temp(self, type):
-         return Scope.allocate_temp(self.global_scope(), type)
-     def release_temp(self, cname):
-         return Scope.release_temp(self.global_scope(), cname)
          
 +
 +class CppClassScope(Scope):
 +    #  Namespace of a C++ class.
 +    inherited_var_entries = []
 +    
 +    def __init__(self, name, outer_scope):
 +        Scope.__init__(self, name, outer_scope, None)
 +        self.directives = outer_scope.directives
 +
 +    def declare_var(self, name, type, pos, 
 +            cname = None, visibility = 'extern', is_cdef = 0, allow_pyobject = 0):
 +        # Add an entry for an attribute.
 +        if not cname:
 +            cname = name
 +        if type.is_cfunction:
 +            type = PyrexTypes.CPtrType(type)
 +        entry = self.declare(name, cname, type, pos, visibility)
 +        entry.is_variable = 1
 +        self.var_entries.append(entry)
 +        if type.is_pyobject and not allow_pyobject:
 +            error(pos,
 +                "C++ class member cannot be a Python object")
 +        return entry
 +
 +    def declare_cfunction(self, name, type, pos,
 +            cname = None, visibility = 'extern', defining = 0,
 +            api = 0, in_pxd = 0, modifiers = ()):
 +        if name == self.name.split('::')[-1] and cname is None:
 +            name = '<init>'
 +        entry = self.declare_var(name, type, pos, cname, visibility)
 +
 +    def declare_inherited_cpp_attributes(self, base_scope):
 +        # Declare entries for all the C++ attributes of an
 +        # inherited type, with cnames modified appropriately
 +        # to work with this type.
 +        for base_entry in \
 +            base_scope.inherited_var_entries + base_scope.var_entries:
 +                entry = self.declare(base_entry.name, base_entry.cname, 
 +                    base_entry.type, None, 'extern')
 +                entry.is_variable = 1
 +                self.inherited_var_entries.append(entry)
 +        for base_entry in base_scope.cfunc_entries:
 +            entry = self.declare_cfunction(base_entry.name, base_entry.type,
 +                                       base_entry.pos, base_entry.cname,
 +                                       base_entry.visibility, base_entry.func_modifiers)
 +            entry.is_inherited = 1
 +    
 +    def specialize(self, values):
 +        scope = CppClassScope(self.name, self.outer_scope)
 +        for entry in self.entries.values():
 +            scope.declare_var(entry.name,
 +                                entry.type.specialize(values),
 +                                entry.pos,
 +                                entry.cname,
 +                                entry.visibility)
 +        return scope
 +        
 +        
  class PropertyScope(Scope):
      #  Scope holding the __get__, __set__ and __del__ methods for
      #  a property of an extension type.
Simple merge
diff --cc tests/bugs.txt
index b099473e1c6bd3b179294ff75717d69728c5ba9b,55e7e2c3df15948183cbb2335604ab9c3cdb361e..a2a7baa9e14687ba126da21dacab50feae390e80
@@@ -3,15 -3,7 +3,9 @@@
  
  methodmangling_T5
  class_attribute_init_values_T18
- return_outside_function_T135
- builtin_types_none_T166
  numpy_ValueError_T172
  unsignedbehaviour_T184
- funcexc_iter_T228
- pxd_override_T230
- ext_instance_type_T232
- large_consts_T237
- bad_c_struct_T252
  missing_baseclass_in_predecl_T262
- cpp_operators
+ cfunc_call_tuple_args_T408
 +ifelseexpr_T267
++cpp_operators
index 21e560600d50807f73edf0b2678a6b0c2e4e224b,548f9ad58b341db6a93e1d0019f406684391cccf..172bae73a6abbd6634efe5313925d2743ff216cf
@@@ -13,8 -12,7 +12,7 @@@ def f(a)
        del s.m # error: deletion of non-Python object
  _ERRORS = u"""
  8:6: Cannot assign to or delete this
 -9:45: Deletion of non-Python object
 -11:6: Deletion of non-Python object
 -12:6: Deletion of non-Python object
 +9:45: Deletion of non-Python, non-C++ object
++11:6: Deletion of non-Python, non-C++ object
 +12:6: Deletion of non-Python, non-C++ object
- 13:6: Deletion of non-Python, non-C++ object
- 11:52: Deletion of local or C global name not supported
  """