Skip to content

Commit 2ac4af6

Browse files
committed
py: Allow viper to have type annotations.
Viper functions can now be annotated with the type of their arguments and return value. Eg: @micropython.viper def f(x:int) -> int: return x + 1
1 parent 6be0b0a commit 2ac4af6

16 files changed

Lines changed: 268 additions & 72 deletions

py/compile.c

Lines changed: 72 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@
4848

4949
// TODO need to mangle __attr names
5050

51-
#define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_THUMB)
52-
5351
typedef enum {
5452
PN_none = 0,
5553
#define DEF_RULE(rule, comp, kind, ...) PN_##rule,
@@ -1745,6 +1743,7 @@ void compile_while_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
17451743
EMIT_ARG(label_assign, break_label);
17461744
}
17471745

1746+
#if !MICROPY_EMIT_CPYTHON
17481747
// TODO preload end and step onto stack if they are not constants
17491748
// Note that, as per semantics of for .. range, the final failing value should not be stored in the loop variable
17501749
// And, if the loop never runs, the loop variable should never be assigned
@@ -1801,6 +1800,7 @@ STATIC void compile_for_stmt_optimised_range(compiler_t *comp, mp_parse_node_t p
18011800

18021801
EMIT_ARG(label_assign, break_label);
18031802
}
1803+
#endif
18041804

18051805
void compile_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
18061806
#if !MICROPY_EMIT_CPYTHON
@@ -2902,11 +2902,10 @@ STATIC void compile_node(compiler_t *comp, mp_parse_node_t pn) {
29022902
}
29032903
}
29042904

2905-
STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn, pn_kind_t pn_name, pn_kind_t pn_star, pn_kind_t pn_dbl_star, bool allow_annotations) {
2905+
STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn, pn_kind_t pn_name, pn_kind_t pn_star, pn_kind_t pn_dbl_star) {
29062906
// TODO verify that *k and **k are last etc
29072907
qstr param_name = MP_QSTR_NULL;
29082908
uint param_flag = ID_FLAG_IS_PARAM;
2909-
mp_parse_node_t pn_annotation = MP_PARSE_NODE_NULL;
29102909
if (MP_PARSE_NODE_IS_ID(pn)) {
29112910
param_name = MP_PARSE_NODE_LEAF_ARG(pn);
29122911
if (comp->have_star) {
@@ -2921,24 +2920,6 @@ STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn
29212920
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
29222921
if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_name) {
29232922
param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
2924-
//int node_index = 1; unused
2925-
if (allow_annotations) {
2926-
if (!MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
2927-
// this parameter has an annotation
2928-
pn_annotation = pns->nodes[1];
2929-
}
2930-
//node_index = 2; unused
2931-
}
2932-
/* this is obsolete now that num dict/default params are calculated in compile_funcdef_param
2933-
if (!MP_PARSE_NODE_IS_NULL(pns->nodes[node_index])) {
2934-
// this parameter has a default value
2935-
if (comp->have_star) {
2936-
comp->scope_cur->num_dict_params += 1;
2937-
} else {
2938-
comp->scope_cur->num_default_params += 1;
2939-
}
2940-
}
2941-
*/
29422923
if (comp->have_star) {
29432924
// comes after a star, so counts as a keyword-only parameter
29442925
comp->scope_cur->num_kwonly_args += 1;
@@ -2957,23 +2938,18 @@ STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn
29572938
// named star
29582939
comp->scope_cur->scope_flags |= MP_SCOPE_FLAG_VARARGS;
29592940
param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
2960-
} else if (allow_annotations && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_tfpdef)) {
2941+
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_tfpdef)) {
29612942
// named star with possible annotation
29622943
comp->scope_cur->scope_flags |= MP_SCOPE_FLAG_VARARGS;
29632944
pns = (mp_parse_node_struct_t*)pns->nodes[0];
29642945
param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
2965-
pn_annotation = pns->nodes[1];
29662946
} else {
29672947
// shouldn't happen
29682948
assert(0);
29692949
}
29702950
} else if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_dbl_star) {
29712951
param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
29722952
param_flag = ID_FLAG_IS_PARAM | ID_FLAG_IS_DBL_STAR_PARAM;
2973-
if (allow_annotations && !MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
2974-
// this parameter has an annotation
2975-
pn_annotation = pns->nodes[1];
2976-
}
29772953
comp->scope_cur->scope_flags |= MP_SCOPE_FLAG_VARKEYWORDS;
29782954
} else {
29792955
// TODO anything to implement?
@@ -2982,9 +2958,6 @@ STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn
29822958
}
29832959

