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
* 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
-------------
......
......@@ -211,7 +211,7 @@ class FusedCFuncDefNode(StatListNode):
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(
cname, node.entry.pymethdef_cname)
......@@ -322,7 +322,8 @@ class FusedCFuncDefNode(StatListNode):
for dtype_category, codewriter in dtypes:
if dtype_category:
cond = '{{itemsize_match}}'
cond = '{{itemsize_match}} and arg.ndim == %d' % (
specialized_type.ndim,)
if dtype.is_int:
cond += ' and {{signed_match}}'
......@@ -587,7 +588,7 @@ class FusedCFuncDefNode(StatListNode):
candidates = []
for sig in signatures:
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 src_type == dst_type:
match_found = True
......
......@@ -86,7 +86,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.scope.merge_in(scope)
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))
if not self.doc is None:
env.doc = EncodedString(env.doc + u'\n' + self.doc)
......
......@@ -1239,7 +1239,12 @@ class CppClassNode(CStructOrUnionDefNode, BlockNode):
scope = None
if self.attributes is not None:
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:
template_types = None
else:
......@@ -2084,6 +2089,8 @@ class CFuncDefNode(FuncDefNode):
if self.return_type.is_array and self.visibility != 'extern':
error(self.pos,
"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 len(self.args) < 1 or not self.args[0].type.is_pyobject:
......
......@@ -3314,6 +3314,11 @@ class CppClassType(CType):
func_type = func_type.base_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):
......
......@@ -570,9 +570,7 @@ class Scope(object):
else:
cname = self.mangle(Naming.var_prefix, name)
if type.is_cpp_class and visibility != 'extern':
constructor = type.scope.lookup(u'<init>')
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")
type.check_nullary_constructor(pos)
entry = self.declare(name, cname, type, pos, visibility)
entry.is_variable = 1
if in_pxd and visibility != 'extern':
......@@ -1783,10 +1781,7 @@ class CClassScope(ClassScope):
if visibility == 'private':
cname = c_safe_identifier(cname)
if type.is_cpp_class and visibility != 'extern':
constructor = type.scope.lookup(u'<init>')
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")
type.check_nullary_constructor(pos)
self.use_utility_code(Code.UtilityCode("#include <new>"))
entry = self.declare(name, cname, type, pos, visibility)
entry.is_variable = 1
......
......@@ -390,7 +390,7 @@ class SimpleAssignmentTypeInferer(object):
types = [assmt.rhs.infer_type(scope)
for assmt in entry.cf_assignments]
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:
# FIXME: raise a warning?
# print "No assignments", entry.pos, entry
......@@ -405,10 +405,10 @@ class SimpleAssignmentTypeInferer(object):
for assmt in entry.cf_assignments
if assmt.type_dependencies(scope) == ()]
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)
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)
del dependancies_by_entry[entry]
if ready_to_infer:
......@@ -438,20 +438,24 @@ def find_spanning_type(type1, type2):
return PyrexTypes.c_double_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)
if result_type.is_reference:
result_type = result_type.ref_base_type
if result_type.is_const:
result_type = result_type.const_base_type
if result_type.is_cpp_class:
result_type.check_nullary_constructor(pos)
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)
if result_type.is_const:
result_type = result_type.const_base_type
if result_type.is_reference:
result_type = result_type.ref_base_type
if result_type.is_cpp_class:
result_type.check_nullary_constructor(pos)
if result_type.is_pyobject:
# In theory, any specific Python type is always safe to
# infer. However, inferring str can cause some existing code
......
# cython.* namespace for pure mode.
__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
_Unspecified = object()
......@@ -241,6 +301,8 @@ class typedef(CythonType):
def __repr__(self):
return self.name or str(self._basetype)
__getitem__ = index_type
class _FusedType(CythonType):
pass
......
......@@ -5,6 +5,7 @@
cdef extern from *:
cdef cppclass string "std::string":
string()
string(char* c_str, size_t size)
@cname("{{cname}}")
......@@ -147,6 +148,7 @@ cdef object {{cname}}(const_cpp_set[X]& s):
cdef extern from *:
cdef cppclass pair "std::pair" [T, U]:
pair()
pair(T&, U&)
@cname("{{cname}}")
......
......@@ -700,7 +700,7 @@ __pyx_FusedFunction_getitem(__pyx_FusedFunctionObject *self, PyObject *idx)
Py_DECREF(string);
}
sep = PyUnicode_FromString(", ");
sep = PyUnicode_FromString("|");
if (sep)
signature = PyUnicode_Join(sep, list);
__pyx_err:
......
......@@ -230,9 +230,10 @@ __signatures__
Finally, function objects from ``def`` or ``cpdef`` functions have an attribute
__signatures__, which maps the signature strings to the actual specialized
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::
......@@ -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
``cython.int`` and ``cython.float`` would resolve that, however.
For memoryview indexing from python space you have to use strings instead of
types::
For memoryview indexing from python space we can do the following:
ctypedef fused my_fused_type:
int[:, ::1]
......@@ -252,6 +252,6 @@ types::
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[:, :]``.
......@@ -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)
str object float int
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
spam 5.60 9 5.60 9
>>> 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):
>>> opt_func[ExtClassA, float, int](ExtClassA(), f, i)
ExtClassA float int
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 5.60 9 5.60 9
......
......@@ -849,4 +849,28 @@ def test_dispatch_memoryview_object():
cdef int[:] m3 = <object> m
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"
......@@ -201,7 +201,6 @@ def ae(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"))
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