From ea8d06c39d9c94036e490b300d46f367c9eb78d9 Mon Sep 17 00:00:00 2001
From: Damien George <damien.p.george@gmail.com>
Date: Thu, 17 Apr 2014 23:19:36 +0100
Subject: [PATCH] py: Add MP_OBJ_STOP_ITERATION and make good use of it.

Also make consistent use of MP_OBJ_NOT_SUPPORTED and MP_OBJ_NULL.
This helps a lot in debugging and understanding of function API.
---
 py/builtin.c         | 12 ++++++------
 py/obj.c             |  4 ++--
 py/obj.h             | 46 ++++++++++++++++++++++++++++----------------
 py/objarray.c        |  6 +++---
 py/objbool.c         |  3 +--
 py/objcomplex.c      |  4 ++--
 py/objdict.c         | 30 ++++++++++++++---------------
 py/objenumerate.c    |  4 ++--
 py/objfilter.c       |  4 ++--
 py/objfloat.c        |  2 +-
 py/objgenerator.c    | 16 +++++++--------
 py/objgetitemiter.c  |  4 ++--
 py/objint.c          |  4 ++--
 py/objint_longlong.c |  7 +++----
 py/objint_mpz.c      |  6 +++---
 py/objlist.c         |  9 ++++-----
 py/objmap.c          |  4 ++--
 py/objnone.c         |  2 +-
 py/objrange.c        |  2 +-
 py/objset.c          | 18 ++++++++---------
 py/objstr.c          |  8 ++++----
 py/objtuple.c        |  6 +++---
 py/objtype.c         | 12 ++++++------
 py/objzip.c          |  6 +++---
 py/runtime.c         | 26 ++++++++++++-------------
 py/runtime.h         |  4 ++--
 py/stream.c          |  2 +-
 py/vm.c              |  2 +-
 28 files changed, 131 insertions(+), 122 deletions(-)