29842960
if (param_name != MP_QSTR_NULL) {
2985-
if (!MP_PARSE_NODE_IS_NULL(pn_annotation)) {
2986-
// TODO this parameter has an annotation
2987-
}
29882961
bool added;
29892962
id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, param_name, &added);
29902963
if (!added) {
@@ -2997,11 +2970,58 @@ STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn
29972970
}
29982971

29992972
STATIC void compile_scope_func_param(compiler_t *comp, mp_parse_node_t pn) {
3000-
compile_scope_func_lambda_param(comp, pn, PN_typedargslist_name, PN_typedargslist_star, PN_typedargslist_dbl_star, true);
2973+
compile_scope_func_lambda_param(comp, pn, PN_typedargslist_name, PN_typedargslist_star, PN_typedargslist_dbl_star);
30012974
}
30022975

30032976
STATIC void compile_scope_lambda_param(compiler_t *comp, mp_parse_node_t pn) {
3004-
compile_scope_func_lambda_param(comp, pn, PN_varargslist_name, PN_varargslist_star, PN_varargslist_dbl_star, false);
2977+
compile_scope_func_lambda_param(comp, pn, PN_varargslist_name, PN_varargslist_star, PN_varargslist_dbl_star);
2978+
}
2979+
2980+
STATIC void compile_scope_func_annotations(compiler_t *comp, mp_parse_node_t pn) {
2981+
if (!MP_PARSE_NODE_IS_STRUCT(pn)) {
2982+
// no annotation
2983+
return;
2984+
}
2985+
2986+
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
2987+
if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_typedargslist_name) {
2988+
// named parameter with possible annotation
2989+
// fallthrough
2990+
} else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_typedargslist_star) {
2991+
if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_tfpdef)) {
2992+
// named star with possible annotation
2993+
pns = (mp_parse_node_struct_t*)pns->nodes[0];
2994+
// fallthrough
2995+
} else {
2996+
// no annotation
2997+
return;
2998+
}
2999+
} else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_typedargslist_dbl_star) {
3000+
// double star with possible annotation
3001+
// fallthrough
3002+
} else {
3003+
// no annotation
3004+
return;
3005+
}
3006+
3007+
mp_parse_node_t pn_annotation = pns->nodes[1];
3008+
3009+
if (!MP_PARSE_NODE_IS_NULL(pn_annotation)) {
3010+
#if MICROPY_EMIT_NATIVE
3011+
qstr param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
3012+
id_info_t *id_info = scope_find(comp->scope_cur, param_name);
3013+
assert(id_info != NULL);
3014+
3015+
if (comp->scope_cur->emit_options == MP_EMIT_OPT_VIPER) {
3016+
if (MP_PARSE_NODE_IS_ID(pn_annotation)) {
3017+
qstr arg_type = MP_PARSE_NODE_LEAF_ARG(pn_annotation);
3018+
EMIT_ARG(set_native_type, MP_EMIT_NATIVE_TYPE_ARG, id_info->local_num, arg_type);
3019+
} else {
3020+
compile_syntax_error(comp, pn_annotation, "annotation must be an identifier");
3021+
}
3022+
}
3023+
#endif // MICROPY_EMIT_NATIVE
3024+
}
30053025
}
30063026

