From c8b60f013b9d624a74ad502460f2bc7d00c39644 Mon Sep 17 00:00:00 2001
From: Damien George <damien.p.george@gmail.com>
Date: Mon, 20 Apr 2015 13:29:31 +0000
Subject: [PATCH] py: Make viper codegen raise proper exception
 (ViperTypeError) on error.

This fixes a long standing problem that viper code generation gave
terrible error messages, and actually no errors on pyboard where
assertions are disabled.

Now all compile-time errors are raised as proper Python exceptions, and
are of type ViperTypeError.

Addresses issue #940.
---
 py/compile.c     |  8 ++---
 py/emit.h        |  8 ++---
 py/emitnative.c  | 85 +++++++++++++++++++++++++++++++-----------------
 py/modbuiltins.c |  3 ++
 py/obj.h         |  1 +
 py/objexcept.c   |  4 +++
 py/qstrdefs.h    |  3 ++
 7 files changed, 74 insertions(+), 38 deletions(-)

diff --git a/py/compile.c b/py/compile.c
index 6dc39b1dc..45217cfe9 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -3764,22 +3764,22 @@ mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, uint emit_opt, bool is
                 case MP_EMIT_OPT_VIPER:
 #if MICROPY_EMIT_X64
                     if (emit_native == NULL) {
-                        emit_native = emit_native_x64_new(max_num_labels);
+                        emit_native = emit_native_x64_new(&comp->compile_error, max_num_labels);
                     }
                     comp->emit_method_table = &emit_native_x64_method_table;
 #elif MICROPY_EMIT_X86
                     if (emit_native == NULL) {
-                        emit_native = emit_native_x86_new(max_num_labels);
+                        emit_native = emit_native_x86_new(&comp->compile_error, max_num_labels);
                     }
                     comp->emit_method_table = &emit_native_x86_method_table;
 #elif MICROPY_EMIT_THUMB
                     if (emit_native == NULL) {
-                        emit_native = emit_native_thumb_new(max_num_labels);
+                        emit_native = emit_native_thumb_new(&comp->compile_error, max_num_labels);
                     }
                     comp->emit_method_table = &emit_native_thumb_method_table;
 #elif MICROPY_EMIT_ARM
                     if (emit_native == NULL) {
-                        emit_native = emit_native_arm_new(max_num_labels);
+                        emit_native = emit_native_arm_new(&comp->compile_error, max_num_labels);
                     }
                     comp->emit_method_table = &emit_native_arm_method_table;
 #endif
diff --git a/py/emit.h b/py/emit.h
index 268c9d66c..35ba08db9 100644
--- a/py/emit.h
+++ b/py/emit.h
@@ -172,10 +172,10 @@ extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_delete_id_ops
 
 emit_t *emit_cpython_new(void);
 emit_t *emit_bc_new(void);
-emit_t *emit_native_x64_new(mp_uint_t max_num_labels);
-emit_t *emit_native_x86_new(mp_uint_t max_num_labels);
-emit_t *emit_native_thumb_new(mp_uint_t max_num_labels);
-emit_t *emit_native_arm_new(mp_uint_t max_num_labels);
+emit_t *emit_native_x64_new(mp_obj_t *error_slot, mp_uint_t max_num_labels);
+emit_t *emit_native_x86_new(mp_obj_t *error_slot, mp_uint_t max_num_labels);
+emit_t *emit_native_thumb_new(mp_obj_t *error_slot, mp_uint_t max_num_labels);
+emit_t *emit_native_arm_new(mp_obj_t *error_slot, mp_uint_t max_num_labels);
 
 void emit_cpython_set_max_num_labels(emit_t* emit, mp_uint_t max_num_labels);
 void emit_bc_set_max_num_labels(emit_t* emit, mp_uint_t max_num_labels);
