Commit 0527db06 authored by Robert Bradshaw's avatar Robert Bradshaw

Merge branch 'ctuple'

parents 28a78cb2 2c104e67
......@@ -47,6 +47,8 @@ Features added
* ``PySlice_*()`` C-API functions are available from the ``cpython.slice``
module.
* Anonymous C tuple types can be declared as (ctype1, ctype2, ...).
* Allow arrays of C++ classes.
Bugs fixed
......
......@@ -256,7 +256,7 @@ class BufferEntry(object):
defcode = code.globalstate['utility_code_def']
funcgen(protocode, defcode, name=funcname, nd=nd)
buf_ptr_type_code = self.buf_ptr_type.declaration_code("")
buf_ptr_type_code = self.buf_ptr_type.empty_declaration_code()
ptrcode = "%s(%s, %s, %s)" % (funcname, buf_ptr_type_code, self.buf_ptr,
", ".join(params))
return ptrcode
......@@ -627,7 +627,7 @@ def mangle_dtype_name(dtype):
prefix = "nn_"
else:
prefix = ""
type_decl = dtype.declaration_code("")
type_decl = dtype.empty_declaration_code()
type_decl = type_decl.replace(" ", "_")
return prefix + type_decl.replace("[", "_").replace("]", "_")
......@@ -665,7 +665,7 @@ def get_type_information_cname(code, dtype, maxdepth=None):
complex_possible = dtype.is_struct_or_union and dtype.can_be_complex()
declcode = dtype.declaration_code("")
declcode = dtype.empty_declaration_code()
if dtype.is_simple_buffer_dtype():
structinfo_name = "NULL"
elif dtype.is_struct:
......@@ -678,7 +678,7 @@ def get_type_information_cname(code, dtype, maxdepth=None):
typecode.putln("static __Pyx_StructField %s[] = {" % structinfo_name, safe=True)
for f, typeinfo in zip(fields, types):
typecode.putln(' {&%s, "%s", offsetof(%s, %s)},' %
(typeinfo, f.name, dtype.declaration_code(""), f.cname), safe=True)
(typeinfo, f.name, dtype.empty_declaration_code(), f.cname), safe=True)
typecode.putln(' {NULL, NULL, 0}', safe=True)
typecode.putln("};", safe=True)
else:
......
......@@ -369,7 +369,7 @@ class UtilityCode(UtilityCodeBase):
def specialize(self, pyrex_type=None, **data):
# Dicts aren't hashable...
if pyrex_type is not None:
data['type'] = pyrex_type.declaration_code('')
data['type'] = pyrex_type.empty_declaration_code()
data['type_name'] = pyrex_type.specialization_name()
key = tuple(sorted(data.items()))
try:
......
......@@ -819,6 +819,10 @@ class ExprNode(Node):
return self
elif type.is_pyobject or type.is_int or type.is_ptr or type.is_float:
return CoerceToBooleanNode(self, env)
elif type.is_ctuple:
bool_value = len(type.components) == 0
return BoolNode(self.pos, value=bool_value,
constant_result=bool_value)
else:
error(self.pos, "Type '%s' not acceptable as a boolean" % type)
return self
......@@ -1556,7 +1560,7 @@ class NewExprNode(AtomicExprNode):
pass
def calculate_result_code(self):
return "new " + self.class_type.declaration_code("")
return "new " + self.class_type.empty_declaration_code()
class NameNode(AtomicExprNode):
......@@ -2919,6 +2923,12 @@ class IndexNode(ExprNode):
return item_type
elif base_type.is_ptr or base_type.is_array:
return base_type.base_type
elif base_type.is_ctuple and isinstance(self.index, IntNode):
index = self.index.constant_result
if index < 0:
index += base_type.size
if 0 <= index < base_type.size:
return base_type.components[index]
if base_type.is_cpp_class:
class FakeOperand:
......@@ -3254,6 +3264,21 @@ class IndexNode(ExprNode):
error(self.pos, "Wrong number of template arguments: expected %s, got %s" % (
(len(base_type.templates), len(self.type_indices))))
self.type = base_type.specialize(dict(zip(base_type.templates, self.type_indices)))
elif base_type.is_ctuple:
if isinstance(self.index, IntNode):
index = self.index.constant_result
if -base_type.size <= index < base_type.size:
if index < 0:
index += base_type.size
self.type = base_type.components[index]
else:
error(self.pos,
"Index %s out of bounds for '%s'" %
(index, base_type))
self.type = PyrexTypes.error_type
else:
self.base = self.base.coerce_to_pyobject(env)
return self.analyse_base_and_index_types(env, getting=getting, setting=setting, analyse_base=False)
else:
error(self.pos,
"Attempting to index non-array type '%s'" %
......@@ -3435,7 +3460,12 @@ class IndexNode(ExprNode):
elif self.base.type.is_cfunction:
return "%s<%s>" % (
self.base.result(),
",".join([param.declaration_code("") for param in self.type_indices]))
",".join([param.empty_declaration_code() for param in self.type_indices]))
elif self.base.type.is_ctuple:
index = self.index.constant_result
if index < 0:
index += self.base.type.size
return "%s.f%s" % (self.base.result(), index)
else:
if (self.type.is_ptr or self.type.is_array) and self.type == self.base.type:
error(self.pos, "Invalid use of pointer slice")
......@@ -3453,7 +3483,7 @@ class IndexNode(ExprNode):
and self.index.constant_result >= 0))
boundscheck = bool(code.globalstate.directives['boundscheck'])
return ", %s, %d, %s, %d, %d, %d" % (
self.original_index_type.declaration_code(""),
self.original_index_type.empty_declaration_code(),
self.original_index_type.signed and 1 or 0,
self.original_index_type.to_py_function,
is_list, wraparound, boundscheck)
......@@ -4365,7 +4395,7 @@ class CallNode(ExprNode):
constructor = type.scope.lookup("<init>")
self.function = RawCNameExprNode(self.function.pos, constructor.type)
self.function.entry = constructor
self.function.set_cname(type.declaration_code(""))
self.function.set_cname(type.empty_declaration_code())
self.analyse_c_function_call(env)
self.type = type
return True
......@@ -4447,7 +4477,7 @@ class SimpleCallNode(CallNode):
func_type = self.function_type()
if func_type.is_pyobject:
self.arg_tuple = TupleNode(self.pos, args = self.args)
self.arg_tuple = self.arg_tuple.analyse_types(env)
self.arg_tuple = self.arg_tuple.analyse_types(env).coerce_to_pyobject(env)
self.args = None
if func_type is Builtin.type_type and function.is_name and \
function.entry and \
......@@ -6070,6 +6100,10 @@ class SequenceNode(ExprNode):
', '.join([ arg.py_result() for arg in self.args ]),
code.error_goto_if_null(target, self.pos)))
code.put_gotref(target)
elif self.type.is_ctuple:
for i, arg in enumerate(self.args):
code.putln("%s.f%s = %s;" % (
target, i, arg.result()))
else:
# build the tuple/list step by step, potentially multiplying it as we go
if self.type is Builtin.list_type:
......@@ -6443,13 +6477,32 @@ class TupleNode(SequenceNode):
gil_message = "Constructing Python tuple"
def infer_type(self, env):
if self.mult_factor or not self.args:
return tuple_type
arg_types = [arg.infer_type(env) for arg in self.args]
if any(type.is_pyobject or type.is_unspecified or type.is_fused for type in arg_types):
return tuple_type
else:
type = PyrexTypes.c_tuple_type(arg_types)
env.declare_tuple_type(self.pos, type)
return type
def analyse_types(self, env, skip_children=False):
if len(self.args) == 0:
node = self
node.is_temp = False
node.is_literal = True
self.is_temp = False
self.is_literal = True
return self
else:
if not skip_children:
self.args = [arg.analyse_types(env) for arg in self.args]
if not self.mult_factor and not any(arg.type.is_pyobject or arg.type.is_fused for arg in self.args):
self.type = PyrexTypes.c_tuple_type(arg.type for arg in self.args)
env.declare_tuple_type(self.pos, self.type)
self.is_temp = 1
return self
else:
node = SequenceNode.analyse_types(self, env, skip_children)
node = SequenceNode.analyse_types(self, env, skip_children=True)
for child in node.args:
if not child.is_literal:
break
......@@ -6465,6 +6518,21 @@ class TupleNode(SequenceNode):
node.is_partly_literal = True
return node
def coerce_to(self, dst_type, env):
if self.type.is_ctuple:
if dst_type.is_ctuple and self.type.size == dst_type.size:
if self.type == dst_type:
return self
coerced_args = [arg.coerce_to(type, env) for arg, type in zip(self.args, dst_type.components)]
return TupleNode(self.pos, args=coerced_args, type=dst_type, is_temp=1)
elif dst_type is tuple_type or dst_type is py_object_type:
coerced_args = [arg.coerce_to_pyobject(env) for arg in self.args]
return TupleNode(self.pos, args=coerced_args, type=tuple_type, is_temp=1).analyse_types(env, skip_children=True)
else:
return self.coerce_to_pyobject(env).coerce_to(dst_type, env)
else:
return SequenceNode.coerce_to(self, dst_type, env)
def is_simple(self):
# either temp or constant => always simple
return True
......@@ -6515,6 +6583,7 @@ class TupleNode(SequenceNode):
self.generate_sequence_packing_code(code)
code.put_giveref(self.py_result())
else:
self.type.entry.used = True
self.generate_sequence_packing_code(code)
......@@ -8029,6 +8098,9 @@ class DefaultsTupleNode(TupleNode):
args.append(arg)
super(DefaultsTupleNode, self).__init__(pos, args=args)
def analyse_types(self, env, skip_children=False):
return super(DefaultsTupleNode, self).analyse_types(env, skip_children).coerce_to_pyobject(env)
class DefaultsKwDictNode(DictNode):
# CyFunction's __kwdefaults__ dict
......@@ -8382,7 +8454,7 @@ class UnopNode(ExprNode):
return self.operand.check_const()
def is_py_operation(self):
return self.operand.type.is_pyobject
return self.operand.type.is_pyobject or self.operand.type.is_ctuple
def nogil_check(self, env):
if self.is_py_operation():
......@@ -8949,7 +9021,7 @@ class CythonArrayNode(ExprNode):
shapes_temp = code.funcstate.allocate_temp(py_object_type, True)
format_temp = code.funcstate.allocate_temp(py_object_type, True)
itemsize = "sizeof(%s)" % dtype.declaration_code("")
itemsize = "sizeof(%s)" % dtype.empty_declaration_code()
type_info = Buffer.get_type_information_cname(code, dtype)
if self.operand.type.is_ptr:
......@@ -9069,7 +9141,7 @@ class SizeofTypeNode(SizeofNode):
# we want the size of the actual struct
arg_code = self.arg_type.declaration_code("", deref=1)
else:
arg_code = self.arg_type.declaration_code("")
arg_code = self.arg_type.empty_declaration_code()
return "(sizeof(%s))" % arg_code
......@@ -9236,7 +9308,7 @@ class BinopNode(ExprNode):
return self.is_py_operation_types(self.operand1.type, self.operand2.type)
def is_py_operation_types(self, type1, type2):
return type1.is_pyobject or type2.is_pyobject
return type1.is_pyobject or type2.is_pyobject or type1.is_ctuple or type2.is_ctuple
def is_cpp_operation(self):
return (self.operand1.type.is_cpp_class
......@@ -9697,12 +9769,12 @@ class DivNode(NumBinopNode):
# explicitly signed, no runtime check needed
minus1_check = 'unlikely(%s == -1)' % self.operand2.result()
else:
type_of_op2 = self.operand2.type.declaration_code('')
type_of_op2 = self.operand2.type.empty_declaration_code()
minus1_check = '(!(((%s)-1) > 0)) && unlikely(%s == (%s)-1)' % (
type_of_op2, self.operand2.result(), type_of_op2)
code.putln("else if (sizeof(%s) == sizeof(long) && %s "
" && unlikely(UNARY_NEG_WOULD_OVERFLOW(%s))) {" % (
self.type.declaration_code(''),
self.type.empty_declaration_code(),
minus1_check,
self.operand1.result()))
code.put_ensure_gil()
......@@ -9850,11 +9922,11 @@ class PowNode(NumBinopNode):
elif self.type.is_float:
self.pow_func = "pow" + self.type.math_h_modifier
elif self.type.is_int:
self.pow_func = "__Pyx_pow_%s" % self.type.declaration_code('').replace(' ', '_')
self.pow_func = "__Pyx_pow_%s" % self.type.empty_declaration_code().replace(' ', '_')
env.use_utility_code(
int_pow_utility_code.specialize(
func_name=self.pow_func,
type=self.type.declaration_code(''),
type=self.type.empty_declaration_code(),
signed=self.type.signed and 1 or 0))
elif not self.type.is_error:
error(self.pos, "got unexpected types for C power operator: %s, %s" %
......@@ -10355,6 +10427,9 @@ class CmpNode(object):
if new_common_type is None:
# fall back to generic type compatibility tests
if type1 == type2:
if type1.is_ctuple:
new_common_type = py_object_type
else:
new_common_type = type1
elif type1.is_pyobject or type2.is_pyobject:
if type2.is_numeric or type2.is_string:
......
......@@ -481,7 +481,7 @@ class FusedCFuncDefNode(StatListNode):
# self._dtype_name(dtype)))
decl_code.putln('ctypedef %s %s "%s"' % (dtype.resolve(),
self._dtype_name(dtype),
dtype.declaration_code("")))
dtype.empty_declaration_code()))
if buffer_type.dtype.is_int:
if str(dtype) not in seen_int_dtypes:
......@@ -729,7 +729,7 @@ class FusedCFuncDefNode(StatListNode):
if self.py_func:
args = [CloneNode(default) for default in defaults if default]
self.defaults_tuple = TupleNode(self.pos, args=args)
self.defaults_tuple = self.defaults_tuple.analyse_types(env, skip_children=True)
self.defaults_tuple = self.defaults_tuple.analyse_types(env, skip_children=True).coerce_to_pyobject(env)
self.defaults_tuple = ProxyNode(self.defaults_tuple)
self.code_object = ProxyNode(self.specialized_pycfuncs[0].code_object)
......
......@@ -237,7 +237,7 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
def _generate_buffer_lookup_code(self, code, axes, cast_result=True):
bufp = self.buf_ptr
type_decl = self.type.dtype.declaration_code("")
type_decl = self.type.dtype.empty_declaration_code()
for dim, index, access, packing in axes:
shape = "%s.shape[%d]" % (self.cname, dim)
......@@ -467,7 +467,7 @@ def copy_broadcast_memview_src_to_dst(src, dst, code):
def get_1d_fill_scalar_func(type, code):
dtype = type.dtype
type_decl = dtype.declaration_code("")
type_decl = dtype.empty_declaration_code()
dtype_name = mangle_dtype_name(dtype)
context = dict(dtype_name=dtype_name, type_decl=type_decl)
......@@ -482,8 +482,8 @@ def assign_scalar(dst, scalar, code):
"""
verify_direct_dimensions(dst)
dtype = dst.type.dtype
type_decl = dtype.declaration_code("")
slice_decl = dst.type.declaration_code("")
type_decl = dtype.empty_declaration_code()
slice_decl = dst.type.empty_declaration_code()
code.begin_block()
code.putln("%s __pyx_temp_scalar = %s;" % (type_decl, scalar.result()))
......@@ -527,7 +527,7 @@ class ContigSliceIter(SliceIter):
code = self.code
code.begin_block()
type_decl = self.slice_type.dtype.declaration_code("")
type_decl = self.slice_type.dtype.empty_declaration_code()
total_size = ' * '.join("%s.shape[%d]" % (self.slice_temp, i)
for i in range(self.ndim))
......@@ -613,7 +613,7 @@ def get_copy_new_utility(pos, from_memview, to_memview):
context=dict(
context,
mode=mode,
dtype_decl=to_memview.dtype.declaration_code(''),
dtype_decl=to_memview.dtype.empty_declaration_code(),
contig_flag=contig_flag,
ndim=to_memview.ndim,
func_cname=copy_c_or_fortran_cname(to_memview),
......
......@@ -251,7 +251,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
% (entry.name, cname, sig))
for entry in api_vars:
cname = env.mangle(Naming.varptr_prefix, entry.name)
sig = entry.type.declaration_code("")
sig = entry.type.empty_declaration_code()
h_code.putln(
'if (__Pyx_ImportVoidPtr(module, "%s", (void **)&%s, "%s") < 0) goto bad;'
% (entry.name, cname, sig))
......@@ -721,6 +721,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
pass
elif type.is_struct_or_union or type.is_cpp_class:
self.generate_struct_union_predeclaration(entry, code)
elif type.is_ctuple and entry.used:
self.generate_struct_union_predeclaration(entry.type.struct_entry, code)
elif type.is_extension_type:
self.generate_objstruct_predeclaration(type, code)
# Actual declarations
......@@ -734,6 +736,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_enum_definition(entry, code)
elif type.is_struct_or_union:
self.generate_struct_union_definition(entry, code)
elif type.is_ctuple and entry.used:
self.generate_struct_union_definition(entry.type.struct_entry, code)
elif type.is_cpp_class:
self.generate_cpp_class_definition(entry, code)
elif type.is_extension_type:
......@@ -776,7 +780,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
def generate_struct_union_predeclaration(self, entry, code):
type = entry.type
if type.is_cpp_class and type.templates:
code.putln("template <typename %s>" % ", typename ".join([T.declaration_code("") for T in type.templates]))
code.putln("template <typename %s>" % ", typename ".join([T.empty_declaration_code() for T in type.templates]))
code.putln(self.sue_predeclaration(type, type.kind, type.cname))
def sue_header_footer(self, type, kind, name):
......@@ -826,12 +830,12 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
scope = type.scope
if scope:
if type.templates:
code.putln("template <class %s>" % ", class ".join([T.declaration_code("") for T in type.templates]))
code.putln("template <class %s>" % ", class ".join([T.empty_declaration_code() for T in type.templates]))
# Just let everything be public.
code.put("struct %s" % type.cname)
if type.base_classes:
base_class_decl = ", public ".join(
[base_class.declaration_code("") for base_class in type.base_classes])
[base_class.empty_declaration_code() for base_class in type.base_classes])
code.put(" : public %s" % base_class_decl)
code.putln(" {")
has_virtual_methods = False
......@@ -1124,7 +1128,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln(
"%s = (%s)o;" % (
type.declaration_code("p"),
type.declaration_code("")))
type.empty_declaration_code()))
def generate_new_function(self, scope, code, cclass_entry):
tp_slot = TypeSlots.ConstructorSlot("tp_new", '__new__')
......@@ -1226,7 +1230,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
for entry in cpp_class_attrs:
code.putln("new((void*)&(p->%s)) %s();" %
(entry.cname, entry.type.declaration_code("")))
(entry.cname, entry.type.empty_declaration_code()))
for entry in py_attrs:
code.put_init_var_to_py_none(entry, "p->%s", nanny=False)
......@@ -2427,7 +2431,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if entries:
env.use_utility_code(UtilityCode.load_cached("VoidPtrExport", "ImportExport.c"))
for entry in entries:
signature = entry.type.declaration_code("")
signature = entry.type.empty_declaration_code()
name = code.intern_identifier(entry.name)
code.putln('if (__Pyx_ExportVoidPtr(%s, (void *)&%s, "%s") < 0) %s' % (
name, entry.cname, signature,
......@@ -2495,7 +2499,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
cname = entry.cname
else:
cname = module.mangle(Naming.varptr_prefix, entry.name)
signature = entry.type.declaration_code("")
signature = entry.type.empty_declaration_code()
code.putln(
'if (__Pyx_ImportVoidPtr(%s, "%s", (void **)&%s, "%s") < 0) %s' % (
temp, entry.name, cname, signature,
......
......@@ -832,7 +832,7 @@ class CArgDeclNode(Node):
if self.base_type.is_basic_c_type:
# char, short, long called "int"
type = self.base_type.analyse(env, could_be_name=True)
arg_name = type.declaration_code("")
arg_name = type.empty_declaration_code()
else:
arg_name = self.base_type.name
self.declarator.name = EncodedString(arg_name)
......@@ -1165,6 +1165,25 @@ class CComplexBaseTypeNode(CBaseTypeNode):
return type
class CTupleBaseTypeNode(CBaseTypeNode):
# components [CBaseTypeNode]
child_attrs = ["components"]
def analyse(self, env, could_be_name=False):
component_types = []
for c in self.components:
type = c.analyse(env)
if type.is_pyobject:
error(type_node.pos, "Tuple types can't (yet) contain Python objects.")
return PyrexType.error_type
component_types.append(type)
type = PyrexTypes.c_tuple_type(component_types)
entry = env.declare_tuple_type(self.pos, type)
entry.used = True
return type
class FusedTypeNode(CBaseTypeNode):
"""
Represents a fused type in a ctypedef statement:
......@@ -1184,7 +1203,7 @@ class FusedTypeNode(CBaseTypeNode):
# Omit the typedef declaration that self.declarator would produce
entry.in_cinclude = True
def analyse(self, env):
def analyse(self, env, could_be_name = False):
types = []
for type_node in self.types:
type = type_node.analyse_as_type(env)
......@@ -1793,7 +1812,7 @@ class FuncDefNode(StatNode, BlockNode):
slot_func_cname = '%s->tp_new' % lenv.scope_class.type.typeptr_cname
code.putln("%s = (%s)%s(%s, %s, NULL);" % (
Naming.cur_scope_cname,
lenv.scope_class.type.declaration_code(''),
lenv.scope_class.type.empty_declaration_code(),
slot_func_cname,
lenv.scope_class.type.typeptr_cname,
Naming.empty_tuple))
......@@ -1815,12 +1834,12 @@ class FuncDefNode(StatNode, BlockNode):
if self.is_cyfunction:
code.putln("%s = (%s) __Pyx_CyFunction_GetClosure(%s);" % (
outer_scope_cname,
cenv.scope_class.type.declaration_code(''),
cenv.scope_class.type.empty_declaration_code(),
Naming.self_cname))
else:
code.putln("%s = (%s) %s;" % (
outer_scope_cname,
cenv.scope_class.type.declaration_code(''),
cenv.scope_class.type.empty_declaration_code(),
Naming.self_cname))
if lenv.is_passthrough:
code.putln("%s = %s;" % (Naming.cur_scope_cname, outer_scope_cname))
......@@ -4657,7 +4676,7 @@ class AssignmentNode(StatNode):
def analyse_expressions(self, env):
node = self.analyse_types(env)
if isinstance(node, AssignmentNode):
if isinstance(node, AssignmentNode) and not isinstance(node, ParallelAssignmentNode):
if node.rhs.type.is_ptr and node.rhs.is_ephemeral():
error(self.pos, "Storing unsafe C derivative of temporary Python reference")
return node
......@@ -4763,11 +4782,19 @@ class SingleAssignmentNode(AssignmentNode):
self.lhs.analyse_target_declaration(env)
def analyse_types(self, env, use_temp = 0):
from . import ExprNodes
from . import ExprNodes, UtilNodes
self.rhs = self.rhs.analyse_types(env)
unrolled_assignment = self.unroll_rhs(env)
if unrolled_assignment:
return unrolled_assignment
self.lhs = self.lhs.analyse_target_types(env)
self.lhs.gil_assignment_check(env)
unrolled_assignment = self.unroll_lhs(env)
if unrolled_assignment:
return unrolled_assignment
if self.lhs.memslice_broadcast or self.rhs.memslice_broadcast:
self.lhs.memslice_broadcast = True
......@@ -4801,6 +4828,130 @@ class SingleAssignmentNode(AssignmentNode):
self.rhs = rhs
return self
def unroll(self, node, target_size, env):
from . import ExprNodes, UtilNodes
if node.type.is_ctuple:
if node.type.size == target_size:
base = node
start_node = None
stop_node = None
step_node = None
check_node = None
else:
error(self.pos, "Unpacking type %s requires exactly %s arguments." % (
node.type, node.type.size))
return
elif node.type.is_ptr:
if isinstance(node, ExprNodes.SliceIndexNode):
base = node.base
start_node = node.start
if start_node:
start_node = start_node.coerce_to(PyrexTypes.c_py_ssize_t_type, env)
stop_node = node.stop
if stop_node:
stop_node = stop_node.coerce_to(PyrexTypes.c_py_ssize_t_type, env)
else:
if node.type.is_array and node.type.size:
stop_node = ExprNodes.IntNode(pos=self.pos, value=str(rhs.type.size))
else:
error(self.pos, "C array iteration requires known end index")
return
step_node = None #node.step
if step_node:
step_node = step_node.coerce_to(PyrexTypes.c_py_ssize_t_type, env)
# TODO: Factor out SliceIndexNode.generate_slice_guard_code() for use here.
def get_const(node, none_value):
if node is None:
return none_value
elif node.has_constant_result:
node.calculate_constant_result()
return node.constant_result
else:
raise ValueError, "Not a constant."
try:
slice_size = (get_const(stop_node, None) - get_const(start_node, 0)) / get_const(step_node, 1)
if target_size != slice_size:
error(self.pos, "Assignment to/from slice of wrong length, expected %d, got %d" % (
slice_size, target_size))
except ValueError:
error(self.pos, "C array assignment currently requires known endpoints")
return
check_node = None
else:
return
else:
return
items = []
base_ref = UtilNodes.LetRefNode(base)
refs = [base_ref]
if start_node:
start_node = UtilNodes.LetRefNode(start_node)
refs.append(start_node)
if stop_node:
stop_node = UtilNodes.LetRefNode(stop_node)
refs.append(stop_node)
if step_node:
step_node = UtilNodes.LetRefNode(step_node)
refs.append(step_node)
for ix in range(target_size):
ix_node = ExprNodes.IntNode(pos=self.pos, value=str(ix))
if step_node is not None:
ix_node = ExprNodes.MulNode(pos=self.pos, operator='*', operand1=step_node, operand2=ix_node).analyse_types(env)
if start_node is not None:
ix_node = ExprNodes.AddNode(pos=self.pos, operator='+', operand1=start_node, operand2=ix_node).analyse_types(env)
items.append(ExprNodes.IndexNode(
pos=self.pos,
base=base_ref,
index=ix_node))
return check_node, refs, items
def unroll_assignments(self, refs, check_node, lhs_list, rhs_list, env):
from . import ExprNodes, UtilNodes
assignments = []
for lhs, rhs in zip(lhs_list, rhs_list):
assignments.append(SingleAssignmentNode(
pos = self.pos,
lhs = lhs,
rhs = rhs,
first = self.first))
all = ParallelAssignmentNode(pos=self.pos, stats=assignments).analyse_expressions(env)
if check_node:
all = StatListNode(pos=self.pos, stats=[check_node, all])
for ref in refs:
all = UtilNodes.LetNode(ref, all)
return all
def unroll_rhs(self, env):
from . import ExprNodes, UtilNodes
if not isinstance(self.lhs, ExprNodes.TupleNode):
return
for arg in self.lhs.args:
if arg.is_starred:
return
unrolled = self.unroll(self.rhs, len(self.lhs.args), env)
if not unrolled:
return
check_node, refs, rhs = unrolled
return self.unroll_assignments(refs, check_node, self.lhs.args, rhs, env)
def unroll_lhs(self, env):
if self.lhs.type.is_ctuple:
# Handled directly.
return
from . import ExprNodes, UtilNodes
if not isinstance(self.rhs, ExprNodes.TupleNode):
return
unrolled = self.unroll(self.lhs, len(self.rhs.args), env)
if not unrolled:
return
check_node, refs, lhs = unrolled
return self.unroll_assignments(refs, check_node, lhs, self.rhs.args, env)
def generate_rhs_evaluation_code(self, code):
self.rhs.generate_evaluation_code(code)
......@@ -5471,7 +5622,7 @@ class AssertStatNode(StatNode):
# prevent tuple values from being interpreted as argument value tuples
from .ExprNodes import TupleNode
value = TupleNode(value.pos, args=[value], slow=True)
self.value = value.analyse_types(env, skip_children=True)
self.value = value.analyse_types(env, skip_children=True).coerce_to_pyobject(env)
else:
self.value = value.coerce_to_pyobject(env)
return self
......@@ -7691,7 +7842,7 @@ class ParallelStatNode(StatNode, ParallelNode):
if not lastprivate or entry.type.is_pyobject:
continue
type_decl = entry.type.declaration_code("")
type_decl = entry.type.empty_declaration_code()
temp_cname = "__pyx_parallel_temp%d" % temp_count
private_cname = entry.cname
......
......@@ -2593,7 +2593,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
constant_result=orig_index_type.signed and 1 or 0,
type=PyrexTypes.c_int_type),
ExprNodes.RawCNameExprNode(index.pos, PyrexTypes.c_void_type,
orig_index_type.declaration_code("")),
orig_index_type.empty_declaration_code()),
ExprNodes.RawCNameExprNode(index.pos, conversion_type, convert_func)],
may_return_none=True,
is_temp=node.is_temp,
......
......@@ -2038,9 +2038,21 @@ def p_c_complex_base_type(s, templates = None):
s.next()
base_type = p_c_base_type(s, templates = templates)
declarator = p_c_declarator(s, empty = 1)
s.expect(')')
type_node = Nodes.CComplexBaseTypeNode(pos,
base_type = base_type, declarator = declarator)
if s.sy == ',':
components = [type_node]
while s.sy == ',':
s.next()
if s.sy == ')':
break
base_type = p_c_base_type(s, templates = templates)
declarator = p_c_declarator(s, empty = 1)
components.append(Nodes.CComplexBaseTypeNode(pos,
base_type = base_type, declarator = declarator))
type_node = Nodes.CTupleBaseTypeNode(pos, components = components)
s.expect(')')
if s.sy == '[':
if is_memoryviewslice_access(s):
type_node = p_memoryviewslice_access(s, type_node)
......
......@@ -6,6 +6,7 @@ from __future__ import absolute_import
import re
import copy
import re
from .Code import UtilityCode, LazyUtilityCode, TempitaUtilityCode
from . import StringEncoding
......@@ -20,18 +21,24 @@ class BaseType(object):
# List of attribute names of any subtypes
subtypes = []
_empty_declaration = None
def can_coerce_to_pyobject(self, env):
return False
def cast_code(self, expr_code):
return "((%s)%s)" % (self.declaration_code(""), expr_code)
return "((%s)%s)" % (self.empty_declaration_code(), expr_code)
def empty_declaration_code(self):
if self._empty_declaration is None:
self._empty_declaration = self.declaration_code('')
return self._empty_declaration
def specialization_name(self):
# This is not entirely robust.
safe = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789'
all = []
for c in self.declaration_code("").replace("unsigned ", "unsigned_").replace("long long", "long_long").replace(" ", "__"):
for c in self.empty_declaration_code().replace("unsigned ", "unsigned_").replace("long long", "long_long").replace(" ", "__"):
if c in safe:
all.append(c)
else:
......@@ -225,6 +232,7 @@ class PyrexType(BaseType):
is_returncode = 0
is_error = 0
is_buffer = 0
is_ctuple = 0
is_memoryviewslice = 0
has_attributes = 0
default_value = ""
......@@ -373,7 +381,7 @@ class CTypedefType(BaseType):
def _create_utility_code(self, template_utility_code,
template_function_name):
type_name = self.typedef_cname.replace(" ","_").replace("::","__")
type_name = type_identifier(self.typedef_cname)
utility_code = template_utility_code.specialize(
type = self.typedef_cname,
TypeName = type_name)
......@@ -388,7 +396,7 @@ class CTypedefType(BaseType):
self.to_py_function = "__Pyx_PyInt_From_" + self.specialization_name()
env.use_utility_code(TempitaUtilityCode.load(
"CIntToPy", "TypeConversion.c",
context={"TYPE": self.declaration_code(''),
context={"TYPE": self.empty_declaration_code(),
"TO_PY_FUNCTION": self.to_py_function}))
return True
elif base_type.is_float:
......@@ -410,7 +418,7 @@ class CTypedefType(BaseType):
self.from_py_function = "__Pyx_PyInt_As_" + self.specialization_name()
env.use_utility_code(TempitaUtilityCode.load(
"CIntFromPy", "TypeConversion.c",
context={"TYPE": self.declaration_code(''),
context={"TYPE": self.empty_declaration_code(),
"FROM_PY_FUNCTION": self.from_py_function}))
return True
elif base_type.is_float:
......@@ -440,7 +448,7 @@ class CTypedefType(BaseType):
def overflow_check_binop(self, binop, env, const_rhs=False):
env.use_utility_code(UtilityCode.load("Common", "Overflow.c"))
type = self.declaration_code("")
type = self.empty_declaration_code()
name = self.specialization_name()
if binop == "lshift":
env.use_utility_code(TempitaUtilityCode.load(
......@@ -701,7 +709,7 @@ class MemoryViewSliceType(PyrexType):
buf_flag = self.flags,
ndim = self.ndim,
axes_specs = ', '.join(self.axes_to_code()),
dtype_typedecl = self.dtype.declaration_code(""),
dtype_typedecl = self.dtype.empty_declaration_code(),
struct_nesting_depth = self.dtype.struct_nesting_depth(),
c_or_f_flag = c_or_f_flag,
funcname = funcname,
......@@ -753,7 +761,7 @@ class MemoryViewSliceType(PyrexType):
context.update(
to_py_function = self.dtype.to_py_function,
from_py_function = self.dtype.from_py_function,
dtype = self.dtype.declaration_code(""),
dtype = self.dtype.empty_declaration_code(),
error_condition = error_condition,
)
......@@ -1472,7 +1480,7 @@ class CIntType(CNumericType):
self.to_py_function = "__Pyx_PyInt_From_" + self.specialization_name()
env.use_utility_code(TempitaUtilityCode.load(
"CIntToPy", "TypeConversion.c",
context={"TYPE": self.declaration_code(''),
context={"TYPE": self.empty_declaration_code(),
"TO_PY_FUNCTION": self.to_py_function}))
return True
......@@ -1481,7 +1489,7 @@ class CIntType(CNumericType):
self.from_py_function = "__Pyx_PyInt_As_" + self.specialization_name()
env.use_utility_code(TempitaUtilityCode.load(
"CIntFromPy", "TypeConversion.c",
context={"TYPE": self.declaration_code(''),
context={"TYPE": self.empty_declaration_code(),
"FROM_PY_FUNCTION": self.from_py_function}))
return True
......@@ -1516,7 +1524,7 @@ class CIntType(CNumericType):
def overflow_check_binop(self, binop, env, const_rhs=False):
env.use_utility_code(UtilityCode.load("Common", "Overflow.c"))
type = self.declaration_code("")
type = self.empty_declaration_code()
name = self.specialization_name()
if binop == "lshift":
env.use_utility_code(TempitaUtilityCode.load(
......@@ -1807,7 +1815,7 @@ class CComplexType(CNumericType):
env.use_utility_code(
utility_code.specialize(
self,
real_type = self.real_type.declaration_code(''),
real_type = self.real_type.empty_declaration_code(),
m = self.funcsuffix,
is_float = self.real_type.is_float))
return True
......@@ -1828,7 +1836,7 @@ class CComplexType(CNumericType):
env.use_utility_code(
utility_code.specialize(
self,
real_type = self.real_type.declaration_code(''),
real_type = self.real_type.empty_declaration_code(),
m = self.funcsuffix,
is_float = self.real_type.is_float))
self.from_py_function = "__Pyx_PyComplex_As_" + self.specialization_name()
......@@ -2683,7 +2691,7 @@ class CFuncType(CType):
func_name, arg_code, trailer)
def signature_string(self):
s = self.declaration_code("")
s = self.empty_declaration_code()
return s
def signature_cast_string(self):
......@@ -3042,7 +3050,7 @@ class ToPyStructUtilityCode(object):
# This is a bit of a hack, we need a forward declaration
# due to the way things are ordered in the module...
if self.forward_decl:
proto.putln(self.type.declaration_code('') + ';')
proto.putln(self.type.empty_declaration_code() + ';')
proto.putln(self.header + ";")
def inject_tree_and_scope_into(self, module_node):
......@@ -3171,8 +3179,8 @@ class CStructOrUnionType(CType):
if len(fields) != 2: return False
a, b = fields
return (a.type.is_float and b.type.is_float and
a.type.declaration_code("") ==
b.type.declaration_code(""))
a.type.empty_declaration_code() ==
b.type.empty_declaration_code())
def struct_nesting_depth(self):
child_depths = [x.type.struct_nesting_depth()
......@@ -3264,21 +3272,21 @@ class CppClassType(CType):
except_clause = "? %s" % except_clause
declarations.append(
" ctypedef %s %s '%s'" % (
except_type.declaration_code("", for_display=True), X[ix], T.declaration_code("")))
except_type.declaration_code("", for_display=True), X[ix], T.empty_declaration_code()))
else:
except_clause = "*"
declarations.append(
" ctypedef struct %s '%s':\n pass" % (
X[ix], T.declaration_code("")))
X[ix], T.empty_declaration_code()))
declarations.append(
" cdef %s %s_from_py '%s' (object) except %s" % (
X[ix], X[ix], T.from_py_function, except_clause))
if self.cname in cpp_string_conversions:
cls = 'string'
tags = self.cname.replace(':', '_'),
tags = type_identifier(self),
else:
cls = self.cname[5:]
cname = '__pyx_convert_%s_from_py_%s' % (cls, '____'.join(tags))
cname = '__pyx_convert_%s_from_py_%s' % (cls, '__and_'.join(tags))
context = {
'template_type_declarations': '\n'.join(declarations),
'cname': cname,
......@@ -3304,14 +3312,14 @@ class CppClassType(CType):
tags.append(T.specialization_name())
declarations.append(
" ctypedef struct %s '%s':\n pass" % (
X[ix], T.declaration_code("")))
X[ix], T.empty_declaration_code()))
declarations.append(
" cdef object %s_to_py '%s' (%s)" % (
X[ix], T.to_py_function, X[ix]))
if self.cname in cpp_string_conversions:
cls = 'string'
prefix = 'PyObject_' # gets specialised by explicit type casts in CoerceToPyTypeNode
tags = self.cname.replace(':', '_'),
tags = type_identifier(self),
else:
cls = self.cname[5:]
prefix = ''
......@@ -3331,6 +3339,17 @@ class CppClassType(CType):
def is_template_type(self):
return self.templates is not None and self.template_type is None
def get_fused_types(self, result=None, seen=None):
if result is None:
result = []
seen = set()
if self.namespace:
self.namespace.get_fused_types(result, seen)
if self.templates:
for T in self.templates:
T.get_fused_types(result, seen)
return result
def specialize_here(self, pos, template_values = None):
if not self.is_template_type():
error(pos, "'%s' type is not a template" % self)
......@@ -3363,16 +3382,16 @@ class CppClassType(CType):
# Need to do these *after* self.specializations[key] is set
# to avoid infinite recursion on circular references.
specialized.base_classes = [b.specialize(values) for b in self.base_classes]
specialized.scope = self.scope.specialize(values, specialized)
if self.namespace is not None:
specialized.namespace = self.namespace.specialize(values)
specialized.scope = self.scope.specialize(values, specialized)
return specialized
def deduce_template_params(self, actual):
if self == actual:
return {}
# TODO(robertwb): Actual type equality.
elif self.declaration_code("") == actual.template_type.declaration_code(""):
elif self.empty_declaration_code() == actual.template_type.empty_declaration_code():
return reduce(
merge_template_deductions,
[formal_param.deduce_template_params(actual_param) for (formal_param, actual_param) in zip(self.templates, actual.templates)],
......@@ -3397,7 +3416,7 @@ class CppClassType(CType):
else:
base_code = "%s%s" % (self.cname, templates)
if self.namespace is not None:
base_code = "%s::%s" % (self.namespace.declaration_code(''), base_code)
base_code = "%s::%s" % (self.namespace.empty_declaration_code(), base_code)
base_code = public_decl(base_code, dll_linkage)
return self.base_declaration_code(base_code, entity_code)
......@@ -3541,6 +3560,87 @@ class CEnumType(CType):
' %s' % code.error_goto_if(error_condition or self.error_condition(result_code), error_pos))
class CTupleType(CType):
# components [PyrexType]
is_ctuple = True
def __init__(self, cname, components):
self.cname = cname
self.components = components
self.size = len(components)
self.to_py_function = "%s_to_py_%s" % (Naming.convert_func_prefix, self.cname)
self.from_py_function = "%s_from_py_%s" % (Naming.convert_func_prefix, self.cname)
self.exception_check = True
self._convert_to_py_code = None
self._convert_from_py_code = None
def __str__(self):
return "(%s)" % ", ".join(str(c) for c in self.components)
def declaration_code(self, entity_code,
for_display = 0, dll_linkage = None, pyrex = 0):
if pyrex or for_display:
return str(self)
else:
return self.base_declaration_code(self.cname, entity_code)
def create_to_py_utility_code(self, env):
if self._convert_to_py_code is False:
return None # tri-state-ish
if self._convert_to_py_code is None:
for component in self.components:
if not component.create_to_py_utility_code(env):
self.to_py_function = None
self._convert_to_py_code = False
return False
context = dict(
struct_type_decl=self.empty_declaration_code(),
components=self.components,
funcname=self.to_py_function,
size=len(self.components)
)
self._convert_to_py_code = TempitaUtilityCode.load(
"ToPyCTupleUtility", "TypeConversion.c", context=context)
env.use_utility_code(self._convert_to_py_code)
return True
def create_from_py_utility_code(self, env):
if self._convert_from_py_code is False:
return None # tri-state-ish
if self._convert_from_py_code is None:
for component in self.components:
if not component.create_from_py_utility_code(env):
self.from_py_function = None
self._convert_from_py_code = False
return False
context = dict(
struct_type_decl=self.empty_declaration_code(),
components=self.components,
funcname=self.from_py_function,
size=len(self.components)
)
self._convert_from_py_code = TempitaUtilityCode.load(
"FromPyCTupleUtility", "TypeConversion.c", context=context)
env.use_utility_code(self._convert_from_py_code)
return True
c_tuple_types = {}
def c_tuple_type(components):
components = tuple(components)
tuple_type = c_tuple_types.get(components)
if tuple_type is None:
cname = '__pyx_tuple_' + type_list_identifier(components)
tuple_type = c_tuple_types[components] = CTupleType(cname, components)
return tuple_type
class UnspecifiedType(PyrexType):
# Used as a placeholder until the type can be determined.
......@@ -3819,7 +3919,7 @@ def best_match(args, functions, pos=None, env=None):
from .Symtab import Entry
specialization = Entry(
name = func.name + "[%s]" % ",".join([str(t) for t in type_list]),
cname = func.cname + "<%s>" % ",".join([t.declaration_code("") for t in type_list]),
cname = func.cname + "<%s>" % ",".join([t.empty_declaration_code() for t in type_list]),
type = func_type.specialize(deductions),
pos = func.pos)
candidates.append((specialization, specialization.type))
......@@ -4138,3 +4238,37 @@ def typecast(to_type, from_type, expr_code):
else:
#print "typecast: to", to_type, "from", from_type ###
return to_type.cast_code(expr_code)
def type_list_identifier(types):
return cap_length('__and_'.join(type_identifier(type) for type in types))
_type_identifier_cache = {}
def type_identifier(type):
decl = type.empty_declaration_code()
safe = _type_identifier_cache.get(decl)
if safe is None:
safe = decl
safe = re.sub(' +', ' ', safe)
safe = re.sub(' ([^a-zA-Z0-9_])', r'\1', safe)
safe = re.sub('([^a-zA-Z0-9_]) ', r'\1', safe)
safe = (safe.replace('__', '__dunder')
.replace(' ', '__space_')
.replace('*', '__ptr')
.replace('&', '__ref')
.replace('[', '__lArr')
.replace(']', '__rArr')
.replace('<', '__lAng')
.replace('>', '__rAng')
.replace('(', '__lParen')
.replace(')', '__rParen')
.replace(',', '__comma_')
.replace('::', '__in_'))
safe = cap_length(re.sub('[^a-zA-Z0-9_]', lambda x: '__%X' % ord(x.group(0)), safe))
_type_identifier_cache[decl] = safe
return safe
def cap_length(s, max_prefix=63, max_len=1024):
if len(s) <= max_prefix:
return s
else:
return '%x__%s__etc' % (abs(hash(s)) % (1<<20), s[:max_len-17])
......@@ -606,6 +606,9 @@ class Scope(object):
self.sue_entries.append(entry)
return entry
def declare_tuple_type(self, pos, type):
return self.outer_scope.declare_tuple_type(pos, type)
def declare_var(self, name, type, pos,
cname = None, visibility = 'private',
api = 0, in_pxd = 0, is_cdef = 0):
......@@ -1061,6 +1064,18 @@ class ModuleScope(Scope):
return self.outer_scope.lookup(name, language_level=language_level)
def declare_tuple_type(self, pos, type):
cname = type.cname
if not self.lookup_here(cname):
scope = StructOrUnionScope(cname)
for ix, component in enumerate(type.components):
scope.declare_var(name="f%s" % ix, type=component, pos=pos)
struct_entry = self.declare_struct_or_union(cname + '_struct', 'struct', scope, typedef_flag=True, pos=pos, cname=cname)
self.type_entries.remove(struct_entry)
type.struct_entry = struct_entry
type.entry = self.declare_type(cname, type, pos, cname)
return type.entry
def declare_builtin(self, name, pos):
if not hasattr(builtins, name) \
and name not in Code.non_portable_builtins_map \
......@@ -2114,8 +2129,8 @@ class CppClassScope(Scope):
entry = self.declare(name, cname, type, pos, visibility)
entry.is_variable = 1
if type.is_cfunction and self.type:
if not self.type.templates or not any(T.is_fused for T in self.type.templates):
entry.func_cname = "%s::%s" % (self.type.declaration_code(""), cname)
if not self.type.get_fused_types:
entry.func_cname = "%s::%s" % (self.type.empty_declaration_code(), cname)
if name != "this" and (defining or name != "<init>"):
self.var_entries.append(entry)
if type.is_pyobject and not allow_pyobject:
......
......@@ -311,6 +311,55 @@ static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) {
}
/////////////// ToPyCTupleUtility.proto ///////////////
static PyObject* {{funcname}}({{struct_type_decl}});
/////////////// ToPyCTupleUtility ///////////////
static PyObject* {{funcname}}({{struct_type_decl}} value) {
PyObject* item = NULL;
PyObject* result = PyTuple_New({{size}});
if (!result) goto bad;
{{for ix, component in enumerate(components):}}
{{py:attr = "value.f%s" % ix}}
item = {{component.to_py_function}}({{attr}});
if (!item) goto bad;
PyTuple_SET_ITEM(result, {{ix}}, item);
{{endfor}}
return result;
bad:
Py_XDECREF(item);
Py_XDECREF(result);
return NULL;
}
/////////////// FromPyCTupleUtility.proto ///////////////
static {{struct_type_decl}} {{funcname}}(PyObject *);
/////////////// FromPyCTupleUtility ///////////////
static {{struct_type_decl}} {{funcname}}(PyObject * o) {
{{struct_type_decl}} result;
if (!PyTuple_Check(o) || PyTuple_GET_SIZE(o) != {{size}}) {
PyErr_Format(PyExc_TypeError, "Expected %.16s of size %.8d, got %.200s", "a tuple", {{size}}, Py_TYPE(o)->tp_name);
goto bad;
}
{{for ix, component in enumerate(components):}}
{{py:attr = "result.f%s" % ix}}
{{attr}} = {{component.from_py_function}}(PyTuple_GET_ITEM(o, {{ix}}));
if ({{component.error_condition(attr)}})
goto bad;
{{endfor}}
return result;
bad:
return result;
}
/////////////// ObjectAsUCS4.proto ///////////////
static CYTHON_INLINE Py_UCS4 __Pyx_PyObject_AsPy_UCS4(PyObject*);
......
......@@ -141,6 +141,24 @@ def test_ptr_literal_list_slice_end():
a[:5] = [1,2,3,4,5]
return (a[0], a[1], a[2], a[3], a[4])
def test_multiple_from_slice():
"""
>>> test_multiple_from_slice()
(5, 4, 3)
"""
cdef int *a = [6,5,4,3,2,1]
x, y, z = a[1:4]
return x, y, z
def test_slice_from_multiple():
"""
>>> test_slice_from_multiple()
(6, -1, -2, -3, 2, 1)
"""
cdef int *a = [6,5,4,3,2,1]
a[1:4] = -1, -2, -3
return a[0], a[1], a[2], a[3], a[4], a[5]
def test_literal_tuple():
"""
>>> test_literal_tuple()
......
import cython
def simple_convert(*o):
"""
>>> simple_convert(1, 2)
(1, 2.0)
>>> simple_convert(1)
Traceback (most recent call last):
...
TypeError: Expected a tuple of size 2, got tuple
>>> simple_convert(1, 2, 3)
Traceback (most recent call last):
...
TypeError: Expected a tuple of size 2, got tuple
"""
cdef (int, double) xy = o
return xy
def indexing((int, double) xy):
"""
>>> indexing((1, 2))
(2, 3.0)
"""
x = xy[0]
y = xy[1]
xy[0] = x + 1
xy[1] = y + 1
return xy
def unpacking((int, double) xy):
"""
>>> unpacking((1, 2))
(1, 2.0)
"""
x, y = xy
return x, y
cdef (int, double) side_effect((int, double) xy):
print "called with", xy
return xy
def unpacking_with_side_effect((int, double) xy):
"""
>>> unpacking_with_side_effect((1, 2))
called with (1, 2.0)
(1, 2.0)
"""
x, y = side_effect(xy)
return x, y
def packing_tuple(int x, double y):
"""
>>> packing_tuple(1, 2)
(1, 2.0)
"""
cdef (int, double) xy = (x, y)
return xy
def coerce_packing_tuple(int x, int y):
cdef (int, double) xy = (x, y)
"""
>>> coerce_packing_tuple(1, 2)
(1, 2.0)
"""
return xy
def c_types(int a, double b):
"""
>>> c_types(1, 2)
(1, 2.0)
"""
cdef int* a_ptr
cdef double* b_ptr
cdef (int*, double*) ab = (&a, &b)
a_ptr, b_ptr = ab
return a_ptr[0], b_ptr[0]
cdef (int, int*) cdef_ctuple_return_type(int x, int* x_ptr):
return x, x_ptr
def call_cdef_ctuple_return_type(int x):
"""
>>> call_cdef_ctuple_return_type(2)
(2, 2)
"""
cdef (int, int*) res = cdef_ctuple_return_type(x, &x)
return res[0], res[1][0]
cpdef (int, double) cpdef_ctuple_return_type(int x, double y):
"""
>>> cpdef_ctuple_return_type(1, 2)
(1, 2.0)
"""
return x, y
@cython.infer_types(True)
def test_type_inference():
"""
>>> test_type_inference()
"""
cdef int x = 1
cdef double y = 2
cdef object o = 3
xy = (x, y)
assert cython.typeof(xy) == "(int, double)", cython.typeof(xy)
xo = (x, o)
assert cython.typeof(xo) == "tuple object", cython.typeof(xo)
def test_equality((int, int) ab, (int, int) cd, (int, int) ef):
"""
>>> test_equality((1, 2), (3, 4), (5, 6))
True
>>> test_equality((1, 2), (3, 4), (3, 4))
True
>>> test_equality((3, 4), (3, 4), (3, 4))
False
"""
return ab < cd <= ef
def test_binop((int, int) ab, (double, double) cd):
"""
>>> test_binop((1, 2), (3, 4))
(1, 2, 3.0, 4.0)
"""
return ab + cd
def test_mul((int, int) ab, int c):
"""
>>> test_mul((1, 2), 3)
(1, 2, 1, 2, 1, 2)
"""
return ab * c
def test_unop((int, int) ab):
"""
>>> test_unop((1, 2))
True
"""
return not ab
......@@ -32,8 +32,10 @@ def simple():
assert typeof(u) == "unicode object", typeof(u)
L = [1,2,3]
assert typeof(L) == "list object", typeof(L)
t = (4,5,6)
t = (4,5,6,())
assert typeof(t) == "tuple object", typeof(t)
t2 = (4, 5.0, 6)
assert typeof(t2) == "(long, double, long)", typeof(t)
def builtin_types():
"""
......@@ -80,7 +82,7 @@ def slicing():
assert typeof(L1) == "list object", typeof(L1)
L2 = L[1:2:2]
assert typeof(L2) == "list object", typeof(L2)
t = (4,5,6)
t = (4,5,6,())
assert typeof(t) == "tuple object", typeof(t)
t1 = t[1:2]
assert typeof(t1) == "tuple object", typeof(t1)
......@@ -107,7 +109,7 @@ def indexing():
assert typeof(L) == "list object", typeof(L)
L1 = L[1]
assert typeof(L1) == "Python object", typeof(L1)
t = (4,5,6)
t = (4,5,())
assert typeof(t) == "tuple object", typeof(t)
t1 = t[1]
assert typeof(t1) == "long", typeof(t1)
......
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