Commit d2f4df7a authored by Dag Sverre Seljebotn's avatar Dag Sverre Seljebotn

Initial implementation of nonecheck directive; some directive design changes

--HG--
rename : tests/run/noneattributeacc.pyx => tests/run/nonecheck.pyx
parent 0ed4800a
...@@ -161,6 +161,12 @@ class GlobalState(object): ...@@ -161,6 +161,12 @@ class GlobalState(object):
# interned_nums # interned_nums
# cached_builtins # cached_builtins
# directives set Temporary variable used to track
# the current set of directives in the code generation
# process.
directives = {}
def __init__(self, rootwriter): def __init__(self, rootwriter):
self.filename_table = {} self.filename_table = {}
self.filename_list = [] self.filename_list = []
...@@ -415,7 +421,7 @@ class CCodeWriter(object): ...@@ -415,7 +421,7 @@ class CCodeWriter(object):
# generation (labels and temps state etc.) # generation (labels and temps state etc.)
# globalstate GlobalState contains state global for a C file (input file info, # globalstate GlobalState contains state global for a C file (input file info,
# utility code, declared constants etc.) # utility code, declared constants etc.)
def __init__(self, create_from=None, buffer=None, copy_formatting=False): def __init__(self, create_from=None, buffer=None, copy_formatting=False):
if buffer is None: buffer = StringIOTree() if buffer is None: buffer = StringIOTree()
self.buffer = buffer self.buffer = buffer
......
...@@ -830,12 +830,10 @@ class NameNode(AtomicExprNode): ...@@ -830,12 +830,10 @@ class NameNode(AtomicExprNode):
# #
# entry Entry Symbol table entry # entry Entry Symbol table entry
# interned_cname string # interned_cname string
# possible_var_values object See Optimize.FindPossibleVariableValues
is_name = 1 is_name = 1
skip_assignment_decref = False skip_assignment_decref = False
entry = None entry = None
possible_var_values = None
def create_analysed_rvalue(pos, env, entry): def create_analysed_rvalue(pos, env, entry):
node = NameNode(pos) node = NameNode(pos)
...@@ -1597,13 +1595,12 @@ class IndexNode(ExprNode): ...@@ -1597,13 +1595,12 @@ class IndexNode(ExprNode):
code.putln("%s = %s;" % (temp, index.result_code)) code.putln("%s = %s;" % (temp, index.result_code))
# Generate buffer access code using these temps # Generate buffer access code using these temps
import Buffer import Buffer
assert self.options is not None
# The above could happen because child_attrs is wrong somewhere so that # The above could happen because child_attrs is wrong somewhere so that
# options are not propagated. # options are not propagated.
return Buffer.put_buffer_lookup_code(entry=self.base.entry, return Buffer.put_buffer_lookup_code(entry=self.base.entry,
index_signeds=[i.type.signed for i in self.indices], index_signeds=[i.type.signed for i in self.indices],
index_cnames=index_temps, index_cnames=index_temps,
options=self.options, options=code.globalstate.directives,
pos=self.pos, code=code) pos=self.pos, code=code)
class SliceIndexNode(ExprNode): class SliceIndexNode(ExprNode):
...@@ -2076,6 +2073,7 @@ class AttributeNode(ExprNode): ...@@ -2076,6 +2073,7 @@ class AttributeNode(ExprNode):
# obj ExprNode # obj ExprNode
# attribute string # attribute string
# needs_none_check boolean Used if obj is an extension type. # needs_none_check boolean Used if obj is an extension type.
# If set to True, it is known that the type is not None.
# #
# Used internally: # Used internally:
# #
...@@ -2324,12 +2322,10 @@ class AttributeNode(ExprNode): ...@@ -2324,12 +2322,10 @@ class AttributeNode(ExprNode):
else: else:
# result_code contains what is needed, but we may need to insert # result_code contains what is needed, but we may need to insert
# a check and raise an exception # a check and raise an exception
if self.obj.type.is_extension_type and self.needs_none_check: if (self.obj.type.is_extension_type
code.globalstate.use_utility_code(raise_noneattr_error_utility_code) and self.needs_none_check
code.putln("if (%s) {" % code.unlikely("%s == Py_None") % self.obj.result_as(PyrexTypes.py_object_type)) and code.globalstate.directives['nonecheck']):
code.putln("__Pyx_RaiseNoneAttributeError(\"%s\");" % self.attribute.encode("UTF-8")) # todo: fix encoding self.put_nonecheck(code)
code.putln(code.error_goto(self.pos))
code.putln("}")
def generate_assignment_code(self, rhs, code): def generate_assignment_code(self, rhs, code):
self.obj.generate_evaluation_code(code) self.obj.generate_evaluation_code(code)
...@@ -2341,6 +2337,11 @@ class AttributeNode(ExprNode): ...@@ -2341,6 +2337,11 @@ class AttributeNode(ExprNode):
rhs.py_result())) rhs.py_result()))
rhs.generate_disposal_code(code) rhs.generate_disposal_code(code)
else: else:
if (self.obj.type.is_extension_type
and self.needs_none_check
and code.globalstate.directives['nonecheck']):
self.put_nonecheck(code)
select_code = self.result_code select_code = self.result_code
if self.type.is_pyobject: if self.type.is_pyobject:
rhs.make_owned_reference(code) rhs.make_owned_reference(code)
...@@ -2370,6 +2371,14 @@ class AttributeNode(ExprNode): ...@@ -2370,6 +2371,14 @@ class AttributeNode(ExprNode):
else: else:
code.annotate(self.pos, AnnotationItem('c_attr', 'c attribute', size=len(self.attribute))) code.annotate(self.pos, AnnotationItem('c_attr', 'c attribute', size=len(self.attribute)))
def put_nonecheck(self, code):
code.globalstate.use_utility_code(raise_noneattr_error_utility_code)
code.putln("if (%s) {" % code.unlikely("%s == Py_None") % self.obj.result_as(PyrexTypes.py_object_type))
code.putln("__Pyx_RaiseNoneAttributeError(\"%s\");" % self.attribute.encode("UTF-8")) # todo: fix encoding
code.putln(code.error_goto(self.pos))
code.putln("}")
#------------------------------------------------------------------- #-------------------------------------------------------------------
# #
# Constructor nodes # Constructor nodes
...@@ -3984,7 +3993,6 @@ class CoercionNode(ExprNode): ...@@ -3984,7 +3993,6 @@ class CoercionNode(ExprNode):
def __init__(self, arg): def __init__(self, arg):
self.pos = arg.pos self.pos = arg.pos
self.arg = arg self.arg = arg
self.options = arg.options
if debug_coercion: if debug_coercion:
print("%s Coercing %s" % (self, self.arg)) print("%s Coercing %s" % (self, self.arg))
......
...@@ -79,9 +79,8 @@ class Context: ...@@ -79,9 +79,8 @@ class Context:
from ParseTreeTransforms import WithTransform, NormalizeTree, PostParse, PxdPostParse from ParseTreeTransforms import WithTransform, NormalizeTree, PostParse, PxdPostParse
from ParseTreeTransforms import AnalyseDeclarationsTransform, AnalyseExpressionsTransform from ParseTreeTransforms import AnalyseDeclarationsTransform, AnalyseExpressionsTransform
from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform
from ParseTreeTransforms import ResolveOptions from ParseTreeTransforms import InterpretCompilerDirectives
from Optimize import FlattenInListTransform, SwitchTransform, OptimizeRefcounting from Optimize import FlattenInListTransform, SwitchTransform, OptimizeRefcounting
from Optimize import OptimizeNoneChecking, FindPossibleVariableValues
from Buffer import IntroduceBufferAuxiliaryVars from Buffer import IntroduceBufferAuxiliaryVars
from ModuleNode import check_c_classes from ModuleNode import check_c_classes
...@@ -96,7 +95,7 @@ class Context: ...@@ -96,7 +95,7 @@ class Context:
NormalizeTree(self), NormalizeTree(self),
PostParse(self), PostParse(self),
_specific_post_parse, _specific_post_parse,
ResolveOptions(self, self.pragma_overrides), InterpretCompilerDirectives(self, self.pragma_overrides),
FlattenInListTransform(), FlattenInListTransform(),
WithTransform(self), WithTransform(self),
DecoratorTransform(self), DecoratorTransform(self),
...@@ -105,9 +104,7 @@ class Context: ...@@ -105,9 +104,7 @@ class Context:
_check_c_classes, _check_c_classes,
AnalyseExpressionsTransform(self), AnalyseExpressionsTransform(self),
SwitchTransform(), SwitchTransform(),
FindPossibleVariableValues(self),
OptimizeRefcounting(self), OptimizeRefcounting(self),
OptimizeNoneChecking(self),
# SpecialFunctions(self), # SpecialFunctions(self),
# CreateClosureClasses(context), # CreateClosureClasses(context),
] ]
......
...@@ -41,6 +41,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -41,6 +41,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
# #
# scope The module scope. # scope The module scope.
# compilation_source A CompilationSource (see Main) # compilation_source A CompilationSource (see Main)
# directives Top-level compiler directives
child_attrs = ["body"] child_attrs = ["body"]
...@@ -52,6 +53,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -52,6 +53,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
env.doc.encoding = self.doc.encoding env.doc.encoding = self.doc.encoding
else: else:
env.doc = self.doc env.doc = self.doc
env.directives = self.directives
self.body.analyse_declarations(env) self.body.analyse_declarations(env)
def process_implementation(self, options, result): def process_implementation(self, options, result):
...@@ -245,6 +247,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -245,6 +247,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_module_preamble(env, modules, h_code) self.generate_module_preamble(env, modules, h_code)
code.globalstate.module_pos = self.pos code.globalstate.module_pos = self.pos
code.globalstate.directives = self.directives
code.putln("") code.putln("")
code.putln("/* Implementation of %s */" % env.qualified_name) code.putln("/* Implementation of %s */" % env.qualified_name)
......
...@@ -71,12 +71,10 @@ class Node(object): ...@@ -71,12 +71,10 @@ class Node(object):
# pos (string, int, int) Source file position # pos (string, int, int) Source file position
# is_name boolean Is a NameNode # is_name boolean Is a NameNode
# is_literal boolean Is a ConstNode # is_literal boolean Is a ConstNode
# options dict Compiler directives in effect for this node
is_name = 0 is_name = 0
is_literal = 0 is_literal = 0
temps = None temps = None
options = None
# All descandants should set child_attrs to a list of the attributes # All descandants should set child_attrs to a list of the attributes
# containing nodes considered "children" in the tree. Each such attribute # containing nodes considered "children" in the tree. Each such attribute
...@@ -204,6 +202,53 @@ class Node(object): ...@@ -204,6 +202,53 @@ class Node(object):
res += "%s %s: %s\n" % (indent, key, dump_child(value, level + 1)) res += "%s %s: %s\n" % (indent, key, dump_child(value, level + 1))
res += "%s>" % indent res += "%s>" % indent
return res return res
class CompilerDirectivesNode(Node):
"""
Sets compiler directives for the children nodes
"""
# directives {string:value} A dictionary holding the right value for
# *all* possible directives.
# body Node
child_attrs = ["body"]
def analyse_control_flow(self, env):
old = env.directives
env.directives = self.directives
self.body.analyse_control_flow(env)
env.directives = old
def analyse_declarations(self, env):
old = env.directives
env.directives = self.directives
self.body.analyse_declarations(env)
env.directives = old
def analyse_expressions(self, env):
old = env.directives
env.directives = self.directives
self.body.analyse_expressions(env)
env.directives = old
def generate_function_definitions(self, env, code):
env_old = env.directives
code_old = code.globalstate.directives
code.globalstate.directives = self.directives
self.body.generate_function_definitions(env, code)
env.directives = env_old
code.globalstate.directives = code_old
def generate_execution_code(self, code):
old = code.globalstate.directives
code.globalstate.directives = self.directives
self.body.generate_execution_code(code)
code.globalstate.directives = old
def annotate(self, code):
old = code.globalstate.directives
code.globalstate.directives = self.directives
self.body.annotate(code)
code.globalstate.directives = old
class BlockNode: class BlockNode:
# Mixin class for nodes representing a declaration block. # Mixin class for nodes representing a declaration block.
...@@ -2568,14 +2613,12 @@ class InPlaceAssignmentNode(AssignmentNode): ...@@ -2568,14 +2613,12 @@ class InPlaceAssignmentNode(AssignmentNode):
target_lhs = ExprNodes.NameNode(self.dup.pos, target_lhs = ExprNodes.NameNode(self.dup.pos,
name = self.dup.name, name = self.dup.name,
is_temp = self.dup.is_temp, is_temp = self.dup.is_temp,
entry = self.dup.entry, entry = self.dup.entry)
options = self.dup.options)
elif isinstance(self.lhs, ExprNodes.AttributeNode): elif isinstance(self.lhs, ExprNodes.AttributeNode):
target_lhs = ExprNodes.AttributeNode(self.dup.pos, target_lhs = ExprNodes.AttributeNode(self.dup.pos,
obj = ExprNodes.CloneNode(self.lhs.obj), obj = ExprNodes.CloneNode(self.lhs.obj),
attribute = self.dup.attribute, attribute = self.dup.attribute,
is_temp = self.dup.is_temp, is_temp = self.dup.is_temp)
options = self.dup.options)
elif isinstance(self.lhs, ExprNodes.IndexNode): elif isinstance(self.lhs, ExprNodes.IndexNode):
if self.lhs.index: if self.lhs.index:
index = ExprNodes.CloneNode(self.lhs.index) index = ExprNodes.CloneNode(self.lhs.index)
...@@ -2589,8 +2632,7 @@ class InPlaceAssignmentNode(AssignmentNode): ...@@ -2589,8 +2632,7 @@ class InPlaceAssignmentNode(AssignmentNode):
base = ExprNodes.CloneNode(self.dup.base), base = ExprNodes.CloneNode(self.dup.base),
index = index, index = index,
indices = indices, indices = indices,
is_temp = self.dup.is_temp, is_temp = self.dup.is_temp)
options = self.dup.options)
self.lhs = target_lhs self.lhs = target_lhs
return self.dup return self.dup
......
...@@ -150,118 +150,3 @@ class OptimizeRefcounting(Visitor.CythonTransform): ...@@ -150,118 +150,3 @@ class OptimizeRefcounting(Visitor.CythonTransform):
lhs.skip_assignment_decref = True lhs.skip_assignment_decref = True
return node return node
class ExtTypePossibleValues:
can_be_none = True
def copy_with(self, can_be_none=None):
result = ExtTypePossibleValues()
if can_be_none is not None:
result.can_be_none = can_be_none
return result
def new(self):
"Polymorphic constructor"
return ExtTypePossibleValues()
class FindPossibleVariableValues(Visitor.CythonTransform):
"""
Annotates NameNodes with information about the possible values
the variable referred to can take, *at that point* in the execution.
This is done on a best effort basis, so we can be as smart or dumb
as we want. A do-nothing-op should always be valid.
Each type of variable keeps a different type of "variable range"
information.
This information is invalid if the tree is reorganized (read:
keep this transform late in the pipeline).
Currently this is done:
- Extension types gets flagged
"""
#
# Manage info stack
#
def create_empty_knowledge(self, scope):
knowledge = {}
for entry in scope.entries.values():
if entry.type.is_extension_type:
knowledge[entry] = ExtTypePossibleValues()
return knowledge
def visit_ModuleNode(self, node):
self.knowledge = self.create_empty_knowledge(node.scope)
self.visitchildren(node)
return node
def visit_FuncDefNode(self, node):
oldknow = self.knowledge
self.knowledge = self.create_empty_knowledge(node.local_scope)
self.visitchildren(node)
self.knowledge = oldknow
return node
def visit_NameNode(self, node):
node.possible_var_values = self.knowledge.get(node.entry, None)
return node
#
# Conditions which restrict possible variable values
#
def visit_IfClauseNode(self, clause):
def process():
self.visitchildren(clause)
return clause
# we're lazy and only check in one specific easy case: single comparison with None
# the code is a bit nasty but handling the proper cases will force through better code
# anyway
cond = clause.condition
if not isinstance(cond, ExprNodes.PrimaryCmpNode): return process()
if clause.condition.cascade is not None: return process()
if isinstance(cond.operand1, ExprNodes.NoneNode):
operand_checked = cond.operand2
elif isinstance(cond.operand2, ExprNodes.NoneNode):
operand_checked = cond.operand1
else:
return process()
if not isinstance(operand_checked, ExprNodes.NameNode):
return process()
entry = operand_checked.entry
if entry not in self.knowledge:
# Not tracking this variable
return process()
# Finally!
if cond.operator == 'is_not':
# Within this block we can assume the variable is not None
# (until it is reassigned)
self.visitchildren(clause, attrs=["condition"])
oldvalues = self.knowledge[entry]
self.knowledge[entry] = oldvalues.copy_with(can_be_none=False)
self.visitchildren(clause, attrs=["body"])
self.knowledge[entry] = oldvalues
return clause
else:
return process()
# Assignments which reset possible variable values
def visit_SingleAssignmentNode(self, node):
if isinstance(node.lhs, ExprNodes.NameNode):
entry = node.lhs.entry
if entry in self.knowledge:
self.knowledge[entry] = self.knowledge[entry].new()
self.visitchildren(node)
return node
class OptimizeNoneChecking(Visitor.CythonTransform):
def visit_AttributeNode(self, node):
if isinstance(node.obj, ExprNodes.NameNode):
obj = node.obj
if obj.type.is_extension_type and not obj.possible_var_values.can_be_none:
node.needs_none_check = False
self.visitchildren(node)
return node
...@@ -56,11 +56,13 @@ c_line_in_traceback = 1 ...@@ -56,11 +56,13 @@ c_line_in_traceback = 1
# Declare pragmas # Declare pragmas
option_types = { option_types = {
'boundscheck' : bool 'boundscheck' : bool,
'nonecheck' : bool
} }
option_defaults = { option_defaults = {
'boundscheck' : True 'boundscheck' : True,
'nonecheck' : False
} }
def parse_option_value(name, value): def parse_option_value(name, value):
......
...@@ -238,7 +238,7 @@ class PxdPostParse(CythonTransform): ...@@ -238,7 +238,7 @@ class PxdPostParse(CythonTransform):
else: else:
return node return node
class ResolveOptions(CythonTransform): class InterpretCompilerDirectives(CythonTransform):
""" """
After parsing, options can be stored in a number of places: After parsing, options can be stored in a number of places:
- #cython-comments at the top of the file (stored in ModuleNode) - #cython-comments at the top of the file (stored in ModuleNode)
...@@ -246,28 +246,38 @@ class ResolveOptions(CythonTransform): ...@@ -246,28 +246,38 @@ class ResolveOptions(CythonTransform):
- @cython.optionname decorators - @cython.optionname decorators
- with cython.optionname: statements - with cython.optionname: statements
This transform is responsible for annotating each node with an This transform is responsible for interpreting these various sources
"options" attribute linking it to a dict containing the exact and store the option in two ways:
options that are in effect for that node. Any corresponding decorators - Set the directives attribute of the ModuleNode for global directives.
or with statements are removed in the process. - Use a CompilerDirectivesNode to override directives for a subtree.
(The first one is primarily to not have to modify with the tree
structure, so that ModuleNode stay on top.)
The directives are stored in dictionaries from name to value in effect.
Each such dictionary is always filled in for all possible directives,
using default values where no value is given by the user.
The available directives are controlled in Options.py.
Note that we have to run this prior to analysis, and so some minor Note that we have to run this prior to analysis, and so some minor
duplication of functionality has to occur: We manually track cimports duplication of functionality has to occur: We manually track cimports
to correctly intercept @cython... and with cython... and which names the "cython" module may have been imported to.
""" """
def __init__(self, context, compilation_option_overrides): def __init__(self, context, compilation_option_overrides):
super(ResolveOptions, self).__init__(context) super(InterpretCompilerDirectives, self).__init__(context)
self.compilation_option_overrides = compilation_option_overrides self.compilation_option_overrides = compilation_option_overrides
self.cython_module_names = set() self.cython_module_names = set()
self.option_names = {} self.option_names = {}
# Set up processing and handle the cython: comments.
def visit_ModuleNode(self, node): def visit_ModuleNode(self, node):
options = copy.copy(Options.option_defaults) options = copy.copy(Options.option_defaults)
options.update(node.option_comments) options.update(node.option_comments)
options.update(self.compilation_option_overrides) options.update(self.compilation_option_overrides)
self.options = options self.options = options
node.options = options node.directives = options
self.visitchildren(node) self.visitchildren(node)
return node return node
...@@ -297,7 +307,6 @@ class ResolveOptions(CythonTransform): ...@@ -297,7 +307,6 @@ class ResolveOptions(CythonTransform):
return node return node
def visit_Node(self, node): def visit_Node(self, node):
node.options = self.options
self.visitchildren(node) self.visitchildren(node)
return node return node
...@@ -329,14 +338,17 @@ class ResolveOptions(CythonTransform): ...@@ -329,14 +338,17 @@ class ResolveOptions(CythonTransform):
return None return None
def visit_with_options(self, node, options): def visit_with_options(self, body, options):
oldoptions = self.options oldoptions = self.options
newoptions = copy.copy(oldoptions) newoptions = copy.copy(oldoptions)
newoptions.update(options) newoptions.update(options)
self.options = newoptions self.options = newoptions
node = self.visit_Node(node) assert isinstance(body, StatListNode), body
retbody = self.visit_Node(body)
directive = CompilerDirectivesNode(pos=retbody.pos, body=retbody,
directives=options)
self.options = oldoptions self.options = oldoptions
return node return directive
# Handle decorators # Handle decorators
def visit_DefNode(self, node): def visit_DefNode(self, node):
...@@ -359,7 +371,8 @@ class ResolveOptions(CythonTransform): ...@@ -359,7 +371,8 @@ class ResolveOptions(CythonTransform):
for option in options: for option in options:
name, value = option name, value = option
optdict[name] = value optdict[name] = value
return self.visit_with_options(node, optdict) body = StatListNode(node.pos, stats=[node])
return self.visit_with_options(body, optdict)
else: else:
return self.visit_Node(node) return self.visit_Node(node)
...@@ -370,8 +383,7 @@ class ResolveOptions(CythonTransform): ...@@ -370,8 +383,7 @@ class ResolveOptions(CythonTransform):
if node.target is not None: if node.target is not None:
raise PostParseError(node.pos, "Compiler option with statements cannot contain 'as'") raise PostParseError(node.pos, "Compiler option with statements cannot contain 'as'")
name, value = option name, value = option
self.visit_with_options(node.body, {name:value}) return self.visit_with_options(node.body, {name:value})
return node.body.stats
else: else:
return self.visit_Node(node) return self.visit_Node(node)
...@@ -427,7 +439,7 @@ class WithTransform(CythonTransform): ...@@ -427,7 +439,7 @@ class WithTransform(CythonTransform):
u'BODY' : node.body, u'BODY' : node.body,
u'TARGET' : node.target, u'TARGET' : node.target,
u'EXCINFO' : excinfo_namenode u'EXCINFO' : excinfo_namenode
}, pos = node.pos) }, pos=node.pos)
# Set except excinfo target to EXCINFO # Set except excinfo target to EXCINFO
result.stats[4].body.stats[0].except_clauses[0].excinfo_target = excinfo_target result.stats[4].body.stats[0].except_clauses[0].excinfo_target = excinfo_target
else: else:
...@@ -435,7 +447,7 @@ class WithTransform(CythonTransform): ...@@ -435,7 +447,7 @@ class WithTransform(CythonTransform):
u'EXPR' : node.manager, u'EXPR' : node.manager,
u'BODY' : node.body, u'BODY' : node.body,
u'EXCINFO' : excinfo_namenode u'EXCINFO' : excinfo_namenode
}, pos = node.pos) }, pos=node.pos)
# Set except excinfo target to EXCINFO # Set except excinfo target to EXCINFO
result.stats[4].body.stats[0].except_clauses[0].excinfo_target = excinfo_target result.stats[4].body.stats[0].except_clauses[0].excinfo_target = excinfo_target
......
...@@ -178,6 +178,8 @@ class Scope: ...@@ -178,6 +178,8 @@ class Scope:
# Python strings in this scope # Python strings in this scope
# control_flow ControlFlow Used for keeping track of environment state # control_flow ControlFlow Used for keeping track of environment state
# nogil boolean In a nogil section # nogil boolean In a nogil section
# directives dict Helper variable for the recursive
# analysis, contains directive values.
is_py_class_scope = 0 is_py_class_scope = 0
is_c_class_scope = 0 is_c_class_scope = 0
...@@ -185,6 +187,7 @@ class Scope: ...@@ -185,6 +187,7 @@ class Scope:
scope_prefix = "" scope_prefix = ""
in_cinclude = 0 in_cinclude = 0
nogil = 0 nogil = 0
directives = {}
temp_prefix = Naming.pyrex_prefix temp_prefix = Naming.pyrex_prefix
......
...@@ -111,6 +111,7 @@ class TreeVisitor(BasicVisitor): ...@@ -111,6 +111,7 @@ class TreeVisitor(BasicVisitor):
childretval = [self.visitchild(x, parent, attr, idx) for idx, x in enumerate(child)] childretval = [self.visitchild(x, parent, attr, idx) for idx, x in enumerate(child)]
else: else:
childretval = self.visitchild(child, parent, attr, None) childretval = self.visitchild(child, parent, attr, None)
assert not isinstance(childretval, list), 'Cannot insert list here: %s in %r' % (attr, parent)
result[attr] = childretval result[attr] = childretval
return result return result
......
...@@ -3,13 +3,24 @@ Tests accessing attributes of extension type variables ...@@ -3,13 +3,24 @@ Tests accessing attributes of extension type variables
set to None set to None
>>> obj = MyClass(2, 3) >>> obj = MyClass(2, 3)
>>> func(obj) >>> getattr_(obj)
2 2
>>> func(None) >>> getattr_(None)
Traceback (most recent call last): Traceback (most recent call last):
... ...
AttributeError: 'NoneType' object has no attribute 'a' AttributeError: 'NoneType' object has no attribute 'a'
>>> setattr_(obj)
>>> getattr_(obj)
10
>>> setattr_(None)
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute 'a'
>>> obj = MyClass(2, 3)
>>> checking(obj) >>> checking(obj)
2 2
2 2
...@@ -23,18 +34,26 @@ AttributeError: 'NoneType' object has no attribute 'a' ...@@ -23,18 +34,26 @@ AttributeError: 'NoneType' object has no attribute 'a'
""" """
cimport cython
cdef class MyClass: cdef class MyClass:
cdef int a, b cdef int a, b
def __init__(self, a, b): def __init__(self, a, b):
self.a = a self.a = a
self.b = b self.b = b
def func(MyClass var): @cython.nonecheck(True)
def getattr_(MyClass var):
print var.a print var.a
@cython.nonecheck(True)
def setattr_(MyClass var):
var.a = 10
def some(): def some():
return MyClass(4, 5) return MyClass(4, 5)
@cython.nonecheck(True)
def checking(MyClass var): def checking(MyClass var):
state = (var is None) state = (var is None)
if not state: if not state:
...@@ -44,6 +63,7 @@ def checking(MyClass var): ...@@ -44,6 +63,7 @@ def checking(MyClass var):
else: else:
print "var is None" print "var is None"
@cython.nonecheck(True)
def check_and_assign(MyClass var): def check_and_assign(MyClass var):
if var is not None: if var is not None:
print var.a print var.a
......
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