summaryrefslogtreecommitdiff
path: root/Cython/Compiler/PyrexTypes.py
diff options
context:
space:
mode:
Diffstat (limited to 'Cython/Compiler/PyrexTypes.py')
-rw-r--r--Cython/Compiler/PyrexTypes.py99
1 files changed, 49 insertions, 50 deletions
diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py
index c773f5c5a..da30809a3 100644
--- a/Cython/Compiler/PyrexTypes.py
+++ b/Cython/Compiler/PyrexTypes.py
@@ -13,6 +13,7 @@ try:
except NameError:
from functools import reduce
from functools import partial
+from itertools import product
from Cython.Utils import cached_function
from .Code import UtilityCode, LazyUtilityCode, TempitaUtilityCode
@@ -205,6 +206,7 @@ class PyrexType(BaseType):
# needs_cpp_construction boolean Needs C++ constructor and destructor when used in a cdef class
# needs_refcounting boolean Needs code to be generated similar to incref/gotref/decref.
# Largely used internally.
+ # equivalent_type type A C or Python type that is equivalent to this Python or C type.
# default_value string Initial value that can be assigned before first user assignment.
# declaration_value string The value statically assigned on declaration (if any).
# entry Entry The Entry for this type
@@ -277,6 +279,7 @@ class PyrexType(BaseType):
has_attributes = 0
needs_cpp_construction = 0
needs_refcounting = 0
+ equivalent_type = None
default_value = ""
declaration_value = ""
@@ -1504,7 +1507,6 @@ class PyExtensionType(PyObjectType):
#
# name string
# scope CClassScope Attribute namespace
- # visibility string
# typedef_flag boolean
# base_type PyExtensionType or None
# module_name string or None Qualified name of defining module
@@ -1518,8 +1520,10 @@ class PyExtensionType(PyObjectType):
# vtable_cname string Name of C method table definition
# early_init boolean Whether to initialize early (as opposed to during module execution).
# defered_declarations [thunk] Used to declare class hierarchies in order
+ # is_external boolean Defined in a extern block
# check_size 'warn', 'error', 'ignore' What to do if tp_basicsize does not match
# dataclass_fields OrderedDict nor None Used for inheriting from dataclasses
+ # multiple_bases boolean Does this class have multiple bases
is_extension_type = 1
has_attributes = 1
@@ -1527,6 +1531,7 @@ class PyExtensionType(PyObjectType):
objtypedef_cname = None
dataclass_fields = None
+ multiple_bases = False
def __init__(self, name, typedef_flag, base_type, is_external=0, check_size=None):
self.name = name
@@ -1833,7 +1838,27 @@ class FusedType(CType):
for t in types:
if t.is_fused:
# recursively merge in subtypes
- for subtype in t.types:
+ if isinstance(t, FusedType):
+ t_types = t.types
+ else:
+ # handle types that aren't a fused type themselves but contain fused types
+ # for example a C++ template where the template type is fused.
+ t_fused_types = t.get_fused_types()
+ t_types = []
+ for substitution in product(
+ *[fused_type.types for fused_type in t_fused_types]
+ ):
+ t_types.append(
+ t.specialize(
+ {
+ fused_type: sub
+ for fused_type, sub in zip(
+ t_fused_types, substitution
+ )
+ }
+ )
+ )
+ for subtype in t_types:
if subtype not in flattened_types:
flattened_types.append(subtype)
elif t not in flattened_types:
@@ -2788,6 +2813,8 @@ class CReferenceBaseType(BaseType):
# Common base type for C reference and C++ rvalue reference types.
+ subtypes = ['ref_base_type']
+
def __init__(self, base_type):
self.ref_base_type = base_type
@@ -3044,6 +3071,9 @@ class CFuncType(CType):
# must catch C++ exceptions if we raise them
return 0
if not other_type.exception_check or other_type.exception_value is not None:
+ # There's no problem if this type doesn't emit exceptions but the other type checks
+ if other_type.exception_check and not (self.exception_check or self.exception_value):
+ return 1
# if other does not *always* check exceptions, self must comply
if not self._same_exception_value(other_type.exception_value):
return 0
@@ -3129,8 +3159,10 @@ class CFuncType(CType):
if (pyrex or for_display) and not self.return_type.is_pyobject:
if self.exception_value and self.exception_check:
trailer = " except? %s" % self.exception_value
- elif self.exception_value:
+ elif self.exception_value and not self.exception_check:
trailer = " except %s" % self.exception_value
+ elif not self.exception_value and not self.exception_check:
+ trailer = " noexcept"
elif self.exception_check == '+':
trailer = " except +"
elif self.exception_check and for_display:
@@ -4429,6 +4461,7 @@ class ErrorType(PyrexType):
class PythonTypeConstructor(PyObjectType):
"""Used to help Cython interpret indexed types from the typing module (or similar)
"""
+ modifier_name = None
def __init__(self, name, base_type=None):
self.python_type_constructor_name = name
@@ -4457,69 +4490,35 @@ class PythonTupleTypeConstructor(PythonTypeConstructor):
not any(v.is_pyobject for v in template_values)):
entry = env.declare_tuple_type(pos, template_values)
if entry:
+ entry.used = True
return entry.type
return super(PythonTupleTypeConstructor, self).specialize_here(pos, env, template_values)
class SpecialPythonTypeConstructor(PythonTypeConstructor):
"""
- For things like ClassVar, Optional, etc, which have extra features on top of being
- a "templated" type.
+ For things like ClassVar, Optional, etc, which are not types and disappear during type analysis.
"""
- def __init__(self, name, template_type=None):
- super(SpecialPythonTypeConstructor, self).__init__(name, None)
- if (name == "typing.ClassVar" and template_type
- and not template_type.is_pyobject):
- # because classvars end up essentially used as globals they have
- # to be PyObjects. Try to find the nearest suitable type (although
- # practically I doubt this matters).
- py_type_name = template_type.py_type_name()
- if py_type_name:
- from .Builtin import builtin_scope
- template_type = (builtin_scope.lookup_type(py_type_name)
- or py_object_type)
- else:
- template_type = py_object_types
- self.template_type = template_type
+ def __init__(self, name):
+ super(SpecialPythonTypeConstructor, self).__init__(name, base_type=None)
+ self.modifier_name = name
def __repr__(self):
- if self.template_type:
- return "%s[%r]" % (self.name, self.template_type)
- else:
- return self.name
-
- def is_template_type(self):
- return self.template_type is None
+ return self.name
def resolve(self):
- if self.template_type:
- return self.template_type.resolve()
- else:
- return self
+ return self
def specialize_here(self, pos, env, template_values=None):
if len(template_values) != 1:
error(pos, "'%s' takes exactly one template argument." % self.name)
- # return a copy of the template type with python_type_constructor_name as an attribute
- # so it can be identified, and a resolve function that gets back to
- # the original type (since types are usually tested with "is")
- new_type = template_values[0]
- if self.python_type_constructor_name == "typing.ClassVar":
- # classvar must remain a py_object_type
- new_type = py_object_type
- if (self.python_type_constructor_name == "typing.Optional" and
- not new_type.is_pyobject):
- # optional must be a py_object, but can be a specialized py_object
- new_type = py_object_type
- return SpecialPythonTypeConstructor(
- self.python_type_constructor_name,
- template_type = template_values[0])
-
- def __getattr__(self, name):
- if self.template_type:
- return getattr(self.template_type, name)
- return super(SpecialPythonTypeConstructor, self).__getattr__(name)
+ return error_type
+ if template_values[0] is None:
+ # FIXME: allowing unknown types for now since we don't recognise all Python types.
+ return None
+ # Replace this type with the actual 'template' argument.
+ return template_values[0].resolve()
rank_to_type_name = (