from StringEncoding import EncodedString
import Options, Naming
import PyrexTypes
-from PyrexTypes import py_object_type
+from PyrexTypes import py_object_type, unspecified_type
import TypeSlots
from TypeSlots import \
pyfunction_signature, pymethod_signature, \
get_special_method_signature, get_property_accessor_signature
import ControlFlow
-import __builtin__
+import Code
+import __builtin__ as builtins
try:
set
except NameError:
# api boolean Generate C API for C class or function
# utility_code string Utility code needed when this entry is used
#
- # buffer_aux BufferAux or None Extra information needed for buffer variables
+ # buffer_aux BufferAux or None Extra information needed for buffer variables
# inline_func_in_pxd boolean Hacky special case for inline function in pxd file.
# Ideally this should not be necesarry.
+ # assignments [ExprNode] List of expressions that get assigned to this entry.
inline_func_in_pxd = False
borrowed = 0
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)
# pyfunc_entries [Entry] Python function entries
# cfunc_entries [Entry] C function entries
# c_class_entries [Entry] All extension type entries
- # temp_entries [Entry] Temporary variable entries
- # free_temp_entries [Entry] Temp variables currently unused
- # temp_counter integer Counter for naming temp vars
# cname_to_entry {string : Entry} Temp cname to entry mapping
# int_to_entry {int : Entry} Temp cname to entry mapping
# return_type PyrexType or None Return type of function owning scope
in_cinclude = 0
nogil = 0
- temp_prefix = Naming.pyrex_prefix
-
def __init__(self, name, outer_scope, parent_scope):
# The outer_scope is the next scope in the lookup chain.
# The parent_scope is used to derive the qualified name of this scope.
self.c_class_entries = []
self.defined_c_classes = []
self.imported_c_classes = {}
- self.temp_entries = []
- self.free_temp_entries = []
- #self.pending_temp_entries = [] # TEMPORARY
- self.temp_counter = 1
self.cname_to_entry = {}
self.string_to_entry = {}
self.identifier_to_entry = {}
self.pystring_entries = []
self.buffer_entries = []
self.control_flow = ControlFlow.LinearControlFlow()
+ self.return_type = None
def start_branching(self, pos):
self.control_flow = self.control_flow.start_branch(pos)
def __str__(self):
return "<%s %s>" % (self.__class__.__name__, self.qualified_name)
- def intern_identifier(self, name):
- return self.global_scope().intern_identifier(name)
-
def qualifying_scope(self):
return self.parent_scope
def qualify_name(self, name):
return "%s.%s" % (self.qualified_name, name)
-
+
def declare_const(self, name, type, value, pos, cname = None, visibility = 'private'):
# Add an entry for a named constant.
if not cname:
cname = self.mangle(Naming.enum_prefix, name)
entry = self.declare(name, cname, type, pos, visibility)
entry.is_const = 1
- entry.value = value
+ entry.value_node = value
return entry
def declare_type(self, name, type, pos,
cname = name
else:
cname = self.mangle(Naming.type_prefix, name)
- type = PyrexTypes.CTypedefType(cname, base_type, (visibility == 'extern'))
+ try:
+ type = PyrexTypes.create_typedef_type(cname, base_type, (visibility == 'extern'))
+ except ValueError, e:
+ error(pos, e.message)
+ type = PyrexTypes.error_type
entry = self.declare_type(name, type, pos, cname, visibility)
type.qualified_name = entry.qualified_name
return entry
entry = self.declare_type(name, type, pos, cname,
visibility = visibility, defining = scope is not None)
self.sue_entries.append(entry)
+ type.entry = entry
else:
if not (entry.is_type and entry.type.is_struct_or_union
and entry.type.kind == kind):
if entry.as_module:
scope = entry.as_module
else:
- error(pos, "'%s' is not a cimported module" % scope.qualified_name)
+ error(pos, "'%s' is not a cimported module" % '.'.join(path))
return None
return scope
if entry and entry.is_type:
return entry.type
- def add_string_const(self, value, identifier = False):
- # Add an entry for a string constant.
- if identifier:
- cname = self.new_string_const_cname(value)
- else:
- cname = self.new_const_cname()
- if value.is_unicode:
- c_type = PyrexTypes.c_utf8_char_array_type
- value = value.utf8encode()
- else:
- c_type = PyrexTypes.c_char_array_type
- value = value.byteencode()
- entry = Entry("", cname, c_type, init = value)
- entry.used = 1
- self.const_entries.append(entry)
- return entry
-
- def get_string_const(self, value, identifier = False):
- # Get entry for string constant. Returns an existing
- # one if possible, otherwise creates a new one.
- genv = self.global_scope()
- if identifier:
- string_map = genv.identifier_to_entry
- else:
- string_map = genv.string_to_entry
- entry = string_map.get(value)
- if not entry:
- entry = self.add_string_const(value, identifier)
- entry.is_identifier = identifier
- string_map[value] = entry
- return entry
-
- def add_py_string(self, entry, identifier = None):
- # If not already done, allocate a C name for a Python version of
- # a string literal, and add it to the list of Python strings to
- # be created at module init time. If the string resembles a
- # Python identifier, it will be interned.
- if entry.pystring_cname:
- return
- value = entry.init
- entry.pystring_cname = Naming.py_const_prefix + entry.cname[len(Naming.const_prefix):]
- self.pystring_entries.append(entry)
- self.global_scope().all_pystring_entries.append(entry)
- if identifier or (identifier is None and possible_identifier(value)):
- entry.is_interned = 1
- self.global_scope().new_interned_string_entries.append(entry)
-
- def add_py_num(self, value):
- # Add an entry for an int constant.
- cname = "%s%s" % (Naming.interned_num_prefix, value)
- cname = cname.replace('-', 'neg_').replace('.','_')
- entry = Entry("", cname, py_object_type, init = value)
- entry.used = 1
- entry.is_interned = 1
- self.const_entries.append(entry)
- self.interned_nums.append(entry)
- return entry
-
- def get_py_num(self, value, longness):
- # Get entry for int constant. Returns an existing
- # one if possible, otherwise creates a new one.
- if longness or Utils.long_literal(value):
- value += "L"
- genv = self.global_scope()
- entry = genv.num_to_entry.get(value)
- if not entry:
- entry = genv.add_py_num(value)
- genv.num_to_entry[value] = entry
- genv.pynum_entries.append(entry)
- return entry
-
- def get_py_obj(self, obj, c_prefix=''):
- # Get entry for a generic constant. Returns an existing
- # one if possible, otherwise creates a new one.
- genv = self.global_scope()
- entry = genv.obj_to_entry.get(obj)
- if not entry:
- entry = genv.add_py_num(obj, c_prefix)
- genv.obj_to_entry[obj] = entry
- return entry
-
- def new_string_const_cname(self, value):
- # Create a new globally-unique nice name for a string constant.
- if len(value) < 20 and nice_identifier(value):
- return "%s%s" % (Naming.const_prefix, value)
- else:
- return self.global_scope().new_const_cname()
-
- def new_const_cname(self):
- # Create a new globally-unique name for a constant.
- return self.global_scope().new_const_cname()
-
- def allocate_temp(self, type):
- # Allocate a temporary variable of the given type from the
- # free list if available, otherwise create a new one.
- # Returns the cname of the variable.
- for entry in self.free_temp_entries:
- if entry.type == type:
- self.free_temp_entries.remove(entry)
- return entry.cname
- n = self.temp_counter
- self.temp_counter = n + 1
- cname = "%s%d" % (self.temp_prefix, n)
- entry = Entry("", cname, type)
- entry.used = 1
- if type.is_pyobject or type == PyrexTypes.c_py_ssize_t_type:
- entry.init = "0"
- self.cname_to_entry[entry.cname] = entry
- self.temp_entries.append(entry)
- return entry.cname
-
- def allocate_temp_pyobject(self):
- # Allocate a temporary PyObject variable.
- return self.allocate_temp(py_object_type)
-
- def release_temp(self, cname):
- # Release a temporary variable for re-use.
- if not cname: # can happen when type of an expr is void
- return
- entry = self.cname_to_entry[cname]
- if entry in self.free_temp_entries:
- raise InternalError("Temporary variable %s released more than once"
- % cname)
- self.free_temp_entries.append(entry)
-
- def temps_in_use(self):
- # Return a new list of temp entries currently in use.
- return [entry for entry in self.temp_entries
- if entry not in self.free_temp_entries]
-
- def use_utility_code(self, new_code, name=None):
- self.global_scope().use_utility_code(new_code, name)
+ def use_utility_code(self, new_code):
+ self.global_scope().use_utility_code(new_code)
def generate_library_function_declarations(self, code):
# Generate extern decls for C library funcs used.
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:
self.declare_var(name, type, None, cname)
def declare_builtin(self, name, pos):
- if not hasattr(__builtin__, name):
+ if not hasattr(builtins, name):
if self.outer_scope is not None:
return self.outer_scope.declare_builtin(name, pos)
else:
var_entry.is_variable = 1
var_entry.is_cglobal = 1
var_entry.is_readonly = 1
+ var_entry.is_builtin = 1
var_entry.utility_code = utility_code
entry.as_variable = var_entry
# method_table_cname string C name of method table
# doc string Module doc string
# doc_cname string C name of module doc string
- # const_counter integer Counter for naming constants (PS: MOVED TO GLOBAL)
- # utility_code_list [(UtilityCode, string)] Queuing utility codes for forwarding to Code.py
- # default_entries [Entry] Function argument default entries
+ # utility_code_list [UtilityCode] Queuing utility codes for forwarding to Code.py
# python_include_files [string] Standard Python headers to be included
# include_files [string] Other C headers to be included
# string_to_entry {string : Entry} Map string const to entry
# 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
self.doc = ""
self.doc_cname = Naming.moddoc_cname
self.utility_code_list = []
- self.default_entries = []
self.module_entries = {}
self.python_include_files = ["Python.h", "structmember.h"]
self.include_files = []
self.type_names = dict(outer_scope.type_names)
self.pxd_file_loaded = 0
self.cimported_modules = []
- self.new_interned_string_entries = []
- self.interned_nums = []
- self.interned_objs = []
- self.all_pystring_entries = []
self.types_imported = {}
self.included_files = []
- self.pynum_entries = []
self.has_extern_class = 0
self.cached_builtins = []
self.undeclared_cached_builtins = []
return self
def declare_builtin(self, name, pos):
- if not hasattr(__builtin__, name):
+ if not hasattr(builtins, name):
if self.has_import_star:
entry = self.declare_var(name, py_object_type, pos)
return entry
entry.is_builtin = 1
return entry
- def intern_identifier(self, name):
- string_entry = self.get_string_const(name, identifier = True)
- self.add_py_string(string_entry, identifier = 1)
- return string_entry.pystring_cname
-
def find_module(self, module_name, pos):
# Find a module in the import namespace, interpreting
# relative imports relative to this module's parent.
if not visibility in ('private', 'public', 'extern'):
error(pos, "Module-level variable cannot be declared %s" % visibility)
if not is_cdef:
+ if type is unspecified_type:
+ type = py_object_type
if not (type.is_pyobject and not type.is_extension_type):
raise InternalError(
"Non-cdef global variable is not a generic Python object")
if not entry:
self.declare_var(name, py_object_type, pos)
- def add_default_value(self, type):
- # Add an entry for holding a function argument
- # default value.
- cname = self.new_const_cname()
- entry = Entry("", cname, type)
- self.default_entries.append(entry)
- return entry
-
- def new_const_cname(self):
- global const_counter
- # Create a new globally-unique name for a constant.
- prefix=''
- n = const_counter
- const_counter = n + 1
- return "%s%s%d" % (Naming.const_prefix, prefix, n)
-
- def use_utility_code(self, new_code, name=None):
+ def use_utility_code(self, new_code):
if new_code is not None:
- self.utility_code_list.append((new_code, name))
+ self.utility_code_list.append(new_code)
def declare_c_class(self, name, pos, defining = 0, implementing = 0,
module_name = None, base_type = None, objstruct_cname = None,
typeobj_cname = None, visibility = 'private', typedef_flag = 0, api = 0,
buffer_defaults = None):
+ # If this is a non-extern typedef class, expose the typedef, but use
+ # the non-typedef struct internally to avoid needing forward
+ # declarations for anonymous structs.
+ if typedef_flag and visibility != 'extern':
+ if visibility != 'public':
+ warning(pos, "ctypedef only valid for public and extern classes", 2)
+ objtypedef_cname = objstruct_cname
+ objstruct_cname = None
+ typedef_flag = 0
+ else:
+ objtypedef_cname = None
#
# Look for previous declaration as a type
#
# Make a new entry if needed
#
if not entry:
- type = PyrexTypes.PyExtensionType(name, typedef_flag, base_type)
+ type = PyrexTypes.PyExtensionType(name, typedef_flag, base_type, visibility == 'extern')
type.pos = pos
type.buffer_defaults = buffer_defaults
+ if objtypedef_cname is not None:
+ type.objtypedef_cname = objtypedef_cname
if visibility == 'extern':
type.module_name = module_name
else:
if objstruct_cname:
type.objstruct_cname = objstruct_cname
elif not entry.in_cinclude:
- type.objstruct_cname = self.mangle(Naming.objstruct_prefix, name)
+ type.objstruct_cname = self.mangle(Naming.objstruct_prefix, name)
else:
error(entry.pos,
"Object name required for 'public' or 'extern' C class")
type.vtabstruct_cname = self.mangle(Naming.vtabstruct_prefix, entry.name)
type.vtabptr_cname = self.mangle(Naming.vtabptr_prefix, entry.name)
+ def check_c_classes_pxd(self):
+ # Performs post-analysis checking and finishing up of extension types
+ # being implemented in this module. This is called only for the .pxd.
+ #
+ # Checks all extension types declared in this scope to
+ # make sure that:
+ #
+ # * The extension type is fully declared
+ #
+ # Also allocates a name for the vtable if needed.
+ #
+ for entry in self.c_class_entries:
+ # Check defined
+ if not entry.type.scope:
+ error(entry.pos, "C class '%s' is declared but not defined" % entry.name)
+
def check_c_classes(self):
# Performs post-analysis checking and finishing up of extension types
# being implemented in this module. This is called only for the main
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
cname, visibility, is_cdef)
if type.is_pyobject and not Options.init_local_none:
entry.init = "0"
- entry.init_to_none = type.is_pyobject and Options.init_local_none
+ entry.init_to_none = (type.is_pyobject or type.is_unspecified) and Options.init_local_none
entry.is_local = 1
self.var_entries.append(entry)
return entry
class GeneratorLocalScope(LocalScope):
- temp_prefix = Naming.cur_scope_cname + "->" + LocalScope.temp_prefix
-
def mangle_closure_cnames(self, scope_var):
- for entry in self.entries.values() + self.temp_entries:
- entry.in_closure = 1
+# for entry in self.entries.values() + self.temp_entries:
+# entry.in_closure = 1
LocalScope.mangle_closure_cnames(self, scope_var)
# def mangle(self, prefix, name):
def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'private', defining = 0,
api = 0, in_pxd = 0, modifiers = ()):
- self.declare_var(name, type, pos, cname, visibility)
+ return self.declare_var(name, type, pos, cname, visibility)
class ClassScope(Scope):
# Abstract base class for namespace of
return self.outer_scope.add_string_const(value, identifier)
def lookup(self, name):
+ entry = Scope.lookup(self, name)
+ if entry:
+ return entry
if name == "classmethod":
# We don't want to use the builtin classmethod here 'cause it won't do the
# right thing in this scope (as the class memebers aren't still functions).
py_object_type,
[PyrexTypes.CFuncTypeArg("", py_object_type, None)], 0, 0))
entry.is_cfunction = 1
- return entry
- else:
- return Scope.lookup(self, name)
+ return entry
class PyClassScope(ClassScope):
# Namespace of a Python class.
#
- # class_dict_cname string C variable holding class dict
# class_obj_cname string C variable holding class object
is_py_class_scope = 1
def declare_var(self, name, type, pos,
cname = None, visibility = 'private', is_cdef = 0):
+ if type is unspecified_type:
+ type = py_object_type
# Add an entry for a class attribute.
entry = Scope.declare_var(self, name, type, pos,
cname, visibility, is_cdef)
entry.is_pyglobal = 1
return entry
- def allocate_temp(self, type):
- return self.outer_scope.allocate_temp(type)
-
- def release_temp(self, cname):
- self.outer_scope.release_temp(cname)
-
- #def recycle_pending_temps(self):
- # self.outer_scope.recycle_pending_temps()
-
def add_default_value(self, type):
return self.outer_scope.add_default_value(type)
# If the type or any of its base types have Python-valued
# C attributes, then it needs to participate in GC.
return self.has_pyobject_attrs or \
- (self.parent_type.base_type and \
+ (self.parent_type.base_type and
+ self.parent_type.base_type.scope is not None and
self.parent_type.base_type.scope.needs_gc())
def declare_var(self, name, type, pos,
if self.defined:
error(pos,
"C attributes cannot be added in implementation part of"
- " extension type")
+ " extension type defined in a pxd")
if get_special_method_signature(name):
error(pos,
"The name '%s' is reserved for a special method."
"Non-generic Python attribute cannot be exposed for writing from Python")
return entry
else:
+ if type is unspecified_type:
+ type = py_object_type
# Add an entry for a class attribute.
entry = Scope.declare_var(self, name, type, pos,
cname, visibility, is_cdef)
# I keep it in for now. is_member should be enough
# later on
self.namespace_cname = "(PyObject *)%s" % self.parent_type.typeptr_cname
- entry.interned_cname = self.intern_identifier(name)
return entry
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):
# Should this go elsewhere (and then get imported)?
#------------------------------------------------------------------------------------
-classmethod_utility_code = Utils.UtilityCode(
+classmethod_utility_code = Code.UtilityCode(
proto = """
#include "descrobject.h"
static PyObject* __Pyx_Method_ClassMethod(PyObject *method); /*proto*/
impl = """
static PyObject* __Pyx_Method_ClassMethod(PyObject *method) {
/* It appears that PyMethodDescr_Type is not anywhere exposed in the Python/C API */
- /* if (!PyObject_TypeCheck(method, &PyMethodDescr_Type)) { */
- if (__Pyx_StrEq(Py_TYPE(method)->tp_name, "method_descriptor")) { /* cdef classes */
+ static PyTypeObject *methoddescr_type = NULL;
+ if (methoddescr_type == NULL) {
+ PyObject *meth = __Pyx_GetAttrString((PyObject*)&PyList_Type, "append");
+ if (!meth) return NULL;
+ methoddescr_type = Py_TYPE(meth);
+ Py_DECREF(meth);
+ }
+ if (PyObject_TypeCheck(method, methoddescr_type)) { /* cdef classes */
PyMethodDescrObject *descr = (PyMethodDescrObject *)method;
- return PyDescr_NewClassMethod(descr->d_type, descr->d_method);
+ #if PY_VERSION_HEX < 0x03020000
+ PyTypeObject *d_type = descr->d_type;
+ #else
+ PyTypeObject *d_type = descr->d_common.d_type;
+ #endif
+ return PyDescr_NewClassMethod(d_type, descr->d_method);
}
- else if (PyMethod_Check(method)) { /* python classes */
+ else if (PyMethod_Check(method)) { /* python classes */
return PyClassMethod_New(PyMethod_GET_FUNCTION(method));
}
else if (PyCFunction_Check(method)) {
return PyClassMethod_New(method);
}
- PyErr_Format(PyExc_TypeError, "Class-level classmethod() can only be called on a method_descriptor or instance method.");
+ PyErr_Format(PyExc_TypeError,
+ "Class-level classmethod() can only be called on"
+ "a method_descriptor or instance method.");
return NULL;
}
""")