Rudimentary const support.
authorRobert Bradshaw <robertwb@gmail.com>
Fri, 14 Sep 2012 07:37:50 +0000 (00:37 -0700)
committerRobert Bradshaw <robertwb@gmail.com>
Tue, 18 Sep 2012 21:42:39 +0000 (14:42 -0700)
Cython/Compiler/ExprNodes.py
Cython/Compiler/Nodes.py
Cython/Compiler/Parsing.py
Cython/Compiler/PyrexTypes.py
Cython/Compiler/Symtab.py
Cython/Compiler/TypeInference.py

index 2ef54e8..16773dc 100755 (executable)
@@ -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)
 
index f5cf1dd..35aa32b 100644 (file)
@@ -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,
index 07a2edd..5cdd6b9 100644 (file)
@@ -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")
index 62eb886..e56018f 100755 (executable)
@@ -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 "<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 @@ 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)
 
index 6ef4e48..5deeccd 100644 (file)
@@ -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
index bbd9c73..3acc042 100644 (file)
@@ -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: