summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--py/bc0.h23
-rw-r--r--py/compile.c10
-rw-r--r--py/emit.h4
-rw-r--r--py/emitbc.c24
-rw-r--r--py/emitcommon.c2
-rw-r--r--py/emitnative.c2
-rw-r--r--py/emitpass1.c3
-rw-r--r--py/scope.h1
-rw-r--r--py/showbc.c15
-rw-r--r--py/vm.c32
-rw-r--r--tests/basics/del-deref.py22
-rw-r--r--tests/basics/del-local.py25
12 files changed, 130 insertions, 33 deletions
diff --git a/py/bc0.h b/py/bc0.h
index 83993f8992..e6a0e21241 100644
--- a/py/bc0.h
+++ b/py/bc0.h
@@ -1,10 +1,6 @@
// Micro Python byte-codes.
// The comment at the end of the line (if it exists) tells the arguments to the byte-code.
-// TODO Add MP_BC_LOAD_FAST_CHECKED and MP_BC_STORE_FAST_CHECKED for acting on those
-// locals which have del called on them anywhere in the function.
-// UnboundLocalError: local variable '%s' referenced before assignment
-
#define MP_BC_LOAD_CONST_FALSE (0x10)
#define MP_BC_LOAD_CONST_NONE (0x11)
#define MP_BC_LOAD_CONST_TRUE (0x12)
@@ -21,12 +17,13 @@
#define MP_BC_LOAD_FAST_1 (0x21)
#define MP_BC_LOAD_FAST_2 (0x22)
#define MP_BC_LOAD_FAST_N (0x23) // uint
-#define MP_BC_LOAD_DEREF (0x24) // uint
-#define MP_BC_LOAD_NAME (0x25) // qstr
-#define MP_BC_LOAD_GLOBAL (0x26) // qstr
-#define MP_BC_LOAD_ATTR (0x27) // qstr
-#define MP_BC_LOAD_METHOD (0x28) // qstr
-#define MP_BC_LOAD_BUILD_CLASS (0x29)
+#define MP_BC_LOAD_FAST_CHECKED (0x24) // uint
+#define MP_BC_LOAD_DEREF (0x25) // uint
+#define MP_BC_LOAD_NAME (0x26) // qstr
+#define MP_BC_LOAD_GLOBAL (0x27) // qstr
+#define MP_BC_LOAD_ATTR (0x28) // qstr
+#define MP_BC_LOAD_METHOD (0x29) // qstr
+#define MP_BC_LOAD_BUILD_CLASS (0x2a)
#define MP_BC_STORE_FAST_0 (0x30)
#define MP_BC_STORE_FAST_1 (0x31)
@@ -38,8 +35,10 @@
#define MP_BC_STORE_ATTR (0x37) // qstr
#define MP_BC_STORE_SUBSCR (0x38)
-#define MP_BC_DELETE_NAME (0x39) // qstr
-#define MP_BC_DELETE_GLOBAL (0x3a) // qstr
+#define MP_BC_DELETE_FAST (0x39) // uint
+#define MP_BC_DELETE_DEREF (0x3a) // uint
+#define MP_BC_DELETE_NAME (0x3b) // qstr
+#define MP_BC_DELETE_GLOBAL (0x3c) // qstr
#define MP_BC_DUP_TOP (0x40)
#define MP_BC_DUP_TOP_TWO (0x41)
diff --git a/py/compile.c b/py/compile.c
index 49dddb0bd2..3ea6dd3a61 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -797,7 +797,7 @@ void close_over_variables_etc(compiler_t *comp, scope_t *this_scope, int n_pos_d
EMIT_ARG(load_closure, id->qstr, id->local_num);
#else
// in Micro Python we load closures using LOAD_FAST
- EMIT_ARG(load_fast, id->qstr, id->local_num);
+ EMIT_ARG(load_fast, id->qstr, id->flags, id->local_num);
#endif
nfree += 1;
}
@@ -2208,8 +2208,8 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar
// get first argument to function
bool found = false;
for (int i = 0; i < comp->scope_cur->id_info_len; i++) {
- if (comp->scope_cur->id_info[i].flags && ID_FLAG_IS_PARAM) {
- EMIT_ARG(load_fast, MP_QSTR_, comp->scope_cur->id_info[i].local_num);
+ if (comp->scope_cur->id_info[i].flags & ID_FLAG_IS_PARAM) {
+ EMIT_ARG(load_fast, MP_QSTR_, comp->scope_cur->id_info[i].flags, comp->scope_cur->id_info[i].local_num);
found = true;
break;
}
@@ -2990,7 +2990,7 @@ void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
#if MICROPY_EMIT_CPYTHON
EMIT_ARG(load_closure, MP_QSTR___class__, 0); // XXX check this is the correct local num
#else
- EMIT_ARG(load_fast, MP_QSTR___class__, id->local_num);
+ EMIT_ARG(load_fast, MP_QSTR___class__, id->flags, id->local_num);
#endif
}
EMIT(return_value);
@@ -3154,7 +3154,7 @@ void compile_scope_compute_things(compiler_t *comp, scope_t *scope) {
if (num_free > 0) {
for (int i = 0; i < scope->id_info_len; i++) {
id_info_t *id = &scope->id_info[i];
- if (id->kind != ID_INFO_KIND_FREE || (id->flags && ID_FLAG_IS_PARAM)) {
+ if (id->kind != ID_INFO_KIND_FREE || (id->flags & ID_FLAG_IS_PARAM)) {
id->local_num += num_free;
}
}
diff --git a/py/emit.h b/py/emit.h
index 47ac045af0..5ce11af92d 100644
--- a/py/emit.h
+++ b/py/emit.h
@@ -43,9 +43,9 @@ typedef struct _emit_method_table_t {
void (*load_const_id)(emit_t *emit, qstr qstr);
void (*load_const_str)(emit_t *emit, qstr qstr, bool bytes);
void (*load_const_verbatim_str)(emit_t *emit, const char *str); // only needed for emitcpy
- void (*load_fast)(emit_t *emit, qstr qstr, int local_num);
+ void (*load_fast)(emit_t *emit, qstr qstr, uint id_flags, int local_num);
void (*load_deref)(emit_t *emit, qstr qstr, int local_num);
- void (*load_closure)(emit_t *emit, qstr qstr, int local_num);
+ void (*load_closure)(emit_t *emit, qstr qstr, int local_num); // only needed for emitcpy
void (*load_name)(emit_t *emit, qstr qstr);
void (*load_global)(emit_t *emit, qstr qstr);
void (*load_attr)(emit_t *emit, qstr qstr);
diff --git a/py/emitbc.c b/py/emitbc.c
index f16ffced11..21d1a61863 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -408,14 +408,20 @@ STATIC void emit_bc_load_null(emit_t *emit) {
emit_write_byte_code_byte(emit, MP_BC_LOAD_NULL);
};
-STATIC void emit_bc_load_fast(emit_t *emit, qstr qstr, int local_num) {
+STATIC void emit_bc_load_fast(emit_t *emit, qstr qstr, uint id_flags, int local_num) {
assert(local_num >= 0);
emit_bc_pre(emit, 1);
- switch (local_num) {
- case 0: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_0); break;
- case 1: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_1); break;
- case 2: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_2); break;
- default: emit_write_byte_code_byte_uint(emit, MP_BC_LOAD_FAST_N, local_num); break;
+ if (id_flags & ID_FLAG_IS_DELETED) {
+ // This local may be deleted, so need to do a checked load.
+ emit_write_byte_code_byte_uint(emit, MP_BC_LOAD_FAST_CHECKED, local_num);
+ } else {
+ // This local is never deleted, so can do a fast, uncheched load.
+ switch (local_num) {
+ case 0: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_0); break;
+ case 1: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_1); break;
+ case 2: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_2); break;
+ default: emit_write_byte_code_byte_uint(emit, MP_BC_LOAD_FAST_N, local_num); break;
+ }
}
}
@@ -491,13 +497,11 @@ STATIC void emit_bc_store_subscr(emit_t *emit) {
}
STATIC void emit_bc_delete_fast(emit_t *emit, qstr qstr, int local_num) {
- emit_bc_load_null(emit);
- emit_bc_store_fast(emit, qstr, local_num);
+ emit_write_byte_code_byte_uint(emit, MP_BC_DELETE_FAST, local_num);
}
STATIC void emit_bc_delete_deref(emit_t *emit, qstr qstr, int local_num) {
- emit_bc_load_null(emit);
- emit_bc_store_deref(emit, qstr, local_num);
+ emit_write_byte_code_byte_uint(emit, MP_BC_DELETE_DEREF, local_num);
}
STATIC void emit_bc_delete_name(emit_t *emit, qstr qstr) {
diff --git a/py/emitcommon.c b/py/emitcommon.c
index f7494668e0..30f5ace923 100644
--- a/py/emitcommon.c
+++ b/py/emitcommon.c
@@ -25,7 +25,7 @@ void emit_common_load_id(emit_t *emit, const emit_method_table_t *emit_method_ta
} else if (id->kind == ID_INFO_KIND_GLOBAL_EXPLICIT) {
EMIT(load_global, qstr);
} else if (id->kind == ID_INFO_KIND_LOCAL) {
- EMIT(load_fast, qstr, id->local_num);
+ EMIT(load_fast, qstr, id->flags, id->local_num);
} else if (id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE) {
EMIT(load_deref, qstr, id->local_num);
} else {
diff --git a/py/emitnative.c b/py/emitnative.c
index 600960f309..e96b12271a 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -704,7 +704,7 @@ STATIC void emit_native_load_const_verbatim_str(emit_t *emit, const char *str) {
assert(0);
}
-STATIC void emit_native_load_fast(emit_t *emit, qstr qstr, int local_num) {
+STATIC void emit_native_load_fast(emit_t *emit, qstr qstr, uint id_flags, int local_num) {
vtype_kind_t vtype = emit->local_vtype[local_num];
if (vtype == VTYPE_UNBOUND) {
printf("ViperTypeError: local %s used before type known\n", qstr_str(qstr));
diff --git a/py/emitpass1.c b/py/emitpass1.c
index d2f7aaa53a..6088489410 100644
--- a/py/emitpass1.c
+++ b/py/emitpass1.c
@@ -91,7 +91,8 @@ STATIC void emit_pass1_store_id(emit_t *emit, qstr qstr) {
}
STATIC void emit_pass1_delete_id(emit_t *emit, qstr qstr) {
- get_id_for_modification(emit->scope, qstr);
+ id_info_t *id = get_id_for_modification(emit->scope, qstr);
+ id->flags |= ID_FLAG_IS_DELETED;
}
const emit_method_table_t emit_pass1_method_table = {
diff --git a/py/scope.h b/py/scope.h
index 68f55e0d03..07b41fe721 100644
--- a/py/scope.h
+++ b/py/scope.h
@@ -8,6 +8,7 @@ enum {
enum {
ID_FLAG_IS_PARAM = 0x01,
+ ID_FLAG_IS_DELETED = 0x02,
};
typedef struct _id_info_t {
diff --git a/py/showbc.c b/py/showbc.c
index c56620435d..c1e420f433 100644
--- a/py/showbc.c
+++ b/py/showbc.c
@@ -123,6 +123,11 @@ void mp_byte_code_print(const byte *ip, int len) {
printf("LOAD_FAST_N " UINT_FMT, unum);
break;
+ case MP_BC_LOAD_FAST_CHECKED:
+ DECODE_UINT;
+ printf("LOAD_FAST_CHECKED " UINT_FMT, unum);
+ break;
+
case MP_BC_LOAD_DEREF:
DECODE_UINT;
printf("LOAD_DEREF " UINT_FMT, unum);
@@ -193,6 +198,16 @@ void mp_byte_code_print(const byte *ip, int len) {
printf("STORE_SUBSCR");
break;
+ case MP_BC_DELETE_FAST:
+ DECODE_UINT;
+ printf("DELETE_FAST " UINT_FMT, unum);
+ break;
+
+ case MP_BC_DELETE_DEREF:
+ DECODE_UINT;
+ printf("DELETE_DEREF " UINT_FMT, unum);
+ break;
+
case MP_BC_DELETE_NAME:
DECODE_QSTR;
printf("DELETE_NAME %s", qstr_str(qstr));
diff --git a/py/vm.c b/py/vm.c
index 170104467c..f8b60acd22 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -241,9 +241,23 @@ dispatch_loop:
PUSH(fastn[-unum]);
break;
+ case MP_BC_LOAD_FAST_CHECKED:
+ DECODE_UINT;
+ obj1 = fastn[-unum];
+ if (obj1 == MP_OBJ_NULL) {
+ local_name_error:
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_NameError, "local variable referenced before assignment"));
+ }
+ PUSH(obj1);
+ break;
+
case MP_BC_LOAD_DEREF:
DECODE_UINT;
- PUSH(mp_obj_cell_get(fastn[-unum]));
+ obj1 = mp_obj_cell_get(fastn[-unum]);
+ if (obj1 == MP_OBJ_NULL) {
+ goto local_name_error;
+ }
+ PUSH(obj1);
break;
case MP_BC_LOAD_NAME:
@@ -314,6 +328,22 @@ dispatch_loop:
sp -= 3;
break;
+ case MP_BC_DELETE_FAST:
+ DECODE_UINT;
+ if (fastn[-unum] == MP_OBJ_NULL) {
+ goto local_name_error;
+ }
+ fastn[-unum] = MP_OBJ_NULL;
+ break;
+
+ case MP_BC_DELETE_DEREF:
+ DECODE_UINT;
+ if (mp_obj_cell_get(fastn[-unum]) == MP_OBJ_NULL) {
+ goto local_name_error;
+ }
+ mp_obj_cell_set(fastn[-unum], MP_OBJ_NULL);
+ break;
+
case MP_BC_DELETE_NAME:
DECODE_QSTR;
mp_delete_name(qst);
diff --git a/tests/basics/del-deref.py b/tests/basics/del-deref.py
new file mode 100644
index 0000000000..1e7c0e41f4
--- /dev/null
+++ b/tests/basics/del-deref.py
@@ -0,0 +1,22 @@
+def f():
+ x = 1
+ y = 2
+ def g():
+ nonlocal x
+ print(y)
+ try:
+ print(x)
+ except NameError:
+ print("NameError")
+ def h():
+ nonlocal x
+ print(y)
+ try:
+ del x
+ except NameError:
+ print("NameError")
+ print(x, y)
+ del x
+ g()
+ h()
+f()
diff --git a/tests/basics/del-local.py b/tests/basics/del-local.py
new file mode 100644
index 0000000000..05aa98b424
--- /dev/null
+++ b/tests/basics/del-local.py
@@ -0,0 +1,25 @@
+# delete local then try to reference it
+def f():
+ x = 1
+ y = 2
+ print(x, y)
+ del x
+ print(y)
+ try:
+ print(x)
+ except NameError:
+ print("NameError");
+f()
+
+# delete local then try to delete it again
+def g():
+ x = 3
+ y = 4
+ print(x, y)
+ del x
+ print(y)
+ try:
+ del x
+ except NameError:
+ print("NameError");
+g()