Commit 11915abb authored by Xavier Thompson's avatar Xavier Thompson

Introduce 'iso' qualifier and 'consume' keyword

parent b24fad38
......@@ -1048,11 +1048,15 @@ class ExprNode(Node):
src = CoerceToComplexNode(src, dst_type, env)
else:
# neither src nor dst are py types
# Qualified cypclass types can have nontrivial aliasing rules
# meaning type equality may not imply the coercion is valid.
if src_type.is_qualified_cyp_class and not dst_type.assignable_from(src_type):
self.fail_assignment(dst_type)
# Added the string comparison, since for c types that
# is enough, but Cython gets confused when the types are
# in different pxi files.
# TODO: Remove this hack and require shared declarations.
if not (src.type == dst_type or str(src.type) == str(dst_type) or dst_type.assignable_from(src_type)):
elif not (src.type == dst_type or str(src.type) == str(dst_type) or dst_type.assignable_from(src_type)):
self.fail_assignment(dst_type)
return src
......@@ -3945,8 +3949,8 @@ class IndexNode(_IndexingBaseNode):
elif self.exception_check == '~':
self.is_temp = True
self.index = self.index.coerce_to(func_type.args[0].type, env)
self.type = func_type.return_type
if setting and not func_type.return_type.is_reference:
self.type = func_type.return_type.as_returned_type()
if setting and not self.type.is_reference:
error(self.pos, "Can't set non-reference result '%s'" % self.type)
return self
......@@ -3979,7 +3983,7 @@ class IndexNode(_IndexingBaseNode):
self.is_temp = True
self.index = self.index.coerce_to(function.type.args[0].type, env)
self.type = func_type.return_type
self.type = func_type.return_type.as_returned_type()
return self
def analyse_cyp_class_setitem(self, env):
......@@ -4019,7 +4023,7 @@ class IndexNode(_IndexingBaseNode):
env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
self.index = self.index.coerce_to(function.type.args[0].type, env)
self.type = func_type.return_type
self.type = func_type.return_type.as_returned_type()
return self
def analyse_as_c_function(self, env):
......@@ -6234,7 +6238,7 @@ class SimpleCallNode(CallNode):
else:
self.type = PyrexTypes.CPtrType(self.function.class_type)
else:
self.type = func_type.return_type
self.type = func_type.return_type.as_returned_type()
if self.function.is_name or self.function.is_attribute:
func_entry = self.function.entry
......@@ -6723,7 +6727,7 @@ class PythonCapiCallNode(SimpleCallNode):
def __init__(self, pos, function_name, func_type,
utility_code = None, py_name=None, **kwargs):
self.type = func_type.return_type
self.type = func_type.return_type.as_returned_type()
self.result_ctype = self.type
self.function = PythonCapiFunctionNode(
pos, py_name, function_name, func_type,
......@@ -11346,6 +11350,78 @@ class SizeofVarNode(SizeofNode):
pass
class ConsumeNode(ExprNode):
# Consume expression
#
# operand ExprNode
subexprs = ['operand']
generate_runtime_check = True
operand_is_named = True
def infer_type(self, env):
operand_type = self.operand.infer_type(env)
if operand_type.is_cyp_class:
return PyrexTypes.cyp_class_qualified_type(operand_type, 'iso~')
else:
return operand_type
def analyse_types(self, env):
self.operand = self.operand.analyse_types(env)
operand_type = self.operand.type
if not operand_type.is_cyp_class:
error(self.pos, "Can only consume cypclass")
self.type = PyrexTypes.error_type
return self
if self.operand.is_name or self.operand.is_attribute:
self.is_temp = self.operand_is_named = True
# We steal the reference of the operand.
self.use_managed_ref = False
if operand_type.is_qualified_cyp_class:
if operand_type.qualifier == 'iso!':
error(self.pos, "Cannot consume iso!")
self.type = PyrexTypes.error_type
return self
self.generate_runtime_check = operand_type.qualifier not in ('iso', 'iso~')
if operand_type.qualifier == 'iso~':
self.type = operand_type
else:
self.type = PyrexTypes.cyp_class_qualified_type(operand_type.qual_base_type, 'iso~')
else:
self.type = PyrexTypes.cyp_class_qualified_type(operand_type, 'iso~')
return self
def may_be_none(self):
return self.operand.may_be_none()
def is_simple(self):
return self.operand.is_simple()
def make_owned_reference(self, code):
# We steal the reference of the consumed operand.
pass
def calculate_result_code(self):
if self.generate_runtime_check:
# TODO: generate runtime check for isolation
return self.operand.result()
else:
return self.operand.result()
def generate_result_code(self, code):
if self.is_temp:
operand_result = self.operand.result()
code.putln("%s = %s;" % (self.result(), operand_result))
if self.generate_runtime_check:
# TODO: generate runtime check for isolation
pass
# We steal the reference of the operand.
code.putln("%s = NULL;" % operand_result)
if self.operand.is_temp:
# TODO: steal the reference of the operand instead
code.put_incref(self.result(), self.type)
class TypeidNode(ExprNode):
# C++ typeid operator applied to a type or variable
#
......@@ -11634,7 +11710,7 @@ class BinopNode(ExprNode):
else:
self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env)
self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env)
self.type = func_type.return_type
self.type = func_type.return_type.as_returned_type()
def result_type(self, type1, type2, env):
if self.is_pythran_operation_types(type1, type2, env):
......@@ -13330,7 +13406,7 @@ class PrimaryCmpNode(ExprNode, CmpNode):
else:
self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env)
self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env)
self.type = func_type.return_type
self.type = func_type.return_type.as_returned_type()
def analyse_memoryviewslice_comparison(self, env):
have_none = self.operand1.is_none or self.operand2.is_none
......
......@@ -309,6 +309,8 @@ def _p_factor(s):
return p_typecast(s)
elif sy == 'IDENT' and s.systring == "sizeof":
return p_sizeof(s)
elif sy == 'IDENT' and s.systring == "consume":
return p_consume(s)
return p_power(s)
def p_typecast(s):
......@@ -357,6 +359,13 @@ def p_sizeof(s):
s.expect(')')
return node
def p_consume(s):
# s.sy == ident "consume"
pos = s.position()
s.next()
operand = p_factor(s)
return ExprNodes.ConsumeNode(pos, operand = operand)
def p_yield_expression(s):
# s.sy == "yield"
......@@ -2535,7 +2544,7 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None):
base_type=base_type, is_const=is_const, is_volatile=is_volatile)
# Handle cypclass qualifiers
if s.sy == 'IDENT' and s.systring in ('active',):
if s.sy == 'IDENT' and s.systring in ('active', 'iso'):
qualifier = s.systring
s.next()
base_type = p_c_base_type(s, self_flag=self_flag, nonempty=nonempty, templates=templates)
......
......@@ -228,6 +228,9 @@ class PyrexType(BaseType):
# Coerces array and C function types into pointer type for use as
# a formal argument type.
#
# as_returned_type():
# Potentially annotates function call return types as pure rvalues.
#
is_pyobject = 0
is_unspecified = 0
......@@ -316,6 +319,9 @@ class PyrexType(BaseType):
def as_argument_type(self):
return self
def as_returned_type(self):
return self
def is_complete(self):
# A type is incomplete if it is an unsized array,
# a struct whose attributes are not defined, etc.
......@@ -648,6 +654,12 @@ class CheckedResultType(BaseType):
def as_argument_type(self):
return self.checked_base_type.as_argument_type()
def as_returned_type(self):
returned_base_type = self.checked_base_type.as_returned_type()
if returned_base_type.same_as(self.checked_base_type):
return self
return CheckedResultType(self.pos, returned_base_type)
def cast_code(self, expr_code):
return self.checked_base_type.cast_code(expr_code)
......@@ -1265,6 +1277,9 @@ class BufferType(BaseType):
def as_argument_type(self):
return self
def as_returned_type(self):
return self
def specialize(self, values):
dtype = self.dtype.specialize(values)
if dtype is not self.dtype:
......@@ -1956,6 +1971,9 @@ class QualifiedMethodType(BaseType):
def as_argument_type(self):
return c_ptr_type(self)
def as_returned_type(self):
return self
# All that follows is just to behave exactly like the underlying function type
def __getattr__(self, name):
......@@ -3000,6 +3018,13 @@ class CReferenceType(BaseType):
else:
return type(self)(base_type)
def as_returned_type(self):
returned_base_type = self.ref_base_type.as_returned_type()
if returned_base_type == self.ref_base_type:
return self
else:
return CReferenceType(returned_base_type)
def deduce_template_params(self, actual):
return self.ref_base_type.deduce_template_params(actual)
......@@ -4618,6 +4643,8 @@ class CypClassType(CppClassType):
def assignable_from_resolved_type(self, other_type):
if other_type.is_const_cyp_class:
return 0
if other_type.is_qualified_cyp_class and other_type.qualifier != 'iso~':
return 0
if other_type.is_ptr and other_type.base_type.is_cpp_class and other_type.base_type.is_subclass(self) or other_type.is_null_ptr:
return 1
return super(CypClassType, self).assignable_from_resolved_type(other_type)
......@@ -4733,6 +4760,9 @@ class ConstCypclassType(BaseType):
def as_argument_type(self):
return self
def as_returned_type(self):
return self
def deduce_template_params(self, actual):
return self.const_base_type.deduce_template_params(actual)
......@@ -4772,13 +4802,20 @@ class ConstCypclassType(BaseType):
class QualifiedCypclassType(BaseType):
"A qualified cypclass reference"
# qualifier string the qualifier keyword
# qualifier string the qualifier keyword: ('active' | 'iso' | 'iso~' | 'iso!' )
subtypes = ['qual_base_type']
is_cyp_class = 1
is_qualified_cyp_class = 1
assignable_to = {
'active': ('active', 'iso~'),
'iso': ('iso~',),
'iso~': (),
'iso!': (),
}
def __init__(self, base_type, qualifier):
assert base_type.is_cyp_class
self.qual_base_type = base_type
......@@ -4830,6 +4867,11 @@ class QualifiedCypclassType(BaseType):
def as_argument_type(self):
return self
def as_returned_type(self):
if self.qualifier == 'iso':
return QualifiedCypclassType(self.qual_base_type, 'iso~')
return self
def deduce_template_params(self, actual):
return self.qual_base_type.deduce_template_params(actual)
......@@ -4848,7 +4890,7 @@ class QualifiedCypclassType(BaseType):
return self.assignable_from_resolved_type(src_type.resolve())
def assignable_from_resolved_type(self, src_type):
if src_type.is_qualified_cyp_class and self.qualifier == src_type.qualifier:
if src_type.is_qualified_cyp_class and src_type.qualifier in self.assignable_to[self.qualifier]:
return self.qual_base_type.assignable_from_resolved_type(src_type.qual_base_type)
return 0
......
......@@ -3272,6 +3272,8 @@ class QualifiedCypclassScope(Scope):
def qualified_cypclass_scope(base_type_scope, qualifier):
if qualifier == 'active':
return ActiveCypclassScope(base_type_scope)
elif qualifier.startswith('iso'):
return IsoCypclassScope(base_type_scope)
else:
return QualifiedCypclassScope(base_type_scope, qualifier)
......@@ -3293,6 +3295,61 @@ class ActiveCypclassScope(QualifiedCypclassScope):
return base_entry.active_entry
class IsoCypclassScope(QualifiedCypclassScope):
def __init__(self, base_type_scope):
QualifiedCypclassScope.__init__(self, base_type_scope, 'iso')
def adapt(self, fieldtype, qualifier='iso'):
if fieldtype.is_qualified_cyp_class:
# attribute is sendable (either 'iso' or 'active')
return fieldtype
elif fieldtype.is_cyp_class:
# viewpoint adaptation
return PyrexTypes.cyp_class_qualified_type(fieldtype, qualifier)
else:
return fieldtype
def adapt_arg_type(self, arg):
arg = copy.copy(arg)
arg.type = self.adapt(arg.type)
return arg
def adapt_method_entry(self, base_entry):
iso_method_type = copy.copy(base_entry.type)
# The return type should be treated as 'iso' but cannot be consumed
return_type = self.adapt(base_entry.type.return_type, qualifier='iso!')
iso_method_type.return_type = return_type
iso_method_type.args = [self.adapt_arg_type(arg) for arg in base_entry.type.args]
if hasattr(base_entry.type, 'op_arg_struct'):
iso_method_type.op_arg_struct = entry.type.op_arg_struct
entry = copy.copy(base_entry)
entry.type = iso_method_type
return entry
def resolve(self, name):
base_entry = self.base_type_scope.lookup_here(name)
if base_entry is None:
return None
if base_entry.is_type:
return base_entry
if base_entry.is_cfunction:
iso_alternatives = []
for e in base_entry.all_alternatives():
iso_entry = self.adapt_method_entry(e)
iso_alternatives.append(iso_entry)
iso_entry.overloaded_alternatives = iso_alternatives
return iso_alternatives[0]
else:
base_entry_type = base_entry.type
adapted_type = self.adapt(base_entry_type)
if adapted_type is base_entry_type:
return base_entry
else:
entry = copy.copy(base_entry)
entry.type = adapted_type
return entry
class TemplateScope(Scope):
def __init__(self, name, outer_scope):
Scope.__init__(self, name, outer_scope, None)
......
......@@ -538,6 +538,8 @@ def simply_type(result_type, pos):
result_type = result_type.ref_base_type
if result_type.is_cv_qualified:
result_type = result_type.cv_base_type
if result_type.is_qualified_cyp_class and result_type.qualifier == 'iso~':
result_type = PyrexTypes.cyp_class_qualified_type(result_type.qual_base_type, 'iso')
if result_type.is_cpp_class:
result_type.check_nullary_constructor(pos)
if result_type.is_array:
......
......@@ -439,7 +439,7 @@
/*
* Check whether a CyObject is an instance of a given type.
*
*
* template:
* - T: the type
*/
......@@ -463,7 +463,7 @@
* Cast from CyObject to PyObject:
* - borrow an atomic reference
* - return a new Python reference
*
*
* Note: an optimisation could be to steal a reference but only decrement
* when Python already has a reference, because calls to this function
* are likely (certain even?) to be followed by a Cy_DECREF; stealing the
......@@ -488,11 +488,11 @@
* Cast from PyObject to CyObject:
* - borrow an Python reference
* - return a new atomic reference
*
*
* In case of conversion failure:
* - raise an exception
* - return NULL
*
*
* template:
* - U: the type of the underlying cypclass
*/
......@@ -791,7 +791,7 @@ void CyLock::unwlock() {
/*
* Atomic counter increment and decrement implementation based on
* @source: https://www.boost.org/doc/libs/1_73_0/doc/html/atomic/usage_examples.html
*/
*/
void CyObject::CyObject_INCREF() const
{
this->nogil_ob_refcnt.fetch_add(1, std::memory_order_relaxed);
......
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