diff --git a/py/builtin.c b/py/builtin.c
index 42bb980f1fc70880a911703400c137ee9ce273a9..281c57dd437920e99c214ea08397ce64382fc221 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 = rt_getiter(o_in);
     mp_obj_t item;
-    while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
+    while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) {
         if (!rt_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 = rt_getiter(o_in);
     mp_obj_t item;
-    while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
+    while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) {
         if (rt_is_true(item)) {
             return mp_const_true;
         }
@@ -232,7 +232,7 @@ STATIC mp_obj_t mp_builtin_max(uint n_args, const mp_obj_t *args) {
         mp_obj_t iterable = rt_getiter(args[0]);
         mp_obj_t max_obj = NULL;
         mp_obj_t item;
-        while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
+        while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) {
             if (max_obj == NULL || mp_obj_less(max_obj, item)) {
                 max_obj = item;
             }
@@ -261,7 +261,7 @@ STATIC mp_obj_t mp_builtin_min(uint n_args, const mp_obj_t *args) {
         mp_obj_t iterable = rt_getiter(args[0]);
         mp_obj_t min_obj = NULL;
         mp_obj_t item;
-        while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
+        while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) {
             if (min_obj == NULL || mp_obj_less(item, min_obj)) {
                 min_obj = item;
             }
@@ -285,8 +285,8 @@ STATIC mp_obj_t mp_builtin_min(uint n_args, const mp_obj_t *args) {
 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 = rt_iternext(o);
-    if (ret == mp_const_stop_iteration) {
+    mp_obj_t ret = rt_iternext_allow_raise(o);
+    if (ret == MP_OBJ_NULL) {
         nlr_jump(mp_obj_new_exception(&mp_type_StopIteration));
     } else {
         return ret;
@@ -362,7 +362,7 @@ STATIC mp_obj_t mp_builtin_sum(uint n_args, const mp_obj_t *args) {
     }
     mp_obj_t iterable = rt_getiter(args[0]);
     mp_obj_t item;
-    while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
+    while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) {
         value = rt_binary_op(RT_BINARY_OP_ADD, value, item);
     }
     return value;
diff --git a/py/emitnative.c b/py/emitnative.c
index 3ed415b04c0d0bc8a3801f496671dd2c9d05b1a2..75086de155fcbaed1403f87bce97391303d79184 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -987,7 +987,7 @@ STATIC void emit_native_for_iter(emit_t *emit, int label) {
     emit_access_stack(emit, 1, &vtype, REG_ARG_1);
     assert(vtype == VTYPE_PYOBJ);
     emit_call(emit, RT_F_ITERNEXT, rt_iternext);
-    ASM_MOV_IMM_TO_REG((machine_uint_t)mp_const_stop_iteration, REG_TEMP1);
+    ASM_MOV_IMM_TO_REG((machine_uint_t)MP_OBJ_NULL, REG_TEMP1);
 #if N_X64
     asm_x64_cmp_r64_with_r64(emit->as, REG_RET, REG_TEMP1);
     asm_x64_jcc_label(emit->as, JCC_JE, label);
diff --git a/py/obj.h b/py/obj.h
index 19d32198b1f80f04c0979da8198819b91356c300..7c3ef8c55bea5c41714b81677ce8f06143dc56ea 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -152,7 +152,7 @@ struct _mp_obj_type_t {
     mp_store_item_fun_t store_item;
 
     mp_fun_1_t getiter;
-    mp_fun_1_t iternext;
+    mp_fun_1_t iternext; // may return MP_OBJ_NULL as an optimisation instead of raising StopIteration() (with no args)
 
     // Alternatively, pointer(s) to interfaces to save space
     // in mp_obj_type_t at the expense of extra pointer and extra dereference
@@ -221,7 +221,6 @@ extern const mp_obj_t mp_const_false;
 extern const mp_obj_t mp_const_true;
 extern const mp_obj_t mp_const_empty_tuple;
 extern const mp_obj_t mp_const_ellipsis;
-extern const mp_obj_t mp_const_stop_iteration; // special object indicating end of iteration (not StopIteration exception!)
 
 // General API for objects
 
diff --git a/py/objarray.c b/py/objarray.c
index 69f60e000a6833e3f5d14092a67cf24d604dfe1a..d77a10107549fdadfbd7cfca83e41049829e74be 100644
--- a/py/objarray.c
+++ b/py/objarray.c
@@ -64,7 +64,7 @@ STATIC mp_obj_t array_construct(char typecode, mp_obj_t initializer) {
     mp_obj_t iterable = rt_getiter(initializer);
     mp_obj_t item;
     int i = 0;
-    while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
+    while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) {
         if (len == 0) {
             array_append(array, item);
         } else {
@@ -212,7 +212,7 @@ STATIC mp_obj_t array_it_iternext(mp_obj_t self_in) {
     if (self->cur < self->array->len) {
         return mp_binary_get_val(self->array->typecode, self->array->items, self->cur++);
     } else {
-        return mp_const_stop_iteration;
+        return MP_OBJ_NULL;
     }
 }
 
diff --git a/py/objdict.c b/py/objdict.c
index 17cc499c5e57b9a6fc95f72606db493aea9feb9a..ead2d7442f20d6cf9dd6a16aea7ceecf111cb4ae 100644
--- a/py/objdict.c
+++ b/py/objdict.c
@@ -106,7 +106,7 @@ mp_obj_t dict_it_iternext(mp_obj_t self_in) {
     if (next != NULL) {
         return next->key;
     } else {
-        return mp_const_stop_iteration;
+        return MP_OBJ_NULL;
     }
 }
 
@@ -171,7 +171,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 = rt_iternext(iter)) != mp_const_stop_iteration) {
+    while ((next = rt_iternext(iter)) != MP_OBJ_NULL) {
         mp_map_lookup(&self->map, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
     }
 
@@ -266,14 +266,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 = rt_getiter(iterable);
     mp_obj_t next = NULL;
-    while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
+    while ((next = rt_iternext(iter)) != MP_OBJ_NULL) {
         mp_obj_t inneriter = rt_getiter(next);
         mp_obj_t key = rt_iternext(inneriter);
         mp_obj_t value = rt_iternext(inneriter);
         mp_obj_t stop = rt_iternext(inneriter);
-        if (key == mp_const_stop_iteration
-            || value == mp_const_stop_iteration
-            || stop != mp_const_stop_iteration) {
+        if (key == MP_OBJ_NULL
+            || value == MP_OBJ_NULL
+            || stop != MP_OBJ_NULL) {
             nlr_jump(mp_obj_new_exception_msg(
                          &mp_type_ValueError,
                          "dictionary update sequence has the wrong length"));
@@ -335,7 +335,7 @@ STATIC mp_obj_t dict_view_it_iternext(mp_obj_t self_in) {
                 return mp_const_none;
         }
     } else {
-        return mp_const_stop_iteration;
+        return MP_OBJ_NULL;
     }
 }
 
@@ -364,7 +364,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_const_stop_iteration) {
+    while ((next = dict_view_it_iternext(self_iter)) != MP_OBJ_NULL) {
         if (!first) {
             print(env, ", ");
         }
diff --git a/py/objenumerate.c b/py/objenumerate.c
index 1862eb74e1944c409106caca2d41a046611f314a..8b33cafd7cbbdaffd4886a4596f7f9092490f07f 100644
--- a/py/objenumerate.c
+++ b/py/objenumerate.c
@@ -1,3 +1,4 @@
+#include <stdlib.h>
 #include <assert.h>
 
 #include "misc.h"
@@ -37,8 +38,8 @@ STATIC mp_obj_t enumerate_iternext(mp_obj_t self_in) {
     assert(MP_OBJ_IS_TYPE(self_in, &enumerate_type));
     mp_obj_enumerate_t *self = self_in;
     mp_obj_t next = rt_iternext(self->iter);
-    if (next == mp_const_stop_iteration) {
-        return mp_const_stop_iteration;
+    if (next == MP_OBJ_NULL) {
+        return MP_OBJ_NULL;
     } 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 ea76b990706f91d26aed48aa86e7926be705a61e..6ba1303a3d431a31d44eb828f240283d9527535e 100644
--- a/py/objfilter.c
+++ b/py/objfilter.c
@@ -1,3 +1,4 @@
+#include <stdlib.h>
 #include <assert.h>
 
 #include "nlr.h"
@@ -29,7 +30,7 @@ STATIC mp_obj_t filter_iternext(mp_obj_t self_in) {
     assert(MP_OBJ_IS_TYPE(self_in, &filter_type));
     mp_obj_filter_t *self = self_in;
     mp_obj_t next;
-    while ((next = rt_iternext(self->iter)) != mp_const_stop_iteration) {
+    while ((next = rt_iternext(self->iter)) != MP_OBJ_NULL) {
         mp_obj_t val;
         if (self->fun != mp_const_none) {
             val = rt_call_function_n_kw(self->fun, 1, 0, &next);
@@ -40,7 +41,7 @@ STATIC mp_obj_t filter_iternext(mp_obj_t self_in) {
             return next;
         }
     }
-    return mp_const_stop_iteration;
+    return MP_OBJ_NULL;
 }
 
 const mp_obj_type_t filter_type = {
diff --git a/py/objgenerator.c b/py/objgenerator.c
index eed5a55111e11b4796538aff76b6010ba880844e..0a4dcd7b177267a96c536e7af8959082e313a7d4 100644
--- a/py/objgenerator.c
+++ b/py/objgenerator.c
@@ -78,7 +78,7 @@ mp_obj_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw
     mp_obj_gen_instance_t *self = self_in;
     if (self->ip == 0) {
         *ret_kind = MP_VM_RETURN_NORMAL;
-        return mp_const_stop_iteration;
+        return MP_OBJ_NULL;
     }
     if (self->sp == self->state - 1) {
         if (send_value != mp_const_none) {
@@ -122,7 +122,7 @@ STATIC mp_obj_t gen_resume_and_raise(mp_obj_t self_in, mp_obj_t send_value, mp_o
         case MP_VM_RETURN_NORMAL:
             // Optimize return w/o value in case generator is used in for loop
             if (ret == mp_const_none) {
-                return mp_const_stop_iteration;
+                return MP_OBJ_NULL;
             } else {
                 nlr_jump(mp_obj_new_exception_args(&mp_type_StopIteration, 1, &ret));
             }
@@ -145,7 +145,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_const_stop_iteration) {
+    if (ret == MP_OBJ_NULL) {
         nlr_jump(mp_obj_new_exception(&mp_type_StopIteration));
     } else {
         return ret;
@@ -156,7 +156,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send);
 
 STATIC mp_obj_t gen_instance_throw(uint n_args, const mp_obj_t *args) {
     mp_obj_t ret = gen_resume_and_raise(args[0], mp_const_none, n_args == 2 ? args[1] : args[2]);
-    if (ret == mp_const_stop_iteration) {
+    if (ret == MP_OBJ_NULL) {
         nlr_jump(mp_obj_new_exception(&mp_type_StopIteration));
     } else {
         return ret;
diff --git a/py/objgetitemiter.c b/py/objgetitemiter.c
index 08ef66f29f1016aeddf271e233eed5b0394fb827..fff3f3807f6d5d1797be79a535a564a3b58cd711 100644
--- a/py/objgetitemiter.c
+++ b/py/objgetitemiter.c
@@ -1,3 +1,4 @@
+#include <stdlib.h>
 
 #include "nlr.h"
 #include "misc.h"
@@ -25,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_const_stop_iteration instead of raising StopIteration
-            return mp_const_stop_iteration;
+            // return MP_OBJ_NULL instead of raising StopIteration
+            return MP_OBJ_NULL;
         } else {
             // re-raise exception
             nlr_jump(nlr.ret_val);
diff --git a/py/objlist.c b/py/objlist.c
index f43670d6037361621b30846fef1dca0410be8190..3083e23bb1fc3e2ed08293d3587bf02cfa07cf15 100644
--- a/py/objlist.c
+++ b/py/objlist.c
@@ -53,7 +53,7 @@ STATIC mp_obj_t list_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp
             mp_obj_t iterable = rt_getiter(args[0]);
             mp_obj_t list = mp_obj_new_list(0, NULL);
             mp_obj_t item;
-            while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
+            while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) {
                 mp_obj_list_append(list, item);
             }
             return list;
@@ -401,7 +401,7 @@ mp_obj_t list_it_iternext(mp_obj_t self_in) {
         self->cur += 1;
         return o_out;
     } else {
-        return mp_const_stop_iteration;
+        return MP_OBJ_NULL;
     }
 }
 
diff --git a/py/objmap.c b/py/objmap.c
index 2b356ec4ea138860fe29f2d3b34826bfafddf791..cc7eae235bac49a5c6e3180cd21b7db7baa28435 100644
--- a/py/objmap.c
+++ b/py/objmap.c
@@ -1,3 +1,4 @@
+#include <stdlib.h>
 #include <assert.h>
 
 #include "nlr.h"
@@ -40,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 = rt_iternext(self->iters[i]);
-        if (next == mp_const_stop_iteration) {
+        if (next == MP_OBJ_NULL) {
             m_del(mp_obj_t, nextses, self->n_iters);
-            return mp_const_stop_iteration;
+            return MP_OBJ_NULL;
         }
         nextses[i] = next;
     }
diff --git a/py/objnone.c b/py/objnone.c
index a4370efebce27f9bc454f2016b6b17d598b9a085..1ec3d46fd4e3fd00dcda74e8fd8257850a192546 100644
--- a/py/objnone.c
+++ b/py/objnone.c
@@ -31,8 +31,3 @@ const mp_obj_type_t none_type = {
 
 STATIC const mp_obj_none_t none_obj = {{&none_type}};
 const mp_obj_t mp_const_none = (mp_obj_t)&none_obj;
-
-// the stop-iteration object just needs to be something unique
-// it's not the StopIteration exception
-STATIC const mp_obj_none_t stop_it_obj = {{&none_type}};
-const mp_obj_t mp_const_stop_iteration = (mp_obj_t)&stop_it_obj;
diff --git a/py/objrange.c b/py/objrange.c
index f80bc08602e503acb08d7efbed7b217902a25235..c527dfa89e3162e6d9d78aed0268702dcbbd3fb6 100644
--- a/py/objrange.c
+++ b/py/objrange.c
@@ -1,3 +1,4 @@
+#include <stdlib.h>
 
 #include "nlr.h"
 #include "misc.h"
@@ -55,7 +56,7 @@ STATIC mp_obj_t range_it_iternext(mp_obj_t o_in) {
         o->cur += o->step;
         return o_out;
     } else {
-        return mp_const_stop_iteration;
+        return MP_OBJ_NULL;
     }
 }
 
diff --git a/py/objset.c b/py/objset.c
index 437bae9bf840cc799159047beab46784eb5ed743..4f0776f893aa86e2f7c5d421acc48751768d4d4e 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 = rt_getiter(args[0]);
             mp_obj_t item;
-            while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
+            while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) {
                 mp_obj_set_store(set, item);
             }
             return set;
@@ -89,7 +89,7 @@ STATIC mp_obj_t set_it_iternext(mp_obj_t self_in) {
         }
     }
 
-    return mp_const_stop_iteration;
+    return MP_OBJ_NULL;
 }
 
 STATIC mp_obj_t set_getiter(mp_obj_t set_in) {
@@ -162,7 +162,7 @@ STATIC mp_obj_t set_diff_int(int n_args, const mp_obj_t *args, bool update) {
         } else {
             mp_obj_t iter = rt_getiter(other);
             mp_obj_t next;
-            while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
+            while ((next = rt_iternext(iter)) != MP_OBJ_NULL) {
                 set_discard(self, next);
             }
         }
@@ -193,7 +193,7 @@ STATIC mp_obj_t set_intersect_int(mp_obj_t self_in, mp_obj_t other, bool update)
 
     mp_obj_t iter = rt_getiter(other);
     mp_obj_t next;
-    while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
+    while ((next = rt_iternext(iter)) != MP_OBJ_NULL) {
         if (mp_set_lookup(&self->set, next, MP_MAP_LOOKUP)) {
             set_add(out, next);
         }
@@ -225,7 +225,7 @@ STATIC mp_obj_t set_isdisjoint(mp_obj_t self_in, mp_obj_t other) {
 
     mp_obj_t iter = rt_getiter(other);
     mp_obj_t next;
-    while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
+    while ((next = rt_iternext(iter)) != MP_OBJ_NULL) {
         if (mp_set_lookup(&self->set, next, MP_MAP_LOOKUP)) {
             return mp_const_false;
         }
@@ -258,7 +258,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_const_stop_iteration) {
+        while ((next = set_it_iternext(iter)) != MP_OBJ_NULL) {
             if (!mp_set_lookup(&other->set, next, MP_MAP_LOOKUP)) {
                 out = false;
                 break;
@@ -332,7 +332,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 = rt_getiter(other_in);
     mp_obj_t next;
-    while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
+    while ((next = rt_iternext(iter)) != MP_OBJ_NULL) {
         mp_set_lookup(&self->set, next, MP_MAP_LOOKUP_REMOVE_IF_FOUND | MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
     }
     return mp_const_none;
@@ -350,7 +350,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 = rt_getiter(other_in);
     mp_obj_t next;
-    while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
+    while ((next = rt_iternext(iter)) != MP_OBJ_NULL) {
         mp_set_lookup(&self->set, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
     }
 }
diff --git a/py/objstr.c b/py/objstr.c
index 00586a3b3b4572688205ba57ef1cb8115b915435..3ace7b051a1d39539a0b9dff8b548b4a1cda6aa3 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 = rt_getiter(args[0]);
     mp_obj_t item;
-    while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
+    while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) {
         if (len == -1) {
             vstr_add_char(vstr, MP_OBJ_SMALL_INT_VALUE(item));
         } else {
@@ -877,7 +877,7 @@ STATIC mp_obj_t str_it_iternext(mp_obj_t self_in) {
         self->cur += 1;
         return o_out;
     } else {
-        return mp_const_stop_iteration;
+        return MP_OBJ_NULL;
     }
 }
 
@@ -895,7 +895,7 @@ STATIC mp_obj_t bytes_it_iternext(mp_obj_t self_in) {
         self->cur += 1;
         return o_out;
     } else {
-        return mp_const_stop_iteration;
+        return MP_OBJ_NULL;
     }
 }
 
diff --git a/py/objtuple.c b/py/objtuple.c
index 3378b4ec1cd5fa15099a64fbc74b8a52110a473a..68f5abefdf2cd3300d8074af615f83df46580d53 100644
--- a/py/objtuple.c
+++ b/py/objtuple.c
@@ -53,7 +53,7 @@ STATIC mp_obj_t tuple_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const m
 
             mp_obj_t iterable = rt_getiter(args[0]);
             mp_obj_t item;
-            while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
+            while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) {
                 if (len >= alloc) {
                     items = m_renew(mp_obj_t, items, alloc, alloc * 2);
                     alloc *= 2;
@@ -245,7 +245,7 @@ STATIC mp_obj_t tuple_it_iternext(mp_obj_t self_in) {
         self->cur += 1;
         return o_out;
     } else {
-        return mp_const_stop_iteration;
+        return MP_OBJ_NULL;
     }
 }
 
diff --git a/py/objzip.c b/py/objzip.c
index b939ff6cb9629234a87cd809932406270c25e7bc..553dabba741b0351e788b43c47d2983c7ce61784 100644
--- a/py/objzip.c
+++ b/py/objzip.c
@@ -34,16 +34,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_const_stop_iteration;
+        return MP_OBJ_NULL;
     }
     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 = rt_iternext(self->iters[i]);
-        if (next == mp_const_stop_iteration) {
+        if (next == MP_OBJ_NULL) {
             mp_obj_tuple_del(o);
-            return mp_const_stop_iteration;
+            return MP_OBJ_NULL;
         }
         items[i] = next;
     }
diff --git a/py/runtime.c b/py/runtime.c
index 3e0ef98d8b6b1312197b86fe857f1ee7cabf2be8..cc5398552e78f58ed2065f33f33794c45d7ecc37 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -616,7 +616,7 @@ mp_obj_t rt_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 = rt_getiter(rhs);
-            while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
+            while ((next = rt_iternext(iter)) != MP_OBJ_NULL) {
                 if (mp_obj_equal(next, lhs)) {
                     return mp_const_true;
                 }
@@ -778,12 +778,12 @@ void rt_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 = rt_iternext(iterable);
-            if (el == mp_const_stop_iteration) {
+            if (el == MP_OBJ_NULL) {
                 goto too_short;
             }
             items[num - 1 - seq_len] = el;
         }
-        if (rt_iternext(iterable) != mp_const_stop_iteration) {
+        if (rt_iternext(iterable) != MP_OBJ_NULL) {
             goto too_long;
         }
     }
@@ -944,7 +944,9 @@ mp_obj_t rt_getiter(mp_obj_t o_in) {
     }
 }
 
-mp_obj_t rt_iternext(mp_obj_t o_in) {
+// may return MP_OBJ_NULL as an optimisation instead of raise StopIteration()
+// may also raise StopIteration()
+mp_obj_t rt_iternext_allow_raise(mp_obj_t o_in) {
     mp_obj_type_t *type = mp_obj_get_type(o_in);
     if (type->iternext != NULL) {
         return type->iternext(o_in);
@@ -961,6 +963,36 @@ mp_obj_t rt_iternext(mp_obj_t o_in) {
     }
 }
 
+// will always return MP_OBJ_NULL instead of raising StopIteration() (or any subclass thereof)
+// may raise other exceptions
+mp_obj_t rt_iternext(mp_obj_t o_in) {
+    mp_obj_type_t *type = mp_obj_get_type(o_in);
+    if (type->iternext != NULL) {
+        return type->iternext(o_in);
+    } else {
+        // check for __next__ method
+        mp_obj_t dest[2];
+        rt_load_method_maybe(o_in, MP_QSTR___next__, dest);
+        if (dest[0] != MP_OBJ_NULL) {
+            // __next__ exists, call it and return its result
+            nlr_buf_t nlr;
+            if (nlr_push(&nlr) == 0) {
+                mp_obj_t ret = rt_call_method_n_kw(0, 0, dest);
+                nlr_pop();
+                return ret;
+            } else {
+                if (mp_obj_is_subclass_fast(mp_obj_get_type(nlr.ret_val), &mp_type_StopIteration)) {
+                    return MP_OBJ_NULL;
+                } else {
+                    nlr_jump(nlr.ret_val);
+                }
+            }
+        } else {
+            nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "'%s' object is not an iterator", mp_obj_get_type_str(o_in)));
+        }
+    }
+}
+
 mp_obj_t rt_make_raise_obj(mp_obj_t o) {
     DEBUG_printf("raise %p\n", o);
     if (mp_obj_is_exception_type(o)) {
diff --git a/py/runtime.h b/py/runtime.h
index 3980e50cc037ecda6407380ac7ce186212d66084..f5e4a49cf827d9ce6b81511660a49a05b15834ef 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -39,7 +39,8 @@ void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest);
 void rt_store_attr(mp_obj_t base, qstr attr, mp_obj_t val);
 void rt_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t val);
 mp_obj_t rt_getiter(mp_obj_t o);
-mp_obj_t rt_iternext(mp_obj_t o);
+mp_obj_t rt_iternext_allow_raise(mp_obj_t o); // may return MP_OBJ_NULL instead of raising StopIteration()
+mp_obj_t rt_iternext(mp_obj_t o); // will always return MP_OBJ_NULL instead of raising StopIteration(...)
 mp_obj_t rt_make_raise_obj(mp_obj_t o);
 mp_obj_t rt_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level);
 mp_obj_t rt_import_from(mp_obj_t module, qstr name);
diff --git a/py/stream.c b/py/stream.c
index 7c97ee10c371c68bc8375547496e55029a5077a4..a0a2c68f803345946f1e1e2c414294bb008376c3 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_const_stop_iteration;
+    return MP_OBJ_NULL;
 }
 
 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 8a0262a8416249ad6a640fb817d5e8700b655164..e34acb488b6bc4d55a4b78697665118ff1c34386 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -430,8 +430,8 @@ unwind_jump:
 
                     case MP_BC_FOR_ITER:
                         DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward
-                        obj1 = rt_iternext(TOP());
-                        if (obj1 == mp_const_stop_iteration) {
+                        obj1 = rt_iternext_allow_raise(TOP());
+                        if (obj1 == MP_OBJ_NULL) {
                             --sp; // pop the exhausted iterator
                             ip += unum; // jump to after for-block
                         } else {
diff --git a/tests/basics/iter1.py b/tests/basics/iter1.py
new file mode 100644
index 0000000000000000000000000000000000000000..c2ef86a6356b5771972f567d065df1b112290b86
--- /dev/null
+++ b/tests/basics/iter1.py
@@ -0,0 +1,52 @@
+# test user defined iterators
+
+class MyStopIteration(StopIteration):
+    pass
+
+class myiter:
+    def __init__(self, i):
+        self.i = i
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        if self.i <= 0:
+            # stop in the usual way
+            raise StopIteration
+        elif self.i == 10:
+            # stop with an argument
+            raise StopIteration(42)
+        elif self.i == 20:
+            # raise a non-stop exception
+            raise TypeError
+        elif self.i == 30:
+            # raise a user-defined stop iteration
+            print('raising MyStopIteration')
+            raise MyStopIteration
+        else:
+            # return the next value
+            self.i -= 1
+            return self.i
+
+for i in myiter(5):
+    print(i)
+
+for i in myiter(12):
+    print(i)
+
+try:
+    for i in myiter(22):
+        print(i)
+except TypeError:
+    print('raised TypeError')
+
+try:
+    for i in myiter(5):
+        print(i)
+        raise StopIteration
+except StopIteration:
+    print('raised StopIteration')
+
+for i in myiter(32):
+    print(i)
diff --git a/tests/basics/iter2.py b/tests/basics/iter2.py
new file mode 100644
index 0000000000000000000000000000000000000000..763f59cb6fd01a1a6a1ca3f9fac9a5bb83b7b379
--- /dev/null
+++ b/tests/basics/iter2.py
@@ -0,0 +1,23 @@
+# user defined iterator used in something other than a for loop
+
+class MyStopIteration(StopIteration):
+    pass
+
+class myiter:
+    def __init__(self, i):
+        self.i = i
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        if self.i == 0:
+            raise StopIteration
+        elif self.i == 1:
+            raise StopIteration(1)
+        elif self.i == 2:
+            raise MyStopIteration
+
+print(list(myiter(0)))
+print(list(myiter(1)))
+print(list(myiter(2)))