Commit 8c4a6299 authored by Robert Bradshaw's avatar Robert Bradshaw

Merge branch 'const'

parents eae1ada8 84ce8038
...@@ -618,6 +618,9 @@ class ExprNode(Node): ...@@ -618,6 +618,9 @@ class ExprNode(Node):
if dst_type.is_reference and not src_type.is_reference: if dst_type.is_reference and not src_type.is_reference:
dst_type = dst_type.ref_base_type 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: if src_type.is_fused or dst_type.is_fused:
# See if we are coercing a fused function to a pointer to a # See if we are coercing a fused function to a pointer to a
# specialized function # specialized function
...@@ -1511,6 +1514,10 @@ class NameNode(AtomicExprNode): ...@@ -1511,6 +1514,10 @@ class NameNode(AtomicExprNode):
self.entry = self.entry.as_variable self.entry = self.entry.as_variable
self.type = self.entry.type 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(): if not self.is_lvalue():
error(self.pos, "Assignment to non-lvalue '%s'" error(self.pos, "Assignment to non-lvalue '%s'"
% self.name) % self.name)
...@@ -2233,6 +2240,8 @@ class NextNode(AtomicExprNode): ...@@ -2233,6 +2240,8 @@ class NextNode(AtomicExprNode):
item_type = env.lookup_operator_for_types(self.pos, "*", [iterator_type]).type.return_type item_type = env.lookup_operator_for_types(self.pos, "*", [iterator_type]).type.return_type
if item_type.is_reference: if item_type.is_reference:
item_type = item_type.ref_base_type item_type = item_type.ref_base_type
if item_type.is_const:
item_type = item_type.const_base_type
return item_type return item_type
else: else:
# Avoid duplication of complicated logic. # Avoid duplication of complicated logic.
...@@ -2598,6 +2607,8 @@ class IndexNode(ExprNode): ...@@ -2598,6 +2607,8 @@ class IndexNode(ExprNode):
def analyse_target_types(self, env): def analyse_target_types(self, env):
self.analyse_base_and_index_types(env, setting = 1) 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(): if not self.is_lvalue():
error(self.pos, "Assignment to non-lvalue of type '%s'" % self.type) error(self.pos, "Assignment to non-lvalue of type '%s'" % self.type)
...@@ -4453,6 +4464,8 @@ class AttributeNode(ExprNode): ...@@ -4453,6 +4464,8 @@ class AttributeNode(ExprNode):
def analyse_target_types(self, env): def analyse_target_types(self, env):
self.analyse_types(env, target = 1) 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(): if not self.is_lvalue():
error(self.pos, "Assignment to non-lvalue of type '%s'" % self.type) error(self.pos, "Assignment to non-lvalue of type '%s'" % self.type)
......
...@@ -1048,6 +1048,19 @@ class FusedTypeNode(CBaseTypeNode): ...@@ -1048,6 +1048,19 @@ class FusedTypeNode(CBaseTypeNode):
return PyrexTypes.FusedType(types, name=self.name) 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): class CVarDefNode(StatNode):
# C variable definition or forward/extern function declaration. # C variable definition or forward/extern function declaration.
# #
...@@ -1941,6 +1954,7 @@ class CFuncDefNode(FuncDefNode): ...@@ -1941,6 +1954,7 @@ class CFuncDefNode(FuncDefNode):
# overridable whether or not this is a cpdef function # overridable whether or not this is a cpdef function
# inline_in_pxd whether this is an inline function in a pxd file # inline_in_pxd whether this is an inline function in a pxd file
# template_declaration String or None Used for c++ class methods # 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"] child_attrs = ["base_type", "declarator", "body", "py_func"]
...@@ -1950,6 +1964,7 @@ class CFuncDefNode(FuncDefNode): ...@@ -1950,6 +1964,7 @@ class CFuncDefNode(FuncDefNode):
directive_returns = None directive_returns = None
override = None override = None
template_declaration = None template_declaration = None
is_const_method = False
def unqualified_name(self): def unqualified_name(self):
return self.entry.name return self.entry.name
...@@ -2021,6 +2036,7 @@ class CFuncDefNode(FuncDefNode): ...@@ -2021,6 +2036,7 @@ class CFuncDefNode(FuncDefNode):
name = name_declarator.name name = name_declarator.name
cname = name_declarator.cname cname = name_declarator.cname
type.is_const_method = self.is_const_method
self.entry = env.declare_cfunction( self.entry = env.declare_cfunction(
name, type, self.pos, name, type, self.pos,
cname = cname, visibility = self.visibility, api = self.api, cname = cname, visibility = self.visibility, api = self.api,
......
...@@ -1982,6 +1982,11 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None): ...@@ -1982,6 +1982,11 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None):
pos = s.position() pos = s.position()
if not s.sy == 'IDENT': if not s.sy == 'IDENT':
error(pos, "Expected an identifier, found '%s'" % s.sy) 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): if looking_at_base_type(s):
#print "p_c_simple_base_type: looking_at_base_type at", s.position() #print "p_c_simple_base_type: looking_at_base_type at", s.position()
is_basic = 1 is_basic = 1
...@@ -2703,6 +2708,11 @@ def p_c_func_or_var_declaration(s, pos, ctx): ...@@ -2703,6 +2708,11 @@ def p_c_func_or_var_declaration(s, pos, ctx):
declarator = p_c_declarator(s, ctx, cmethod_flag = cmethod_flag, declarator = p_c_declarator(s, ctx, cmethod_flag = cmethod_flag,
assignable = 1, nonempty = 1) assignable = 1, nonempty = 1)
declarator.overridable = ctx.overridable 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 s.sy == ':':
if ctx.level not in ('module', 'c_class', 'module_pxd', 'c_class_pxd', 'cpp_class') and not ctx.templates: 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") s.error("C function definition not allowed here")
...@@ -2715,7 +2725,8 @@ def p_c_func_or_var_declaration(s, pos, ctx): ...@@ -2715,7 +2725,8 @@ def p_c_func_or_var_declaration(s, pos, ctx):
doc = doc, doc = doc,
modifiers = modifiers, modifiers = modifiers,
api = ctx.api, api = ctx.api,
overridable = ctx.overridable) overridable = ctx.overridable,
is_const_method = is_const_method)
else: else:
#if api: #if api:
# s.error("'api' not allowed with variable declaration") # s.error("'api' not allowed with variable declaration")
......
...@@ -138,6 +138,7 @@ class PyrexType(BaseType): ...@@ -138,6 +138,7 @@ class PyrexType(BaseType):
# is_ptr boolean Is a C pointer type # is_ptr boolean Is a C pointer type
# is_null_ptr boolean Is the type of NULL # is_null_ptr boolean Is the type of NULL
# is_reference boolean Is a C reference type # 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_cfunction boolean Is a C function type
# is_struct_or_union boolean Is a C struct or union type # is_struct_or_union boolean Is a C struct or union type
# is_struct boolean Is a C struct type # is_struct boolean Is a C struct type
...@@ -192,6 +193,7 @@ class PyrexType(BaseType): ...@@ -192,6 +193,7 @@ class PyrexType(BaseType):
is_ptr = 0 is_ptr = 0
is_null_ptr = 0 is_null_ptr = 0
is_reference = 0 is_reference = 0
is_const = 0
is_cfunction = 0 is_cfunction = 0
is_struct_or_union = 0 is_struct_or_union = 0
is_cpp_class = 0 is_cpp_class = 0
...@@ -1151,6 +1153,42 @@ class CType(PyrexType): ...@@ -1151,6 +1153,42 @@ class CType(PyrexType):
return 0 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): class FusedType(CType):
""" """
Represents a Fused Type. All it needs to do is keep track of the types Represents a Fused Type. All it needs to do is keep track of the types
...@@ -2281,6 +2319,8 @@ class CPtrType(CPointerBaseType): ...@@ -2281,6 +2319,8 @@ class CPtrType(CPointerBaseType):
return 1 return 1
if other_type.is_null_ptr: if other_type.is_null_ptr:
return 1 return 1
if self.base_type.is_const:
self = CPtrType(self.base_type.const_base_type)
if self.base_type.is_cfunction: if self.base_type.is_cfunction:
if other_type.is_ptr: if other_type.is_ptr:
other_type = other_type.base_type.resolve() other_type = other_type.base_type.resolve()
...@@ -2328,9 +2368,6 @@ class CReferenceType(BaseType): ...@@ -2328,9 +2368,6 @@ class CReferenceType(BaseType):
def __str__(self): def __str__(self):
return "%s &" % self.ref_base_type return "%s &" % self.ref_base_type
def as_argument_type(self):
return self
def declaration_code(self, entity_code, def declaration_code(self, entity_code,
for_display = 0, dll_linkage = None, pyrex = 0): for_display = 0, dll_linkage = None, pyrex = 0):
#print "CReferenceType.declaration_code: pointer to", self.base_type ### #print "CReferenceType.declaration_code: pointer to", self.base_type ###
...@@ -2364,11 +2401,13 @@ class CFuncType(CType): ...@@ -2364,11 +2401,13 @@ class CFuncType(CType):
# C function # C function
# is_strict_signature boolean function refuses to accept coerced arguments # is_strict_signature boolean function refuses to accept coerced arguments
# (used for optimisation overrides) # (used for optimisation overrides)
# is_const_method boolean
is_cfunction = 1 is_cfunction = 1
original_sig = None original_sig = None
cached_specialized_types = None cached_specialized_types = None
from_fused = False from_fused = False
is_const_method = False
subtypes = ['return_type', 'args'] subtypes = ['return_type', 'args']
...@@ -2575,13 +2614,19 @@ class CFuncType(CType): ...@@ -2575,13 +2614,19 @@ class CFuncType(CType):
if (not entity_code and cc) or entity_code.startswith("*"): if (not entity_code and cc) or entity_code.startswith("*"):
entity_code = "(%s%s)" % (cc, entity_code) entity_code = "(%s%s)" % (cc, entity_code)
cc = "" cc = ""
if self.is_const_method:
trailer += " const"
return self.return_type.declaration_code( return self.return_type.declaration_code(
"%s%s(%s)%s" % (cc, entity_code, arg_decl_code, trailer), "%s%s(%s)%s" % (cc, entity_code, arg_decl_code, trailer),
for_display, dll_linkage, pyrex) for_display, dll_linkage, pyrex)
def function_header_code(self, func_name, arg_code): def function_header_code(self, func_name, arg_code):
return "%s%s(%s)" % (self.calling_convention_prefix(), if self.is_const_method:
func_name, arg_code) trailer = " const"
else:
trailer = ""
return "%s%s(%s)%s" % (self.calling_convention_prefix(),
func_name, arg_code, trailer)
def signature_string(self): def signature_string(self):
s = self.declaration_code("") s = self.declaration_code("")
...@@ -3802,6 +3847,13 @@ def c_ref_type(base_type): ...@@ -3802,6 +3847,13 @@ def c_ref_type(base_type):
else: else:
return CReferenceType(base_type) 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): def same_type(type1, type2):
return type1.same_as(type2) return type1.same_as(type2)
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# Symbol Table # Symbol Table
# #
import copy
import re import re
from Errors import warning, error, InternalError from Errors import warning, error, InternalError
from StringEncoding import EncodedString from StringEncoding import EncodedString
...@@ -2151,3 +2152,20 @@ class PropertyScope(Scope): ...@@ -2151,3 +2152,20 @@ class PropertyScope(Scope):
error(pos, "Only __get__, __set__ and __del__ methods allowed " error(pos, "Only __get__, __set__ and __del__ methods allowed "
"in a property declaration") "in a property declaration")
return None 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
...@@ -442,10 +442,14 @@ def aggressive_spanning_type(types, might_overflow): ...@@ -442,10 +442,14 @@ def aggressive_spanning_type(types, might_overflow):
result_type = reduce(find_spanning_type, types) result_type = reduce(find_spanning_type, types)
if result_type.is_reference: if result_type.is_reference:
result_type = result_type.ref_base_type result_type = result_type.ref_base_type
if result_type.is_const:
result_type = result_type.const_base_type
return result_type return result_type
def safe_spanning_type(types, might_overflow): def safe_spanning_type(types, might_overflow):
result_type = reduce(find_spanning_type, types) result_type = reduce(find_spanning_type, types)
if result_type.is_const:
result_type = result_type.const_base_type
if result_type.is_reference: if result_type.is_reference:
result_type = result_type.ref_base_type result_type = result_type.ref_base_type
if result_type.is_pyobject: if result_type.is_pyobject:
......
# 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
# 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'
"""
# 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()
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