1"""Code generation for native function bodies.""" 2 3from typing import Union, Optional 4from typing_extensions import Final 5 6from mypyc.common import ( 7 REG_PREFIX, NATIVE_PREFIX, STATIC_PREFIX, TYPE_PREFIX, MODULE_PREFIX, 8) 9from mypyc.codegen.emit import Emitter 10from mypyc.ir.ops import ( 11 OpVisitor, Goto, Branch, Return, Assign, Integer, LoadErrorValue, GetAttr, SetAttr, 12 LoadStatic, InitStatic, TupleGet, TupleSet, Call, IncRef, DecRef, Box, Cast, Unbox, 13 BasicBlock, Value, MethodCall, Unreachable, NAMESPACE_STATIC, NAMESPACE_TYPE, NAMESPACE_MODULE, 14 RaiseStandardError, CallC, LoadGlobal, Truncate, IntOp, LoadMem, GetElementPtr, 15 LoadAddress, ComparisonOp, SetMem, Register, LoadLiteral, AssignMulti, KeepAlive 16) 17from mypyc.ir.rtypes import ( 18 RType, RTuple, RArray, is_tagged, is_int32_rprimitive, is_int64_rprimitive, RStruct, 19 is_pointer_rprimitive, is_int_rprimitive 20) 21from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD, all_values 22from mypyc.ir.class_ir import ClassIR 23from mypyc.ir.pprint import generate_names_for_ir 24 25# Whether to insert debug asserts for all error handling, to quickly 26# catch errors propagating without exceptions set. 27DEBUG_ERRORS = False 28 29 30def native_function_type(fn: FuncIR, emitter: Emitter) -> str: 31 args = ', '.join(emitter.ctype(arg.type) for arg in fn.args) or 'void' 32 ret = emitter.ctype(fn.ret_type) 33 return '{} (*)({})'.format(ret, args) 34 35 36def native_function_header(fn: FuncDecl, emitter: Emitter) -> str: 37 args = [] 38 for arg in fn.sig.args: 39 args.append('{}{}{}'.format(emitter.ctype_spaced(arg.type), REG_PREFIX, arg.name)) 40 41 return '{ret_type}{name}({args})'.format( 42 ret_type=emitter.ctype_spaced(fn.sig.ret_type), 43 name=emitter.native_function_name(fn), 44 args=', '.join(args) or 'void') 45 46 47def generate_native_function(fn: FuncIR, 48 emitter: Emitter, 49 source_path: str, 50 module_name: str) -> None: 51 declarations = Emitter(emitter.context) 52 names = generate_names_for_ir(fn.arg_regs, fn.blocks) 53 body = Emitter(emitter.context, names) 54 visitor = FunctionEmitterVisitor(body, declarations, source_path, module_name) 55 56 declarations.emit_line('{} {{'.format(native_function_header(fn.decl, emitter))) 57 body.indent() 58 59 for r in all_values(fn.arg_regs, fn.blocks): 60 if isinstance(r.type, RTuple): 61 emitter.declare_tuple_struct(r.type) 62 if isinstance(r.type, RArray): 63 continue # Special: declared on first assignment 64 65 if r in fn.arg_regs: 66 continue # Skip the arguments 67 68 ctype = emitter.ctype_spaced(r.type) 69 init = '' 70 declarations.emit_line('{ctype}{prefix}{name}{init};'.format(ctype=ctype, 71 prefix=REG_PREFIX, 72 name=names[r], 73 init=init)) 74 75 # Before we emit the blocks, give them all labels 76 blocks = fn.blocks 77 for i, block in enumerate(blocks): 78 block.label = i 79 80 for i in range(len(blocks)): 81 block = blocks[i] 82 next_block = None 83 if i + 1 < len(blocks): 84 next_block = blocks[i + 1] 85 body.emit_label(block) 86 visitor.next_block = next_block 87 for op in block.ops: 88 op.accept(visitor) 89 90 body.emit_line('}') 91 92 emitter.emit_from_emitter(declarations) 93 emitter.emit_from_emitter(body) 94 95 96class FunctionEmitterVisitor(OpVisitor[None]): 97 def __init__(self, 98 emitter: Emitter, 99 declarations: Emitter, 100 source_path: str, 101 module_name: str) -> None: 102 self.emitter = emitter 103 self.names = emitter.names 104 self.declarations = declarations 105 self.source_path = source_path 106 self.module_name = module_name 107 self.literals = emitter.context.literals 108 self.next_block = None # type: Optional[BasicBlock] 109 110 def temp_name(self) -> str: 111 return self.emitter.temp_name() 112 113 def visit_goto(self, op: Goto) -> None: 114 if op.label is not self.next_block: 115 self.emit_line('goto %s;' % self.label(op.label)) 116 117 def visit_branch(self, op: Branch) -> None: 118 true, false = op.true, op.false 119 negated = op.negated 120 if true is self.next_block and op.traceback_entry is None: 121 # Switch true/false since it avoids an else block. 122 true, false = false, true 123 negated = not negated 124 125 neg = '!' if negated else '' 126 cond = '' 127 if op.op == Branch.BOOL: 128 expr_result = self.reg(op.value) 129 cond = '{}{}'.format(neg, expr_result) 130 elif op.op == Branch.IS_ERROR: 131 typ = op.value.type 132 compare = '!=' if negated else '==' 133 if isinstance(typ, RTuple): 134 # TODO: What about empty tuple? 135 cond = self.emitter.tuple_undefined_check_cond(typ, 136 self.reg(op.value), 137 self.c_error_value, 138 compare) 139 else: 140 cond = '{} {} {}'.format(self.reg(op.value), 141 compare, 142 self.c_error_value(typ)) 143 else: 144 assert False, "Invalid branch" 145 146 # For error checks, tell the compiler the branch is unlikely 147 if op.traceback_entry is not None or op.rare: 148 cond = 'unlikely({})'.format(cond) 149 150 if false is self.next_block: 151 if op.traceback_entry is None: 152 self.emit_line('if ({}) goto {};'.format(cond, self.label(true))) 153 else: 154 self.emit_line('if ({}) {{'.format(cond)) 155 self.emit_traceback(op) 156 self.emit_lines( 157 'goto %s;' % self.label(true), 158 '}' 159 ) 160 else: 161 self.emit_line('if ({}) {{'.format(cond)) 162 self.emit_traceback(op) 163 self.emit_lines( 164 'goto %s;' % self.label(true), 165 '} else', 166 ' goto %s;' % self.label(false) 167 ) 168 169 def visit_return(self, op: Return) -> None: 170 value_str = self.reg(op.value) 171 self.emit_line('return %s;' % value_str) 172 173 def visit_tuple_set(self, op: TupleSet) -> None: 174 dest = self.reg(op) 175 tuple_type = op.tuple_type 176 self.emitter.declare_tuple_struct(tuple_type) 177 if len(op.items) == 0: # empty tuple 178 self.emit_line('{}.empty_struct_error_flag = 0;'.format(dest)) 179 else: 180 for i, item in enumerate(op.items): 181 self.emit_line('{}.f{} = {};'.format(dest, i, self.reg(item))) 182 self.emit_inc_ref(dest, tuple_type) 183 184 def visit_assign(self, op: Assign) -> None: 185 dest = self.reg(op.dest) 186 src = self.reg(op.src) 187 # clang whines about self assignment (which we might generate 188 # for some casts), so don't emit it. 189 if dest != src: 190 self.emit_line('%s = %s;' % (dest, src)) 191 192 def visit_assign_multi(self, op: AssignMulti) -> None: 193 typ = op.dest.type 194 assert isinstance(typ, RArray) 195 dest = self.reg(op.dest) 196 # RArray values can only be assigned to once, so we can always 197 # declare them on initialization. 198 self.emit_line('%s%s[%d] = {%s};' % ( 199 self.emitter.ctype_spaced(typ.item_type), 200 dest, 201 len(op.src), 202 ', '.join(self.reg(s) for s in op.src))) 203 204 def visit_load_error_value(self, op: LoadErrorValue) -> None: 205 if isinstance(op.type, RTuple): 206 values = [self.c_undefined_value(item) for item in op.type.types] 207 tmp = self.temp_name() 208 self.emit_line('%s %s = { %s };' % (self.ctype(op.type), tmp, ', '.join(values))) 209 self.emit_line('%s = %s;' % (self.reg(op), tmp)) 210 else: 211 self.emit_line('%s = %s;' % (self.reg(op), 212 self.c_error_value(op.type))) 213 214 def visit_load_literal(self, op: LoadLiteral) -> None: 215 index = self.literals.literal_index(op.value) 216 s = repr(op.value) 217 if not any(x in s for x in ('/*', '*/', '\0')): 218 ann = ' /* %s */' % s 219 else: 220 ann = '' 221 if not is_int_rprimitive(op.type): 222 self.emit_line('%s = CPyStatics[%d];%s' % (self.reg(op), index, ann)) 223 else: 224 self.emit_line('%s = (CPyTagged)CPyStatics[%d] | 1;%s' % ( 225 self.reg(op), index, ann)) 226 227 def get_attr_expr(self, obj: str, op: Union[GetAttr, SetAttr], decl_cl: ClassIR) -> str: 228 """Generate attribute accessor for normal (non-property) access. 229 230 This either has a form like obj->attr_name for attributes defined in non-trait 231 classes, and *(obj + attr_offset) for attributes defined by traits. We also 232 insert all necessary C casts here. 233 """ 234 cast = '({} *)'.format(op.class_type.struct_name(self.emitter.names)) 235 if decl_cl.is_trait and op.class_type.class_ir.is_trait: 236 # For pure trait access find the offset first, offsets 237 # are ordered by attribute position in the cl.attributes dict. 238 # TODO: pre-calculate the mapping to make this faster. 239 trait_attr_index = list(decl_cl.attributes).index(op.attr) 240 # TODO: reuse these names somehow? 241 offset = self.emitter.temp_name() 242 self.declarations.emit_line('size_t {};'.format(offset)) 243 self.emitter.emit_line('{} = {};'.format( 244 offset, 245 'CPy_FindAttrOffset({}, {}, {})'.format( 246 self.emitter.type_struct_name(decl_cl), 247 '({}{})->vtable'.format(cast, obj), 248 trait_attr_index, 249 ) 250 )) 251 attr_cast = '({} *)'.format(self.ctype(op.class_type.attr_type(op.attr))) 252 return '*{}((char *){} + {})'.format(attr_cast, obj, offset) 253 else: 254 # Cast to something non-trait. Note: for this to work, all struct 255 # members for non-trait classes must obey monotonic linear growth. 256 if op.class_type.class_ir.is_trait: 257 assert not decl_cl.is_trait 258 cast = '({} *)'.format(decl_cl.struct_name(self.emitter.names)) 259 return '({}{})->{}'.format( 260 cast, obj, self.emitter.attr(op.attr) 261 ) 262 263 def visit_get_attr(self, op: GetAttr) -> None: 264 dest = self.reg(op) 265 obj = self.reg(op.obj) 266 rtype = op.class_type 267 cl = rtype.class_ir 268 attr_rtype, decl_cl = cl.attr_details(op.attr) 269 if cl.get_method(op.attr): 270 # Properties are essentially methods, so use vtable access for them. 271 version = '_TRAIT' if cl.is_trait else '' 272 self.emit_line('%s = CPY_GET_ATTR%s(%s, %s, %d, %s, %s); /* %s */' % ( 273 dest, 274 version, 275 obj, 276 self.emitter.type_struct_name(rtype.class_ir), 277 rtype.getter_index(op.attr), 278 rtype.struct_name(self.names), 279 self.ctype(rtype.attr_type(op.attr)), 280 op.attr)) 281 else: 282 # Otherwise, use direct or offset struct access. 283 attr_expr = self.get_attr_expr(obj, op, decl_cl) 284 self.emitter.emit_line('{} = {};'.format(dest, attr_expr)) 285 if attr_rtype.is_refcounted: 286 self.emitter.emit_undefined_attr_check( 287 attr_rtype, attr_expr, '==', unlikely=True 288 ) 289 exc_class = 'PyExc_AttributeError' 290 self.emitter.emit_lines( 291 'PyErr_SetString({}, "attribute {} of {} undefined");'.format( 292 exc_class, repr(op.attr), repr(cl.name)), 293 '} else {') 294 self.emitter.emit_inc_ref(attr_expr, attr_rtype) 295 self.emitter.emit_line('}') 296 297 def visit_set_attr(self, op: SetAttr) -> None: 298 dest = self.reg(op) 299 obj = self.reg(op.obj) 300 src = self.reg(op.src) 301 rtype = op.class_type 302 cl = rtype.class_ir 303 attr_rtype, decl_cl = cl.attr_details(op.attr) 304 if cl.get_method(op.attr): 305 # Again, use vtable access for properties... 306 version = '_TRAIT' if cl.is_trait else '' 307 self.emit_line('%s = CPY_SET_ATTR%s(%s, %s, %d, %s, %s, %s); /* %s */' % ( 308 dest, 309 version, 310 obj, 311 self.emitter.type_struct_name(rtype.class_ir), 312 rtype.setter_index(op.attr), 313 src, 314 rtype.struct_name(self.names), 315 self.ctype(rtype.attr_type(op.attr)), 316 op.attr)) 317 else: 318 # ...and struct access for normal attributes. 319 attr_expr = self.get_attr_expr(obj, op, decl_cl) 320 if attr_rtype.is_refcounted: 321 self.emitter.emit_undefined_attr_check(attr_rtype, attr_expr, '!=') 322 self.emitter.emit_dec_ref(attr_expr, attr_rtype) 323 self.emitter.emit_line('}') 324 # This steal the reference to src, so we don't need to increment the arg 325 self.emitter.emit_lines( 326 '{} = {};'.format(attr_expr, src), 327 '{} = 1;'.format(dest), 328 ) 329 330 PREFIX_MAP = { 331 NAMESPACE_STATIC: STATIC_PREFIX, 332 NAMESPACE_TYPE: TYPE_PREFIX, 333 NAMESPACE_MODULE: MODULE_PREFIX, 334 } # type: Final 335 336 def visit_load_static(self, op: LoadStatic) -> None: 337 dest = self.reg(op) 338 prefix = self.PREFIX_MAP[op.namespace] 339 name = self.emitter.static_name(op.identifier, op.module_name, prefix) 340 if op.namespace == NAMESPACE_TYPE: 341 name = '(PyObject *)%s' % name 342 ann = '' 343 if op.ann: 344 s = repr(op.ann) 345 if not any(x in s for x in ('/*', '*/', '\0')): 346 ann = ' /* %s */' % s 347 self.emit_line('%s = %s;%s' % (dest, name, ann)) 348 349 def visit_init_static(self, op: InitStatic) -> None: 350 value = self.reg(op.value) 351 prefix = self.PREFIX_MAP[op.namespace] 352 name = self.emitter.static_name(op.identifier, op.module_name, prefix) 353 if op.namespace == NAMESPACE_TYPE: 354 value = '(PyTypeObject *)%s' % value 355 self.emit_line('%s = %s;' % (name, value)) 356 self.emit_inc_ref(name, op.value.type) 357 358 def visit_tuple_get(self, op: TupleGet) -> None: 359 dest = self.reg(op) 360 src = self.reg(op.src) 361 self.emit_line('{} = {}.f{};'.format(dest, src, op.index)) 362 self.emit_inc_ref(dest, op.type) 363 364 def get_dest_assign(self, dest: Value) -> str: 365 if not dest.is_void: 366 return self.reg(dest) + ' = ' 367 else: 368 return '' 369 370 def visit_call(self, op: Call) -> None: 371 """Call native function.""" 372 dest = self.get_dest_assign(op) 373 args = ', '.join(self.reg(arg) for arg in op.args) 374 lib = self.emitter.get_group_prefix(op.fn) 375 cname = op.fn.cname(self.names) 376 self.emit_line('%s%s%s%s(%s);' % (dest, lib, NATIVE_PREFIX, cname, args)) 377 378 def visit_method_call(self, op: MethodCall) -> None: 379 """Call native method.""" 380 dest = self.get_dest_assign(op) 381 obj = self.reg(op.obj) 382 383 rtype = op.receiver_type 384 class_ir = rtype.class_ir 385 name = op.method 386 method = rtype.class_ir.get_method(name) 387 assert method is not None 388 389 # Can we call the method directly, bypassing vtable? 390 is_direct = class_ir.is_method_final(name) 391 392 # The first argument gets omitted for static methods and 393 # turned into the class for class methods 394 obj_args = ( 395 [] if method.decl.kind == FUNC_STATICMETHOD else 396 ['(PyObject *)Py_TYPE({})'.format(obj)] if method.decl.kind == FUNC_CLASSMETHOD else 397 [obj]) 398 args = ', '.join(obj_args + [self.reg(arg) for arg in op.args]) 399 mtype = native_function_type(method, self.emitter) 400 version = '_TRAIT' if rtype.class_ir.is_trait else '' 401 if is_direct: 402 # Directly call method, without going through the vtable. 403 lib = self.emitter.get_group_prefix(method.decl) 404 self.emit_line('{}{}{}{}({});'.format( 405 dest, lib, NATIVE_PREFIX, method.cname(self.names), args)) 406 else: 407 # Call using vtable. 408 method_idx = rtype.method_index(name) 409 self.emit_line('{}CPY_GET_METHOD{}({}, {}, {}, {}, {})({}); /* {} */'.format( 410 dest, version, obj, self.emitter.type_struct_name(rtype.class_ir), 411 method_idx, rtype.struct_name(self.names), mtype, args, op.method)) 412 413 def visit_inc_ref(self, op: IncRef) -> None: 414 src = self.reg(op.src) 415 self.emit_inc_ref(src, op.src.type) 416 417 def visit_dec_ref(self, op: DecRef) -> None: 418 src = self.reg(op.src) 419 self.emit_dec_ref(src, op.src.type, op.is_xdec) 420 421 def visit_box(self, op: Box) -> None: 422 self.emitter.emit_box(self.reg(op.src), self.reg(op), op.src.type, can_borrow=True) 423 424 def visit_cast(self, op: Cast) -> None: 425 self.emitter.emit_cast(self.reg(op.src), self.reg(op), op.type, 426 src_type=op.src.type) 427 428 def visit_unbox(self, op: Unbox) -> None: 429 self.emitter.emit_unbox(self.reg(op.src), self.reg(op), op.type) 430 431 def visit_unreachable(self, op: Unreachable) -> None: 432 self.emitter.emit_line('CPy_Unreachable();') 433 434 def visit_raise_standard_error(self, op: RaiseStandardError) -> None: 435 # TODO: Better escaping of backspaces and such 436 if op.value is not None: 437 if isinstance(op.value, str): 438 message = op.value.replace('"', '\\"') 439 self.emitter.emit_line( 440 'PyErr_SetString(PyExc_{}, "{}");'.format(op.class_name, message)) 441 elif isinstance(op.value, Value): 442 self.emitter.emit_line( 443 'PyErr_SetObject(PyExc_{}, {});'.format(op.class_name, 444 self.emitter.reg(op.value))) 445 else: 446 assert False, 'op value type must be either str or Value' 447 else: 448 self.emitter.emit_line('PyErr_SetNone(PyExc_{});'.format(op.class_name)) 449 self.emitter.emit_line('{} = 0;'.format(self.reg(op))) 450 451 def visit_call_c(self, op: CallC) -> None: 452 if op.is_void: 453 dest = '' 454 else: 455 dest = self.get_dest_assign(op) 456 args = ', '.join(self.reg(arg) for arg in op.args) 457 self.emitter.emit_line("{}{}({});".format(dest, op.function_name, args)) 458 459 def visit_truncate(self, op: Truncate) -> None: 460 dest = self.reg(op) 461 value = self.reg(op.src) 462 # for C backend the generated code are straight assignments 463 self.emit_line("{} = {};".format(dest, value)) 464 465 def visit_load_global(self, op: LoadGlobal) -> None: 466 dest = self.reg(op) 467 ann = '' 468 if op.ann: 469 s = repr(op.ann) 470 if not any(x in s for x in ('/*', '*/', '\0')): 471 ann = ' /* %s */' % s 472 self.emit_line('%s = %s;%s' % (dest, op.identifier, ann)) 473 474 def visit_int_op(self, op: IntOp) -> None: 475 dest = self.reg(op) 476 lhs = self.reg(op.lhs) 477 rhs = self.reg(op.rhs) 478 self.emit_line('%s = %s %s %s;' % (dest, lhs, op.op_str[op.op], rhs)) 479 480 def visit_comparison_op(self, op: ComparisonOp) -> None: 481 dest = self.reg(op) 482 lhs = self.reg(op.lhs) 483 rhs = self.reg(op.rhs) 484 lhs_cast = "" 485 rhs_cast = "" 486 signed_op = {ComparisonOp.SLT, ComparisonOp.SGT, ComparisonOp.SLE, ComparisonOp.SGE} 487 unsigned_op = {ComparisonOp.ULT, ComparisonOp.UGT, ComparisonOp.ULE, ComparisonOp.UGE} 488 if op.op in signed_op: 489 lhs_cast = self.emit_signed_int_cast(op.lhs.type) 490 rhs_cast = self.emit_signed_int_cast(op.rhs.type) 491 elif op.op in unsigned_op: 492 lhs_cast = self.emit_unsigned_int_cast(op.lhs.type) 493 rhs_cast = self.emit_unsigned_int_cast(op.rhs.type) 494 self.emit_line('%s = %s%s %s %s%s;' % (dest, lhs_cast, lhs, 495 op.op_str[op.op], rhs_cast, rhs)) 496 497 def visit_load_mem(self, op: LoadMem) -> None: 498 dest = self.reg(op) 499 src = self.reg(op.src) 500 # TODO: we shouldn't dereference to type that are pointer type so far 501 type = self.ctype(op.type) 502 self.emit_line('%s = *(%s *)%s;' % (dest, type, src)) 503 504 def visit_set_mem(self, op: SetMem) -> None: 505 dest = self.reg(op.dest) 506 src = self.reg(op.src) 507 dest_type = self.ctype(op.dest_type) 508 # clang whines about self assignment (which we might generate 509 # for some casts), so don't emit it. 510 if dest != src: 511 self.emit_line('*(%s *)%s = %s;' % (dest_type, dest, src)) 512 513 def visit_get_element_ptr(self, op: GetElementPtr) -> None: 514 dest = self.reg(op) 515 src = self.reg(op.src) 516 # TODO: support tuple type 517 assert isinstance(op.src_type, RStruct) 518 assert op.field in op.src_type.names, "Invalid field name." 519 self.emit_line('%s = (%s)&((%s *)%s)->%s;' % (dest, op.type._ctype, op.src_type.name, 520 src, op.field)) 521 522 def visit_load_address(self, op: LoadAddress) -> None: 523 typ = op.type 524 dest = self.reg(op) 525 src = self.reg(op.src) if isinstance(op.src, Register) else op.src 526 self.emit_line('%s = (%s)&%s;' % (dest, typ._ctype, src)) 527 528 def visit_keep_alive(self, op: KeepAlive) -> None: 529 # This is a no-op. 530 pass 531 532 # Helpers 533 534 def label(self, label: BasicBlock) -> str: 535 return self.emitter.label(label) 536 537 def reg(self, reg: Value) -> str: 538 if isinstance(reg, Integer): 539 val = reg.value 540 if val == 0 and is_pointer_rprimitive(reg.type): 541 return "NULL" 542 s = str(val) 543 if val >= (1 << 31): 544 # Avoid overflowing signed 32-bit int 545 s += 'U' 546 return s 547 else: 548 return self.emitter.reg(reg) 549 550 def ctype(self, rtype: RType) -> str: 551 return self.emitter.ctype(rtype) 552 553 def c_error_value(self, rtype: RType) -> str: 554 return self.emitter.c_error_value(rtype) 555 556 def c_undefined_value(self, rtype: RType) -> str: 557 return self.emitter.c_undefined_value(rtype) 558 559 def emit_line(self, line: str) -> None: 560 self.emitter.emit_line(line) 561 562 def emit_lines(self, *lines: str) -> None: 563 self.emitter.emit_lines(*lines) 564 565 def emit_inc_ref(self, dest: str, rtype: RType) -> None: 566 self.emitter.emit_inc_ref(dest, rtype) 567 568 def emit_dec_ref(self, dest: str, rtype: RType, is_xdec: bool) -> None: 569 self.emitter.emit_dec_ref(dest, rtype, is_xdec) 570 571 def emit_declaration(self, line: str) -> None: 572 self.declarations.emit_line(line) 573 574 def emit_traceback(self, op: Branch) -> None: 575 if op.traceback_entry is not None: 576 globals_static = self.emitter.static_name('globals', self.module_name) 577 self.emit_line('CPy_AddTraceback("%s", "%s", %d, %s);' % ( 578 self.source_path.replace("\\", "\\\\"), 579 op.traceback_entry[0], 580 op.traceback_entry[1], 581 globals_static)) 582 if DEBUG_ERRORS: 583 self.emit_line('assert(PyErr_Occurred() != NULL && "failure w/o err!");') 584 585 def emit_signed_int_cast(self, type: RType) -> str: 586 if is_tagged(type): 587 return '(Py_ssize_t)' 588 else: 589 return '' 590 591 def emit_unsigned_int_cast(self, type: RType) -> str: 592 if is_int32_rprimitive(type): 593 return '(uint32_t)' 594 elif is_int64_rprimitive(type): 595 return '(uint64_t)' 596 else: 597 return '' 598