From cbd2a528f2fd5df13242d2513c53975281dbc234 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Thu, 31 May 2007 17:47:16 -0700 Subject: [PATCH] type narrowing for cdef methods Now if you inherit cdef methods from another class, you may re-declare the arguments and return variables to be sub-types of the original declared type. This will be especially convenient for the SAGE arithmetic architecture. Type-checking is performed if necessary. --- Cython/Compiler/Nodes.py | 27 +++++++++++++++++++++++++-- Cython/Compiler/PyrexTypes.py | 30 ++++++++++++++++++++++++++++-- Cython/Compiler/Symtab.py | 6 +++++- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index ebbe9f3c..5f8d83f7 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1944,7 +1944,7 @@ class CFuncDefNode(FuncDefNode): for arg in self.type.args: if not arg.name: error(arg.pos, "Missing argument name") - self.declare_argument(env, arg) + arg.entry = self.declare_argument(env, arg) def generate_function_header(self, code, with_pymethdef): arg_decls = [] @@ -1989,7 +1989,27 @@ class CFuncDefNode(FuncDefNode): pass def generate_argument_type_tests(self, code): - pass + # Generate type tests for args whose type in a parent + # class is a supertype of the declared type. + for arg in self.type.args: + if arg.needs_type_test: + self.generate_arg_type_test(arg, code) + + def generate_arg_type_test(self, arg, code): + # Generate type test for one argument. + if arg.type.typeobj_is_available(): + typeptr_cname = arg.type.typeptr_cname + arg_code = "((PyObject *)%s)" % arg.entry.cname + code.putln( + 'if (!__Pyx_ArgTypeTest(%s, %s, %d, "%s")) %s' % ( + arg_code, + typeptr_cname, + not arg.not_none, + arg.name, + code.error_goto(arg.pos))) + else: + error(arg.pos, "Cannot test type of extern C class " + "without type object name specification") def error_value(self): if self.return_type.is_pyobject: @@ -3804,6 +3824,9 @@ static int __Pyx_GetVtable(PyObject *dict, void *vtabptr); /*proto*/ static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name, char *modname); /*proto*/ static int __Pyx_InternStrings(__Pyx_InternTabEntry *t); /*proto*/ static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); /*proto*/ + +#DEFINE __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False)) +#DEFINE __Pyx_PyObject_IsTrue(x) ({PyObject *_x = (x); _x == Py_True ? 1 : (_x) == Py_False ? 0 : PyObject_IsTrue(_x)}) """ get_name_predeclaration = \ diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 1cad0735..fba2f78b 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -347,8 +347,8 @@ class CBIntType(CIntType): # TODO: this should be a macro "(__ ? Py_True : Py_False)" # and no error checking should be needed (just an incref). - to_py_function = "PyBool_FromLong" - from_py_function = "PyObject_IsTrue" + to_py_function = "__Pyx_PyBool_FromLong" + from_py_function = "__Pyx_PyObject_IsTrue" class CPySSizeTType(CIntType): @@ -528,6 +528,30 @@ class CFuncType(CType): if not self.return_type.same_as(other_type.return_type): return 0 return 1 + + def narrower_c_signature_than(self, other_type, as_cmethod = 0): + return self.narrower_c_signature_than_resolved_type(other_type.resolve(), as_cmethod) + + def narrower_c_signature_than_resolved_type(self, other_type, as_cmethod): + if other_type is error_type: + return 1 + if not other_type.is_cfunction: + return 0 + nargs = len(self.args) + if nargs <> len(other_type.args): + return 0 + for i in range(as_cmethod, nargs): + if not self.args[i].type.subtype_of_resolved_type(other_type.args[i].type): + return 0 + else: + self.args[i].needs_type_test = other_type.args[i].needs_type_test \ + or not self.args[i].type.same_as(other_type.args[i].type) + if self.has_varargs <> other_type.has_varargs: + return 0 + if not self.return_type.subtype_of_resolved_type(other_type.return_type): + return 0 + return 1 + def same_exception_signature_as(self, other_type): return self.same_exception_signature_as_resolved_type( @@ -576,6 +600,8 @@ class CFuncTypeArg: self.cname = Naming.var_prefix + name self.type = type self.pos = pos + self.not_none = False + self.needs_type_test = False # TODO: should these defaults be set in analyse_types()? def __repr__(self): return "%s:%s" % (self.name, repr(self.type)) diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 515225d9..2e97dfcd 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -1075,7 +1075,11 @@ class CClassScope(ClassScope): if defining and entry.func_cname: error(pos, "'%s' already defined" % name) if not entry.type.same_as(type, as_cmethod = 1): - error(pos, "Signature does not match previous declaration") + old_type = entry.type + if type.narrower_c_signature_than(entry.type, as_cmethod = 1): + entry.type = type + else: + error(pos, "Signature not compatible with previous declaration") else: if self.defined: error(pos, -- 2.26.2