diff --git a/py/bc0.h b/py/bc0.h
index 2c8c47bd2e5172728b6f27273255145882feff3f..3d4b106d3245e41b4c7be73e074e5fc9ba41b5cc 100644
--- a/py/bc0.h
+++ b/py/bc0.h
@@ -95,6 +95,7 @@
 #define MP_BC_CALL_METHOD_KW     (0x98) // uint
 #define MP_BC_CALL_METHOD_VAR_KW (0x99) // uint
 #define MP_BC_MAKE_FUNCTION_DEFARGS  (0x9a) // uint
+#define MP_BC_MAKE_CLOSURE_DEFARGS   (0x9b) // uint
 
 #define MP_BC_IMPORT_NAME        (0xe0) // qstr
 #define MP_BC_IMPORT_FROM        (0xe1) // qstr
diff --git a/py/compile.c b/py/compile.c
index 0a10b817682442b53c1fc9cb5a3532ae1ed0f83d..dc001cd88f507ace8f252304c94d84b762820736 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -766,6 +766,9 @@ void c_assign(compiler_t *comp, mp_parse_node_t pn, assign_kind_t assign_kind) {
 
 // stuff for lambda and comprehensions and generators
 void close_over_variables_etc(compiler_t *comp, scope_t *this_scope, int n_dict_params, int n_default_params) {
+    if (n_default_params) {
+        EMIT_ARG(build_tuple, n_default_params);
+    }
     // make closed over variables, if any
     // ensure they are closed over in the order defined in the outer scope (mainly to agree with CPython)
     int nfree = 0;
diff --git a/py/emitbc.c b/py/emitbc.c
index ef5da3a622e8d70eeb4daf354d1ecfcc92c1c9c1..653b14487520559b3efb8611b0bc48f206acd6bc 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -722,16 +722,20 @@ STATIC void emit_bc_make_function(emit_t *emit, scope_t *scope, int n_dict_param
         emit_pre(emit, 1);
         emit_write_byte_code_byte_uint(emit, MP_BC_MAKE_FUNCTION, scope->unique_code_id);
     } else {
-        emit_bc_build_tuple(emit, n_default_params);
         emit_pre(emit, 0);
         emit_write_byte_code_byte_uint(emit, MP_BC_MAKE_FUNCTION_DEFARGS, scope->unique_code_id);
     }
 }
 
 STATIC void emit_bc_make_closure(emit_t *emit, scope_t *scope, int n_dict_params, int n_default_params) {
-    assert(n_default_params == 0 && n_dict_params == 0);
-    emit_pre(emit, 0);
-    emit_write_byte_code_byte_uint(emit, MP_BC_MAKE_CLOSURE, scope->unique_code_id);
+    assert(n_dict_params == 0);
+    if (n_default_params == 0) {
+        emit_pre(emit, 0);
+        emit_write_byte_code_byte_uint(emit, MP_BC_MAKE_CLOSURE, scope->unique_code_id);
+    } else {
+        emit_pre(emit, -1);
+        emit_write_byte_code_byte_uint(emit, MP_BC_MAKE_CLOSURE_DEFARGS, scope->unique_code_id);
+    }
 }
 
 STATIC void emit_bc_call_function(emit_t *emit, int n_positional, int n_keyword, bool have_star_arg, bool have_dbl_star_arg) {
diff --git a/py/runtime.c b/py/runtime.c
index 6590e8e0d0992d1df513d1f692a1aafc287482ad..3fc7d6ac083e017a478905e8a6b713ab12300099 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -683,10 +683,10 @@ mp_obj_t rt_make_function_from_id(int unique_code_id, mp_obj_t def_args) {
     return fun;
 }
 
-mp_obj_t rt_make_closure_from_id(int unique_code_id, mp_obj_t closure_tuple) {
+mp_obj_t rt_make_closure_from_id(int unique_code_id, mp_obj_t closure_tuple, mp_obj_t def_args) {
     DEBUG_OP_printf("make_closure_from_id %d\n", unique_code_id);
     // make function object
-    mp_obj_t ffun = rt_make_function_from_id(unique_code_id, MP_OBJ_NULL);
+    mp_obj_t ffun = rt_make_function_from_id(unique_code_id, def_args);
     // wrap function in closure object
     return mp_obj_new_closure(ffun, closure_tuple);
 }
diff --git a/py/runtime.h b/py/runtime.h
index f5e4a49cf827d9ce6b81511660a49a05b15834ef..553a83468944baecceaf0b066bb28f5f2e7d47ba 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -19,7 +19,7 @@ mp_obj_t rt_make_function_from_id(int unique_code_id, mp_obj_t def_args);
 mp_obj_t rt_make_function_n(int n_args, void *fun); // fun must have the correct signature for n_args fixed arguments
 mp_obj_t rt_make_function_var(int n_args_min, mp_fun_var_t fun);
 mp_obj_t rt_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var_t fun); // min and max are inclusive
-mp_obj_t rt_make_closure_from_id(int unique_code_id, mp_obj_t closure_tuple);
+mp_obj_t rt_make_closure_from_id(int unique_code_id, mp_obj_t closure_tuple, mp_obj_t def_args);
 mp_obj_t rt_call_function_0(mp_obj_t fun);
 mp_obj_t rt_call_function_1(mp_obj_t fun, mp_obj_t arg);
 mp_obj_t rt_call_function_2(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2);
diff --git a/py/showbc.c b/py/showbc.c
index 9bdf811d9aebc932bbf9ef53fc2f1c94649f6b50..eb743bd29e583627a704d6a101514c1774c9bda3 100644
--- a/py/showbc.c
+++ b/py/showbc.c
@@ -371,6 +371,11 @@ void mp_byte_code_print(const byte *ip, int len) {
                 printf("MAKE_CLOSURE " UINT_FMT, unum);
                 break;
 
+            case MP_BC_MAKE_CLOSURE_DEFARGS:
+                DECODE_UINT;
+                printf("MAKE_CLOSURE_DEFARGS " UINT_FMT, unum);
+                break;
+
             case MP_BC_CALL_FUNCTION:
                 DECODE_UINT;
                 printf("CALL_FUNCTION n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff);
diff --git a/py/vm.c b/py/vm.c
index e34acb488b6bc4d55a4b78697665118ff1c34386..b98781c7616c2e62ad5c0ce96a8306fb220e855b 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -561,7 +561,13 @@ unwind_jump:
 
                     case MP_BC_MAKE_CLOSURE:
                         DECODE_UINT;
-                        SET_TOP(rt_make_closure_from_id(unum, TOP()));
+                        SET_TOP(rt_make_closure_from_id(unum, TOP(), MP_OBJ_NULL));
+                        break;
+
+                    case MP_BC_MAKE_CLOSURE_DEFARGS:
+                        DECODE_UINT;
+                        obj1 = POP();
+                        SET_TOP(rt_make_closure_from_id(unum, obj1, TOP()));
                         break;
 
                     case MP_BC_CALL_FUNCTION:
diff --git a/tests/basics/closure-defargs.py b/tests/basics/closure-defargs.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff8ada04142838b85ecd1af7cc2af595089e2f4a
--- /dev/null
+++ b/tests/basics/closure-defargs.py
@@ -0,0 +1,8 @@
+def f():
+    a = 1
+    def bar(b = 10, c = 20):
+        print(a + b + c)
+    bar()
+
+print(f())
+