diff --git a/py/emitnative.c b/py/emitnative.c
index 0b1347676..126596b46 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -482,6 +482,10 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = {
 
 #endif
 
+#define EMIT_NATIVE_VIPER_TYPE_ERROR(emit, ...) do { \
+        *emit->error_slot = mp_obj_new_exception_msg_varg(&mp_type_ViperTypeError, __VA_ARGS__); \
+    } while (0)
+
 typedef enum {
     STACK_VALUE,
     STACK_REG,
@@ -505,6 +509,19 @@ typedef enum {
     VTYPE_BUILTIN_CAST = 0x60 | MP_NATIVE_TYPE_OBJ,
 } vtype_kind_t;
 
+STATIC qstr vtype_to_qstr(vtype_kind_t vtype) {
+    switch (vtype) {
+        case VTYPE_PYOBJ: return MP_QSTR_object;
+        case VTYPE_BOOL: return MP_QSTR_bool;
+        case VTYPE_INT: return MP_QSTR_int;
+        case VTYPE_UINT: return MP_QSTR_uint;
+        case VTYPE_PTR: return MP_QSTR_ptr;
+        case VTYPE_PTR8: return MP_QSTR_ptr8;
+        case VTYPE_PTR16: return MP_QSTR_ptr16;
+        case VTYPE_PTR_NONE: default: return MP_QSTR_None;
+    }
+}
+
 typedef struct _stack_info_t {
     vtype_kind_t vtype;
     stack_info_kind_t kind;
@@ -515,6 +532,7 @@ typedef struct _stack_info_t {
 } stack_info_t;
 
 struct _emit_t {
+    mp_obj_t *error_slot;
     int pass;
 
     bool do_viper_types;
@@ -542,8 +560,9 @@ struct _emit_t {
     ASM_T *as;
 };
 
-emit_t *EXPORT_FUN(new)(mp_uint_t max_num_labels) {
+emit_t *EXPORT_FUN(new)(mp_obj_t *error_slot, mp_uint_t max_num_labels) {
     emit_t *emit = m_new0(emit_t, 1);
+    emit->error_slot = error_slot;
     emit->as = ASM_NEW(max_num_labels);
     return emit;
 }
@@ -571,7 +590,7 @@ STATIC void emit_native_set_native_type(emit_t *emit, mp_uint_t op, mp_uint_t ar
                 case MP_QSTR_ptr: type = VTYPE_PTR; break;
                 case MP_QSTR_ptr8: type = VTYPE_PTR8; break;
                 case MP_QSTR_ptr16: type = VTYPE_PTR16; break;
-                default: mp_printf(&mp_plat_print, "ViperTypeError: unknown type %q\n", arg2); return;
+                default: EMIT_NATIVE_VIPER_TYPE_ERROR(emit, "unknown type '%q'", arg2); return;
             }
             if (op == MP_EMIT_NATIVE_TYPE_RETURN) {
                 emit->return_vtype = type;
@@ -1288,7 +1307,7 @@ STATIC void emit_native_load_fast(emit_t *emit, qstr qst, mp_uint_t local_num) {
     DEBUG_printf("load_fast(%s, " UINT_FMT ")\n", qstr_str(qst), local_num);
     vtype_kind_t vtype = emit->local_vtype[local_num];
     if (vtype == VTYPE_UNBOUND) {
-        mp_printf(&mp_plat_print, "ViperTypeError: local %q used before type known\n", qst);
+        EMIT_NATIVE_VIPER_TYPE_ERROR(emit, "local '%q' used before type known", qst);
     }
     emit_native_pre(emit);
     if (local_num == 0) {
@@ -1438,7 +1457,8 @@ STATIC void emit_native_load_subscr(emit_t *emit) {
                     break;
                 }
                 default:
-                    mp_printf(&mp_plat_print, "ViperTypeError: can't load from type %d\n", vtype_base);
+                    EMIT_NATIVE_VIPER_TYPE_ERROR(emit,
+                        "can't load from '%q'", vtype_to_qstr(vtype_base));
             }
         } else {
             // index is not an immediate
@@ -1464,7 +1484,8 @@ STATIC void emit_native_load_subscr(emit_t *emit) {
                     break;
                 }
                 default:
-                    mp_printf(&mp_plat_print, "ViperTypeError: can't load from type %d\n", vtype_base);
+                    EMIT_NATIVE_VIPER_TYPE_ERROR(emit,
+                        "can't load from '%q'", vtype_to_qstr(vtype_base));
             }
         }
         emit_post_push_reg(emit, VTYPE_INT, REG_RET);
@@ -1495,7 +1516,9 @@ STATIC void emit_native_store_fast(emit_t *emit, qstr qst, mp_uint_t local_num)
         emit->local_vtype[local_num] = vtype;
     } else if (emit->local_vtype[local_num] != vtype) {
         // type of local is not the same as object stored in it
-        mp_printf(&mp_plat_print, "ViperTypeError: type mismatch, local %q has type %d but source object has type %d\n", qst, emit->local_vtype[local_num], vtype);
+        EMIT_NATIVE_VIPER_TYPE_ERROR(emit,
+            "local '%q' has type '%q' but source is '%q'",
+            qst, vtype_to_qstr(emit->local_vtype[local_num]), vtype_to_qstr(vtype));
     }
 }
 
@@ -1625,7 +1648,8 @@ STATIC void emit_native_store_subscr(emit_t *emit) {
                     break;
                 }
                 default:
-                    mp_printf(&mp_plat_print, "ViperTypeError: can't store to type %d\n", vtype_base);
+                    EMIT_NATIVE_VIPER_TYPE_ERROR(emit,
+                        "can't store to '%q'", vtype_to_qstr(vtype_base));
             }
         } else {
             // index is not an immediate
@@ -1666,7 +1690,8 @@ STATIC void emit_native_store_subscr(emit_t *emit) {
                     break;
                 }
                 default:
-                    mp_printf(&mp_plat_print, "ViperTypeError: can't store to type %d\n", vtype_base);
+                    EMIT_NATIVE_VIPER_TYPE_ERROR(emit,
+                        "can't store to '%q'", vtype_to_qstr(vtype_base));
             }
         }
 
