From d1a536dd6fb587aa5cddbcf685a087cac31c678b Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Fri, 14 Sep 2012 00:37:50 -0700 Subject: [PATCH] Rudimentary const support. --- Cython/Compiler/ExprNodes.py | 13 +++++++++ Cython/Compiler/Nodes.py | 16 +++++++++++ Cython/Compiler/Parsing.py | 13 ++++++++- Cython/Compiler/PyrexTypes.py | 62 ++++++++++++++++++++++++++++++++++++---- Cython/Compiler/Symtab.py | 18 ++++++++++++ Cython/Compiler/TypeInference.py | 4 +++ 6 files changed, 120 insertions(+), 6 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 2ef54e8..16773dc 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -618,6 +618,9 @@ class ExprNode(Node): 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 @@ class NameNode(AtomicExprNode): 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 @@ 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 return item_type else: # Avoid duplication of complicated logic. @@ -2598,6 +2607,8 @@ class IndexNode(ExprNode): 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 @@ class AttributeNode(ExprNode): 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) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index f5cf1dd..35aa32b 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1048,6 +1048,19 @@ class FusedTypeNode(CBaseTypeNode): 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 @@ class CFuncDefNode(FuncDefNode): 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, diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index 07a2edd..5cdd6b9 100644 --- a/Cython/Compiler/Parsing.py +++ b/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") diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 62eb886..e56018f 100755 --- a/Cython/Compiler/PyrexTypes.py +++ b/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 @@ class CType(PyrexType): 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 "" % 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 @@ 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_cfunction: if other_type.is_ptr: other_type = other_type.base_type.resolve() @@ -2328,9 +2368,6 @@ class CReferenceType(BaseType): 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 @@ class CFuncType(CType): 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) diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 6ef4e48..5deeccd 100644 --- a/Cython/Compiler/Symtab.py +++ b/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 @@ class PropertyScope(Scope): 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 diff --git a/Cython/Compiler/TypeInference.py b/Cython/Compiler/TypeInference.py index bbd9c73..3acc042 100644 --- a/Cython/Compiler/TypeInference.py +++ b/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: -- 2.7.4