Skip to content
Browse files

Merge branch 'const'

  • Loading branch information...
2 parents eae1ada + 84ce803 commit 8c4a629904de8c21cd655bb63d8e1ea1ab89142f @robertwb robertwb committed Sep 18, 2012
View
13 Cython/Compiler/ExprNodes.py
@@ -618,6 +618,9 @@ def coerce_to(self, dst_type, env):
if dst_type.is_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_fused or dst_type.is_fused:
# See if we are coercing a fused function to a pointer to a
# specialized function
@@ -1511,6 +1514,10 @@ def analyse_target_types(self, env):
self.entry = self.entry.as_variable
self.type = self.entry.type
+ if self.type.is_const:
+ error(self.pos, "Assignment to const '%s'" % self.name)
+ if self.type.is_reference:
+ error(self.pos, "Assignment to reference '%s'" % self.name)
if not self.is_lvalue():
error(self.pos, "Assignment to non-lvalue '%s'"
% self.name)
@@ -2233,6 +2240,8 @@ def infer_type(self, env, iterator_type = None):
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
return item_type
else:
# Avoid duplication of complicated logic.
@@ -2598,6 +2607,8 @@ def analyse_types(self, env):
def analyse_target_types(self, env):
self.analyse_base_and_index_types(env, setting = 1)
+ if self.type.is_const:
+ error(self.pos, "Assignment to const dereference")
if not self.is_lvalue():
error(self.pos, "Assignment to non-lvalue of type '%s'" % self.type)
@@ -4453,6 +4464,8 @@ def analyse_target_declaration(self, env):
def analyse_target_types(self, env):
self.analyse_types(env, target = 1)
+ if self.type.is_const:
+ error(self.pos, "Assignment to const attribute '%s'" % self.attribute)
if not self.is_lvalue():
error(self.pos, "Assignment to non-lvalue of type '%s'" % self.type)
View
16 Cython/Compiler/Nodes.py
@@ -1048,6 +1048,19 @@ def analyse(self, env):
return PyrexTypes.FusedType(types, name=self.name)
+class CConstTypeNode(CBaseTypeNode):
+ # base_type CBaseTypeNode
+
+ child_attrs = ["base_type"]
+
+ def analyse(self, env, could_be_name = False):
+ 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)
+
+
class CVarDefNode(StatNode):
# C variable definition or forward/extern function declaration.
#
@@ -1941,6 +1954,7 @@ class CFuncDefNode(FuncDefNode):
# overridable whether or not this is a cpdef function
# inline_in_pxd whether this is an inline function in a pxd file
# template_declaration String or None Used for c++ class methods
+ # is_const_method whether this is a const method
child_attrs = ["base_type", "declarator", "body", "py_func"]
@@ -1950,6 +1964,7 @@ class CFuncDefNode(FuncDefNode):
directive_returns = None
override = None
template_declaration = None
+ is_const_method = False
def unqualified_name(self):
return self.entry.name
@@ -2021,6 +2036,7 @@ def analyse_declarations(self, env):
name = name_declarator.name
cname = name_declarator.cname
+ type.is_const_method = self.is_const_method
self.entry = env.declare_cfunction(
name, type, self.pos,
cname = cname, visibility = self.visibility, api = self.api,
View
13 Cython/Compiler/Parsing.py
@@ -1982,6 +1982,11 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None):
pos = s.position()
if not s.sy == 'IDENT':
error(pos, "Expected an identifier, found '%s'" % s.sy)
+ if s.systring == 'const':
+ s.next()
+ base_type = p_c_base_type(s,
+ self_flag = self_flag, nonempty = nonempty, templates = templates)
+ return Nodes.CConstTypeNode(pos, base_type = base_type)
if looking_at_base_type(s):
#print "p_c_simple_base_type: looking_at_base_type at", s.position()
is_basic = 1
@@ -2703,6 +2708,11 @@ def p_c_func_or_var_declaration(s, pos, ctx):
declarator = p_c_declarator(s, ctx, cmethod_flag = cmethod_flag,
assignable = 1, nonempty = 1)
declarator.overridable = ctx.overridable
+ if s.sy == 'IDENT' and s.systring == 'const' and ctx.level == 'cpp_class':
+ s.next()
+ is_const_method = 1
+ else:
+ is_const_method = 0
if s.sy == ':':
if ctx.level not in ('module', 'c_class', 'module_pxd', 'c_class_pxd', 'cpp_class') and not ctx.templates:
s.error("C function definition not allowed here")
@@ -2715,7 +2725,8 @@ def p_c_func_or_var_declaration(s, pos, ctx):
doc = doc,
modifiers = modifiers,
api = ctx.api,
- overridable = ctx.overridable)
+ overridable = ctx.overridable,
+ is_const_method = is_const_method)
else:
#if api:
# s.error("'api' not allowed with variable declaration")
View
62 Cython/Compiler/PyrexTypes.py
@@ -138,6 +138,7 @@ 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_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
@@ -192,6 +193,7 @@ class PyrexType(BaseType):
is_ptr = 0
is_null_ptr = 0
is_reference = 0
+ is_const = 0
is_cfunction = 0
is_struct_or_union = 0
is_cpp_class = 0
@@ -1151,6 +1153,42 @@ def error_condition(self, result_code):
return 0
+class CConstType(BaseType):
+
+ is_const = 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:
+ import Symtab
+ self.scope = Symtab.CConstScope(const_base_type.scope)
+
+ def __repr__(self):
+ return "<CConstType %s>" % repr(self.const_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):
+ return self.const_base_type.declaration_code("const %s" % 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:
+ return self
+ else:
+ return ConstType(base_type)
+
+ 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
+ return True
+
+ def __getattr__(self, name):
+ return getattr(self.const_base_type, name)
+
+
class FusedType(CType):
"""
Represents a Fused Type. All it needs to do is keep track of the types
@@ -2281,6 +2319,8 @@ def assignable_from_resolved_type(self, other_type):
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_cfunction:
if other_type.is_ptr:
other_type = other_type.base_type.resolve()
@@ -2328,9 +2368,6 @@ def __repr__(self):
def __str__(self):
return "%s &" % self.ref_base_type
- def as_argument_type(self):
- return self
-
def declaration_code(self, entity_code,
for_display = 0, dll_linkage = None, pyrex = 0):
#print "CReferenceType.declaration_code: pointer to", self.base_type ###
@@ -2364,11 +2401,13 @@ class CFuncType(CType):
# C function
# is_strict_signature boolean function refuses to accept coerced arguments
# (used for optimisation overrides)
+ # is_const_method boolean
is_cfunction = 1
original_sig = None
cached_specialized_types = None
from_fused = False
+ is_const_method = False
subtypes = ['return_type', 'args']
@@ -2575,13 +2614,19 @@ def declaration_code(self, entity_code,
if (not entity_code and cc) or entity_code.startswith("*"):
entity_code = "(%s%s)" % (cc, entity_code)
cc = ""
+ if self.is_const_method:
+ trailer += " const"
return self.return_type.declaration_code(
"%s%s(%s)%s" % (cc, entity_code, arg_decl_code, trailer),
for_display, dll_linkage, pyrex)
def function_header_code(self, func_name, arg_code):
- return "%s%s(%s)" % (self.calling_convention_prefix(),
- func_name, arg_code)
+ if self.is_const_method:
+ trailer = " const"
+ else:
+ trailer = ""
+ return "%s%s(%s)%s" % (self.calling_convention_prefix(),
+ func_name, arg_code, trailer)
def signature_string(self):
s = self.declaration_code("")
@@ -3802,6 +3847,13 @@ def c_ref_type(base_type):
else:
return CReferenceType(base_type)
+def c_const_type(base_type):
+ # Construct a C const type.
+ if base_type is error_type:
+ return error_type
+ else:
+ return CConstType(base_type)
+
def same_type(type1, type2):
return type1.same_as(type2)
View
18 Cython/Compiler/Symtab.py
@@ -2,6 +2,7 @@
# Symbol Table
#
+import copy
import re
from Errors import warning, error, InternalError
from StringEncoding import EncodedString
@@ -2151,3 +2152,20 @@ def declare_pyfunction(self, name, pos, allow_redefine=False):
error(pos, "Only __get__, __set__ and __del__ methods allowed "
"in a property declaration")
return None
+
+class CConstScope(Scope):
+
+ def __init__(self, const_base_type_scope):
+ 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
+
+ def lookup_here(self, name):
+ entry = self.const_base_type_scope.lookup_here(name)
+ if entry is not None:
+ entry = copy.copy(entry)
+ entry.type = PyrexTypes.c_const_type(entry.type)
+ return entry
View
4 Cython/Compiler/TypeInference.py
@@ -442,10 +442,14 @@ def aggressive_spanning_type(types, might_overflow):
result_type = reduce(find_spanning_type, types)
if result_type.is_reference:
result_type = result_type.ref_base_type
+ if result_type.is_const:
+ result_type = result_type.const_base_type
return result_type
def safe_spanning_type(types, might_overflow):
result_type = reduce(find_spanning_type, types)
+ if result_type.is_const:
+ result_type = result_type.const_base_type
if result_type.is_reference:
result_type = result_type.ref_base_type
if result_type.is_pyobject:
View
7 tests/compile/const_decl.pyx
@@ -0,0 +1,7 @@
+# mode: compile
+
+cdef const_args(const int a, const int *b, const (int*) c):
+ print a
+ print b[0]
+ b = NULL # OK, the pointer itself is not const
+ c[0] = 4 # OK, the value is not const
View
21 tests/errors/const_decl_errors.pyx
@@ -0,0 +1,21 @@
+# mode: error
+
+cdef const object o
+
+# TODO: This requires making the assignment at declaration time.
+# (We could fake this case by dropping the const here in the C code,
+# as it's not needed for agreeing with external libraries.
+cdef const int x = 10
+
+cdef func(const int a, const int* b, const (int*) c):
+ a = 10
+ b[0] = 100
+ c = NULL
+
+_ERRORS = """
+3:5: Const base type cannot be a Python object
+8:5: Assignment to const 'x'
+11:6: Assignment to const 'a'
+12:5: Assignment to const dereference
+13:6: Assignment to const 'c'
+"""
View
86 tests/run/cpp_const_method.pyx
@@ -0,0 +1,86 @@
+# cython: experimental_cpp_class_def=True
+# tag: cpp
+
+from libcpp.vector cimport vector
+
+cdef cppclass Wrapper[T]:
+ T value
+ __init__(T &value):
+ this.value = value
+ void set(T &value):
+ this.value = value
+ T get() const:
+ return this.value
+
+
+def test_const_get(int x):
+ """
+ >>> test_const_get(10)
+ 10
+ """
+ cdef const Wrapper[int] *wrapper = new Wrapper[int](x)
+ try:
+ return const_get(wrapper[0])
+ finally:
+ del wrapper
+
+cdef int const_get(const Wrapper[int] wrapper):
+ return wrapper.get()
+
+def test_const_ref_get(int x):
+ """
+ >>> test_const_ref_get(100)
+ 100
+ """
+ cdef const Wrapper[int] *wrapper = new Wrapper[int](x)
+ try:
+ return const_ref_get(wrapper[0])
+ finally:
+ del wrapper
+
+cdef int const_ref_get(const Wrapper[int] &wrapper):
+ return wrapper.get()
+
+def test_const_pointer_get(int x):
+ """
+ >>> test_const_pointer_get(1000)
+ 1000
+ """
+ cdef Wrapper[int] *wrapper = new Wrapper[int](x)
+ cdef const Wrapper[int] *const_wrapper = wrapper
+ try:
+ return const_wrapper.get()
+ finally:
+ del wrapper
+
+
+# TODO: parse vector[Wrapper[int]*]
+ctypedef Wrapper[int] wrapInt
+
+def test_vector_members(py_a, py_b):
+ """
+ >>> test_vector_members([1, 2, 3], [4,5, 6])
+ ([1, 2, 3], 4)
+ """
+ cdef Wrapper[int] *value
+ cdef const Wrapper[int] *const_value
+ cdef vector[const Wrapper[int]*] a
+ cdef vector[wrapInt*] b
+ for x in py_a:
+ a.push_back(new Wrapper[int](x))
+ for x in py_b:
+ b.push_back(new Wrapper[int](x))
+ try:
+ return vector_members(a, b)
+ finally:
+ for const_value in a:
+ del const_value
+ for value in b:
+ del value
+
+cdef vector_members(vector[const Wrapper[int]*] a, const vector[wrapInt*] b):
+ # TODO: Cython-level error.
+ # b[0].set(100)
+
+ # TODO: const_iterator
+ return [x.get() for x in a], b[0].get()

0 comments on commit 8c4a629

Please sign in to comment.
Something went wrong with that request. Please try again.