diff --git a/py/builtin.c b/py/builtin.c
index 217dcc186..58a86ac9b 100644
--- a/py/builtin.c
+++ b/py/builtin.c
@@ -103,7 +103,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_abs_obj, mp_builtin_abs);
 STATIC mp_obj_t mp_builtin_all(mp_obj_t o_in) {
     mp_obj_t iterable = mp_getiter(o_in);
     mp_obj_t item;
-    while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) {
+    while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
         if (!mp_obj_is_true(item)) {
             return mp_const_false;
         }
@@ -116,7 +116,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_all_obj, mp_builtin_all);
 STATIC mp_obj_t mp_builtin_any(mp_obj_t o_in) {
     mp_obj_t iterable = mp_getiter(o_in);
     mp_obj_t item;
-    while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) {
+    while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
         if (mp_obj_is_true(item)) {
             return mp_const_true;
         }
@@ -244,7 +244,7 @@ STATIC mp_obj_t mp_builtin_max(uint n_args, const mp_obj_t *args) {
         mp_obj_t iterable = mp_getiter(args[0]);
         mp_obj_t max_obj = NULL;
         mp_obj_t item;
-        while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) {
+        while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
             if (max_obj == NULL || mp_binary_op(MP_BINARY_OP_LESS, max_obj, item)) {
                 max_obj = item;
             }
@@ -273,7 +273,7 @@ STATIC mp_obj_t mp_builtin_min(uint n_args, const mp_obj_t *args) {
         mp_obj_t iterable = mp_getiter(args[0]);
         mp_obj_t min_obj = NULL;
         mp_obj_t item;
-        while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) {
+        while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
             if (min_obj == NULL || mp_binary_op(MP_BINARY_OP_LESS, item, min_obj)) {
                 min_obj = item;
             }
@@ -298,7 +298,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR(mp_builtin_min_obj, 1, mp_builtin_min);
 
 STATIC mp_obj_t mp_builtin_next(mp_obj_t o) {
     mp_obj_t ret = mp_iternext_allow_raise(o);
-    if (ret == MP_OBJ_NULL) {
+    if (ret == MP_OBJ_STOP_ITERATION) {
         nlr_raise(mp_obj_new_exception(&mp_type_StopIteration));
     } else {
         return ret;
@@ -381,7 +381,7 @@ STATIC mp_obj_t mp_builtin_sum(uint n_args, const mp_obj_t *args) {
     }
     mp_obj_t iterable = mp_getiter(args[0]);
     mp_obj_t item;
-    while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) {
+    while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
         value = mp_binary_op(MP_BINARY_OP_ADD, value, item);
     }
     return value;
diff --git a/py/obj.c b/py/obj.c
index 3ace631a9..514a6941b 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -90,7 +90,7 @@ int mp_obj_is_true(mp_obj_t arg) {
         mp_obj_type_t *type = mp_obj_get_type(arg);
         if (type->unary_op != NULL) {
             mp_obj_t result = type->unary_op(MP_UNARY_OP_BOOL, arg);
-            if (result != MP_OBJ_NULL) {
+            if (result != MP_OBJ_NOT_SUPPORTED) {
                 return result == mp_const_true;
             }
         }
@@ -180,7 +180,7 @@ bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2) {
     mp_obj_type_t *type = mp_obj_get_type(o1);
     if (type->binary_op != NULL) {
         mp_obj_t r = type->binary_op(MP_BINARY_OP_EQUAL, o1, o2);
-        if (r != MP_OBJ_NULL) {
+        if (r != MP_OBJ_NOT_SUPPORTED) {
             return r == mp_const_true ? true : false;
         }
     }
diff --git a/py/obj.h b/py/obj.h
index 3482aa041..0330857d0 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -1,3 +1,8 @@
+// A Micro Python object is a machine word having the following form:
+//  - xxxx...xxx1 : a small int, bits 1 and above are the value
+//  - xxxx...xx10 : a qstr, bits 2 and above are the value
+//  - xxxx...xx00 : a pointer to an mp_obj_base_t (unless a fake object)
+
 // All Micro Python objects are at least this type
 // It must be of pointer size
 
@@ -10,7 +15,7 @@ typedef machine_const_ptr_t mp_const_obj_t;
 typedef machine_int_t mp_small_int_t;
 
 // Anything that wants to be a Micro Python object must have
-// mp_obj_base_t as its first member (except NULL and small ints)
+// mp_obj_base_t as its first member (except small ints and qstrs)
 
 struct _mp_obj_type_t;
 struct _mp_obj_base_t {
@@ -18,24 +23,31 @@ struct _mp_obj_base_t {
 };
 typedef struct _mp_obj_base_t mp_obj_base_t;
 
-// The NULL object is used to indicate the absence of an object
-// It *cannot* be used when an mp_obj_t is expected, except where explicitly allowed
-
-#define MP_OBJ_NULL ((mp_obj_t)0)
-
-// The SENTINEL object is used for various internal purposes where one needs
-// an object which is unique from all other objects, including MP_OBJ_NULL.
-
-#define MP_OBJ_SENTINEL ((mp_obj_t)8)
-
-// The NOT_SUPPORTED object is a return value that indicates an unsupported operation.
-
-#define MP_OBJ_NOT_SUPPORTED ((mp_obj_t)16)
+// These fake objects are used to indicate certain things in arguments or return
+// values, and should only be used when explicitly allowed.
+//
+//  - MP_OBJ_NULL : used to indicate the absence of an object.
+//  - MP_OBJ_NOT_SUPPORTED : a return value that indicates an unsupported operation.
+//  - MP_OBJ_STOP_ITERATION : used instead of throwing a StopIteration, for efficiency.
+//  - MP_OBJ_SENTINEL : used for various internal purposes where one needs
+//    an object which is unique from all other objects, including MP_OBJ_NULL.
+//
+// For debugging purposes they are all different.  For non-debug mode, we alias
+// as many as we can to MP_OBJ_NULL because it's cheaper to load/compare 0.
+
+#if NDEBUG
+#define MP_OBJ_NULL             ((mp_obj_t)0)
+#define MP_OBJ_NOT_SUPPORTED    ((mp_obj_t)0)
+#define MP_OBJ_STOP_ITERATION   ((mp_obj_t)0)
+#define MP_OBJ_SENTINEL         ((mp_obj_t)4)
+#else
+#define MP_OBJ_NULL             ((mp_obj_t)0)
+#define MP_OBJ_NOT_SUPPORTED    ((mp_obj_t)4)
+#define MP_OBJ_STOP_ITERATION   ((mp_obj_t)8)
+#define MP_OBJ_SENTINEL         ((mp_obj_t)12)
+#endif
 
 // These macros check for small int, qstr or object, and access small int and qstr values
-//  - xxxx...xxx1: a small int, bits 1 and above are the value
-//  - xxxx...xx10: a qstr, bits 2 and above are the value
-//  - xxxx...xx00: a pointer to an mp_obj_base_t
 
 // In SMALL_INT, next-to-highest bits is used as sign, so both must match for value in range
 #define MP_SMALL_INT_MIN ((mp_small_int_t)(((machine_int_t)WORD_MSBIT_HIGH) >> 1))
diff --git a/py/objarray.c b/py/objarray.c
index 001194876..5c160bfab 100644
--- a/py/objarray.c
+++ b/py/objarray.c
@@ -63,7 +63,7 @@ STATIC mp_obj_t array_construct(char typecode, mp_obj_t initializer) {
     mp_obj_t iterable = mp_getiter(initializer);
     mp_obj_t item;
     int i = 0;
-    while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) {
+    while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
         if (len == 0) {
             array_append(array, item);
         } else {
@@ -108,7 +108,7 @@ STATIC mp_obj_t array_unary_op(int op, mp_obj_t o_in) {
     switch (op) {
         case MP_UNARY_OP_BOOL: return MP_BOOL(o->len != 0);
         case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(o->len);
-        default: return MP_OBJ_NULL; // op not supported
+        default: return MP_OBJ_NOT_SUPPORTED;
     }
 }
 
@@ -227,7 +227,7 @@ STATIC mp_obj_t array_it_iternext(mp_obj_t self_in) {
     if (self->cur < self->array->len) {
         return mp_binary_get_val_array(self->array->typecode, self->array->items, self->cur++);
     } else {
-        return MP_OBJ_NULL;
+        return MP_OBJ_STOP_ITERATION;
     }
 }
 
diff --git a/py/objbool.c b/py/objbool.c
index 11c736e3e..56020914d 100644
--- a/py/objbool.c
+++ b/py/objbool.c
@@ -48,8 +48,7 @@ STATIC mp_obj_t bool_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
     if (MP_BINARY_OP_OR <= op && op <= MP_BINARY_OP_NOT_EQUAL) {
         return mp_binary_op(op, MP_OBJ_NEW_SMALL_INT((machine_int_t)mp_obj_is_true(lhs_in)), rhs_in);
     }
-    // operation not supported
-    return MP_OBJ_NULL;
+    return MP_OBJ_NOT_SUPPORTED;
 }
 
 const mp_obj_type_t mp_type_bool = {
diff --git a/py/objcomplex.c b/py/objcomplex.c
index 8d8ebd2b2..924420964 100644
--- a/py/objcomplex.c
+++ b/py/objcomplex.c
@@ -98,7 +98,7 @@ STATIC mp_obj_t complex_unary_op(int op, mp_obj_t o_in) {
         case MP_UNARY_OP_BOOL: return MP_BOOL(o->real != 0 || o->imag != 0);
         case MP_UNARY_OP_POSITIVE: return o_in;
         case MP_UNARY_OP_NEGATIVE: return mp_obj_new_complex(-o->real, -o->imag);
-        default: return MP_OBJ_NULL; // op not supported
+        default: return MP_OBJ_NOT_SUPPORTED;
     }
 }
 
@@ -208,7 +208,7 @@ mp_obj_t mp_obj_complex_binary_op(int op, mp_float_t lhs_real, mp_float_t lhs_im
         case MP_BINARY_OP_EQUAL: return MP_BOOL(lhs_real == rhs_real && lhs_imag == rhs_imag);
 
         default:
-            return MP_OBJ_NULL; // op not supported
+            return MP_OBJ_NOT_SUPPORTED;
     }
     return mp_obj_new_complex(lhs_real, lhs_imag);
 }
diff --git a/py/objdict.c b/py/objdict.c
index 8d82f16d3..0654a198e 100644
--- a/py/objdict.c
+++ b/py/objdict.c
@@ -22,7 +22,7 @@ STATIC void dict_print(void (*print)(void *env, const char *fmt, ...), void *env
     print(env, "{");
     mp_obj_t *dict_iter = mp_obj_new_dict_iterator(self, 0);
     mp_map_elem_t *next = NULL;
-    while ((next = dict_it_iternext_elem(dict_iter)) != NULL) {
+    while ((next = dict_it_iternext_elem(dict_iter)) != MP_OBJ_STOP_ITERATION) {
         if (!first) {
             print(env, ", ");
         }
@@ -52,7 +52,7 @@ STATIC mp_obj_t dict_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp
             mp_obj_t dict = mp_obj_new_dict(0);
             // TODO: support arbitrary seq as a pair
             mp_obj_t item;
-            while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) {
+            while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
                 mp_obj_t *sub_items;
                 mp_obj_get_array_fixed_n(item, 2, &sub_items);
                 mp_obj_dict_store(dict, sub_items[0], sub_items[1]);
@@ -77,7 +77,7 @@ STATIC mp_obj_t dict_unary_op(int op, mp_obj_t self_in) {
     switch (op) {
         case MP_UNARY_OP_BOOL: return MP_BOOL(self->map.used != 0);
         case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT((machine_int_t)self->map.used);
-        default: return MP_OBJ_NULL; // op not supported for None
+        default: return MP_OBJ_NOT_SUPPORTED;
     }
 }
 
@@ -114,7 +114,7 @@ STATIC mp_obj_t dict_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
         }
         default:
             // op not supported
-            return NULL;
+            return MP_OBJ_NOT_SUPPORTED;
     }
 }
 
@@ -160,16 +160,16 @@ STATIC mp_map_elem_t *dict_it_iternext_elem(mp_obj_t self_in) {
         }
     }
 
-    return NULL;
+    return MP_OBJ_STOP_ITERATION;
 }
 
 mp_obj_t dict_it_iternext(mp_obj_t self_in) {
     mp_map_elem_t *next = dict_it_iternext_elem(self_in);
 
-    if (next != NULL) {
+    if (next != MP_OBJ_STOP_ITERATION) {
         return next->key;
     } else {
-        return MP_OBJ_NULL;
+        return MP_OBJ_STOP_ITERATION;
     }
 }
 
@@ -237,7 +237,7 @@ STATIC mp_obj_t dict_fromkeys(uint n_args, const mp_obj_t *args) {
         self = mp_obj_new_dict(MP_OBJ_SMALL_INT_VALUE(len));
     }
 
-    while ((next = mp_iternext(iter)) != MP_OBJ_NULL) {
+    while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
         mp_map_lookup(&self->map, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
     }
 
@@ -328,14 +328,14 @@ STATIC mp_obj_t dict_update(mp_obj_t self_in, mp_obj_t iterable) {
     /* TODO: check for the "keys" method */
     mp_obj_t iter = mp_getiter(iterable);
     mp_obj_t next = NULL;
-    while ((next = mp_iternext(iter)) != MP_OBJ_NULL) {
+    while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
         mp_obj_t inneriter = mp_getiter(next);
         mp_obj_t key = mp_iternext(inneriter);
         mp_obj_t value = mp_iternext(inneriter);
         mp_obj_t stop = mp_iternext(inneriter);
-        if (key == MP_OBJ_NULL
-            || value == MP_OBJ_NULL
-            || stop != MP_OBJ_NULL) {
+        if (key == MP_OBJ_STOP_ITERATION
+            || value == MP_OBJ_STOP_ITERATION
+            || stop != MP_OBJ_STOP_ITERATION) {
             nlr_raise(mp_obj_new_exception_msg(
                          &mp_type_ValueError,
                          "dictionary update sequence has the wrong length"));
@@ -381,7 +381,7 @@ STATIC mp_obj_t dict_view_it_iternext(mp_obj_t self_in) {
     mp_obj_dict_view_it_t *self = self_in;
     mp_map_elem_t *next = dict_it_iternext_elem(self->iter);
 
-    if (next != NULL) {
+    if (next != MP_OBJ_STOP_ITERATION) {
         switch (self->kind) {
             case MP_DICT_VIEW_ITEMS:
             {
@@ -397,7 +397,7 @@ STATIC mp_obj_t dict_view_it_iternext(mp_obj_t self_in) {
                 return mp_const_none;
         }
     } else {
-        return MP_OBJ_NULL;
+        return MP_OBJ_STOP_ITERATION;
     }
 }
 
@@ -426,7 +426,7 @@ STATIC void dict_view_print(void (*print)(void *env, const char *fmt, ...), void
     print(env, "([");
     mp_obj_t *self_iter = dict_view_getiter(self);
     mp_obj_t *next = NULL;
-    while ((next = dict_view_it_iternext(self_iter)) != MP_OBJ_NULL) {
+    while ((next = dict_view_it_iternext(self_iter)) != MP_OBJ_STOP_ITERATION) {
         if (!first) {
             print(env, ", ");
         }
diff --git a/py/objenumerate.c b/py/objenumerate.c
index 23a3f7036..1adf18b1f 100644
--- a/py/objenumerate.c
+++ b/py/objenumerate.c
@@ -38,8 +38,8 @@ STATIC mp_obj_t enumerate_iternext(mp_obj_t self_in) {
     assert(MP_OBJ_IS_TYPE(self_in, &mp_type_enumerate));
     mp_obj_enumerate_t *self = self_in;
     mp_obj_t next = mp_iternext(self->iter);
-    if (next == MP_OBJ_NULL) {
-        return MP_OBJ_NULL;
+    if (next == MP_OBJ_STOP_ITERATION) {
+        return MP_OBJ_STOP_ITERATION;
     } else {
         mp_obj_t items[] = {MP_OBJ_NEW_SMALL_INT(self->cur++), next};
         return mp_obj_new_tuple(2, items);
diff --git a/py/objfilter.c b/py/objfilter.c
index 1224950bd..3eacdfc9b 100644
--- a/py/objfilter.c
+++ b/py/objfilter.c
@@ -30,7 +30,7 @@ STATIC mp_obj_t filter_iternext(mp_obj_t self_in) {
     assert(MP_OBJ_IS_TYPE(self_in, &mp_type_filter));
     mp_obj_filter_t *self = self_in;
     mp_obj_t next;
-    while ((next = mp_iternext(self->iter)) != MP_OBJ_NULL) {
+    while ((next = mp_iternext(self->iter)) != MP_OBJ_STOP_ITERATION) {
         mp_obj_t val;
         if (self->fun != mp_const_none) {
             val = mp_call_function_n_kw(self->fun, 1, 0, &next);
@@ -41,7 +41,7 @@ STATIC mp_obj_t filter_iternext(mp_obj_t self_in) {
             return next;
         }
     }
-    return MP_OBJ_NULL;
+    return MP_OBJ_STOP_ITERATION;
 }
 
 const mp_obj_type_t mp_type_filter = {
diff --git a/py/objfloat.c b/py/objfloat.c
index 16d4fbfd5..804101978 100644
--- a/py/objfloat.c
+++ b/py/objfloat.c
@@ -140,7 +140,7 @@ mp_obj_t mp_obj_float_binary_op(int op, mp_float_t lhs_val, mp_obj_t rhs_in) {
         case MP_BINARY_OP_MORE_EQUAL: return MP_BOOL(lhs_val >= rhs_val);
 
         default:
-            return MP_OBJ_NULL; // op not supported
+            return MP_OBJ_NOT_SUPPORTED;
     }
     return mp_obj_new_float(lhs_val);
 }
diff --git a/py/objgenerator.c b/py/objgenerator.c
index 18807e75f..895c03cc2 100644
--- a/py/objgenerator.c
+++ b/py/objgenerator.c
@@ -79,7 +79,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_
     assert(MP_OBJ_IS_TYPE(self_in, &mp_type_gen_instance));
     mp_obj_gen_instance_t *self = self_in;
     if (self->ip == 0) {
-        *ret_val = MP_OBJ_NULL;
+        *ret_val = MP_OBJ_STOP_ITERATION;
         return MP_VM_RETURN_NORMAL;
     }
     if (self->sp == self->state - 1) {
@@ -130,8 +130,8 @@ STATIC mp_obj_t gen_resume_and_raise(mp_obj_t self_in, mp_obj_t send_value, mp_o
     switch (mp_obj_gen_resume(self_in, send_value, throw_value, &ret)) {
         case MP_VM_RETURN_NORMAL:
             // Optimize return w/o value in case generator is used in for loop
-            if (ret == mp_const_none || ret == MP_OBJ_NULL) {
-                return MP_OBJ_NULL;
+            if (ret == mp_const_none || ret == MP_OBJ_STOP_ITERATION) {
+                return MP_OBJ_STOP_ITERATION;
             } else {
                 nlr_raise(mp_obj_new_exception_args(&mp_type_StopIteration, 1, &ret));
             }
@@ -143,11 +143,11 @@ STATIC mp_obj_t gen_resume_and_raise(mp_obj_t self_in, mp_obj_t send_value, mp_o
             return ret;
 
         case MP_VM_RETURN_EXCEPTION:
-            // TODO: Optimization of returning MP_OBJ_NULL is really part
+            // TODO: Optimization of returning MP_OBJ_STOP_ITERATION is really part
             // of mp_iternext() protocol, but this function is called by other methods
-            // too, which may not handled MP_OBJ_NULL.
+            // too, which may not handled MP_OBJ_STOP_ITERATION.
             if (mp_obj_is_subclass_fast(mp_obj_get_type(ret), &mp_type_StopIteration)) {
-                return MP_OBJ_NULL;
+                return MP_OBJ_STOP_ITERATION;
             } else {
                 nlr_raise(ret);
             }
@@ -164,7 +164,7 @@ mp_obj_t gen_instance_iternext(mp_obj_t self_in) {
 
 STATIC mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) {
     mp_obj_t ret = gen_resume_and_raise(self_in, send_value, MP_OBJ_NULL);
-    if (ret == MP_OBJ_NULL) {
+    if (ret == MP_OBJ_STOP_ITERATION) {
         nlr_raise(mp_obj_new_exception(&mp_type_StopIteration));
     } else {
         return ret;
@@ -179,7 +179,7 @@ STATIC mp_obj_t gen_instance_throw(uint n_args, const mp_obj_t *args) {
     exc = mp_make_raise_obj(exc);
 
     mp_obj_t ret = gen_resume_and_raise(args[0], mp_const_none, exc);
-    if (ret == MP_OBJ_NULL) {
+    if (ret == MP_OBJ_STOP_ITERATION) {
         nlr_raise(mp_obj_new_exception(&mp_type_StopIteration));
     } else {
         return ret;
diff --git a/py/objgetitemiter.c b/py/objgetitemiter.c
index f848415b8..faa8321c3 100644
--- a/py/objgetitemiter.c
+++ b/py/objgetitemiter.c
@@ -26,8 +26,8 @@ STATIC mp_obj_t it_iternext(mp_obj_t self_in) {
     } else {
         // an exception was raised
         if (mp_obj_get_type(nlr.ret_val) == &mp_type_StopIteration) {
-            // return MP_OBJ_NULL instead of raising StopIteration
-            return MP_OBJ_NULL;
+            // return MP_OBJ_STOP_ITERATION instead of raising StopIteration
+            return MP_OBJ_STOP_ITERATION;
         } else {
             // re-raise exception
             nlr_raise(nlr.ret_val);
diff --git a/py/objint.c b/py/objint.c
index 2eabff8d4..2c71f51cd 100644
--- a/py/objint.c
+++ b/py/objint.c
@@ -193,7 +193,7 @@ bool mp_obj_int_is_positive(mp_obj_t self_in) {
 
 // This is called for operations on SMALL_INT that are not handled by mp_unary_op
 mp_obj_t mp_obj_int_unary_op(int op, mp_obj_t o_in) {
-    return MP_OBJ_NULL;
+    return MP_OBJ_NOT_SUPPORTED;
 }
 
 // This is called for operations on SMALL_INT that are not handled by mp_binary_op
@@ -262,7 +262,7 @@ mp_obj_t mp_obj_int_binary_op_extra_cases(int op, mp_obj_t lhs_in, mp_obj_t rhs_
             return mp_binary_op(op, rhs_in, lhs_in);
         }
     }
-    return MP_OBJ_NULL;
+    return MP_OBJ_NOT_SUPPORTED;
 }
 
 // this is a classmethod
diff --git a/py/objint_longlong.c b/py/objint_longlong.c
index 7dc0573bc..7d71c5a69 100644
--- a/py/objint_longlong.c
+++ b/py/objint_longlong.c
@@ -37,7 +37,7 @@ mp_obj_t mp_obj_int_unary_op(int op, mp_obj_t o_in) {
         case MP_UNARY_OP_POSITIVE: return o_in;
         case MP_UNARY_OP_NEGATIVE: return mp_obj_new_int_from_ll(-o->val);
         case MP_UNARY_OP_INVERT: return mp_obj_new_int_from_ll(~o->val);
-        default: return NULL; // op not supported
+        default: return MP_OBJ_NOT_SUPPORTED;
     }
 }
 
@@ -50,7 +50,7 @@ mp_obj_t mp_obj_int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
     } else if (MP_OBJ_IS_TYPE(lhs_in, &mp_type_int)) {
         lhs_val = ((mp_obj_int_t*)lhs_in)->val;
     } else {
-        return MP_OBJ_NULL;
+        return MP_OBJ_NOT_SUPPORTED;
     }
 
     if (MP_OBJ_IS_SMALL_INT(rhs_in)) {
@@ -108,8 +108,7 @@ mp_obj_t mp_obj_int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
             return MP_BOOL(lhs_val == rhs_val);
 
         default:
-            // op not supported
-            return MP_OBJ_NULL;
+            return MP_OBJ_NOT_SUPPORTED;
     }
 }
 
diff --git a/py/objint_mpz.c b/py/objint_mpz.c
index 2d10ed471..b94dcfee3 100644
--- a/py/objint_mpz.c
+++ b/py/objint_mpz.c
@@ -82,7 +82,7 @@ mp_obj_t mp_obj_int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
         zlhs = &((mp_obj_int_t*)lhs_in)->mpz;
     } else {
         // unsupported type
-        return MP_OBJ_NULL;
+        return MP_OBJ_NOT_SUPPORTED;
     }
 
     // if rhs is small int, then lhs was not (otherwise mp_binary_op handles it)
@@ -187,7 +187,7 @@ mp_obj_t mp_obj_int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
                 break;
 
             default:
-                return MP_OBJ_NULL;
+                return MP_OBJ_NOT_SUPPORTED;
         }
 
         return res;
@@ -207,7 +207,7 @@ mp_obj_t mp_obj_int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
                 return MP_BOOL(cmp == 0);
 
             default:
-                return MP_OBJ_NULL;
+                return MP_OBJ_NOT_SUPPORTED;
         }
     }
 }
diff --git a/py/objlist.c b/py/objlist.c
index 28011dc83..350ad2ca2 100644
--- a/py/objlist.c
+++ b/py/objlist.c
@@ -36,7 +36,7 @@ STATIC void list_print(void (*print)(void *env, const char *fmt, ...), void *env
 STATIC mp_obj_t list_extend_from_iter(mp_obj_t list, mp_obj_t iterable) {
     mp_obj_t iter = mp_getiter(iterable);
     mp_obj_t item;
-    while ((item = mp_iternext(iter)) != MP_OBJ_NULL) {
+    while ((item = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
         mp_obj_list_append(list, item);
     }
     return list;
@@ -81,7 +81,7 @@ STATIC mp_obj_t list_unary_op(int op, mp_obj_t self_in) {
     switch (op) {
         case MP_UNARY_OP_BOOL: return MP_BOOL(self->len != 0);
         case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->len);
-        default: return MP_OBJ_NULL; // op not supported for None
+        default: return MP_OBJ_NOT_SUPPORTED;
     }
 }
 
@@ -121,8 +121,7 @@ STATIC mp_obj_t list_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
             return MP_BOOL(list_cmp_helper(op, lhs, rhs));
 
         default:
-            // op not supported
-            return NULL;
+            return MP_OBJ_NOT_SUPPORTED;
     }
 }
 
@@ -423,7 +422,7 @@ mp_obj_t list_it_iternext(mp_obj_t self_in) {
         self->cur += 1;
         return o_out;
     } else {
-        return MP_OBJ_NULL;
+        return MP_OBJ_STOP_ITERATION;
     }
 }
 
diff --git a/py/objmap.c b/py/objmap.c
index 42bcc436f..45e65549d 100644
--- a/py/objmap.c
+++ b/py/objmap.c
@@ -41,9 +41,9 @@ STATIC mp_obj_t map_iternext(mp_obj_t self_in) {
 
     for (int i = 0; i < self->n_iters; i++) {
         mp_obj_t next = mp_iternext(self->iters[i]);
-        if (next == MP_OBJ_NULL) {
+        if (next == MP_OBJ_STOP_ITERATION) {
             m_del(mp_obj_t, nextses, self->n_iters);
-            return MP_OBJ_NULL;
+            return MP_OBJ_STOP_ITERATION;
         }
         nextses[i] = next;
     }
diff --git a/py/objnone.c b/py/objnone.c
index c79c8189a..fc55132f3 100644
--- a/py/objnone.c
+++ b/py/objnone.c
@@ -18,7 +18,7 @@ STATIC void none_print(void (*print)(void *env, const char *fmt, ...), void *env
 STATIC mp_obj_t none_unary_op(int op, mp_obj_t o_in) {
     switch (op) {
         case MP_UNARY_OP_BOOL: return mp_const_false;
-        default: return MP_OBJ_NULL; // op not supported for None
+        default: return MP_OBJ_NOT_SUPPORTED;
     }
 }
 
diff --git a/py/objrange.c b/py/objrange.c
index 45f83580d..7815a5951 100644
--- a/py/objrange.c
+++ b/py/objrange.c
@@ -25,7 +25,7 @@ STATIC mp_obj_t range_it_iternext(mp_obj_t o_in) {
         o->cur += o->step;
         return o_out;
     } else {
-        return MP_OBJ_NULL;
+        return MP_OBJ_STOP_ITERATION;
     }
 }
 
diff --git a/py/objset.c b/py/objset.c
index bdc8d7f38..db3683f60 100644
--- a/py/objset.c
+++ b/py/objset.c
@@ -59,7 +59,7 @@ STATIC mp_obj_t set_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_
             mp_obj_t set = mp_obj_new_set(0, NULL);
             mp_obj_t iterable = mp_getiter(args[0]);
             mp_obj_t item;
-            while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) {
+            while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
                 mp_obj_set_store(set, item);
             }
             return set;
@@ -90,7 +90,7 @@ STATIC mp_obj_t set_it_iternext(mp_obj_t self_in) {
         }
     }
 
-    return MP_OBJ_NULL;
+    return MP_OBJ_STOP_ITERATION;
 }
 
 STATIC mp_obj_t set_getiter(mp_obj_t set_in) {
@@ -163,7 +163,7 @@ STATIC mp_obj_t set_diff_int(int n_args, const mp_obj_t *args, bool update) {
         } else {
             mp_obj_t iter = mp_getiter(other);
             mp_obj_t next;
-            while ((next = mp_iternext(iter)) != MP_OBJ_NULL) {
+            while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
                 set_discard(self, next);
             }
         }
@@ -194,7 +194,7 @@ STATIC mp_obj_t set_intersect_int(mp_obj_t self_in, mp_obj_t other, bool update)
 
     mp_obj_t iter = mp_getiter(other);
     mp_obj_t next;
-    while ((next = mp_iternext(iter)) != MP_OBJ_NULL) {
+    while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
         if (mp_set_lookup(&self->set, next, MP_MAP_LOOKUP)) {
             set_add(out, next);
         }
@@ -226,7 +226,7 @@ STATIC mp_obj_t set_isdisjoint(mp_obj_t self_in, mp_obj_t other) {
 
     mp_obj_t iter = mp_getiter(other);
     mp_obj_t next;
-    while ((next = mp_iternext(iter)) != MP_OBJ_NULL) {
+    while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
         if (mp_set_lookup(&self->set, next, MP_MAP_LOOKUP)) {
             return mp_const_false;
         }
@@ -259,7 +259,7 @@ STATIC mp_obj_t set_issubset_internal(mp_obj_t self_in, mp_obj_t other_in, bool
     } else {
         mp_obj_t iter = set_getiter(self);
         mp_obj_t next;
-        while ((next = set_it_iternext(iter)) != MP_OBJ_NULL) {
+        while ((next = set_it_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
             if (!mp_set_lookup(&other->set, next, MP_MAP_LOOKUP)) {
                 out = false;
                 break;
@@ -331,7 +331,7 @@ STATIC mp_obj_t set_symmetric_difference_update(mp_obj_t self_in, mp_obj_t other
     mp_obj_set_t *self = self_in;
     mp_obj_t iter = mp_getiter(other_in);
     mp_obj_t next;
-    while ((next = mp_iternext(iter)) != MP_OBJ_NULL) {
+    while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
         mp_set_lookup(&self->set, next, MP_MAP_LOOKUP_REMOVE_IF_FOUND | MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
     }
     return mp_const_none;
@@ -349,7 +349,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(set_symmetric_difference_obj, set_symmetric_dif
 STATIC void set_update_int(mp_obj_set_t *self, mp_obj_t other_in) {
     mp_obj_t iter = mp_getiter(other_in);
     mp_obj_t next;
-    while ((next = mp_iternext(iter)) != MP_OBJ_NULL) {
+    while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
         mp_set_lookup(&self->set, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
     }
 }
@@ -379,7 +379,7 @@ STATIC mp_obj_t set_unary_op(int op, mp_obj_t self_in) {
     switch (op) {
         case MP_UNARY_OP_BOOL: return MP_BOOL(self->set.used != 0);
         case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT((machine_int_t)self->set.used);
-        default: return MP_OBJ_NULL; // op not supported for None
+        default: return MP_OBJ_NOT_SUPPORTED;
     }
 }
 
diff --git a/py/objstr.c b/py/objstr.c
index b9ca8a8ab..e444ec7d4 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -161,7 +161,7 @@ STATIC mp_obj_t bytes_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const m
 
     mp_obj_t iterable = mp_getiter(args[0]);
     mp_obj_t item;
-    while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) {
+    while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
         if (len == -1) {
             vstr_add_char(vstr, MP_OBJ_SMALL_INT_VALUE(item));
         } else {
@@ -285,7 +285,7 @@ STATIC mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
             }
     }
 
-    return MP_OBJ_NULL; // op not supported
+    return MP_OBJ_NOT_SUPPORTED;
 }
 
 STATIC mp_obj_t str_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
@@ -1554,7 +1554,7 @@ STATIC mp_obj_t str_it_iternext(mp_obj_t self_in) {
         self->cur += 1;
         return o_out;
     } else {
-        return MP_OBJ_NULL;
+        return MP_OBJ_STOP_ITERATION;
     }
 }
 
@@ -1573,7 +1573,7 @@ STATIC mp_obj_t bytes_it_iternext(mp_obj_t self_in) {
         self->cur += 1;
         return o_out;
     } else {
-        return MP_OBJ_NULL;
+        return MP_OBJ_STOP_ITERATION;
     }
 }
 
diff --git a/py/objtuple.c b/py/objtuple.c
index 57b313fd6..e6b902d74 100644
--- a/py/objtuple.c
+++ b/py/objtuple.c
@@ -52,7 +52,7 @@ mp_obj_t mp_obj_tuple_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const m
 
             mp_obj_t iterable = mp_getiter(args[0]);
             mp_obj_t item;
-            while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) {
+            while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
                 if (len >= alloc) {
                     items = m_renew(mp_obj_t, items, alloc, alloc * 2);
                     alloc *= 2;
@@ -88,7 +88,7 @@ mp_obj_t tuple_unary_op(int op, mp_obj_t self_in) {
     switch (op) {
         case MP_UNARY_OP_BOOL: return MP_BOOL(self->len != 0);
         case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->len);
-        default: return MP_OBJ_NULL; // op not supported for None
+        default: return MP_OBJ_NOT_SUPPORTED;
     }
 }
 
@@ -249,7 +249,7 @@ STATIC mp_obj_t tuple_it_iternext(mp_obj_t self_in) {
         self->cur += 1;
         return o_out;
     } else {
-        return MP_OBJ_NULL;
+        return MP_OBJ_STOP_ITERATION;
     }
 }
 
diff --git a/py/objtype.c b/py/objtype.c
index c7726035a..f281f799e 100644
--- a/py/objtype.c
+++ b/py/objtype.c
@@ -43,14 +43,14 @@ STATIC mp_obj_t mp_obj_class_lookup(const mp_obj_type_t *type, qstr attr) {
 
         // for a const struct, this entry might be NULL
         if (type->bases_tuple == MP_OBJ_NULL) {
-            return NULL;
+            return MP_OBJ_NULL;
         }
 
         uint len;
         mp_obj_t *items;
         mp_obj_tuple_get(type->bases_tuple, &len, &items);
         if (len == 0) {
-            return NULL;
+            return MP_OBJ_NULL;
         }
         for (uint i = 0; i < len - 1; i++) {
             assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type));
@@ -133,13 +133,13 @@ STATIC mp_obj_t class_unary_op(int op, mp_obj_t self_in) {
     mp_obj_class_t *self = self_in;
     qstr op_name = unary_op_method_name[op];
     if (op_name == 0) {
-        return MP_OBJ_NULL;
+        return MP_OBJ_NOT_SUPPORTED;
     }
     mp_obj_t member = mp_obj_class_lookup(self->base.type, op_name);
     if (member != MP_OBJ_NULL) {
         return mp_call_function_1(member, self_in);
     } else {
-        return MP_OBJ_NULL;
+        return MP_OBJ_NOT_SUPPORTED;
     }
 }
 
@@ -211,7 +211,7 @@ STATIC mp_obj_t class_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
     mp_obj_class_t *lhs = lhs_in;
     qstr op_name = binary_op_method_name[op];
     if (op_name == 0) {
-        return MP_OBJ_NULL;
+        return MP_OBJ_NOT_SUPPORTED;
     }
     mp_obj_t member = mp_obj_class_lookup(lhs->base.type, op_name);
     if (member != MP_OBJ_NULL) {
@@ -221,7 +221,7 @@ STATIC mp_obj_t class_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
         dest[2] = rhs_in;
         return mp_call_method_n_kw(1, 0, dest);
     } else {
-        return MP_OBJ_NULL;
+        return MP_OBJ_NOT_SUPPORTED;
     }
 }
 
diff --git a/py/objzip.c b/py/objzip.c
index 9ed8b56f6..4c7070b7c 100644
--- a/py/objzip.c
+++ b/py/objzip.c
@@ -30,16 +30,16 @@ STATIC mp_obj_t zip_iternext(mp_obj_t self_in) {
     mp_obj_zip_t *self = self_in;
     mp_obj_t *items;
     if (self->n_iters == 0) {
-        return MP_OBJ_NULL;
+        return MP_OBJ_STOP_ITERATION;
     }
     mp_obj_t o = mp_obj_new_tuple(self->n_iters, NULL);
     mp_obj_tuple_get(o, NULL, &items);
 
     for (int i = 0; i < self->n_iters; i++) {
         mp_obj_t next = mp_iternext(self->iters[i]);
-        if (next == MP_OBJ_NULL) {
+        if (next == MP_OBJ_STOP_ITERATION) {
             mp_obj_tuple_del(o);
-            return MP_OBJ_NULL;
+            return MP_OBJ_STOP_ITERATION;
         }
         items[i] = next;
     }
diff --git a/py/runtime.c b/py/runtime.c
index 5dc86ff16..7830301c7 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -163,7 +163,7 @@ mp_obj_t mp_unary_op(int op, mp_obj_t arg) {
         mp_obj_type_t *type = mp_obj_get_type(arg);
         if (type->unary_op != NULL) {
             mp_obj_t result = type->unary_op(op, arg);
-            if (result != NULL) {
+            if (result != MP_OBJ_NOT_SUPPORTED) {
                 return result;
             }
         }
@@ -402,7 +402,7 @@ mp_obj_t mp_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
         mp_obj_type_t *type = mp_obj_get_type(rhs);
         if (type->binary_op != NULL) {
             mp_obj_t res = type->binary_op(op, rhs, lhs);
-            if (res != MP_OBJ_NULL) {
+            if (res != MP_OBJ_NOT_SUPPORTED) {
                 return res;
             }
         }
@@ -410,7 +410,7 @@ mp_obj_t mp_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
             /* second attempt, walk the iterator */
             mp_obj_t next = NULL;
             mp_obj_t iter = mp_getiter(rhs);
-            while ((next = mp_iternext(iter)) != MP_OBJ_NULL) {
+            while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
                 if (mp_obj_equal(next, lhs)) {
                     return mp_const_true;
                 }
@@ -430,7 +430,7 @@ generic_binary_op:
     type = mp_obj_get_type(lhs);
     if (type->binary_op != NULL) {
         mp_obj_t result = type->binary_op(op, lhs, rhs);
-        if (result != MP_OBJ_NULL) {
+        if (result != MP_OBJ_NOT_SUPPORTED) {
             return result;
         }
     }
@@ -580,7 +580,7 @@ mp_obj_t mp_call_method_n_kw_var(bool have_self, uint n_args_n_kw, const mp_obj_
         // extract the variable position args from the iterator
         mp_obj_t iterable = mp_getiter(pos_seq);
         mp_obj_t item;
-        while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) {
+        while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
             if (args2_len >= args2_alloc) {
                 args2 = m_renew(mp_obj_t, args2, args2_alloc, args2_alloc * 2);
                 args2_alloc *= 2;
@@ -617,7 +617,7 @@ mp_obj_t mp_call_method_n_kw_var(bool have_self, uint n_args_n_kw, const mp_obj_
         mp_load_method(kw_dict, MP_QSTR_items, dest);
         mp_obj_t iterable = mp_getiter(mp_call_method_n_kw(0, 0, dest));
         mp_obj_t item;
-        while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) {
+        while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
             if (args2_len + 1 >= args2_alloc) {
                 uint new_alloc = args2_alloc * 2;
                 if (new_alloc < 4) {
@@ -662,12 +662,12 @@ void mp_unpack_sequence(mp_obj_t seq_in, uint num, mp_obj_t *items) {
 
         for (seq_len = 0; seq_len < num; seq_len++) {
             mp_obj_t el = mp_iternext(iterable);
-            if (el == MP_OBJ_NULL) {
+            if (el == MP_OBJ_STOP_ITERATION) {
                 goto too_short;
             }
             items[num - 1 - seq_len] = el;
         }
-        if (mp_iternext(iterable) != MP_OBJ_NULL) {
+        if (mp_iternext(iterable) != MP_OBJ_STOP_ITERATION) {
             goto too_long;
         }
     }
@@ -716,13 +716,13 @@ void mp_unpack_ex(mp_obj_t seq_in, uint num_in, mp_obj_t *items) {
         mp_obj_t item;
         for (seq_len = 0; seq_len < num_left; seq_len++) {
             item = mp_iternext(iterable);
-            if (item == MP_OBJ_NULL) {
+            if (item == MP_OBJ_STOP_ITERATION) {
                 goto too_short;
             }
             items[num_left + num_right + 1 - 1 - seq_len] = item;
         }
         mp_obj_t rest = mp_obj_new_list(0, NULL);
-        while ((item = mp_iternext(iterable)) != MP_OBJ_NULL) {
+        while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
             mp_obj_list_append(rest, item);
         }
         uint rest_len;
@@ -864,7 +864,7 @@ mp_obj_t mp_getiter(mp_obj_t o_in) {
     }
 }
 
-// may return MP_OBJ_NULL as an optimisation instead of raise StopIteration()
+// may return MP_OBJ_STOP_ITERATION as an optimisation instead of raise StopIteration()
 // may also raise StopIteration()
 mp_obj_t mp_iternext_allow_raise(mp_obj_t o_in) {
     mp_obj_type_t *type = mp_obj_get_type(o_in);
@@ -883,7 +883,7 @@ mp_obj_t mp_iternext_allow_raise(mp_obj_t o_in) {
     }
 }
 
-// will always return MP_OBJ_NULL instead of raising StopIteration() (or any subclass thereof)
+// will always return MP_OBJ_STOP_ITERATION instead of raising StopIteration() (or any subclass thereof)
 // may raise other exceptions
 mp_obj_t mp_iternext(mp_obj_t o_in) {
     mp_obj_type_t *type = mp_obj_get_type(o_in);
@@ -902,7 +902,7 @@ mp_obj_t mp_iternext(mp_obj_t o_in) {
                 return ret;
             } else {
                 if (mp_obj_is_subclass_fast(mp_obj_get_type(nlr.ret_val), &mp_type_StopIteration)) {
-                    return MP_OBJ_NULL;
+                    return MP_OBJ_STOP_ITERATION;
                 } else {
                     nlr_raise(nlr.ret_val);
                 }
diff --git a/py/runtime.h b/py/runtime.h
index 6d49e49b7..3297a21a3 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -50,8 +50,8 @@ void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest);
 void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t val);
 
 mp_obj_t mp_getiter(mp_obj_t o);
-mp_obj_t mp_iternext_allow_raise(mp_obj_t o); // may return MP_OBJ_NULL instead of raising StopIteration()
-mp_obj_t mp_iternext(mp_obj_t o); // will always return MP_OBJ_NULL instead of raising StopIteration(...)
+mp_obj_t mp_iternext_allow_raise(mp_obj_t o); // may return MP_OBJ_STOP_ITERATION instead of raising StopIteration()
+mp_obj_t mp_iternext(mp_obj_t o); // will always return MP_OBJ_STOP_ITERATION instead of raising StopIteration(...)
 mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val);
 
 mp_obj_t mp_make_raise_obj(mp_obj_t o);
diff --git a/py/stream.c b/py/stream.c
index f3df70fa5..ba8025021 100644
--- a/py/stream.c
+++ b/py/stream.c
@@ -154,7 +154,7 @@ mp_obj_t mp_stream_unbuffered_iter(mp_obj_t self) {
     if (sz != 0) {
         return l_in;
     }
-    return MP_OBJ_NULL;
+    return MP_OBJ_STOP_ITERATION;
 }
 
 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj, 1, 2, stream_read);
diff --git a/py/vm.c b/py/vm.c
index dbefdc32c..f14899ab4 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -623,7 +623,7 @@ unwind_jump:
                     DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward
                     save_sp = sp;
                     obj1 = mp_iternext_allow_raise(TOP());
-                    if (obj1 == MP_OBJ_NULL) {
+                    if (obj1 == MP_OBJ_STOP_ITERATION) {
                         --sp; // pop the exhausted iterator
                         ip += unum; // jump to after for-block
                     } else {
-- 
GitLab