30073027
STATIC void compile_scope_comp_iter(compiler_t *comp, mp_parse_node_t pn_iter, mp_parse_node_t pn_inner_expr, int l_top, int for_depth) {
@@ -3128,10 +3148,26 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
31283148
if (comp->pass == MP_PASS_SCOPE) {
31293149
comp->have_star = false;
31303150
apply_to_single_or_list(comp, pns->nodes[1], PN_typedargslist, compile_scope_func_param);
3151+
} else {
3152+
// compile annotations; only needed on latter compiler passes
3153+
3154+
// argument annotations
3155+
apply_to_single_or_list(comp, pns->nodes[1], PN_typedargslist, compile_scope_func_annotations);
3156+
3157+
// pns->nodes[2] is return/whole function annotation
3158+
#if MICROPY_EMIT_NATIVE
3159+
if (scope->emit_options == MP_EMIT_OPT_VIPER) {
3160+
// nodes[2] can be null or a test-expr
3161+
if (MP_PARSE_NODE_IS_ID(pns->nodes[2])) {
3162+
qstr ret_type = MP_PARSE_NODE_LEAF_ARG(pns->nodes[2]);
3163+
EMIT_ARG(set_native_type, MP_EMIT_NATIVE_TYPE_RETURN, 0, ret_type);
3164+
} else {
3165+
compile_syntax_error(comp, pns->nodes[2], "annotation must be an identifier");
3166+
}
3167+
}
3168+
#endif // MICROPY_EMIT_NATIVE
31313169
}
31323170

3133-
// pns->nodes[2] is return/whole function annotation
3134-
31353171
compile_node(comp, pns->nodes[3]); // 3 is function body
31363172
// emit return if it wasn't the last opcode
31373173
if (!EMIT(last_emit_was_return_value)) {
@@ -3589,7 +3625,7 @@ mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, uint emit_opt, bool is
35893625
comp->emit_method_table = &emit_native_thumb_method_table;
35903626
#endif
35913627
comp->emit = emit_native;
3592-
comp->emit_method_table->set_native_types(comp->emit, s->emit_options == MP_EMIT_OPT_VIPER);
3628+
comp->emit_method_table->set_native_type(comp->emit, MP_EMIT_NATIVE_TYPE_ENABLE, s->emit_options == MP_EMIT_OPT_VIPER, 0);
35933629

35943630
// native emitters need an extra pass to compute stack size
35953631
compile_scope(comp, s, MP_PASS_STACK_SIZE);

py/emit.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,14 @@ typedef enum {
4646

4747
#define MP_EMIT_BREAK_FROM_FOR (0x8000)
4848

49+
#define MP_EMIT_NATIVE_TYPE_ENABLE (0)
50+
#define MP_EMIT_NATIVE_TYPE_RETURN (1)
51+
#define MP_EMIT_NATIVE_TYPE_ARG (2)
52+
4953
typedef struct _emit_t emit_t;
5054

5155
typedef struct _emit_method_table_t {
52-
void (*set_native_types)(emit_t *emit, bool do_native_types);
56+
void (*set_native_type)(emit_t *emit, mp_uint_t op, mp_uint_t arg1, qstr arg2);
5357
void (*start_pass)(emit_t *emit, pass_kind_t pass, scope_t *scope);
5458
void (*end_pass)(emit_t *emit);
5559
bool (*last_emit_was_return_value)(emit_t *emit);

py/emitbc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ STATIC void emit_write_bytecode_byte_signed_label(emit_t* emit, byte b1, uint la
265265
c[2] = bytecode_offset >> 8;
266266
}
267267

268-
STATIC void emit_bc_set_native_types(emit_t *emit, bool do_native_types) {
268+
STATIC void emit_bc_set_native_type(emit_t *emit, mp_uint_t op, mp_uint_t arg1, qstr arg2) {
269269
}
270270

271271
STATIC void emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) {
@@ -872,7 +872,7 @@ STATIC void emit_bc_end_except_handler(emit_t *emit) {
872872
}
873873

874874
const emit_method_table_t emit_bc_method_table = {
875-
emit_bc_set_native_types,
875+
emit_bc_set_native_type,
876876
emit_bc_start_pass,
877877
emit_bc_end_pass,
878878
emit_bc_last_emit_was_return_value,

py/emitcpy.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ emit_t *emit_cpython_new(uint max_num_labels) {
6363
return emit;
6464
}
6565

66-
STATIC void emit_cpy_set_native_types(emit_t *emit, bool do_native_types) {
66+
STATIC void emit_cpy_set_native_type(emit_t *emit, mp_uint_t op, mp_uint_t arg1, qstr arg2) {
6767
}
6868

6969
STATIC void emit_cpy_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) {
@@ -822,7 +822,7 @@ STATIC void emit_cpy_setup_loop(emit_t *emit, uint label) {
822822
}
823823

824824
const emit_method_table_t emit_cpython_method_table = {
825-
emit_cpy_set_native_types,
825+
emit_cpy_set_native_type,
826826
emit_cpy_start_pass,
827827
emit_cpy_end_pass,
828828
emit_cpy_last_emit_was_return_value,

py/emitglue.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,14 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, uint len, uint
8686
#endif
8787
}
8888

89-
void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun, uint len, int n_args) {
89+
#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_THUMB
90+
void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun, uint len, int n_args, mp_uint_t type_sig) {
9091
assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM);
9192
rc->kind = kind;
9293
rc->scope_flags = 0;
9394
rc->n_pos_args = n_args;
9495
rc->u_native.fun = fun;
96+
rc->u_native.type_sig = type_sig;
9597

9698
#ifdef DEBUG_PRINT
9799
DEBUG_printf("assign native: kind=%d fun=%p len=%u n_args=%d\n", kind, fun, len, n_args);
@@ -111,6 +113,7 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void
111113
#endif
112114
#endif
113115
}
116+
#endif
114117

115118
mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args) {
116119
DEBUG_OP_printf("make_function_from_raw_code %p\n", rc);
@@ -128,13 +131,19 @@ mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp
128131
case MP_CODE_BYTECODE:
129132
fun = mp_obj_new_fun_bc(rc->scope_flags, rc->arg_names, rc->n_pos_args, rc->n_kwonly_args, def_args, def_kw_args, rc->u_byte.code);
130133
break;
134+
#if MICROPY_EMIT_NATIVE
131135
case MP_CODE_NATIVE_PY:
132136
fun = mp_make_function_n(rc->n_pos_args, rc->u_native.fun);
133137
break;
134138
case MP_CODE_NATIVE_VIPER:
139+
fun = mp_obj_new_fun_viper(rc->n_pos_args, rc->u_native.fun, rc->u_native.type_sig);
140+
break;
141+
#endif
142+
#if MICROPY_EMIT_INLINE_THUMB
135143
case MP_CODE_NATIVE_ASM:
136144
fun = mp_obj_new_fun_asm(rc->n_pos_args, rc->u_native.fun);
137145
break;
146+
#endif
138147
default:
139148
// raw code was never set (this should not happen)
140149
assert(0);

py/emitglue.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,15 @@ typedef struct _mp_code_t {
4848
} u_byte;
4949
struct {
5050
void *fun;
51+
mp_uint_t type_sig; // for viper, compressed as 2-bit types; ret is MSB, then arg0, arg1, etc
5152
} u_native;
5253
};
5354
} mp_raw_code_t;
5455

5556
mp_raw_code_t *mp_emit_glue_new_raw_code(void);
5657

5758
void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, uint len, uint n_pos_args, uint n_kwonly_args, qstr *arg_names, uint scope_flags);
58-
void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *f, uint len, int n_args);
59+
void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *f, uint len, int n_args, mp_uint_t type_sig);
5960

6061
mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args);
6162
mp_obj_t mp_make_closure_from_raw_code(mp_raw_code_t *rc, uint n_closed_over, const mp_obj_t *args);

py/emitinlinethumb.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ STATIC bool emit_inline_thumb_end_pass(emit_inline_asm_t *emit) {
9999

100100
if (emit->pass == MP_PASS_EMIT) {
101101
void *f = asm_thumb_get_code(emit->as);
102-
mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args);
102+
mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args, 0);
103103
}
104104

105105
return emit->success;

0 commit comments

Comments
 (0)