summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeroen Demeyer <jdemeyer@cage.ugent.be>2017-04-10 10:49:52 +0200
committerJeroen Demeyer <jdemeyer@cage.ugent.be>2019-01-08 15:35:58 +0100
commitec5a056e51c46227f4797fee9ec846f17c8744d4 (patch)
tree2ddcf8cd0884aa788c6c3a88988871b4470102ec
parent4b64bbe1c792c0b0f40d97bb98e4192e28923ca2 (diff)
downloadcython-ec5a056e51c46227f4797fee9ec846f17c8744d4.tar.gz
Support for "volatile" keyword
-rw-r--r--Cython/Compiler/Code.py4
-rw-r--r--Cython/Compiler/ExprNodes.py8
-rw-r--r--Cython/Compiler/MemoryView.py2
-rw-r--r--Cython/Compiler/Nodes.py8
-rw-r--r--Cython/Compiler/Parsing.py29
-rw-r--r--Cython/Compiler/PyrexTypes.py122
-rw-r--r--Cython/Compiler/Symtab.py20
-rw-r--r--Cython/Compiler/TypeInference.py4
-rw-r--r--tests/compile/volatile.pyx17
-rw-r--r--tests/errors/const_decl_errors.pyx5
-rw-r--r--tests/errors/duplicate_const.pyx13
11 files changed, 158 insertions, 74 deletions
diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py
index 11463be2e..343a3a778 100644
--- a/Cython/Compiler/Code.py
+++ b/Cython/Compiler/Code.py
@@ -822,8 +822,8 @@ class FunctionState(object):
A C string referring to the variable is returned.
"""
- if type.is_const and not type.is_reference:
- type = type.const_base_type
+ if type.is_cv_qualified and not type.is_reference:
+ type = type.cv_base_type
elif type.is_reference and not type.is_fake_reference:
type = type.ref_base_type
if not type.is_pyobject and not type.is_memoryviewslice:
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py
index 45c677171..ca6fa950e 100644
--- a/Cython/Compiler/ExprNodes.py
+++ b/Cython/Compiler/ExprNodes.py
@@ -881,8 +881,8 @@ class ExprNode(Node):
if used_as_reference and not src_type.is_reference:
dst_type = dst_type.ref_base_type
- if src_type.is_const:
- src_type = src_type.const_base_type
+ if src_type.is_cv_qualified:
+ src_type = src_type.cv_base_type
if src_type.is_fused or dst_type.is_fused:
# See if we are coercing a fused function to a pointer to a
@@ -2868,8 +2868,8 @@ class NextNode(AtomicExprNode):
item_type = env.lookup_operator_for_types(self.pos, "*", [iterator_type]).type.return_type
if item_type.is_reference:
item_type = item_type.ref_base_type
- if item_type.is_const:
- item_type = item_type.const_base_type
+ if item_type.is_cv_qualified:
+ item_type = item_type.cv_base_type
return item_type
else:
# Avoid duplication of complicated logic.
diff --git a/Cython/Compiler/MemoryView.py b/Cython/Compiler/MemoryView.py
index 6ac1d88e4..995b0f83f 100644
--- a/Cython/Compiler/MemoryView.py
+++ b/Cython/Compiler/MemoryView.py
@@ -487,7 +487,7 @@ def copy_c_or_fortran_cname(memview):
def get_copy_new_utility(pos, from_memview, to_memview):
if (from_memview.dtype != to_memview.dtype and
- not (from_memview.dtype.is_const and from_memview.dtype.const_base_type == to_memview.dtype)):
+ not (from_memview.dtype.is_cv_qualified and from_memview.dtype.cv_base_type == to_memview.dtype)):
error(pos, "dtypes must be the same!")
return
if len(from_memview.axes) != len(to_memview.axes):
diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py
index 38183481d..752c39e90 100644
--- a/Cython/Compiler/Nodes.py
+++ b/Cython/Compiler/Nodes.py
@@ -1273,8 +1273,10 @@ class FusedTypeNode(CBaseTypeNode):
return PyrexTypes.FusedType(types, name=self.name)
-class CConstTypeNode(CBaseTypeNode):
+class CConstOrVolatileTypeNode(CBaseTypeNode):
# base_type CBaseTypeNode
+ # is_const boolean
+ # is_volatile boolean
child_attrs = ["base_type"]
@@ -1282,8 +1284,8 @@ class CConstTypeNode(CBaseTypeNode):
base = self.base_type.analyse(env, could_be_name)
if base.is_pyobject:
error(self.pos,
- "Const base type cannot be a Python object")
- return PyrexTypes.c_const_type(base)
+ "Const/volatile base type cannot be a Python object")
+ return PyrexTypes.c_const_or_volatile_type(base, self.is_const, self.is_volatile)
class CVarDefNode(StatNode):
diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py
index 2eda2bd07..1da626a6a 100644
--- a/Cython/Compiler/Parsing.py
+++ b/Cython/Compiler/Parsing.py
@@ -317,8 +317,8 @@ def p_typecast(s):
base_type = p_c_base_type(s)
is_memslice = isinstance(base_type, Nodes.MemoryViewSliceTypeNode)
is_template = isinstance(base_type, Nodes.TemplatedTypeNode)
- is_const = isinstance(base_type, Nodes.CConstTypeNode)
- if (not is_memslice and not is_template and not is_const
+ is_const_volatile = isinstance(base_type, Nodes.CConstOrVolatileTypeNode)
+ if (not is_memslice and not is_template and not is_const_volatile
and base_type.name is None):
s.error("Unknown type")
declarator = p_c_declarator(s, empty = 1)
@@ -2479,16 +2479,31 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None):
complex = 0
module_path = []
pos = s.position()
- if not s.sy == 'IDENT':
- error(pos, "Expected an identifier, found '%s'" % s.sy)
- if s.systring == 'const':
+
+ # Handle const/volatile
+ is_const = is_volatile = 0
+ while True:
+ if s.systring == 'const':
+ if is_const: error(pos, "Duplicate 'const'")
+ is_const = 1
+ elif s.systring == 'volatile':
+ if is_volatile: error(pos, "Duplicate 'volatile'")
+ is_volatile = 1
+ else:
+ break
s.next()
+ if is_const or is_volatile:
base_type = p_c_base_type(s, self_flag=self_flag, nonempty=nonempty, templates=templates)
if isinstance(base_type, Nodes.MemoryViewSliceTypeNode):
# reverse order to avoid having to write "(const int)[:]"
- base_type.base_type_node = Nodes.CConstTypeNode(pos, base_type=base_type.base_type_node)
+ base_type.base_type_node = Nodes.CConstOrVolatileTypeNode(pos,
+ base_type=base_type.base_type_node, is_const=is_const, is_volatile=is_volatile)
return base_type
- return Nodes.CConstTypeNode(pos, base_type=base_type)
+ return Nodes.CConstOrVolatileTypeNode(pos,
+ base_type=base_type, is_const=is_const, is_volatile=is_volatile)
+
+ if s.sy != 'IDENT':
+ error(pos, "Expected an identifier, found '%s'" % s.sy)
if looking_at_base_type(s):
#print "p_c_simple_base_type: looking_at_base_type at", s.position()
is_basic = 1
diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py
index 2b604bd27..b2e376f43 100644
--- a/Cython/Compiler/PyrexTypes.py
+++ b/Cython/Compiler/PyrexTypes.py
@@ -176,7 +176,9 @@ class PyrexType(BaseType):
# is_ptr boolean Is a C pointer type
# is_null_ptr boolean Is the type of NULL
# is_reference boolean Is a C reference type
- # is_const boolean Is a C const type.
+ # is_const boolean Is a C const type
+ # is_volatile boolean Is a C volatile type
+ # is_cv_qualified boolean Is a C const or volatile type
# is_cfunction boolean Is a C function type
# is_struct_or_union boolean Is a C struct or union type
# is_struct boolean Is a C struct type
@@ -236,6 +238,8 @@ class PyrexType(BaseType):
is_null_ptr = 0
is_reference = 0
is_const = 0
+ is_volatile = 0
+ is_cv_qualified = 0
is_cfunction = 0
is_struct_or_union = 0
is_cpp_class = 0
@@ -713,8 +717,8 @@ class MemoryViewSliceType(PyrexType):
to_axes_f = contig_dim + follow_dim * (ndim -1)
dtype = self.dtype
- if dtype.is_const:
- dtype = dtype.const_base_type
+ if dtype.is_cv_qualified:
+ dtype = dtype.cv_base_type
to_memview_c = MemoryViewSliceType(dtype, to_axes_c)
to_memview_f = MemoryViewSliceType(dtype, to_axes_f)
@@ -791,15 +795,18 @@ class MemoryViewSliceType(PyrexType):
# return False
src_dtype, dst_dtype = src.dtype, dst.dtype
- if dst_dtype.is_const:
- # Requesting read-only views is always ok => consider only the non-const base type.
- dst_dtype = dst_dtype.const_base_type
- if src_dtype.is_const:
- # When assigning between read-only views, compare only the non-const base types.
- src_dtype = src_dtype.const_base_type
- elif copying and src_dtype.is_const:
- # Copying by value => ignore const on source.
- src_dtype = src_dtype.const_base_type
+ # We can add but not remove const/volatile modifiers
+ # (except if we are copying by value, then anything is fine)
+ if not copying:
+ if src_dtype.is_const and not dst_dtype.is_const:
+ return False
+ if src_dtype.is_volatile and not dst_dtype.is_volatile:
+ return False
+ # const/volatile checks are done, remove those qualifiers
+ if src_dtype.is_cv_qualified:
+ src_dtype = src_dtype.cv_base_type
+ if dst_dtype.is_cv_qualified:
+ dst_dtype = dst_dtype.cv_base_type
if src_dtype != dst_dtype:
return False
@@ -1558,58 +1565,74 @@ class PythranExpr(CType):
return hash(self.pythran_type)
-class CConstType(BaseType):
+class CConstOrVolatileType(BaseType):
+ "A C const or volatile type"
- is_const = 1
+ is_cv_qualified = 1
- def __init__(self, const_base_type):
- self.const_base_type = const_base_type
- if const_base_type.has_attributes and const_base_type.scope is not None:
- from . import Symtab
- self.scope = Symtab.CConstScope(const_base_type.scope)
+ def __init__(self, base_type, is_const=0, is_volatile=0):
+ self.cv_base_type = base_type
+ self.is_const = is_const
+ self.is_volatile = is_volatile
+ if base_type.has_attributes and base_type.scope is not None:
+ from .Symtab import CConstOrVolatileScope
+ self.scope = CConstOrVolatileScope(base_type.scope, is_const, is_volatile)
+
+ def cv_string(self):
+ cvstring = ""
+ if self.is_const:
+ cvstring = "const " + cvstring
+ if self.is_volatile:
+ cvstring = "volatile " + cvstring
+ return cvstring
def __repr__(self):
- return "<CConstType %s>" % repr(self.const_base_type)
+ return "<CConstOrVolatileType %s%r>" % (self.cv_string(), self.cv_base_type)
def __str__(self):
return self.declaration_code("", for_display=1)
def declaration_code(self, entity_code,
for_display = 0, dll_linkage = None, pyrex = 0):
+ cv = self.cv_string()
if for_display or pyrex:
- return "const " + self.const_base_type.declaration_code(entity_code, for_display, dll_linkage, pyrex)
+ return cv + self.cv_base_type.declaration_code(entity_code, for_display, dll_linkage, pyrex)
else:
- return self.const_base_type.declaration_code("const %s" % entity_code, for_display, dll_linkage, pyrex)
+ return self.cv_base_type.declaration_code(cv + entity_code, for_display, dll_linkage, pyrex)
def specialize(self, values):
- base_type = self.const_base_type.specialize(values)
- if base_type == self.const_base_type:
+ base_type = self.cv_base_type.specialize(values)
+ if base_type == self.cv_base_type:
return self
- else:
- return CConstType(base_type)
+ return CConstOrVolatileType(base_type,
+ self.is_const, self.is_volatile)
def deduce_template_params(self, actual):
- return self.const_base_type.deduce_template_params(actual)
+ return self.cv_base_type.deduce_template_params(actual)
def can_coerce_to_pyobject(self, env):
- return self.const_base_type.can_coerce_to_pyobject(env)
+ return self.cv_base_type.can_coerce_to_pyobject(env)
def can_coerce_from_pyobject(self, env):
- return self.const_base_type.can_coerce_from_pyobject(env)
+ return self.cv_base_type.can_coerce_from_pyobject(env)
def create_to_py_utility_code(self, env):
- if self.const_base_type.create_to_py_utility_code(env):
- self.to_py_function = self.const_base_type.to_py_function
+ if self.cv_base_type.create_to_py_utility_code(env):
+ self.to_py_function = self.cv_base_type.to_py_function
return True
def same_as_resolved_type(self, other_type):
- if other_type.is_const:
- return self.const_base_type.same_as_resolved_type(other_type.const_base_type)
- # Accept const LHS <- non-const RHS.
- return self.const_base_type.same_as_resolved_type(other_type)
+ if other_type.is_cv_qualified:
+ return self.cv_base_type.same_as_resolved_type(other_type.cv_base_type)
+ # Accept cv LHS <- non-cv RHS.
+ return self.cv_base_type.same_as_resolved_type(other_type)
def __getattr__(self, name):
- return getattr(self.const_base_type, name)
+ return getattr(self.cv_base_type, name)
+
+
+def CConstType(base_type):
+ return CConstOrVolatileType(base_type, is_const=1)
class FusedType(CType):
@@ -2302,8 +2325,8 @@ class CPointerBaseType(CType):
def __init__(self, base_type):
self.base_type = base_type
- if base_type.is_const:
- base_type = base_type.const_base_type
+ if base_type.is_cv_qualified:
+ base_type = base_type.cv_base_type
for char_type in (c_char_type, c_uchar_type, c_schar_type):
if base_type.same_as(char_type):
self.is_string = 1
@@ -2527,8 +2550,8 @@ class CPtrType(CPointerBaseType):
return 1
if other_type.is_null_ptr:
return 1
- if self.base_type.is_const:
- self = CPtrType(self.base_type.const_base_type)
+ if self.base_type.is_cv_qualified:
+ self = CPtrType(self.base_type.cv_base_type)
if self.base_type.is_cfunction:
if other_type.is_ptr:
other_type = other_type.base_type.resolve()
@@ -3709,8 +3732,8 @@ class CppClassType(CType):
return specialized
def deduce_template_params(self, actual):
- if actual.is_const:
- actual = actual.const_base_type
+ if actual.is_cv_qualified:
+ actual = actual.cv_base_type
if actual.is_reference:
actual = actual.ref_base_type
if self == actual:
@@ -4452,10 +4475,10 @@ def widest_numeric_type(type1, type2):
type1 = type1.ref_base_type
if type2.is_reference:
type2 = type2.ref_base_type
- if type1.is_const:
- type1 = type1.const_base_type
- if type2.is_const:
- type2 = type2.const_base_type
+ if type1.is_cv_qualified:
+ type1 = type1.cv_base_type
+ if type2.is_cv_qualified:
+ type2 = type2.cv_base_type
if type1 == type2:
widest_type = type1
elif type1.is_complex or type2.is_complex:
@@ -4675,6 +4698,13 @@ def c_const_type(base_type):
else:
return CConstType(base_type)
+def c_const_or_volatile_type(base_type, is_const, is_volatile):
+ # Construct a C const/volatile type.
+ if base_type is error_type:
+ return error_type
+ else:
+ return CConstOrVolatileType(base_type, is_const, is_volatile)
+
def same_type(type1, type2):
return type1.same_as(type2)
diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py
index dff22b7ae..5dbb205fa 100644
--- a/Cython/Compiler/Symtab.py
+++ b/Cython/Compiler/Symtab.py
@@ -2523,23 +2523,27 @@ class PropertyScope(Scope):
return None
-class CConstScope(Scope):
+class CConstOrVolatileScope(Scope):
- def __init__(self, const_base_type_scope):
+ def __init__(self, base_type_scope, is_const=0, is_volatile=0):
Scope.__init__(
self,
- 'const_' + const_base_type_scope.name,
- const_base_type_scope.outer_scope,
- const_base_type_scope.parent_scope)
- self.const_base_type_scope = const_base_type_scope
+ 'cv_' + base_type_scope.name,
+ base_type_scope.outer_scope,
+ base_type_scope.parent_scope)
+ self.base_type_scope = base_type_scope
+ self.is_const = is_const
+ self.is_volatile = is_volatile
def lookup_here(self, name):
- entry = self.const_base_type_scope.lookup_here(name)
+ entry = self.base_type_scope.lookup_here(name)
if entry is not None:
entry = copy.copy(entry)
- entry.type = PyrexTypes.c_const_type(entry.type)
+ entry.type = PyrexTypes.c_const_or_volatile_type(
+ entry.type, self.is_const, self.is_volatile)
return entry
+
class TemplateScope(Scope):
def __init__(self, name, outer_scope):
Scope.__init__(self, name, outer_scope, None)
diff --git a/Cython/Compiler/TypeInference.py b/Cython/Compiler/TypeInference.py
index c7ffee7d2..6f89b134b 100644
--- a/Cython/Compiler/TypeInference.py
+++ b/Cython/Compiler/TypeInference.py
@@ -533,8 +533,8 @@ def find_spanning_type(type1, type2):
def simply_type(result_type, pos):
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_cv_qualified:
+ result_type = result_type.cv_base_type
if result_type.is_cpp_class:
result_type.check_nullary_constructor(pos)
if result_type.is_array:
diff --git a/tests/compile/volatile.pyx b/tests/compile/volatile.pyx
new file mode 100644
index 000000000..d69d8b355
--- /dev/null
+++ b/tests/compile/volatile.pyx
@@ -0,0 +1,17 @@
+# mode: compile
+
+cdef volatile int x = 1
+
+cdef const volatile char* greeting1 = "hello world"
+cdef volatile const char* greeting2 = "goodbye"
+
+
+cdef extern from "stdlib.h":
+ volatile void* malloc(size_t)
+
+cdef volatile long* test(volatile size_t s):
+ cdef volatile long* arr = <long*><volatile long*>malloc(s)
+ return arr
+
+
+test(64)
diff --git a/tests/errors/const_decl_errors.pyx b/tests/errors/const_decl_errors.pyx
index 459480adb..184cdc60c 100644
--- a/tests/errors/const_decl_errors.pyx
+++ b/tests/errors/const_decl_errors.pyx
@@ -19,9 +19,11 @@ cdef func(const int a, const int* b, const (int*) c, const S s, int *const d,
d = NULL
t = &s
+cdef volatile object v
+
_ERRORS = """
-3:5: Const base type cannot be a Python object
+3:5: Const/volatile base type cannot be a Python object
8:5: Assignment to const 'x'
15:4: Assignment to const 'a'
16:4: Assignment to const 'c'
@@ -29,4 +31,5 @@ _ERRORS = """
18:5: Assignment to const attribute 'member'
19:4: Assignment to const 'd'
20:4: Assignment to const 't'
+22:5: Const/volatile base type cannot be a Python object
"""
diff --git a/tests/errors/duplicate_const.pyx b/tests/errors/duplicate_const.pyx
new file mode 100644
index 000000000..1367dd13b
--- /dev/null
+++ b/tests/errors/duplicate_const.pyx
@@ -0,0 +1,13 @@
+# mode: error
+
+cdef extern from *:
+ cdef const const int a
+ cdef const volatile int b
+ cdef volatile const int c
+ cdef volatile volatile int d
+
+
+_ERRORS = """
+4:9: Duplicate 'const'
+7:9: Duplicate 'volatile'
+"""