To: vim_dev@googlegroups.com Subject: Patch 8.2.3364 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3364 Problem: Vim9: crash when :for is skipped. Solution: Skip more code generation. (Naruhiko Nishino, closes #8777) Files: src/vim9compile.c, src/testdir/test_vim9_script.vim *** ../vim-8.2.3363/src/vim9compile.c 2021-08-20 20:54:20.558119674 +0200 --- src/vim9compile.c 2021-08-21 17:25:19.035271801 +0200 *************** *** 8041,8191 **** } arg_end = arg; ! // If we know the type of "var" and it is a not a supported type we can ! // give an error now. ! vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; ! if (vartype->tt_type != VAR_LIST && vartype->tt_type != VAR_STRING ! && vartype->tt_type != VAR_BLOB && vartype->tt_type != VAR_ANY) { ! semsg(_(e_for_loop_on_str_not_supported), vartype_name(vartype->tt_type)); ! drop_scope(cctx); ! return NULL; ! } ! if (vartype->tt_type == VAR_STRING) ! item_type = &t_string; ! else if (vartype->tt_type == VAR_BLOB) ! item_type = &t_number; ! else if (vartype->tt_type == VAR_LIST && vartype->tt_member->tt_type != VAR_ANY) ! { ! if (!var_list) ! item_type = vartype->tt_member; ! else if (vartype->tt_member->tt_type == VAR_LIST ! && vartype->tt_member->tt_member->tt_type != VAR_ANY) ! // TODO: should get the type for each lhs ! item_type = vartype->tt_member->tt_member; ! } ! ! // CMDMOD_REV must come before the FOR instruction. ! generate_undo_cmdmods(cctx); ! // "for_end" is set when ":endfor" is found ! scope->se_u.se_for.fs_top_label = current_instr_idx(cctx); ! generate_FOR(cctx, loop_lvar->lv_idx); ! arg = arg_start; ! if (var_list) ! { ! generate_UNPACK(cctx, var_count, semicolon); ! arg = skipwhite(arg + 1); // skip white after '[' ! // the list item is replaced by a number of items ! if (GA_GROW_FAILS(stack, var_count - 1)) { ! drop_scope(cctx); ! return NULL; ! } ! --stack->ga_len; ! for (idx = 0; idx < var_count; ++idx) ! { ! ((type_T **)stack->ga_data)[stack->ga_len] = ! (semicolon && idx == 0) ? vartype : item_type; ! ++stack->ga_len; ! } ! } ! for (idx = 0; idx < var_count; ++idx) ! { ! assign_dest_T dest = dest_local; ! int opt_flags = 0; ! int vimvaridx = -1; ! type_T *type = &t_any; ! type_T *lhs_type = &t_any; ! where_T where = WHERE_INIT; ! ! p = skip_var_one(arg, FALSE); ! varlen = p - arg; ! name = vim_strnsave(arg, varlen); ! if (name == NULL) ! goto failed; ! if (*p == ':') ! { ! p = skipwhite(p + 1); ! lhs_type = parse_type(&p, cctx->ctx_type_list, TRUE); } ! // TODO: script var not supported? ! if (get_var_dest(name, &dest, CMD_for, &opt_flags, ! &vimvaridx, &type, cctx) == FAIL) ! goto failed; ! if (dest != dest_local) ! { ! if (generate_store_var(cctx, dest, opt_flags, vimvaridx, ! 0, 0, type, name) == FAIL) ! goto failed; ! } ! else if (varlen == 1 && *arg == '_') { ! // Assigning to "_": drop the value. ! if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL) goto failed; ! } ! else ! { ! if (lookup_local(arg, varlen, NULL, cctx) == OK) { ! semsg(_(e_variable_already_declared), arg); ! goto failed; } ! if (STRNCMP(name, "s:", 2) == 0) ! { ! semsg(_(e_cannot_declare_script_variable_in_function), name); goto failed; } ! ! // Reserve a variable to store "var". ! where.wt_index = var_list ? idx + 1 : 0; ! where.wt_variable = TRUE; ! if (lhs_type == &t_any) ! lhs_type = item_type; ! else if (item_type != &t_unknown ! && (item_type == &t_any ! ? need_type(item_type, lhs_type, -1, 0, cctx, FALSE, FALSE) ! : check_type(lhs_type, item_type, TRUE, where)) ! == FAIL) ! goto failed; ! var_lvar = reserve_local(cctx, arg, varlen, TRUE, lhs_type); ! if (var_lvar == NULL) ! // out of memory or used as an argument ! goto failed; ! if (semicolon && idx == var_count - 1) ! var_lvar->lv_type = vartype; ! else ! var_lvar->lv_type = item_type; ! generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL); } ! if (*p == ',' || *p == ';') ! ++p; ! arg = skipwhite(p); ! vim_free(name); ! } ! ! if (cctx->ctx_compile_type == CT_DEBUG) ! { ! int save_prev_lnum = cctx->ctx_prev_lnum; ! // Add ISN_DEBUG here, so that the loop variables can be inspected. ! // Use the prev_lnum from the ISN_DEBUG instruction removed above. ! cctx->ctx_prev_lnum = prev_lnum; ! generate_instr_debug(cctx); ! cctx->ctx_prev_lnum = save_prev_lnum; } return arg_end; --- 8041,8194 ---- } arg_end = arg; ! if (cctx->ctx_skip != SKIP_YES) { ! // If we know the type of "var" and it is a not a supported type we can ! // give an error now. ! vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; ! if (vartype->tt_type != VAR_LIST && vartype->tt_type != VAR_STRING ! && vartype->tt_type != VAR_BLOB && vartype->tt_type != VAR_ANY) ! { ! semsg(_(e_for_loop_on_str_not_supported), vartype_name(vartype->tt_type)); ! drop_scope(cctx); ! return NULL; ! } ! if (vartype->tt_type == VAR_STRING) ! item_type = &t_string; ! else if (vartype->tt_type == VAR_BLOB) ! item_type = &t_number; ! else if (vartype->tt_type == VAR_LIST && vartype->tt_member->tt_type != VAR_ANY) ! { ! if (!var_list) ! item_type = vartype->tt_member; ! else if (vartype->tt_member->tt_type == VAR_LIST ! && vartype->tt_member->tt_member->tt_type != VAR_ANY) ! // TODO: should get the type for each lhs ! item_type = vartype->tt_member->tt_member; ! } ! // CMDMOD_REV must come before the FOR instruction. ! generate_undo_cmdmods(cctx); ! // "for_end" is set when ":endfor" is found ! scope->se_u.se_for.fs_top_label = current_instr_idx(cctx); ! generate_FOR(cctx, loop_lvar->lv_idx); ! arg = arg_start; ! if (var_list) { ! generate_UNPACK(cctx, var_count, semicolon); ! arg = skipwhite(arg + 1); // skip white after '[' ! // the list item is replaced by a number of items ! if (GA_GROW_FAILS(stack, var_count - 1)) ! { ! drop_scope(cctx); ! return NULL; ! } ! --stack->ga_len; ! for (idx = 0; idx < var_count; ++idx) ! { ! ((type_T **)stack->ga_data)[stack->ga_len] = ! (semicolon && idx == 0) ? vartype : item_type; ! ++stack->ga_len; ! } } ! for (idx = 0; idx < var_count; ++idx) { ! assign_dest_T dest = dest_local; ! int opt_flags = 0; ! int vimvaridx = -1; ! type_T *type = &t_any; ! type_T *lhs_type = &t_any; ! where_T where = WHERE_INIT; ! ! p = skip_var_one(arg, FALSE); ! varlen = p - arg; ! name = vim_strnsave(arg, varlen); ! if (name == NULL) goto failed; ! if (*p == ':') { ! p = skipwhite(p + 1); ! lhs_type = parse_type(&p, cctx->ctx_type_list, TRUE); } ! // TODO: script var not supported? ! if (get_var_dest(name, &dest, CMD_for, &opt_flags, ! &vimvaridx, &type, cctx) == FAIL) goto failed; + if (dest != dest_local) + { + if (generate_store_var(cctx, dest, opt_flags, vimvaridx, + 0, 0, type, name) == FAIL) + goto failed; } ! else if (varlen == 1 && *arg == '_') ! { ! // Assigning to "_": drop the value. ! if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL) ! goto failed; ! } ! else ! { ! if (lookup_local(arg, varlen, NULL, cctx) == OK) ! { ! semsg(_(e_variable_already_declared), arg); ! goto failed; ! } ! ! if (STRNCMP(name, "s:", 2) == 0) ! { ! semsg(_(e_cannot_declare_script_variable_in_function), name); ! goto failed; ! } ! ! // Reserve a variable to store "var". ! where.wt_index = var_list ? idx + 1 : 0; ! where.wt_variable = TRUE; ! if (lhs_type == &t_any) ! lhs_type = item_type; ! else if (item_type != &t_unknown ! && (item_type == &t_any ! ? need_type(item_type, lhs_type, -1, 0, cctx, FALSE, FALSE) ! : check_type(lhs_type, item_type, TRUE, where)) ! == FAIL) ! goto failed; ! var_lvar = reserve_local(cctx, arg, varlen, TRUE, lhs_type); ! if (var_lvar == NULL) ! // out of memory or used as an argument ! goto failed; ! ! if (semicolon && idx == var_count - 1) ! var_lvar->lv_type = vartype; ! else ! var_lvar->lv_type = item_type; ! generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL); ! } ! if (*p == ',' || *p == ';') ! ++p; ! arg = skipwhite(p); ! vim_free(name); } ! if (cctx->ctx_compile_type == CT_DEBUG) ! { ! int save_prev_lnum = cctx->ctx_prev_lnum; ! // Add ISN_DEBUG here, so that the loop variables can be inspected. ! // Use the prev_lnum from the ISN_DEBUG instruction removed above. ! cctx->ctx_prev_lnum = prev_lnum; ! generate_instr_debug(cctx); ! cctx->ctx_prev_lnum = save_prev_lnum; ! } } return arg_end; *************** *** 8217,8237 **** } forscope = &scope->se_u.se_for; cctx->ctx_scope = scope->se_outer; ! unwind_locals(cctx, scope->se_local_count); ! // At end of ":for" scope jump back to the FOR instruction. ! generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label); ! // Fill in the "end" label in the FOR statement so it can jump here. ! isn = ((isn_T *)instr->ga_data) + forscope->fs_top_label; ! isn->isn_arg.forloop.for_end = instr->ga_len; ! // Fill in the "end" label any BREAK statements ! compile_fill_jump_to_end(&forscope->fs_end_label, instr->ga_len, cctx); ! // Below the ":for" scope drop the "expr" list from the stack. ! if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL) ! return NULL; vim_free(scope); --- 8220,8243 ---- } forscope = &scope->se_u.se_for; cctx->ctx_scope = scope->se_outer; ! if (cctx->ctx_skip != SKIP_YES) ! { ! unwind_locals(cctx, scope->se_local_count); ! // At end of ":for" scope jump back to the FOR instruction. ! generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label); ! // Fill in the "end" label in the FOR statement so it can jump here. ! isn = ((isn_T *)instr->ga_data) + forscope->fs_top_label; ! isn->isn_arg.forloop.for_end = instr->ga_len; ! // Fill in the "end" label any BREAK statements ! compile_fill_jump_to_end(&forscope->fs_end_label, instr->ga_len, cctx); ! // Below the ":for" scope drop the "expr" list from the stack. ! if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL) ! return NULL; ! } vim_free(scope); *** ../vim-8.2.3363/src/testdir/test_vim9_script.vim 2021-08-21 17:13:08.569405922 +0200 --- src/testdir/test_vim9_script.vim 2021-08-21 17:20:24.836285504 +0200 *************** *** 2552,2557 **** --- 2552,2621 ---- delete('Xvim9for.vim') enddef + def Test_for_skipped_block() + # test skipped blocks at outside of function + var lines =<< trim END + var result = [] + if true + for n in [1, 2] + result += [n] + endfor + else + for n in [3, 4] + result += [n] + endfor + endif + assert_equal([1, 2], result) + + result = [] + if false + for n in [1, 2] + result += [n] + endfor + else + for n in [3, 4] + result += [n] + endfor + endif + assert_equal([3, 4], result) + END + CheckDefAndScriptSuccess(lines) + + # test skipped blocks at inside of function + lines =<< trim END + def DefTrue() + var result = [] + if true + for n in [1, 2] + result += [n] + endfor + else + for n in [3, 4] + result += [n] + endfor + endif + assert_equal([1, 2], result) + enddef + DefTrue() + + def DefFalse() + var result = [] + if false + for n in [1, 2] + result += [n] + endfor + else + for n in [3, 4] + result += [n] + endfor + endif + assert_equal([3, 4], result) + enddef + DefFalse() + END + CheckDefAndScriptSuccess(lines) + enddef + def Test_for_loop() var lines =<< trim END var result = '' *** ../vim-8.2.3363/src/version.c 2021-08-21 17:13:08.569405922 +0200 --- src/version.c 2021-08-21 17:22:19.299865867 +0200 *************** *** 757,758 **** --- 757,760 ---- { /* Add new patch number below this line */ + /**/ + 3364, /**/ -- No children may attend school with their breath smelling of "wild onions." [real standing law in West Virginia, United States of America] /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///