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