diff options
author | Stefan Behnel <stefan_ml@behnel.de> | 2019-01-08 18:36:10 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-08 18:36:10 +0100 |
commit | 7d7e041380d8b8dcf3990cf9b6afc881f8f854b4 (patch) | |
tree | 60f67c65576405f9a921dbe896d583ac6956f9bd | |
parent | 4ee7ae7769cc870637f77cd424152b1bacc7f987 (diff) | |
parent | ec5a056e51c46227f4797fee9ec846f17c8744d4 (diff) | |
download | cython-7d7e041380d8b8dcf3990cf9b6afc881f8f854b4.tar.gz |
Merge pull request #1667 from jdemeyer/volatile
Support for "volatile" keyword
-rw-r--r-- | Cython/Compiler/Code.py | 4 | ||||
-rw-r--r-- | Cython/Compiler/ExprNodes.py | 8 | ||||
-rw-r--r-- | Cython/Compiler/MemoryView.py | 2 | ||||
-rw-r--r-- | Cython/Compiler/Nodes.py | 8 | ||||
-rw-r--r-- | Cython/Compiler/Parsing.py | 29 | ||||
-rw-r--r-- | Cython/Compiler/PyrexTypes.py | 122 | ||||
-rw-r--r-- | Cython/Compiler/Symtab.py | 20 | ||||
-rw-r--r-- | Cython/Compiler/TypeInference.py | 4 | ||||
-rw-r--r-- | tests/compile/volatile.pyx | 17 | ||||
-rw-r--r-- | tests/errors/const_decl_errors.pyx | 5 | ||||
-rw-r--r-- | tests/errors/duplicate_const.pyx | 13 |
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' +""" |