From a16f251333e324c94ca8e3e92d1fcf3193dfa382 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Tue, 23 Apr 2024 20:14:46 +0200 Subject: patch 9.1.0367: compile_def_function is too long Problem: compile_def_function is too long Solution: Move out the code to compile the body of a function (Yegappan Lakshmanan) closes: #14622 Signed-off-by: Yegappan Lakshmanan Signed-off-by: Christian Brabandt --- src/testdir/test_vim9_builtin.vim | 2 + src/version.c | 2 + src/vim9compile.c | 704 +++++++++++++++++++------------------- src/vim9type.c | 24 +- 4 files changed, 375 insertions(+), 357 deletions(-) diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index 35ff091a45..42d09fe9a5 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -4785,6 +4785,8 @@ def Test_typename() endif assert_equal('class', typename(null_class)) assert_equal('object', typename(null_object)) + var l: list): number> = [function('min')] + assert_equal('list): number>', typename(l)) enddef def Test_undofile() diff --git a/src/version.c b/src/version.c index 7113699f3a..fc3969164a 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 367, /**/ 366, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 0e05f820c5..a091c57963 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -3271,261 +3271,25 @@ add_def_function(ufunc_T *ufunc) } /* - * After ex_function() has collected all the function lines: parse and compile - * the lines into instructions. - * Adds the function to "def_functions". - * When "check_return_type" is set then set ufunc->uf_ret_type to the type of - * the return statement (used for lambda). When uf_ret_type is already set - * then check that it matches. - * When "profiling" is true add ISN_PROF_START instructions. - * "outer_cctx" is set for a nested function. - * This can be used recursively through compile_lambda(), which may reallocate - * "def_functions". - * Returns OK or FAIL. + * Compile def function body. Loop over all the lines in the function and + * generate instructions. */ - int -compile_def_function( - ufunc_T *ufunc, - int check_return_type, - compiletype_T compile_type, - cctx_T *outer_cctx) + static int +compile_def_function_body( + cctx_T *cctx, + int last_func_lnum, + int check_return_type, + garray_T *lines_to_free, + char **errormsg) { char_u *line = NULL; - garray_T lines_to_free; char_u *p; - char *errormsg = NULL; // error message - cctx_T cctx; - garray_T *instr; int did_emsg_before = did_emsg; - int did_emsg_silent_before = did_emsg_silent; - int ret = FAIL; - sctx_T save_current_sctx = current_sctx; - int save_estack_compiling = estack_compiling; - int save_cmod_flags = cmdmod.cmod_flags; - int do_estack_push; - int new_def_function = FALSE; #ifdef FEAT_PROFILE int prof_lnum = -1; #endif int debug_lnum = -1; - // allocated lines are freed at the end - ga_init2(&lines_to_free, sizeof(char_u *), 50); - - // When using a function that was compiled before: Free old instructions. - // The index is reused. Otherwise add a new entry in "def_functions". - if (ufunc->uf_dfunc_idx > 0) - { - dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) - + ufunc->uf_dfunc_idx; - isn_T *instr_dest = NULL; - - switch (compile_type) - { - case CT_PROFILE: -#ifdef FEAT_PROFILE - instr_dest = dfunc->df_instr_prof; break; -#endif - case CT_NONE: instr_dest = dfunc->df_instr; break; - case CT_DEBUG: instr_dest = dfunc->df_instr_debug; break; - } - if (instr_dest != NULL) - // Was compiled in this mode before: Free old instructions. - delete_def_function_contents(dfunc, FALSE); - ga_clear_strings(&dfunc->df_var_names); - dfunc->df_defer_var_idx = 0; - } - else - { - if (add_def_function(ufunc) == FAIL) - return FAIL; - new_def_function = TRUE; - } - - if ((ufunc->uf_flags & FC_CLOSURE) && outer_cctx == NULL) - { - semsg(_(e_compiling_closure_without_context_str), - printable_func_name(ufunc)); - return FAIL; - } - - ufunc->uf_def_status = UF_COMPILING; - - CLEAR_FIELD(cctx); - - cctx.ctx_compile_type = compile_type; - cctx.ctx_ufunc = ufunc; - cctx.ctx_lnum = -1; - cctx.ctx_outer = outer_cctx; - ga_init2(&cctx.ctx_locals, sizeof(lvar_T), 10); - // Each entry on the type stack consists of two type pointers. - ga_init2(&cctx.ctx_type_stack, sizeof(type2_T), 50); - cctx.ctx_type_list = &ufunc->uf_type_list; - ga_init2(&cctx.ctx_instr, sizeof(isn_T), 50); - instr = &cctx.ctx_instr; - - // Set the context to the function, it may be compiled when called from - // another script. Set the script version to the most modern one. - // The line number will be set in next_line_from_context(). - current_sctx = ufunc->uf_script_ctx; - current_sctx.sc_version = SCRIPT_VERSION_VIM9; - - // Don't use the flag from ":legacy" here. - cmdmod.cmod_flags &= ~CMOD_LEGACY; - - // Make sure error messages are OK. - do_estack_push = !estack_top_is_ufunc(ufunc, 1); - if (do_estack_push) - estack_push_ufunc(ufunc, 1); - estack_compiling = TRUE; - - if (check_args_shadowing(ufunc, &cctx) == FAIL) - goto erret; - - // For an object method and constructor "this" is the first local variable. - if (ufunc->uf_flags & (FC_OBJECT|FC_NEW)) - { - dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) - + ufunc->uf_dfunc_idx; - if (GA_GROW_FAILS(&dfunc->df_var_names, 1)) - goto erret; - ((char_u **)dfunc->df_var_names.ga_data)[0] = - vim_strsave((char_u *)"this"); - ++dfunc->df_var_names.ga_len; - - // In the constructor allocate memory for the object and initialize the - // object members. - if (IS_CONSTRUCTOR_METHOD(ufunc)) - { - generate_CONSTRUCT(&cctx, ufunc->uf_class); - - for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i) - { - ocmember_T *m = &ufunc->uf_class->class_obj_members[i]; - - if (i < 2 && IS_ENUM(ufunc->uf_class)) - // The first two object variables in an enum are the name - // and the ordinal. These are set by the ISN_CONSTRUCT - // instruction. So don't generate instructions to set - // these variables. - continue; - - if (m->ocm_init != NULL) - { - char_u *expr = m->ocm_init; - if (compile_expr0(&expr, &cctx) == FAIL) - goto erret; - if (!ends_excmd2(m->ocm_init, expr)) - { - semsg(_(e_trailing_characters_str), expr); - goto erret; - } - - type_T *type = get_type_on_stack(&cctx, 0); - if (m->ocm_type->tt_type == VAR_ANY - && !(m->ocm_flags & OCMFLAG_HAS_TYPE) - && type->tt_type != VAR_SPECIAL) - { - // If the member variable type is not yet set, then use - // the initialization expression type. - m->ocm_type = type; - } - else if (m->ocm_type->tt_type != type->tt_type) - { - // The type of the member initialization expression is - // determined at run time. Add a runtime type check. - where_T where = WHERE_INIT; - where.wt_kind = WT_MEMBER; - where.wt_func_name = (char *)m->ocm_name; - if (need_type_where(type, m->ocm_type, FALSE, -1, - where, &cctx, FALSE, FALSE) == FAIL) - goto erret; - } - } - else - push_default_value(&cctx, m->ocm_type->tt_type, - FALSE, NULL); - generate_STORE_THIS(&cctx, i); - } - } - } - - if (ufunc->uf_def_args.ga_len > 0) - { - int count = ufunc->uf_def_args.ga_len; - int first_def_arg = ufunc->uf_args.ga_len - count; - int i; - int off = STACK_FRAME_SIZE + (ufunc->uf_va_name != NULL ? 1 : 0); - int did_set_arg_type = FALSE; - - // Produce instructions for the default values of optional arguments. - SOURCING_LNUM = 0; // line number unknown - for (i = 0; i < count; ++i) - { - char_u *arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i]; - if (STRCMP(arg, "v:none") == 0) - // "arg = v:none" means the argument is optional without - // setting a value when the argument is missing. - continue; - - type_T *val_type; - int arg_idx = first_def_arg + i; - where_T where = WHERE_INIT; - int jump_instr_idx = instr->ga_len; - isn_T *isn; - - // Use a JUMP_IF_ARG_SET instruction to skip if the value was given. - if (generate_JUMP_IF_ARG(&cctx, ISN_JUMP_IF_ARG_SET, - i - count - off) == FAIL) - goto erret; - - // Make sure later arguments are not found. - ufunc->uf_args_visible = arg_idx; - - int r = compile_expr0(&arg, &cctx); - if (r == FAIL) - goto erret; - - // If no type specified use the type of the default value. - // Otherwise check that the default value type matches the - // specified type. - val_type = get_type_on_stack(&cctx, 0); - where.wt_index = arg_idx + 1; - where.wt_kind = WT_ARGUMENT; - if (ufunc->uf_arg_types[arg_idx] == &t_unknown) - { - did_set_arg_type = TRUE; - ufunc->uf_arg_types[arg_idx] = val_type; - } - else if (need_type_where(val_type, ufunc->uf_arg_types[arg_idx], - FALSE, -1, where, &cctx, FALSE, FALSE) == FAIL) - goto erret; - - if (generate_STORE(&cctx, ISN_STORE, i - count - off, NULL) == FAIL) - goto erret; - - // set instruction index in JUMP_IF_ARG_SET to here - isn = ((isn_T *)instr->ga_data) + jump_instr_idx; - isn->isn_arg.jumparg.jump_where = instr->ga_len; - } - - if (did_set_arg_type) - set_function_type(ufunc); - } - ufunc->uf_args_visible = ufunc->uf_args.ga_len; - - // Compiling a function in an interface is done to get the function type. - // No code is actually compiled. - if (ufunc->uf_class != NULL && IS_INTERFACE(ufunc->uf_class)) - { - ufunc->uf_def_status = UF_NOT_COMPILED; - ret = OK; - goto erret; - } - - /* - * Loop over all the lines of the function and generate instructions. - */ for (;;) { exarg_T ea; @@ -3536,29 +3300,29 @@ compile_def_function( // Bail out on the first error to avoid a flood of errors and report // the right line number when inside try/catch. if (did_emsg_before != did_emsg) - goto erret; + return FAIL; if (line != NULL && *line == '|') // the line continues after a '|' ++line; else if (line != NULL && *skipwhite(line) != NUL - && !(*line == '#' && (line == cctx.ctx_line_start + && !(*line == '#' && (line == cctx->ctx_line_start || VIM_ISWHITE(line[-1])))) { semsg(_(e_trailing_characters_str), line); - goto erret; + return FAIL; } else if (line != NULL && vim9_bad_comment(skipwhite(line))) - goto erret; + return FAIL; else { - line = next_line_from_context(&cctx, FALSE); - if (cctx.ctx_lnum >= ufunc->uf_lines.ga_len) + line = next_line_from_context(cctx, FALSE); + if (cctx->ctx_lnum >= last_func_lnum) { // beyond the last line #ifdef FEAT_PROFILE - if (cctx.ctx_skip != SKIP_YES) - may_generate_prof_end(&cctx, prof_lnum); + if (cctx->ctx_skip != SKIP_YES) + may_generate_prof_end(cctx, prof_lnum); #endif break; } @@ -3567,42 +3331,42 @@ compile_def_function( if (line != NULL) { line = vim_strsave(line); - if (ga_add_string(&lines_to_free, line) == FAIL) - goto erret; + if (ga_add_string(lines_to_free, line) == FAIL) + return FAIL; } } CLEAR_FIELD(ea); ea.cmdlinep = &line; ea.cmd = skipwhite(line); - ea.skip = cctx.ctx_skip == SKIP_YES; + ea.skip = cctx->ctx_skip == SKIP_YES; if (*ea.cmd == '#') { // "#" starts a comment, but "#{" is an error if (vim9_bad_comment(ea.cmd)) - goto erret; + return FAIL; line = (char_u *)""; continue; } #ifdef FEAT_PROFILE - if (cctx.ctx_compile_type == CT_PROFILE && cctx.ctx_lnum != prof_lnum - && cctx.ctx_skip != SKIP_YES) + if (cctx->ctx_compile_type == CT_PROFILE && cctx->ctx_lnum != prof_lnum + && cctx->ctx_skip != SKIP_YES) { - may_generate_prof_end(&cctx, prof_lnum); + may_generate_prof_end(cctx, prof_lnum); - prof_lnum = cctx.ctx_lnum; - generate_instr(&cctx, ISN_PROF_START); + prof_lnum = cctx->ctx_lnum; + generate_instr(cctx, ISN_PROF_START); } #endif - if (cctx.ctx_compile_type == CT_DEBUG && cctx.ctx_lnum != debug_lnum - && cctx.ctx_skip != SKIP_YES) + if (cctx->ctx_compile_type == CT_DEBUG && cctx->ctx_lnum != debug_lnum + && cctx->ctx_skip != SKIP_YES) { - debug_lnum = cctx.ctx_lnum; - generate_instr_debug(&cctx); + debug_lnum = cctx->ctx_lnum; + generate_instr_debug(cctx); } - cctx.ctx_prev_lnum = cctx.ctx_lnum + 1; + cctx->ctx_prev_lnum = cctx->ctx_lnum + 1; // Some things can be recognized by the first character. switch (*ea.cmd) @@ -3610,18 +3374,18 @@ compile_def_function( case '}': { // "}" ends a block scope - scopetype_T stype = cctx.ctx_scope == NULL - ? NO_SCOPE : cctx.ctx_scope->se_type; + scopetype_T stype = cctx->ctx_scope == NULL + ? NO_SCOPE : cctx->ctx_scope->se_type; if (stype == BLOCK_SCOPE) { - compile_endblock(&cctx); + compile_endblock(cctx); line = ea.cmd; } else { emsg(_(e_using_rcurly_outside_if_block_scope)); - goto erret; + return FAIL; } if (line != NULL) line = skipwhite(ea.cmd + 1); @@ -3633,7 +3397,7 @@ compile_def_function( // "{'a': 1}->func() is something else if (ends_excmd(*skipwhite(ea.cmd + 1))) { - line = compile_block(ea.cmd, &cctx); + line = compile_block(ea.cmd, cctx); continue; } break; @@ -3642,11 +3406,11 @@ compile_def_function( /* * COMMAND MODIFIERS */ - cctx.ctx_has_cmdmod = FALSE; - if (parse_command_modifiers(&ea, &errormsg, &local_cmdmod, FALSE) + cctx->ctx_has_cmdmod = FALSE; + if (parse_command_modifiers(&ea, errormsg, &local_cmdmod, FALSE) == FAIL) - goto erret; - generate_cmdmods(&cctx, &local_cmdmod); + return FAIL; + generate_cmdmods(cctx, &local_cmdmod); undo_cmdmod(&local_cmdmod); // Check if there was a colon after the last command modifier or before @@ -3677,11 +3441,11 @@ compile_def_function( int assign; // Check for assignment after command modifiers. - assign = may_compile_assignment(&ea, &line, &cctx); + assign = may_compile_assignment(&ea, &line, cctx); if (assign == OK) goto nextline; if (assign == FAIL) - goto erret; + return FAIL; } } @@ -3709,13 +3473,13 @@ compile_def_function( && !(local_cmdmod.cmod_flags & CMOD_LEGACY)) { semsg(_(e_colon_required_before_range_str), cmd); - goto erret; + return FAIL; } ea.addr_count = 1; if (ends_excmd2(line, ea.cmd)) { // A range without a command: jump to the line. - generate_EXEC(&cctx, ISN_EXECRANGE, + generate_EXEC(cctx, ISN_EXECRANGE, vim_strnsave(cmd, ea.cmd - cmd)); line = ea.cmd; goto nextline; @@ -3724,13 +3488,13 @@ compile_def_function( } p = find_ex_command(&ea, NULL, starts_with_colon || (local_cmdmod.cmod_flags & CMOD_LEGACY) - ? NULL : item_exists, &cctx); + ? NULL : item_exists, cctx); if (p == NULL) { - if (cctx.ctx_skip != SKIP_YES) + if (cctx->ctx_skip != SKIP_YES) semsg(_(e_ambiguous_use_of_user_defined_command_str), ea.cmd); - goto erret; + return FAIL; } // When using ":legacy cmd" always use compile_exec(). @@ -3755,7 +3519,7 @@ compile_def_function( case CMD_finally: case CMD_endtry: semsg(_(e_cannot_use_legacy_with_command_str), ea.cmd); - goto erret; + return FAIL; default: break; } @@ -3772,7 +3536,7 @@ compile_def_function( // "p" is equal to "ea.cmd" for a valid command. if (ea.cmdidx == CMD_eval || ea.cmdidx == CMD_var) ; - else if (cctx.ctx_skip == SKIP_YES) + else if (cctx->ctx_skip == SKIP_YES) { line += STRLEN(line); goto nextline; @@ -3780,11 +3544,11 @@ compile_def_function( else { semsg(_(e_command_not_recognized_str), ea.cmd); - goto erret; + return FAIL; } } - if ((cctx.ctx_had_return || cctx.ctx_had_throw) + if ((cctx->ctx_had_return || cctx->ctx_had_throw) && ea.cmdidx != CMD_elseif && ea.cmdidx != CMD_else && ea.cmdidx != CMD_endif @@ -3796,10 +3560,10 @@ compile_def_function( && !ignore_unreachable_code_for_testing) { semsg(_(e_unreachable_code_after_str), - cctx.ctx_had_return ? "return" : "throw"); - goto erret; + cctx->ctx_had_return ? "return" : "throw"); + return FAIL; } - cctx.ctx_had_throw = FALSE; + cctx->ctx_had_throw = FALSE; p = skipwhite(p); if (ea.cmdidx != CMD_SIZE @@ -3815,7 +3579,7 @@ compile_def_function( if ((ea.argt & EX_RANGE) == 0 && ea.addr_count > 0) { emsg(_(e_no_range_allowed)); - goto erret; + return FAIL; } } @@ -3824,13 +3588,13 @@ compile_def_function( case CMD_def: case CMD_function: ea.arg = p; - line = compile_nested_function(&ea, &cctx, &lines_to_free); + line = compile_nested_function(&ea, cctx, lines_to_free); break; case CMD_return: line = compile_return(p, check_return_type, - local_cmdmod.cmod_flags & CMOD_LEGACY, &cctx); - cctx.ctx_had_return = TRUE; + local_cmdmod.cmod_flags & CMOD_LEGACY, cctx); + cctx->ctx_had_return = TRUE; break; case CMD_let: @@ -3841,7 +3605,7 @@ compile_def_function( case CMD_const: case CMD_increment: case CMD_decrement: - line = compile_assignment(p, &ea, ea.cmdidx, &cctx); + line = compile_assignment(p, &ea, ea.cmdidx, cctx); if (line == p) { emsg(_(e_invalid_assignment)); @@ -3852,7 +3616,7 @@ compile_def_function( case CMD_unlet: case CMD_unlockvar: case CMD_lockvar: - line = compile_unletlock(p, &ea, &cctx); + line = compile_unletlock(p, &ea, cctx); break; case CMD_import: @@ -3861,67 +3625,67 @@ compile_def_function( break; case CMD_if: - line = compile_if(p, &cctx); + line = compile_if(p, cctx); break; case CMD_elseif: - line = compile_elseif(p, &cctx); - cctx.ctx_had_return = FALSE; + line = compile_elseif(p, cctx); + cctx->ctx_had_return = FALSE; break; case CMD_else: - line = compile_else(p, &cctx); - cctx.ctx_had_return = FALSE; + line = compile_else(p, cctx); + cctx->ctx_had_return = FALSE; break; case CMD_endif: - line = compile_endif(p, &cctx); + line = compile_endif(p, cctx); break; case CMD_while: - line = compile_while(p, &cctx); + line = compile_while(p, cctx); break; case CMD_endwhile: - line = compile_endwhile(p, &cctx); - cctx.ctx_had_return = FALSE; + line = compile_endwhile(p, cctx); + cctx->ctx_had_return = FALSE; break; case CMD_for: - line = compile_for(p, &cctx); + line = compile_for(p, cctx); break; case CMD_endfor: - line = compile_endfor(p, &cctx); - cctx.ctx_had_return = FALSE; + line = compile_endfor(p, cctx); + cctx->ctx_had_return = FALSE; break; case CMD_continue: - line = compile_continue(p, &cctx); + line = compile_continue(p, cctx); break; case CMD_break: - line = compile_break(p, &cctx); + line = compile_break(p, cctx); break; case CMD_try: - line = compile_try(p, &cctx); + line = compile_try(p, cctx); break; case CMD_catch: - line = compile_catch(p, &cctx); - cctx.ctx_had_return = FALSE; + line = compile_catch(p, cctx); + cctx->ctx_had_return = FALSE; break; case CMD_finally: - line = compile_finally(p, &cctx); - cctx.ctx_had_return = FALSE; + line = compile_finally(p, cctx); + cctx->ctx_had_return = FALSE; break; case CMD_endtry: - line = compile_endtry(p, &cctx); + line = compile_endtry(p, cctx); break; case CMD_throw: - line = compile_throw(p, &cctx); - cctx.ctx_had_throw = TRUE; + line = compile_throw(p, cctx); + cctx->ctx_had_throw = TRUE; break; case CMD_eval: - line = compile_eval(p, &cctx); + line = compile_eval(p, cctx); break; case CMD_defer: - line = compile_defer(p, &cctx); + line = compile_defer(p, cctx); break; #ifdef HAS_MESSAGE_WINDOW @@ -3932,7 +3696,7 @@ compile_def_function( line = NULL; else line = compile_mult_expr(p, ea.cmdidx, - cmd_count, &cctx); + cmd_count, cctx); } break; #endif @@ -3942,29 +3706,29 @@ compile_def_function( case CMD_echoerr: case CMD_echomsg: case CMD_execute: - line = compile_mult_expr(p, ea.cmdidx, 0, &cctx); + line = compile_mult_expr(p, ea.cmdidx, 0, cctx); break; case CMD_put: ea.cmd = cmd; - line = compile_put(p, &ea, &cctx); + line = compile_put(p, &ea, cctx); break; case CMD_substitute: if (check_global_and_subst(ea.cmd, p) == FAIL) - goto erret; - if (cctx.ctx_skip == SKIP_YES) + return FAIL; + if (cctx->ctx_skip == SKIP_YES) line = (char_u *)""; else { ea.arg = p; - line = compile_substitute(line, &ea, &cctx); + line = compile_substitute(line, &ea, cctx); } break; case CMD_redir: ea.arg = p; - line = compile_redir(line, &ea, &cctx); + line = compile_redir(line, &ea, cctx); break; case CMD_cexpr: @@ -3975,7 +3739,7 @@ compile_def_function( case CMD_lgetexpr: #ifdef FEAT_QUICKFIX ea.arg = p; - line = compile_cexpr(line, &ea, &cctx); + line = compile_cexpr(line, &ea, cctx); #else ex_ni(&ea); line = NULL; @@ -3989,13 +3753,13 @@ compile_def_function( case CMD_t: case CMD_xit: not_in_vim9(&ea); - goto erret; + return FAIL; case CMD_SIZE: - if (cctx.ctx_skip != SKIP_YES) + if (cctx->ctx_skip != SKIP_YES) { semsg(_(e_invalid_command_str), ea.cmd); - goto erret; + return FAIL; } // We don't check for a next command here. line = (char_u *)""; @@ -4012,55 +3776,309 @@ compile_def_function( case CMD_tcl: ea.arg = p; if (vim_strchr(line, '\n') == NULL) - line = compile_exec(line, &ea, &cctx); + line = compile_exec(line, &ea, cctx); else // heredoc lines have been concatenated with NL // characters in get_function_body() - line = compile_script(line, &cctx); + line = compile_script(line, cctx); break; case CMD_vim9script: - if (cctx.ctx_skip != SKIP_YES) + if (cctx->ctx_skip != SKIP_YES) { emsg(_(e_vim9script_can_only_be_used_in_script)); - goto erret; + return FAIL; } line = (char_u *)""; break; case CMD_class: emsg(_(e_class_can_only_be_used_in_script)); - goto erret; + return FAIL; case CMD_type: emsg(_(e_type_can_only_be_used_in_script)); - goto erret; + return FAIL; case CMD_global: if (check_global_and_subst(ea.cmd, p) == FAIL) - goto erret; + return FAIL; // FALLTHROUGH default: // Not recognized, execute with do_cmdline_cmd(). ea.arg = p; - line = compile_exec(line, &ea, &cctx); + line = compile_exec(line, &ea, cctx); break; } nextline: if (line == NULL) - goto erret; + return FAIL; line = skipwhite(line); // Undo any command modifiers. - generate_undo_cmdmods(&cctx); + generate_undo_cmdmods(cctx); - if (cctx.ctx_type_stack.ga_len < 0) + if (cctx->ctx_type_stack.ga_len < 0) { iemsg("Type stack underflow"); - goto erret; + return FAIL; } } // END of the loop over all the function body lines. + return OK; +} + +/* + * After ex_function() has collected all the function lines: parse and compile + * the lines into instructions. + * Adds the function to "def_functions". + * When "check_return_type" is set then set ufunc->uf_ret_type to the type of + * the return statement (used for lambda). When uf_ret_type is already set + * then check that it matches. + * When "profiling" is true add ISN_PROF_START instructions. + * "outer_cctx" is set for a nested function. + * This can be used recursively through compile_lambda(), which may reallocate + * "def_functions". + * Returns OK or FAIL. + */ + int +compile_def_function( + ufunc_T *ufunc, + int check_return_type, + compiletype_T compile_type, + cctx_T *outer_cctx) +{ + garray_T lines_to_free; + char *errormsg = NULL; // error message + cctx_T cctx; + garray_T *instr; + int did_emsg_before = did_emsg; + int did_emsg_silent_before = did_emsg_silent; + int ret = FAIL; + sctx_T save_current_sctx = current_sctx; + int save_estack_compiling = estack_compiling; + int save_cmod_flags = cmdmod.cmod_flags; + int do_estack_push; + int new_def_function = FALSE; + + // allocated lines are freed at the end + ga_init2(&lines_to_free, sizeof(char_u *), 50); + + // When using a function that was compiled before: Free old instructions. + // The index is reused. Otherwise add a new entry in "def_functions". + if (ufunc->uf_dfunc_idx > 0) + { + dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + + ufunc->uf_dfunc_idx; + isn_T *instr_dest = NULL; + + switch (compile_type) + { + case CT_PROFILE: +#ifdef FEAT_PROFILE + instr_dest = dfunc->df_instr_prof; break; +#endif + case CT_NONE: instr_dest = dfunc->df_instr; break; + case CT_DEBUG: instr_dest = dfunc->df_instr_debug; break; + } + if (instr_dest != NULL) + // Was compiled in this mode before: Free old instructions. + delete_def_function_contents(dfunc, FALSE); + ga_clear_strings(&dfunc->df_var_names); + dfunc->df_defer_var_idx = 0; + } + else + { + if (add_def_function(ufunc) == FAIL) + return FAIL; + new_def_function = TRUE; + } + + if ((ufunc->uf_flags & FC_CLOSURE) && outer_cctx == NULL) + { + semsg(_(e_compiling_closure_without_context_str), + printable_func_name(ufunc)); + return FAIL; + } + + ufunc->uf_def_status = UF_COMPILING; + + CLEAR_FIELD(cctx); + + cctx.ctx_compile_type = compile_type; + cctx.ctx_ufunc = ufunc; + cctx.ctx_lnum = -1; + cctx.ctx_outer = outer_cctx; + ga_init2(&cctx.ctx_locals, sizeof(lvar_T), 10); + // Each entry on the type stack consists of two type pointers. + ga_init2(&cctx.ctx_type_stack, sizeof(type2_T), 50); + cctx.ctx_type_list = &ufunc->uf_type_list; + ga_init2(&cctx.ctx_instr, sizeof(isn_T), 50); + instr = &cctx.ctx_instr; + + // Set the context to the function, it may be compiled when called from + // another script. Set the script version to the most modern one. + // The line number will be set in next_line_from_context(). + current_sctx = ufunc->uf_script_ctx; + current_sctx.sc_version = SCRIPT_VERSION_VIM9; + + // Don't use the flag from ":legacy" here. + cmdmod.cmod_flags &= ~CMOD_LEGACY; + + // Make sure error messages are OK. + do_estack_push = !estack_top_is_ufunc(ufunc, 1); + if (do_estack_push) + estack_push_ufunc(ufunc, 1); + estack_compiling = TRUE; + + if (check_args_shadowing(ufunc, &cctx) == FAIL) + goto erret; + + // For an object method and constructor "this" is the first local variable. + if (ufunc->uf_flags & (FC_OBJECT|FC_NEW)) + { + dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + + ufunc->uf_dfunc_idx; + if (GA_GROW_FAILS(&dfunc->df_var_names, 1)) + goto erret; + ((char_u **)dfunc->df_var_names.ga_data)[0] = + vim_strsave((char_u *)"this"); + ++dfunc->df_var_names.ga_len; + + // In the constructor allocate memory for the object and initialize the + // object members. + if (IS_CONSTRUCTOR_METHOD(ufunc)) + { + generate_CONSTRUCT(&cctx, ufunc->uf_class); + + for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i) + { + ocmember_T *m = &ufunc->uf_class->class_obj_members[i]; + + if (i < 2 && IS_ENUM(ufunc->uf_class)) + // The first two object variables in an enum are the name + // and the ordinal. These are set by the ISN_CONSTRUCT + // instruction. So don't generate instructions to set + // these variables. + continue; + + if (m->ocm_init != NULL) + { + char_u *expr = m->ocm_init; + if (compile_expr0(&expr, &cctx) == FAIL) + goto erret; + if (!ends_excmd2(m->ocm_init, expr)) + { + semsg(_(e_trailing_characters_str), expr); + goto erret; + } + + type_T *type = get_type_on_stack(&cctx, 0); + if (m->ocm_type->tt_type == VAR_ANY + && !(m->ocm_flags & OCMFLAG_HAS_TYPE) + && type->tt_type != VAR_SPECIAL) + { + // If the member variable type is not yet set, then use + // the initialization expression type. + m->ocm_type = type; + } + else if (m->ocm_type->tt_type != type->tt_type) + { + // The type of the member initialization expression is + // determined at run time. Add a runtime type check. + where_T where = WHERE_INIT; + where.wt_kind = WT_MEMBER; + where.wt_func_name = (char *)m->ocm_name; + if (need_type_where(type, m->ocm_type, FALSE, -1, + where, &cctx, FALSE, FALSE) == FAIL) + goto erret; + } + } + else + push_default_value(&cctx, m->ocm_type->tt_type, + FALSE, NULL); + generate_STORE_THIS(&cctx, i); + } + } + } + + if (ufunc->uf_def_args.ga_len > 0) + { + int count = ufunc->uf_def_args.ga_len; + int first_def_arg = ufunc->uf_args.ga_len - count; + int i; + int off = STACK_FRAME_SIZE + (ufunc->uf_va_name != NULL ? 1 : 0); + int did_set_arg_type = FALSE; + + // Produce instructions for the default values of optional arguments. + SOURCING_LNUM = 0; // line number unknown + for (i = 0; i < count; ++i) + { + char_u *arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i]; + if (STRCMP(arg, "v:none") == 0) + // "arg = v:none" means the argument is optional without + // setting a value when the argument is missing. + continue; + + type_T *val_type; + int arg_idx = first_def_arg + i; + where_T where = WHERE_INIT; + int jump_instr_idx = instr->ga_len; + isn_T *isn; + + // Use a JUMP_IF_ARG_SET instruction to skip if the value was given. + if (generate_JUMP_IF_ARG(&cctx, ISN_JUMP_IF_ARG_SET, + i - count - off) == FAIL) + goto erret; + + // Make sure later arguments are not found. + ufunc->uf_args_visible = arg_idx; + + int r = compile_expr0(&arg, &cctx); + if (r == FAIL) + goto erret; + + // If no type specified use the type of the default value. + // Otherwise check that the default value type matches the + // specified type. + val_type = get_type_on_stack(&cctx, 0); + where.wt_index = arg_idx + 1; + where.wt_kind = WT_ARGUMENT; + if (ufunc->uf_arg_types[arg_idx] == &t_unknown) + { + did_set_arg_type = TRUE; + ufunc->uf_arg_types[arg_idx] = val_type; + } + else if (need_type_where(val_type, ufunc->uf_arg_types[arg_idx], + FALSE, -1, where, &cctx, FALSE, FALSE) == FAIL) + goto erret; + + if (generate_STORE(&cctx, ISN_STORE, i - count - off, NULL) == FAIL) + goto erret; + + // set instruction index in JUMP_IF_ARG_SET to here + isn = ((isn_T *)instr->ga_data) + jump_instr_idx; + isn->isn_arg.jumparg.jump_where = instr->ga_len; + } + + if (did_set_arg_type) + set_function_type(ufunc); + } + ufunc->uf_args_visible = ufunc->uf_args.ga_len; + + // Compiling a function in an interface is done to get the function type. + // No code is actually compiled. + if (ufunc->uf_class != NULL && IS_INTERFACE(ufunc->uf_class)) + { + ufunc->uf_def_status = UF_NOT_COMPILED; + ret = OK; + goto erret; + } + + if (compile_def_function_body(&cctx, ufunc->uf_lines.ga_len, + check_return_type, &lines_to_free, &errormsg) == FAIL) + goto erret; + if (cctx.ctx_scope != NULL) { if (cctx.ctx_scope->se_type == IF_SCOPE) diff --git a/src/vim9type.c b/src/vim9type.c index 228df6e2fa..775291e743 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -1894,14 +1894,12 @@ type_name_list_or_dict(char *name, type_T *type, char **tofree) size_t len = STRLEN(name) + STRLEN(member_name) + 3; *tofree = alloc(len); - if (*tofree != NULL) - { - vim_snprintf(*tofree, len, "%s<%s>", name, member_name); - vim_free(member_free); - return *tofree; - } + if (*tofree == NULL) + return name; - return name; + vim_snprintf(*tofree, len, "%s<%s>", name, member_name); + vim_free(member_free); + return *tofree; } /* @@ -1924,17 +1922,15 @@ type_name_class_or_obj(char *name, type_T *type, char **tofree) size_t len = STRLEN(name) + STRLEN(class_name) + 3; *tofree = alloc(len); - if (*tofree != NULL) - { - vim_snprintf(*tofree, len, "%s<%s>", name, class_name); - return *tofree; - } + if (*tofree == NULL) + return name; - return name; + vim_snprintf(*tofree, len, "%s<%s>", name, class_name); + return *tofree; } /* - * Return the type name of a functio. + * Return the type name of a function. * The result may be in allocated memory, in which case "tofree" is set. */ static char * -- cgit v1.2.3