2
2
3
3
from mypy .backports import OrderedDict
4
4
from typing import List , Set , Dict , Optional , Callable , Union , Tuple
5
+ from typing_extensions import Final
6
+
5
7
import sys
6
8
7
9
from mypyc .common import (
23
25
from mypyc .sametype import is_same_type
24
26
from mypyc .codegen .literals import Literals
25
27
28
+ # Whether to insert debug asserts for all error handling, to quickly
29
+ # catch errors propagating without exceptions set.
30
+ DEBUG_ERRORS : Final = False
31
+
26
32
27
33
class HeaderDeclaration :
28
34
"""A representation of a declaration in C.
@@ -104,6 +110,20 @@ def __init__(self, label: str) -> None:
104
110
self .label = label
105
111
106
112
113
+ class TracebackAndGotoHandler (ErrorHandler ):
114
+ """Add traceback item and goto label on error."""
115
+
116
+ def __init__ (self ,
117
+ label : str ,
118
+ source_path : str ,
119
+ module_name : str ,
120
+ traceback_entry : Tuple [str , int ]) -> None :
121
+ self .label = label
122
+ self .source_path = source_path
123
+ self .module_name = module_name
124
+ self .traceback_entry = traceback_entry
125
+
126
+
107
127
class ReturnHandler (ErrorHandler ):
108
128
"""Return a constant value on error."""
109
129
@@ -439,18 +459,6 @@ def emit_cast(self,
439
459
likely: If the cast is likely to succeed (can be False for unions)
440
460
"""
441
461
error = error or AssignHandler ()
442
- if isinstance (error , AssignHandler ):
443
- handle_error = '%s = NULL;' % dest
444
- elif isinstance (error , GotoHandler ):
445
- handle_error = 'goto %s;' % error .label
446
- else :
447
- assert isinstance (error , ReturnHandler )
448
- handle_error = 'return %s;' % error .value
449
- if raise_exception :
450
- raise_exc = f'CPy_TypeError("{ self .pretty_name (typ )} ", { src } ); '
451
- err = raise_exc + handle_error
452
- else :
453
- err = handle_error
454
462
455
463
# Special case casting *from* optional
456
464
if src_type and is_optional_type (src_type ) and not is_object_rprimitive (typ ):
@@ -465,9 +473,9 @@ def emit_cast(self,
465
473
self .emit_arg_check (src , dest , typ , check .format (src ), optional )
466
474
self .emit_lines (
467
475
f' { dest } = { src } ;' ,
468
- 'else {' ,
469
- err ,
470
- '}' )
476
+ 'else {' )
477
+ self . emit_cast_error_handler ( error , src , dest , typ , raise_exception )
478
+ self . emit_line ( '}' )
471
479
return
472
480
473
481
# TODO: Verify refcount handling.
@@ -500,9 +508,9 @@ def emit_cast(self,
500
508
self .emit_arg_check (src , dest , typ , check .format (prefix , src ), optional )
501
509
self .emit_lines (
502
510
f' { dest } = { src } ;' ,
503
- 'else {' ,
504
- err ,
505
- '}' )
511
+ 'else {' )
512
+ self . emit_cast_error_handler ( error , src , dest , typ , raise_exception )
513
+ self . emit_line ( '}' )
506
514
elif is_bytes_rprimitive (typ ):
507
515
if declare_dest :
508
516
self .emit_line (f'PyObject *{ dest } ;' )
@@ -512,9 +520,9 @@ def emit_cast(self,
512
520
self .emit_arg_check (src , dest , typ , check .format (src , src ), optional )
513
521
self .emit_lines (
514
522
f' { dest } = { src } ;' ,
515
- 'else {' ,
516
- err ,
517
- '}' )
523
+ 'else {' )
524
+ self . emit_cast_error_handler ( error , src , dest , typ , raise_exception )
525
+ self . emit_line ( '}' )
518
526
elif is_tuple_rprimitive (typ ):
519
527
if declare_dest :
520
528
self .emit_line (f'{ self .ctype (typ )} { dest } ;' )
@@ -525,9 +533,9 @@ def emit_cast(self,
525
533
check .format (src ), optional )
526
534
self .emit_lines (
527
535
f' { dest } = { src } ;' ,
528
- 'else {' ,
529
- err ,
530
- '}' )
536
+ 'else {' )
537
+ self . emit_cast_error_handler ( error , src , dest , typ , raise_exception )
538
+ self . emit_line ( '}' )
531
539
elif isinstance (typ , RInstance ):
532
540
if declare_dest :
533
541
self .emit_line (f'PyObject *{ dest } ;' )
@@ -551,10 +559,10 @@ def emit_cast(self,
551
559
check = f'(likely{ check } )'
552
560
self .emit_arg_check (src , dest , typ , check , optional )
553
561
self .emit_lines (
554
- f' { dest } = { src } ;' ,
555
- 'else {' ,
556
- err ,
557
- '}' )
562
+ f' { dest } = { src } ;' . format ( dest , src ) ,
563
+ 'else {' )
564
+ self . emit_cast_error_handler ( error , src , dest , typ , raise_exception )
565
+ self . emit_line ( '}' )
558
566
elif is_none_rprimitive (typ ):
559
567
if declare_dest :
560
568
self .emit_line (f'PyObject *{ dest } ;' )
@@ -565,9 +573,9 @@ def emit_cast(self,
565
573
check .format (src ), optional )
566
574
self .emit_lines (
567
575
f' { dest } = { src } ;' ,
568
- 'else {' ,
569
- err ,
570
- '}' )
576
+ 'else {' )
577
+ self . emit_cast_error_handler ( error , src , dest , typ , raise_exception )
578
+ self . emit_line ( '}' )
571
579
elif is_object_rprimitive (typ ):
572
580
if declare_dest :
573
581
self .emit_line (f'PyObject *{ dest } ;' )
@@ -576,21 +584,51 @@ def emit_cast(self,
576
584
if optional :
577
585
self .emit_line ('}' )
578
586
elif isinstance (typ , RUnion ):
579
- self .emit_union_cast (src , dest , typ , declare_dest , err , optional , src_type )
587
+ self .emit_union_cast (src , dest , typ , declare_dest , error , optional , src_type ,
588
+ raise_exception )
580
589
elif isinstance (typ , RTuple ):
581
590
assert not optional
582
- self .emit_tuple_cast (src , dest , typ , declare_dest , err , src_type )
591
+ self .emit_tuple_cast (src , dest , typ , declare_dest , error , src_type )
583
592
else :
584
593
assert False , 'Cast not implemented: %s' % typ
585
594
595
+ def emit_cast_error_handler (self ,
596
+ error : ErrorHandler ,
597
+ src : str ,
598
+ dest : str ,
599
+ typ : RType ,
600
+ raise_exception : bool ) -> None :
601
+ if raise_exception :
602
+ if isinstance (error , TracebackAndGotoHandler ):
603
+ # Merge raising and emitting traceback entry into a single call.
604
+ self .emit_type_error_traceback (
605
+ error .source_path , error .module_name , error .traceback_entry ,
606
+ typ = typ ,
607
+ src = src )
608
+ self .emit_line ('goto %s;' % error .label )
609
+ return
610
+ self .emit_line ('CPy_TypeError("{}", {}); ' .format (self .pretty_name (typ ), src ))
611
+ if isinstance (error , AssignHandler ):
612
+ self .emit_line ('%s = NULL;' % dest )
613
+ elif isinstance (error , GotoHandler ):
614
+ self .emit_line ('goto %s;' % error .label )
615
+ elif isinstance (error , TracebackAndGotoHandler ):
616
+ self .emit_line ('%s = NULL;' % dest )
617
+ self .emit_traceback (error .source_path , error .module_name , error .traceback_entry )
618
+ self .emit_line ('goto %s;' % error .label )
619
+ else :
620
+ assert isinstance (error , ReturnHandler )
621
+ self .emit_line ('return %s;' % error .value )
622
+
586
623
def emit_union_cast (self ,
587
624
src : str ,
588
625
dest : str ,
589
626
typ : RUnion ,
590
627
declare_dest : bool ,
591
- err : str ,
628
+ error : ErrorHandler ,
592
629
optional : bool ,
593
- src_type : Optional [RType ]) -> None :
630
+ src_type : Optional [RType ],
631
+ raise_exception : bool ) -> None :
594
632
"""Emit cast to a union type.
595
633
596
634
The arguments are similar to emit_cast.
@@ -613,11 +651,11 @@ def emit_union_cast(self,
613
651
likely = False )
614
652
self .emit_line (f'if ({ dest } != NULL) goto { good_label } ;' )
615
653
# Handle cast failure.
616
- self .emit_line ( err )
654
+ self .emit_cast_error_handler ( error , src , dest , typ , raise_exception )
617
655
self .emit_label (good_label )
618
656
619
657
def emit_tuple_cast (self , src : str , dest : str , typ : RTuple , declare_dest : bool ,
620
- err : str , src_type : Optional [RType ]) -> None :
658
+ error : ErrorHandler , src_type : Optional [RType ]) -> None :
621
659
"""Emit cast to a tuple type.
622
660
623
661
The arguments are similar to emit_cast.
@@ -740,7 +778,8 @@ def emit_unbox(self,
740
778
self .emit_line ('} else {' )
741
779
742
780
cast_temp = self .temp_name ()
743
- self .emit_tuple_cast (src , cast_temp , typ , declare_dest = True , err = '' , src_type = None )
781
+ self .emit_tuple_cast (src , cast_temp , typ , declare_dest = True , error = error ,
782
+ src_type = None )
744
783
self .emit_line (f'if (unlikely({ cast_temp } == NULL)) {{' )
745
784
746
785
# self.emit_arg_check(src, dest, typ,
@@ -886,3 +925,44 @@ def emit_gc_clear(self, target: str, rtype: RType) -> None:
886
925
self .emit_line (f'Py_CLEAR({ target } );' )
887
926
else :
888
927
assert False , 'emit_gc_clear() not implemented for %s' % repr (rtype )
928
+
929
+ def emit_traceback (self ,
930
+ source_path : str ,
931
+ module_name : str ,
932
+ traceback_entry : Tuple [str , int ]) -> None :
933
+ return self ._emit_traceback ('CPy_AddTraceback' , source_path , module_name , traceback_entry )
934
+
935
+ def emit_type_error_traceback (
936
+ self ,
937
+ source_path : str ,
938
+ module_name : str ,
939
+ traceback_entry : Tuple [str , int ],
940
+ * ,
941
+ typ : RType ,
942
+ src : str ) -> None :
943
+ func = 'CPy_TypeErrorTraceback'
944
+ type_str = f'"{ self .pretty_name (typ )} "'
945
+ return self ._emit_traceback (
946
+ func , source_path , module_name , traceback_entry , type_str = type_str , src = src )
947
+
948
+ def _emit_traceback (self ,
949
+ func : str ,
950
+ source_path : str ,
951
+ module_name : str ,
952
+ traceback_entry : Tuple [str , int ],
953
+ type_str : str = '' ,
954
+ src : str = '' ) -> None :
955
+ globals_static = self .static_name ('globals' , module_name )
956
+ line = '%s("%s", "%s", %d, %s' % (
957
+ func ,
958
+ source_path .replace ("\\ " , "\\ \\ " ),
959
+ traceback_entry [0 ],
960
+ traceback_entry [1 ],
961
+ globals_static )
962
+ if type_str :
963
+ assert src
964
+ line += f', { type_str } , { src } '
965
+ line += ');'
966
+ self .emit_line (line )
967
+ if DEBUG_ERRORS :
968
+ self .emit_line ('assert(PyErr_Occurred() != NULL && "failure w/o err!");' )
0 commit comments