def visit_BinOp(self, node): if isinstance(node.op, ast.Pow): return self.visit(resolve_pow(node.type, [node.left, node.right])) self.generic_visit(node) if is_obj(node.left.type) or is_obj(node.right.type): op_name = type(node.op).__name__ op_method = getattr(self, '_object_%s' % op_name, None) if op_method: node = op_method(node) else: raise error.NumbaError( node, 'Unsupported binary operation for object: %s' % op_name) return node
def single_compare(self, node): rhs = node.comparators[0] if is_obj(node.left.type): node = self.single_compare_objects(node) elif node.left.type.is_pointer and rhs.type.is_pointer: # Coerce pointers to integer values before comparing node.left = nodes.CoercionNode(node.left, Py_uintptr_t) node.comparators = [nodes.CoercionNode(rhs, Py_uintptr_t)] elif node.left.type.is_complex and rhs.type.is_complex: real1, imag1 = extract(node.left) real2, imag2 = extract(rhs) op = type(node.ops[0]) if op == ast.Eq: lhs = compare(real1, ast.Eq(), real2) rhs = compare(imag1, ast.Eq(), imag2) result = ast.BoolOp(ast.And(), [lhs, rhs]) elif op == ast.NotEq: lhs = compare(real1, ast.NotEq(), real2) rhs = compare(imag1, ast.NotEq(), imag2) result = ast.BoolOp(ast.Or(), [lhs, rhs]) else: raise NotImplementedError("ordered comparisons are not " "implemented for complex numbers") node = nodes.typednode(result, bool_) elif node.left.type.is_complex and rhs.type.is_datetime: raise error.NumbaError( node, "datetime comparisons not yet implemented") return node
def single_compare(self, node): rhs = node.comparators[0] if is_obj(node.left.type): node = self.single_compare_objects(node) elif node.left.type.is_pointer and rhs.type.is_pointer: # Coerce pointers to integer values before comparing node.left = nodes.CoercionNode(node.left, Py_uintptr_t) node.comparators = [nodes.CoercionNode(rhs, Py_uintptr_t)] elif node.left.type.is_complex and rhs.type.is_complex: real1, imag1 = extract(node.left) real2, imag2 = extract(rhs) op = type(node.ops[0]) if op == ast.Eq: lhs = compare(real1, ast.Eq(), real2) rhs = compare(imag1, ast.Eq(), imag2) result = ast.BoolOp(ast.And(), [lhs, rhs]) elif op == ast.NotEq: lhs = compare(real1, ast.NotEq(), real2) rhs = compare(imag1, ast.NotEq(), imag2) result = ast.BoolOp(ast.Or(), [lhs, rhs]) else: raise NotImplementedError("ordered comparisons are not " "implemented for complex numbers") node = nodes.typednode(result, bool_) return node
def build_wrapper_function_ast(env, wrapper_lfunc, llvm_module): """ Build AST for LLVM function wrapper. lfunc: LLVM function to wrap llvm_module: module the wrapper is being defined in The resulting AST has a NativeCallNode to the wrapped function. The arguments are LLVMValueRefNode nodes which still need their llvm_value set to the object from the tuple. This happens in visit_FunctionWrapperNode during codegen. """ func = env.crnt.func func_signature = env.crnt.func_signature func_name = env.crnt.func_name # Insert external declaration lfunc = llvm_module.get_or_insert_function(func_signature.to_llvm(env.context), env.crnt.lfunc.name) # Build AST wrapper = nodes.FunctionWrapperNode(lfunc, func_signature, func, fake_pyfunc, func_name) error_return = ast.Return(nodes.CoercionNode(nodes.NULL_obj, object_)) is_closure = bool(closures.is_closure_signature(func_signature)) nargs = len(func_signature.args) - is_closure # Call wrapped function with unpacked object arguments # (delay actual arguments) args = [nodes.LLVMValueRefNode(object_, None) for i in range(nargs)] if is_closure: # Insert m_self as scope argument type closure_scope = get_closure_scope(func_signature, wrapper_lfunc.args[0]) args.insert(0, closure_scope) func_call = nodes.NativeCallNode(func_signature, args, lfunc) if not is_obj(func_signature.return_type): # Check for error using PyErr_Occurred() func_call = nodes.PyErr_OccurredNode(func_call) # Coerce and return result if func_signature.return_type.is_void: wrapper.body = func_call result_node = nodes.ObjectInjectNode(None) else: wrapper.body = None result_node = func_call wrapper.return_result = ast.Return(value=nodes.CoercionNode(result_node, object_)) # Update wrapper wrapper.error_return = error_return wrapper.cellvars = [] wrapper.wrapped_nargs = nargs wrapper.wrapped_args = args[is_closure:] return wrapper
def visit_Attribute(self, node): if (self.nopython and not node.value.type.is_module and not node.value.type.is_complex): raise error.NumbaError( node, "Cannot access Python attribute in nopython context (%s)" % node.attr) if node.value.type.is_complex: value = self.visit(node.value) return nodes.ComplexAttributeNode(value, node.attr) elif node.type.is_numpy_attribute: return nodes.ObjectInjectNode(node.type.value) elif node.type.is_numpy_dtype: dtype_type = node.type.dtype return nodes.ObjectInjectNode(dtype_type.get_dtype()) elif is_obj(node.value.type): if node.value.type.is_module: # Resolve module attributes as constants if node.type.is_module_attribute: new_node = nodes.ObjectInjectNode(node.type.value) else: new_node = nodes.ConstNode( getattr(node.value.type.module, node.attr)) else: new_node = function_util.external_call( self.context, self.llvm_module, 'PyObject_GetAttrString', args=[node.value, nodes.ConstNode(node.attr)]) return self.visit(new_node) self.generic_visit(node) return node
def visit_Attribute(self, node): if (self.nopython and not node.value.type.is_module and not node.value.type.is_complex): raise error.NumbaError( node, "Cannot access Python attribute in nopython context (%s)" % node.attr) if node.value.type.is_complex: value = self.visit(node.value) return nodes.ComplexAttributeNode(value, node.attr) elif node.type.is_numpy_attribute: return nodes.ObjectInjectNode(node.type.value) elif node.type.is_numpy_dtype: dtype_type = node.type.dtype return nodes.ObjectInjectNode(dtype_type.get_dtype()) elif is_obj(node.value.type): if node.value.type.is_module: # Resolve module attributes as constants if node.type.is_module_attribute: new_node = nodes.ObjectInjectNode(node.type.value) else: new_node = nodes.ConstNode(getattr(node.value.type.module, node.attr)) else: new_node = function_util.external_call( self.context, self.llvm_module, 'PyObject_GetAttrString', args=[node.value, nodes.ConstNode(node.attr)]) return self.visit(new_node) self.generic_visit(node) return node
def const(obj, type): if typesystem.is_obj(type): node = ObjectInjectNode(obj, type) else: node = ConstNode(obj, type) return node
def visit_NativeCallNode(self, node): if is_obj(node.signature.return_type): if self.nopython: raise error.NumbaError(node, "Cannot call function returning object in " "nopython context") self.generic_visit(node) return nodes.ObjectTempNode(node) self.generic_visit(node) return node
def visit_NativeCallNode(self, node): if is_obj(node.signature.return_type): if self.nopython: raise error.NumbaError( node, "Cannot call function returning object in " "nopython context") self.generic_visit(node) return nodes.ObjectTempNode(node) self.generic_visit(node) return node
def visit_UnaryOp(self, node): self.generic_visit(node) if is_obj(node.type): op_name = type(node.op).__name__ op_method = getattr(self, '_object_%s' % op_name, None) if op_method: node = op_method(node) else: raise error.NumbaError( node, 'Unsupported unary operation for objects: %s' % op_name) return node
def visit_CoercionNode(self, node): if not isinstance(node, nodes.CoercionNode): # CoercionNode.__new__ returns the node to be coerced if it doesn't # need coercion return node node_type = node.node.type dst_type = node.dst_type if __debug__ and self.env and self.env.debug_coercions: logger.debug('coercion: %s --> %s\n%s', node_type, dst_type, utils.pformat_ast(node)) # TODO: the below is a problem due to implicit string <-> int coercions! if (node_type.is_string and dst_type.is_numeric and not (node_type.is_pointer or node_type.is_null)): if dst_type.typename in ('char', 'uchar'): raise error.NumbaError( node, "Conversion from string to (u)char not yet supported") result = self.str_to_int(dst_type, node) elif self.nopython and (is_obj(node_type) ^ is_obj(dst_type)): raise error.NumbaError( node, "Cannot coerce to or from object in " "nopython context") elif is_obj(node.dst_type) and not is_obj(node_type): node = nodes.ObjectTempNode( nodes.CoerceToObject(node.node, node.dst_type, name=node.name)) result = self.visit(node) elif is_obj(node_type) and not is_obj(node.dst_type): node = nodes.CoerceToNative(node.node, node.dst_type, name=node.name) result = self.visit(node) elif node_type.is_null: if not dst_type.is_pointer: raise error.NumbaError( node.node, "NULL must be cast or implicitly " "coerced to a pointer type") result = self.visit(nodes.NULL.coerce(dst_type)) elif node_type.is_numeric and dst_type.is_bool: to_bool = ast.Compare(node.node, [ast.NotEq()], [nodes.const(0, node_type)]) to_bool = nodes.typednode(to_bool, bool_) result = self.visit(to_bool) else: self.generic_visit(node) if dst_type == node.node.type: result = node.node else: result = node if __debug__ and self.env and self.env.debug_coercions: logger.debug('result = %s', utils.pformat_ast(result)) return result
def setup_error_return(self, node, ret_type): """ Set FunctionDef.error_return to the AST statement that returns a "bad value" that can be used as error indicator. """ value = nodes.badval(ret_type) if value is not None: value = nodes.CoercionNode(value, dst_type=ret_type).cloneable error_return = ast.Return(value=value) if self.nopython and is_obj(self.func_signature.return_type): error_return = nodes.WithPythonNode(body=[error_return]) node.error_return = error_return
def setup_error_return(self, node, ret_type): """ Set FunctionDef.error_return to the AST statement that returns a "bad value" that can be used as error indicator. """ value = nodes.badval(ret_type) if value is not None: value = nodes.CoercionNode(value, dst_type=ret_type).cloneable error_return = ast.Return(value=value) if self.nopython and is_obj(self.func_signature.return_type): error_return = nodes.WithPythonNode(body=[error_return]) error_return = self.visit(error_return) node.error_return = error_return
def visit_CoercionNode(self, node): if not isinstance(node, nodes.CoercionNode): # CoercionNode.__new__ returns the node to be coerced if it doesn't # need coercion return node node_type = node.node.type dst_type = node.dst_type if __debug__ and self.env and self.env.debug_coercions: logger.debug('coercion: %s --> %s\n%s', node_type, dst_type, utils.pformat_ast(node)) # TODO: the below is a problem due to implicit string <-> int coercions! if (node_type.is_string and dst_type.is_numeric and not (node_type.is_pointer or node_type.is_null)): if dst_type.typename in ('char', 'uchar'): raise error.NumbaError( node, "Conversion from string to (u)char not yet supported") result = self.str_to_int(dst_type, node) elif self.nopython and (is_obj(node_type) ^ is_obj(dst_type)): raise error.NumbaError(node, "Cannot coerce to or from object in " "nopython context") elif is_obj(node.dst_type) and not is_obj(node_type): node = nodes.ObjectTempNode(nodes.CoerceToObject( node.node, node.dst_type, name=node.name)) result = self.visit(node) elif is_obj(node_type) and not is_obj(node.dst_type): node = nodes.CoerceToNative(node.node, node.dst_type, name=node.name) result = self.visit(node) elif node_type.is_null: if not dst_type.is_pointer: raise error.NumbaError(node.node, "NULL must be cast or implicitly " "coerced to a pointer type") result = self.visit(nodes.NULL.coerce(dst_type)) elif node_type.is_numeric and dst_type.is_bool: to_bool = ast.Compare(node.node, [ast.NotEq()], [nodes.const(0, node_type)]) to_bool = nodes.typednode(to_bool, bool_) result = self.visit(to_bool) else: self.generic_visit(node) if dst_type == node.node.type: result = node.node else: result = node if __debug__ and self.env and self.env.debug_coercions: logger.debug('result = %s', utils.pformat_ast(result)) return result
def visit_Name(self, node): if (is_obj(node.type) and isinstance(node.ctx, ast.Load) and getattr(node, 'cf_maybe_null', False)): # Check for unbound objects and raise UnboundLocalError if so value = nodes.LLVMValueRefNode(Py_uintptr_t, None) node.loaded_name = value exc_msg = node.variable.name if hasattr(node, 'lineno'): exc_msg = '%s%s' % (error.format_pos(node), exc_msg) check_unbound = nodes.CheckErrorNode( value, badval=nodes.const(0, Py_uintptr_t), exc_type=UnboundLocalError, exc_msg=exc_msg) node.check_unbound = self.visit(check_unbound) return node
def visit_Name(self, node): if (is_obj(node.type) and isinstance(node.ctx, ast.Load) and getattr(node, 'cf_maybe_null', False)): # Check for unbound objects and raise UnboundLocalError if so value = nodes.LLVMValueRefNode(Py_uintptr_t, None) node.loaded_name = value exc_msg = node.variable.name if hasattr(node, 'lineno'): exc_msg = '%s%s' % (error.format_pos(node), exc_msg) check_unbound = nodes.CheckErrorNode(value, badval=nodes.const( 0, Py_uintptr_t), exc_type=UnboundLocalError, exc_msg=exc_msg) node.check_unbound = self.visit(check_unbound) return node
def single_compare(self, node): rhs = node.comparators[0] if is_obj(node.left.type): node = self.single_compare_objects(node) elif node.left.type.is_pointer and rhs.type.is_pointer: # Coerce pointers to integer values before comparing node.left = nodes.CoercionNode(node.left, Py_uintptr_t) node.comparators = [nodes.CoercionNode(rhs, Py_uintptr_t)] elif node.left.type.is_complex and rhs.type.is_complex: real1, imag1 = extract(node.left) real2, imag2 = extract(rhs) lhs = compare(real1, real2) rhs = compare(imag1, imag2) result = ast.BoolOp(ast.And(), [lhs, rhs]) node = nodes.typednode(result, bool_) return node
def lstr(self, types, fmt=None): "Get an llvm format string for the given types" typestrs = [] result_types = [] for type in types: if is_obj(type): type = object_ elif type.is_int: type = promote_to_native(type) result_types.append(type) typestrs.append(self.type_to_buildvalue_str[type]) str = "".join(typestrs) if fmt is not None: str = fmt % str if debug.debug_conversion: self.translator.puts("fmt: %s" % str) result = self._create_llvm_string(str) return result_types, result
def lstr(self, types, fmt=None): "Get an llvm format string for the given types" typestrs = [] result_types = [] for type in types: if is_obj(type): type = object_ elif type.is_int: type = promote_to_native(type) result_types.append(type) typestrs.append(self.type_to_buildvalue_str[type]) str = "".join(typestrs) if fmt is not None: str = fmt % str if debug_conversion: self.translator.puts("fmt: %s" % str) result = self._create_llvm_string(str) return result_types, result
def visit_Assign(self, node): target = node.targets[0] target_is_subscript = (len(node.targets) == 1 and isinstance(target, ast.Subscript)) if target_is_subscript and is_obj(target.type): # Slice assignment / index assignment w/ objects # TODO: discount array indexing with dtype object target = self.visit(target) obj = target.value key = target.slice value = self.visit(node.value) call = function_util.external_call(self.context, self.llvm_module, 'PyObject_SetItem', args=[obj, key, value]) return self.visit(call) elif target.type.is_struct and nodes.is_name(target): node = self.allocate_struct_on_stack(node, target) return node self.generic_visit(node) return node
def visit_CoercionNode(self, node): if not isinstance(node, nodes.CoercionNode): # CoercionNode.__new__ returns the node to be coerced if it doesn't # need coercion return node node_type = node.node.type dst_type = node.dst_type if __debug__ and self.env and self.env.debug_coercions: logger.debug('coercion: %s --> %s\n%s', node_type, dst_type, utils.pformat_ast(node)) if self.nopython and (is_obj(node_type) ^ is_obj(dst_type)): raise error.NumbaError( node, "Cannot coerce to or from object in " "nopython context") if is_obj(node.dst_type) and not is_obj(node_type): node = nodes.ObjectTempNode( nodes.CoerceToObject(node.node, node.dst_type, name=node.name)) result = self.visit(node) elif is_obj(node_type) and not is_obj(node.dst_type): node = nodes.CoerceToNative(node.node, node.dst_type, name=node.name) result = self.visit(node) elif node_type.is_null: if not dst_type.is_pointer: raise error.NumbaError( node.node, "NULL must be cast or implicitly " "coerced to a pointer type") result = self.visit(nodes.NULL.coerce(dst_type)) elif node_type.is_numeric and dst_type.is_bool: to_bool = ast.Compare(node.node, [ast.NotEq()], [nodes.const(0, node_type)]) to_bool = nodes.typednode(to_bool, bool_) result = self.visit(to_bool) elif node_type.is_c_string and dst_type.is_numeric: # TODO: int <-> string conversions are explicit, this should not # TODO: be a coercion if self.nopython: node = nodes.CoercionNode( function_util.external_call( self.context, self.llvm_module, ('atol' if dst_type.is_int else 'atof'), args=[node.node]), dst_type, name=node.name, ) else: if dst_type.is_int: cvtobj = function_util.external_call( self.context, self.llvm_module, 'PyInt_FromString' if not PY3 else 'PyLong_FromString', args=[node.node, nodes.NULL, nodes.const(10, int_)]) else: cvtobj = function_util.external_call( self.context, self.llvm_module, 'PyFloat_FromString', args=[node.node, nodes.const(0, Py_ssize_t)]) node = nodes.CoerceToNative(nodes.ObjectTempNode(cvtobj), dst_type, name=node.name) result = self.visit(node) else: self.generic_visit(node) if dst_type == node.node.type: result = node.node else: result = node if __debug__ and self.env and self.env.debug_coercions: logger.debug('result = %s', utils.pformat_ast(result)) return result
def build_wrapper_function_ast(env, wrapper_lfunc, llvm_module): """ Build AST for LLVM function wrapper. lfunc: LLVM function to wrap llvm_module: module the wrapper is being defined in The resulting AST has a NativeCallNode to the wrapped function. The arguments are LLVMValueRefNode nodes which still need their llvm_value set to the object from the tuple. This happens in visit_FunctionWrapperNode during codegen. """ func = env.crnt.func func_signature = env.crnt.func_signature func_name = env.crnt.func_name # Insert external declaration lfunc = llvm_module.get_or_insert_function( func_signature.to_llvm(env.context), env.crnt.lfunc.name) # Build AST wrapper = nodes.FunctionWrapperNode(lfunc, func_signature, func, fake_pyfunc, func_name) error_return = ast.Return(nodes.CoercionNode(nodes.NULL_obj, object_)) is_closure = bool(closures.is_closure_signature(func_signature)) nargs = len(func_signature.args) - is_closure # Call wrapped function with unpacked object arguments # (delay actual arguments) args = [nodes.LLVMValueRefNode(object_, None) for i in range(nargs)] if is_closure: # Insert m_self as scope argument type closure_scope = get_closure_scope(func_signature, wrapper_lfunc.args[0]) args.insert(0, closure_scope) func_call = nodes.NativeCallNode(func_signature, args, lfunc) if not is_obj(func_signature.return_type): # Check for error using PyErr_Occurred() func_call = nodes.PyErr_OccurredNode(func_call) # Coerce and return result if func_signature.return_type.is_void: wrapper.body = func_call result_node = nodes.ObjectInjectNode(None) else: wrapper.body = None result_node = func_call wrapper.return_result = ast.Return(value=nodes.CoercionNode(result_node, object_)) # Update wrapper wrapper.error_return = error_return wrapper.cellvars = [] wrapper.wrapped_nargs = nargs wrapper.wrapped_args = args[is_closure:] return wrapper
def visit_CoercionNode(self, node): if not isinstance(node, nodes.CoercionNode): # CoercionNode.__new__ returns the node to be coerced if it doesn't # need coercion return node node_type = node.node.type dst_type = node.dst_type if __debug__ and self.env and self.env.debug_coercions: logger.debug('coercion: %s --> %s\n%s', node_type, dst_type, utils.pformat_ast(node)) if self.nopython and is_obj(node_type): raise error.NumbaError(node, "Cannot coerce to or from object in " "nopython context") if is_obj(node.dst_type) and not is_obj(node_type): node = nodes.ObjectTempNode(nodes.CoerceToObject( node.node, node.dst_type, name=node.name)) result = self.visit(node) elif is_obj(node_type) and not is_obj(node.dst_type): node = nodes.CoerceToNative(node.node, node.dst_type, name=node.name) result = self.visit(node) elif node_type.is_null: if not dst_type.is_pointer: raise error.NumbaError(node.node, "NULL must be cast or implicitly " "coerced to a pointer type") result = self.visit(nodes.NULL.coerce(dst_type)) elif node_type.is_numeric and dst_type.is_bool: to_bool = ast.Compare(node.node, [ast.NotEq()], [nodes.const(0, node_type)]) to_bool = nodes.typednode(to_bool, bool_) result = self.visit(to_bool) elif node_type.is_c_string and dst_type.is_numeric: # TODO: int <-> string conversions are explicit, this should not # TODO: be a coercion if self.nopython: node = nodes.CoercionNode( function_util.external_call( self.context, self.llvm_module, ('atol' if dst_type.is_int else 'atof'), args=[node.node]), dst_type, name=node.name,) else: if dst_type.is_int: cvtobj = function_util.external_call( self.context, self.llvm_module, 'PyInt_FromString' if not PY3 else 'PyLong_FromString', args=[node.node, nodes.NULL, nodes.const(10, int_)]) else: cvtobj = function_util.external_call( self.context, self.llvm_module, 'PyFloat_FromString', args=[node.node, nodes.const(0, Py_ssize_t)]) node = nodes.CoerceToNative(nodes.ObjectTempNode(cvtobj), dst_type, name=node.name) result = self.visit(node) else: self.generic_visit(node) if dst_type == node.node.type: result = node.node else: result = node if __debug__ and self.env and self.env.debug_coercions: logger.debug('result = %s', utils.pformat_ast(result)) return result