@@ -1761,25 +1786,21 @@ STATIC void emit_native_jump(emit_t *emit, mp_uint_t label) {
 
 STATIC void emit_native_jump_helper(emit_t *emit, bool pop) {
     vtype_kind_t vtype = peek_vtype(emit, 0);
-    switch (vtype) {
-        case VTYPE_PYOBJ:
-            emit_pre_pop_reg(emit, &vtype, REG_ARG_1);
-            if (!pop) {
-                adjust_stack(emit, 1);
-            }
-            emit_call(emit, MP_F_OBJ_IS_TRUE);
-            break;
-        case VTYPE_BOOL:
-        case VTYPE_INT:
-        case VTYPE_UINT:
-            emit_pre_pop_reg(emit, &vtype, REG_RET);
-            if (!pop) {
-                adjust_stack(emit, 1);
-            }
-            break;
-        default:
-            mp_printf(&mp_plat_print, "ViperTypeError: expecting a bool or pyobj, got %d\n", vtype);
-            assert(0);
+    if (vtype == VTYPE_PYOBJ) {
+        emit_pre_pop_reg(emit, &vtype, REG_ARG_1);
+        if (!pop) {
+            adjust_stack(emit, 1);
+        }
+        emit_call(emit, MP_F_OBJ_IS_TRUE);
+    } else {
+        emit_pre_pop_reg(emit, &vtype, REG_RET);
+        if (!pop) {
+            adjust_stack(emit, 1);
+        }
+        if (!(vtype == VTYPE_BOOL || vtype == VTYPE_INT || vtype == VTYPE_UINT)) {
+            EMIT_NATIVE_VIPER_TYPE_ERROR(emit,
+                "can't implicitly convert '%q' to 'bool'", vtype_to_qstr(vtype));
+        }
     }
     // For non-pop need to save the vtype so that emit_native_adjust_stack_size
     // can use it.  This is a bit of a hack.
@@ -2056,7 +2077,9 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) {
         }
         emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
     } else {
-        mp_printf(&mp_plat_print, "ViperTypeError: can't do binary op between types %d and %d\n", vtype_lhs, vtype_rhs);
+        EMIT_NATIVE_VIPER_TYPE_ERROR(emit,
+            "can't do binary op between '%q' and '%q'",
+            vtype_to_qstr(vtype_lhs), vtype_to_qstr(vtype_rhs));
         emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
     }
 }
