Commit d7a2258c authored by Robert Bradshaw's avatar Robert Bradshaw

Merge branch 'bugs'

Conflicts:
	Cython/Compiler/Optimize.py
	Cython/Compiler/TypeInference.py
parents a7707ebe 15dce275
...@@ -25,6 +25,14 @@ Bugs fixed ...@@ -25,6 +25,14 @@ Bugs fixed
* C++ class nesting was broken. * C++ class nesting was broken.
* Better checking for required nullary constructors for stack-allocated C++ instances.
* Remove module docstring in no-docstring mode.
* Fix specialization for varargs function signatures.
* Fix several compiler crashes.
Other changes Other changes
------------- -------------
......
...@@ -211,7 +211,7 @@ class FusedCFuncDefNode(StatListNode): ...@@ -211,7 +211,7 @@ class FusedCFuncDefNode(StatListNode):
for fused_type in fused_types for fused_type in fused_types
] ]
node.specialized_signature_string = ', '.join(type_strings) node.specialized_signature_string = '|'.join(type_strings)
node.entry.pymethdef_cname = PyrexTypes.get_fused_cname( node.entry.pymethdef_cname = PyrexTypes.get_fused_cname(
cname, node.entry.pymethdef_cname) cname, node.entry.pymethdef_cname)
...@@ -322,7 +322,8 @@ class FusedCFuncDefNode(StatListNode): ...@@ -322,7 +322,8 @@ class FusedCFuncDefNode(StatListNode):
for dtype_category, codewriter in dtypes: for dtype_category, codewriter in dtypes:
if dtype_category: if dtype_category:
cond = '{{itemsize_match}}' cond = '{{itemsize_match}} and arg.ndim == %d' % (
specialized_type.ndim,)
if dtype.is_int: if dtype.is_int:
cond += ' and {{signed_match}}' cond += ' and {{signed_match}}'
...@@ -587,7 +588,7 @@ class FusedCFuncDefNode(StatListNode): ...@@ -587,7 +588,7 @@ class FusedCFuncDefNode(StatListNode):
candidates = [] candidates = []
for sig in signatures: for sig in signatures:
match_found = False match_found = False
for src_type, dst_type in zip(sig.strip('()').split(', '), dest_sig): for src_type, dst_type in zip(sig.strip('()').split('|'), dest_sig):
if dst_type is not None: if dst_type is not None:
if src_type == dst_type: if src_type == dst_type:
match_found = True match_found = True
......
...@@ -86,7 +86,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -86,7 +86,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.scope.merge_in(scope) self.scope.merge_in(scope)
def analyse_declarations(self, env): def analyse_declarations(self, env):
if Options.embed_pos_in_docstring: if not Options.docstrings:
env.doc = self.doc = None
elif Options.embed_pos_in_docstring:
env.doc = EncodedString(u'File: %s (starting at line %s)' % Nodes.relative_position(self.pos)) env.doc = EncodedString(u'File: %s (starting at line %s)' % Nodes.relative_position(self.pos))
if not self.doc is None: if not self.doc is None:
env.doc = EncodedString(env.doc + u'\n' + self.doc) env.doc = EncodedString(env.doc + u'\n' + self.doc)
......
...@@ -1239,7 +1239,12 @@ class CppClassNode(CStructOrUnionDefNode, BlockNode): ...@@ -1239,7 +1239,12 @@ class CppClassNode(CStructOrUnionDefNode, BlockNode):
scope = None scope = None
if self.attributes is not None: if self.attributes is not None:
scope = CppClassScope(self.name, env, templates = self.templates) scope = CppClassScope(self.name, env, templates = self.templates)
base_class_types = [b.analyse(scope or env) for b in self.base_classes] def base_ok(base_class):
if base_class.is_cpp_class or base_class.is_struct:
return True
else:
error(self.pos, "Base class '%s' not a struct or class." % base_class)
base_class_types = filter(base_ok, [b.analyse(scope or env) for b in self.base_classes])
if self.templates is None: if self.templates is None:
template_types = None template_types = None
else: else:
...@@ -2084,6 +2089,8 @@ class CFuncDefNode(FuncDefNode): ...@@ -2084,6 +2089,8 @@ class CFuncDefNode(FuncDefNode):
if self.return_type.is_array and self.visibility != 'extern': if self.return_type.is_array and self.visibility != 'extern':
error(self.pos, error(self.pos,
"Function cannot return an array") "Function cannot return an array")
if self.return_type.is_cpp_class:
self.return_type.check_nullary_constructor(self.pos, "used as a return value")
if self.overridable and not env.is_module_scope: if self.overridable and not env.is_module_scope:
if len(self.args) < 1 or not self.args[0].type.is_pyobject: if len(self.args) < 1 or not self.args[0].type.is_pyobject:
......
...@@ -3314,6 +3314,11 @@ class CppClassType(CType): ...@@ -3314,6 +3314,11 @@ class CppClassType(CType):
func_type = func_type.base_type func_type = func_type.base_type
return func_type.return_type return func_type.return_type
def check_nullary_constructor(self, pos, msg="stack allocated"):
constructor = self.scope.lookup(u'<init>')
if constructor is not None and best_match([], constructor.all_alternatives()) is None:
error(pos, "C++ class must have a nullary constructor to be %s" % msg)
class TemplatePlaceholderType(CType): class TemplatePlaceholderType(CType):
......
...@@ -570,9 +570,7 @@ class Scope(object): ...@@ -570,9 +570,7 @@ class Scope(object):
else: else:
cname = self.mangle(Naming.var_prefix, name) cname = self.mangle(Naming.var_prefix, name)
if type.is_cpp_class and visibility != 'extern': if type.is_cpp_class and visibility != 'extern':
constructor = type.scope.lookup(u'<init>') type.check_nullary_constructor(pos)
if constructor is not None and PyrexTypes.best_match([], constructor.all_alternatives()) is None:
error(pos, "C++ class must have a no-arg constructor to be stack allocated")
entry = self.declare(name, cname, type, pos, visibility) entry = self.declare(name, cname, type, pos, visibility)
entry.is_variable = 1 entry.is_variable = 1
if in_pxd and visibility != 'extern': if in_pxd and visibility != 'extern':
...@@ -1783,10 +1781,7 @@ class CClassScope(ClassScope): ...@@ -1783,10 +1781,7 @@ class CClassScope(ClassScope):
if visibility == 'private': if visibility == 'private':
cname = c_safe_identifier(cname) cname = c_safe_identifier(cname)
if type.is_cpp_class and visibility != 'extern': if type.is_cpp_class and visibility != 'extern':
constructor = type.scope.lookup(u'<init>') type.check_nullary_constructor(pos)
if constructor is not None and \
PyrexTypes.best_match([], constructor.all_alternatives()) is None:
error(pos, "C++ class must have a no-arg constructor to be a member of an extension type; use a pointer instead")
self.use_utility_code(Code.UtilityCode("#include <new>")) self.use_utility_code(Code.UtilityCode("#include <new>"))
entry = self.declare(name, cname, type, pos, visibility) entry = self.declare(name, cname, type, pos, visibility)
entry.is_variable = 1 entry.is_variable = 1
......
...@@ -390,7 +390,7 @@ class SimpleAssignmentTypeInferer(object): ...@@ -390,7 +390,7 @@ class SimpleAssignmentTypeInferer(object):
types = [assmt.rhs.infer_type(scope) types = [assmt.rhs.infer_type(scope)
for assmt in entry.cf_assignments] for assmt in entry.cf_assignments]
if types and Utils.all(types): if types and Utils.all(types):
entry.type = spanning_type(types, entry.might_overflow) entry.type = spanning_type(types, entry.might_overflow, entry.pos)
else: else:
# FIXME: raise a warning? # FIXME: raise a warning?
# print "No assignments", entry.pos, entry # print "No assignments", entry.pos, entry
...@@ -405,10 +405,10 @@ class SimpleAssignmentTypeInferer(object): ...@@ -405,10 +405,10 @@ class SimpleAssignmentTypeInferer(object):
for assmt in entry.cf_assignments for assmt in entry.cf_assignments
if assmt.type_dependencies(scope) == ()] if assmt.type_dependencies(scope) == ()]
if types: if types:
entry.type = spanning_type(types, entry.might_overflow) entry.type = spanning_type(types, entry.might_overflow, entry.pos)
types = [assmt.infer_type(scope) types = [assmt.infer_type(scope)
for assmt in entry.cf_assignments] for assmt in entry.cf_assignments]
entry.type = spanning_type(types, entry.might_overflow) # might be wider... entry.type = spanning_type(types, entry.might_overflow, entry.pos) # might be wider...
resolve_dependancy(entry) resolve_dependancy(entry)
del dependancies_by_entry[entry] del dependancies_by_entry[entry]
if ready_to_infer: if ready_to_infer:
...@@ -438,20 +438,24 @@ def find_spanning_type(type1, type2): ...@@ -438,20 +438,24 @@ def find_spanning_type(type1, type2):
return PyrexTypes.c_double_type return PyrexTypes.c_double_type
return result_type return result_type
def aggressive_spanning_type(types, might_overflow): def aggressive_spanning_type(types, might_overflow, pos):
result_type = reduce(find_spanning_type, types) result_type = reduce(find_spanning_type, types)
if result_type.is_reference: if result_type.is_reference:
result_type = result_type.ref_base_type result_type = result_type.ref_base_type
if result_type.is_const: if result_type.is_const:
result_type = result_type.const_base_type result_type = result_type.const_base_type
if result_type.is_cpp_class:
result_type.check_nullary_constructor(pos)
return result_type return result_type
def safe_spanning_type(types, might_overflow): def safe_spanning_type(types, might_overflow, pos):
result_type = reduce(find_spanning_type, types) result_type = reduce(find_spanning_type, types)
if result_type.is_const: if result_type.is_const:
result_type = result_type.const_base_type result_type = result_type.const_base_type
if result_type.is_reference: if result_type.is_reference:
result_type = result_type.ref_base_type result_type = result_type.ref_base_type
if result_type.is_cpp_class:
result_type.check_nullary_constructor(pos)
if result_type.is_pyobject: if result_type.is_pyobject:
# In theory, any specific Python type is always safe to # In theory, any specific Python type is always safe to
# infer. However, inferring str can cause some existing code # infer. However, inferring str can cause some existing code
......
# cython.* namespace for pure mode. # cython.* namespace for pure mode.
__version__ = "0.18-pre" __version__ = "0.18-pre"
# Shamelessly copied from Cython/minivect/minitypes.py
class _ArrayType(object):
is_array = True
subtypes = ['dtype']
def __init__(self, dtype, ndim, is_c_contig=False, is_f_contig=False,
inner_contig=False, broadcasting=None):
self.dtype = dtype
self.ndim = ndim
self.is_c_contig = is_c_contig
self.is_f_contig = is_f_contig
self.inner_contig = inner_contig or is_c_contig or is_f_contig
self.broadcasting = broadcasting
def __repr__(self):
axes = [":"] * self.ndim
if self.is_c_contig:
axes[-1] = "::1"
elif self.is_f_contig:
axes[0] = "::1"
return "%s[%s]" % (self.dtype, ", ".join(axes))
def index_type(base_type, item):
"""
Support array type creation by slicing, e.g. double[:, :] specifies
a 2D strided array of doubles. The syntax is the same as for
Cython memoryviews.
"""
assert isinstance(item, (tuple, slice))
def verify_slice(s):
if s.start or s.stop or s.step not in (None, 1):
raise minierror.InvalidTypeSpecification(
"Only a step of 1 may be provided to indicate C or "
"Fortran contiguity")
if isinstance(item, tuple):
step_idx = None
for idx, s in enumerate(item):
verify_slice(s)
if s.step and (step_idx or idx not in (0, len(item) - 1)):
raise minierror.InvalidTypeSpecification(
"Step may only be provided once, and only in the "
"first or last dimension.")
if s.step == 1:
step_idx = idx
return _ArrayType(base_type, len(item),
is_c_contig=step_idx == len(item) - 1,
is_f_contig=step_idx == 0)
else:
verify_slice(item)
return _ArrayType(base_type, 1, is_c_contig=bool(item.step))
# END shameless copy
compiled = False compiled = False
_Unspecified = object() _Unspecified = object()
...@@ -241,6 +301,8 @@ class typedef(CythonType): ...@@ -241,6 +301,8 @@ class typedef(CythonType):
def __repr__(self): def __repr__(self):
return self.name or str(self._basetype) return self.name or str(self._basetype)
__getitem__ = index_type
class _FusedType(CythonType): class _FusedType(CythonType):
pass pass
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
cdef extern from *: cdef extern from *:
cdef cppclass string "std::string": cdef cppclass string "std::string":
string()
string(char* c_str, size_t size) string(char* c_str, size_t size)
@cname("{{cname}}") @cname("{{cname}}")
...@@ -147,6 +148,7 @@ cdef object {{cname}}(const_cpp_set[X]& s): ...@@ -147,6 +148,7 @@ cdef object {{cname}}(const_cpp_set[X]& s):
cdef extern from *: cdef extern from *:
cdef cppclass pair "std::pair" [T, U]: cdef cppclass pair "std::pair" [T, U]:
pair()
pair(T&, U&) pair(T&, U&)
@cname("{{cname}}") @cname("{{cname}}")
......
...@@ -700,7 +700,7 @@ __pyx_FusedFunction_getitem(__pyx_FusedFunctionObject *self, PyObject *idx) ...@@ -700,7 +700,7 @@ __pyx_FusedFunction_getitem(__pyx_FusedFunctionObject *self, PyObject *idx)
Py_DECREF(string); Py_DECREF(string);
} }
sep = PyUnicode_FromString(", "); sep = PyUnicode_FromString("|");
if (sep) if (sep)
signature = PyUnicode_Join(sep, list); signature = PyUnicode_Join(sep, list);
__pyx_err: __pyx_err:
......
...@@ -230,9 +230,10 @@ __signatures__ ...@@ -230,9 +230,10 @@ __signatures__
Finally, function objects from ``def`` or ``cpdef`` functions have an attribute Finally, function objects from ``def`` or ``cpdef`` functions have an attribute
__signatures__, which maps the signature strings to the actual specialized __signatures__, which maps the signature strings to the actual specialized
functions. This may be useful for inspection. Listed signature strings may also functions. This may be useful for inspection. Listed signature strings may also
be used as indices to the fused function:: be used as indices to the fused function, but the index format may change between
Cython versions::
specialized_function = fused_function["MyExtensionClass, int, float"] specialized_function = fused_function["MyExtensionClass|int|float"]
It would usually be preferred to index like this, however:: It would usually be preferred to index like this, however::
...@@ -242,8 +243,7 @@ Although the latter will select the biggest types for ``int`` and ``float`` from ...@@ -242,8 +243,7 @@ Although the latter will select the biggest types for ``int`` and ``float`` from
Python space, as they are not type identifiers but builtin types there. Passing Python space, as they are not type identifiers but builtin types there. Passing
``cython.int`` and ``cython.float`` would resolve that, however. ``cython.int`` and ``cython.float`` would resolve that, however.
For memoryview indexing from python space you have to use strings instead of For memoryview indexing from python space we can do the following:
types::
ctypedef fused my_fused_type: ctypedef fused my_fused_type:
int[:, ::1] int[:, ::1]
...@@ -252,6 +252,6 @@ types:: ...@@ -252,6 +252,6 @@ types::
def func(my_fused_type array): def func(my_fused_type array):
... ...
my_fused_type['int[:, ::1]'](myarray) my_fused_type[cython.int[:, ::1]](myarray)
The same goes for when using e.g. ``cython.numeric[:, :]``. The same goes for when using e.g. ``cython.numeric[:, :]``.
...@@ -48,7 +48,7 @@ def opt_func(fused_t obj, cython.floating myf = 1.2, cython.integral myi = 7): ...@@ -48,7 +48,7 @@ def opt_func(fused_t obj, cython.floating myf = 1.2, cython.integral myi = 7):
>>> opt_func[str, float, int]("spam", f, i) >>> opt_func[str, float, int]("spam", f, i)
str object float int str object float int
spam 5.60 9 5.60 9 spam 5.60 9 5.60 9
>>> opt_func["str, double, long"]("spam", f, i) >>> opt_func[str, cy.double, cy.long]("spam", f, i)
str object double long str object double long
spam 5.60 9 5.60 9 spam 5.60 9 5.60 9
>>> opt_func[str, float, cy.int]("spam", f, i) >>> opt_func[str, float, cy.int]("spam", f, i)
...@@ -62,7 +62,7 @@ def opt_func(fused_t obj, cython.floating myf = 1.2, cython.integral myi = 7): ...@@ -62,7 +62,7 @@ def opt_func(fused_t obj, cython.floating myf = 1.2, cython.integral myi = 7):
>>> opt_func[ExtClassA, float, int](ExtClassA(), f, i) >>> opt_func[ExtClassA, float, int](ExtClassA(), f, i)
ExtClassA float int ExtClassA float int
ExtClassA 5.60 9 5.60 9 ExtClassA 5.60 9 5.60 9
>>> opt_func["ExtClassA, double, long"](ExtClassA(), f, i) >>> opt_func[ExtClassA, cy.double, cy.long](ExtClassA(), f, i)
ExtClassA double long ExtClassA double long
ExtClassA 5.60 9 5.60 9 ExtClassA 5.60 9 5.60 9
......
...@@ -849,4 +849,28 @@ def test_dispatch_memoryview_object(): ...@@ -849,4 +849,28 @@ def test_dispatch_memoryview_object():
cdef int[:] m3 = <object> m cdef int[:] m3 = <object> m
test_fused_memslice(m3) test_fused_memslice(m3)
cdef fused ndim_t:
double[:]
double[:, :]
double[:, :, :]
@testcase
def test_dispatch_ndim(ndim_t array):
"""
>>> test_dispatch_ndim(np.empty(5, dtype=np.double))
double[:] 1
>>> test_dispatch_ndim(np.empty((5, 5), dtype=np.double))
double[:, :] 2
>>> test_dispatch_ndim(np.empty((5, 5, 5), dtype=np.double))
double[:, :, :] 3
Test indexing using Cython.Shadow
>>> import cython
>>> test_dispatch_ndim[cython.double[:]](np.empty(5, dtype=np.double))
double[:] 1
>>> test_dispatch_ndim[cython.double[:, :]](np.empty((5, 5), dtype=np.double))
double[:, :] 2
"""
print cython.typeof(array), np.asarray(array).ndim
include "numpy_common.pxi" include "numpy_common.pxi"
...@@ -201,7 +201,6 @@ def ae(result, expected): ...@@ -201,7 +201,6 @@ def ae(result, expected):
assert result == expected assert result == expected
ae(a_mod.public_cpdef["int, float, list"](5, 6, [7]), ("int", "float", "list object"))
ae(a_mod.public_cpdef[int, float, list](5, 6, [7]), ("int", "float", "list object")) ae(a_mod.public_cpdef[int, float, list](5, 6, [7]), ("int", "float", "list object"))
idx = cy.typeof(0), cy.typeof(0.0), cy.typeof([]) idx = cy.typeof(0), cy.typeof(0.0), cy.typeof([])
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment