Skip to content

Commit

Permalink
Merge branch 'bugs'
Browse files Browse the repository at this point in the history
Conflicts:
	Cython/Compiler/Optimize.py
	Cython/Compiler/TypeInference.py
  • Loading branch information
robertwb committed Nov 9, 2012
2 parents a7707eb + 15dce27 commit d7a2258
Show file tree
Hide file tree
Showing 14 changed files with 135 additions and 26 deletions.
8 changes: 8 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------------

Expand Down
7 changes: 4 additions & 3 deletions Cython/Compiler/FusedNode.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ def specialize_copied_def(self, node, cname, py_entry, f2s, 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(
cname, node.entry.pymethdef_cname)
Expand Down Expand Up @@ -322,7 +322,8 @@ def _buffer_check_numpy_dtype(self, pyx_code, specialized_buffer_types):

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}}'

Expand Down Expand Up @@ -587,7 +588,7 @@ def __pyx_fused_cpdef(signatures, args, kwargs, defaults):
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
Expand Down
4 changes: 3 additions & 1 deletion Cython/Compiler/ModuleNode.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ def extend_if_not_in(L1, L2):
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)
Expand Down
9 changes: 8 additions & 1 deletion Cython/Compiler/Nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1239,7 +1239,12 @@ def analyse_declarations(self, env):
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:
Expand Down Expand Up @@ -2084,6 +2089,8 @@ def analyse_declarations(self, env):
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:
Expand Down
5 changes: 5 additions & 0 deletions Cython/Compiler/PyrexTypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3314,6 +3314,11 @@ def find_cpp_operation_type(self, operator, operand_type=None):
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):

Expand Down
9 changes: 2 additions & 7 deletions Cython/Compiler/Symtab.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,9 +570,7 @@ def declare_var(self, name, type, pos,
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':
Expand Down Expand Up @@ -1783,10 +1781,7 @@ def declare_var(self, name, type, pos,
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
Expand Down
14 changes: 9 additions & 5 deletions Cython/Compiler/TypeInference.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ def resolve_dependancy(dep):
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
Expand All @@ -405,10 +405,10 @@ def resolve_dependancy(dep):
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:
Expand Down Expand Up @@ -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
Expand Down
62 changes: 62 additions & 0 deletions Cython/Shadow.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,66 @@
# 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()
Expand Down Expand Up @@ -241,6 +301,8 @@ def __call__(self, *arg):
def __repr__(self):
return self.name or str(self._basetype)

__getitem__ = index_type

class _FusedType(CythonType):
pass

Expand Down
2 changes: 2 additions & 0 deletions Cython/Utility/CppConvert.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

cdef extern from *:
cdef cppclass string "std::string":
string()
string(char* c_str, size_t size)

@cname("{{cname}}")
Expand Down Expand Up @@ -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}}")
Expand Down
2 changes: 1 addition & 1 deletion Cython/Utility/CythonFunction.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
10 changes: 5 additions & 5 deletions docs/src/userguide/fusedtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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::

Expand All @@ -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]
Expand All @@ -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[:, :]``.
4 changes: 2 additions & 2 deletions tests/run/fused_def.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
24 changes: 24 additions & 0 deletions tests/run/numpy_test.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
1 change: 0 additions & 1 deletion tests/run/public_fused_types.srctree
Original file line number Diff line number Diff line change
Expand Up @@ -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([])
Expand Down

0 comments on commit d7a2258

Please sign in to comment.