@@ -2302,7 +2325,9 @@ STATIC void emit_native_return_value(emit_t *emit) {
             vtype_kind_t vtype;
             emit_pre_pop_reg(emit, &vtype, REG_RET);
             if (vtype != emit->return_vtype) {
-                mp_printf(&mp_plat_print, "ViperTypeError: incompatible return type\n");
+                EMIT_NATIVE_VIPER_TYPE_ERROR(emit,
+                    "return expected '%q' but got '%q'",
+                    vtype_to_qstr(emit->return_vtype), vtype_to_qstr(vtype));
             }
         }
     } else {
@@ -2320,7 +2345,7 @@ STATIC void emit_native_raise_varargs(emit_t *emit, mp_uint_t n_args) {
     vtype_kind_t vtype_exc;
     emit_pre_pop_reg(emit, &vtype_exc, REG_ARG_1); // arg1 = object to raise
     if (vtype_exc != VTYPE_PYOBJ) {
-        mp_printf(&mp_plat_print, "ViperTypeError: must raise an object\n");
+        EMIT_NATIVE_VIPER_TYPE_ERROR(emit, "must raise an object");
     }
     // TODO probably make this 1 call to the runtime (which could even call convert, native_raise(obj, type))
     emit_call(emit, MP_F_NATIVE_RAISE);
diff --git a/py/modbuiltins.c b/py/modbuiltins.c
index bec47787c..455e1cc1b 100644
--- a/py/modbuiltins.c
+++ b/py/modbuiltins.c
@@ -703,6 +703,9 @@ STATIC const mp_map_elem_t mp_module_builtins_globals_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR_UnicodeError), (mp_obj_t)&mp_type_UnicodeError },
     #endif
     { MP_OBJ_NEW_QSTR(MP_QSTR_ValueError), (mp_obj_t)&mp_type_ValueError },
+    #if MICROPY_EMIT_NATIVE
+    { MP_OBJ_NEW_QSTR(MP_QSTR_ViperTypeError), (mp_obj_t)&mp_type_ViperTypeError },
+    #endif
     { MP_OBJ_NEW_QSTR(MP_QSTR_ZeroDivisionError), (mp_obj_t)&mp_type_ZeroDivisionError },
     // Somehow CPython managed to have OverflowError not inherit from ValueError ;-/
     // TODO: For MICROPY_CPYTHON_COMPAT==0 use ValueError to avoid exc proliferation
diff --git a/py/obj.h b/py/obj.h
index 86f560ff5..11c0198bb 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -434,6 +434,7 @@ extern const mp_obj_type_t mp_type_SystemExit;
 extern const mp_obj_type_t mp_type_TypeError;
 extern const mp_obj_type_t mp_type_UnicodeError;
 extern const mp_obj_type_t mp_type_ValueError;
+extern const mp_obj_type_t mp_type_ViperTypeError;
 extern const mp_obj_type_t mp_type_ZeroDivisionError;
 
 // Constant objects, globally accessible
diff --git a/py/objexcept.c b/py/objexcept.c
index 4623e7318..6c108d99c 100644
--- a/py/objexcept.c
+++ b/py/objexcept.c
@@ -252,6 +252,10 @@ MP_DEFINE_EXCEPTION(Exception, BaseException)
       */
   //MP_DEFINE_EXCEPTION(SystemError, Exception)
   MP_DEFINE_EXCEPTION(TypeError, Exception)
+#if MICROPY_EMIT_NATIVE
+    MP_DEFINE_EXCEPTION_BASE(TypeError)
+    MP_DEFINE_EXCEPTION(ViperTypeError, TypeError)
+#endif
   MP_DEFINE_EXCEPTION(ValueError, Exception)
 #if MICROPY_PY_BUILTINS_STR_UNICODE
     MP_DEFINE_EXCEPTION_BASE(ValueError)
diff --git a/py/qstrdefs.h b/py/qstrdefs.h
index acf63e738..d50fc1f5e 100644
--- a/py/qstrdefs.h
+++ b/py/qstrdefs.h
@@ -144,6 +144,9 @@ Q(SystemExit)
 Q(TypeError)
 Q(UnboundLocalError)
 Q(ValueError)
+#if MICROPY_EMIT_NATIVE
+Q(ViperTypeError)
+#endif
 Q(ZeroDivisionError)
 #if MICROPY_PY_BUILTINS_STR_UNICODE
 Q(UnicodeError)
-- 
GitLab