From: Robert Bradshaw Date: Tue, 15 Dec 2009 11:54:36 +0000 (-0800) Subject: big C++ mergeback X-Git-Tag: 0.13.beta0~353^2~31 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=73da9b353950aa68b4e059ca86ab58076af2103d;p=cython.git big C++ mergeback --- 73da9b353950aa68b4e059ca86ab58076af2103d diff --cc Cython/Compiler/ExprNodes.py index eac47237,d2789833..f42ab943 mode 100755,100644..100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@@ -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'') + if constructor is None: + return_type = PyrexTypes.CFuncType(type, []) + return_type = PyrexTypes.CPtrType(return_type) + type.scope.declare_cfunction(u'', return_type, self.pos) + constructor = type.scope.lookup(u'') + 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. @@@ -1250,19 -1228,16 +1262,17 @@@ 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 = "" - 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) @@@ -3777,9 -4089,10 +4123,14 @@@ 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_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 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 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) @@@ -4974,36 -5448,7 +5552,30 @@@ 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 = "" + 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) diff --cc Cython/Compiler/Main.py index 15774e86,e869e981..4d35ed3c --- a/Cython/Compiler/Main.py +++ b/Cython/Compiler/Main.py @@@ -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() diff --cc Cython/Compiler/Nodes.py index c2fbf486,e6b00487..68dd042d --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@@ -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): diff --cc Cython/Compiler/ParseTreeTransforms.py index bd035601,49762e4a..6cbb70af --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@@ -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. diff --cc Cython/Compiler/Parsing.py index 81642945,d459b4b2..871633b5 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@@ -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 diff --cc Cython/Compiler/PyrexTypes.py index 26e4fb09,6d7d608a..b0bd832b mode 100755,100644..100755 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@@ -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 @@@ -1326,10 -1596,45 +1676,48 @@@ 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 diff --cc Cython/Compiler/Symtab.py index aa9c51d3,c8fb01c7..cb5f41a7 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@@ -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 @@@ -1247,13 -1061,16 +1121,19 @@@ var_entry.is_readonly = 1 entry.as_variable = var_entry + def is_cpp(self): + return self.cpp + - class LocalScope(Scope): + 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 = '' + 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. diff --cc tests/bugs.txt index b099473e,55e7e2c3..a2a7baa9 --- a/tests/bugs.txt +++ b/tests/bugs.txt @@@ -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 + cfunc_call_tuple_args_T408 +ifelseexpr_T267 - cpp_operators ++cpp_operators diff --cc tests/errors/e_del.pyx index 21e56060,548f9ad5..172bae73 --- a/tests/errors/e_del.pyx +++ b/tests/errors/e_del.pyx @@@ -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 """