changeset 20239:2135b4641680 v8.2.0675

patch 8.2.0675: Vim9: no support for closures Commit: https://github.com/vim/vim/commit/b84a381c75e50ca0e0a24cc3e152d0c70f8c2c7d Author: Bram Moolenaar <Bram@vim.org> Date: Fri May 1 15:44:29 2020 +0200 patch 8.2.0675: Vim9: no support for closures Problem: Vim9: no support for closures. Solution: Do not re-use stack entries.
author Bram Moolenaar <Bram@vim.org>
date Fri, 01 May 2020 15:45:04 +0200
parents d3fee66f8f3e
children 194676a30660
files src/evalvars.c src/ex_docmd.c src/proto/evalvars.pro src/proto/ex_docmd.pro src/version.c src/vim9compile.c
diffstat 6 files changed, 85 insertions(+), 78 deletions(-) [+]
line wrap: on
line diff
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -2495,19 +2495,19 @@ get_script_local_ht(void)
 
 /*
  * Look for "name[len]" in script-local variables.
- * Return -1 when not found.
+ * Return a non-NULL pointer when found, NULL when not found.
  */
-    int
+    void *
 lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy UNUSED)
 {
     hashtab_T	*ht = get_script_local_ht();
     char_u	buffer[30];
     char_u	*p;
-    int		res;
+    void	*res;
     hashitem_T	*hi;
 
     if (ht == NULL)
-	return -1;
+	return NULL;
     if (len < sizeof(buffer) - 1)
     {
 	vim_strncpy(buffer, name, len);
@@ -2517,15 +2517,15 @@ lookup_scriptvar(char_u *name, size_t le
     {
 	p = vim_strnsave(name, (int)len);
 	if (p == NULL)
-	    return -1;
+	    return NULL;
     }
 
     hi = hash_find(ht, p);
-    res = HASHITEM_EMPTY(hi) ? -1 : 1;
+    res = HASHITEM_EMPTY(hi) ? NULL : hi;
 
     // if not script-local, then perhaps imported
-    if (res == -1 && find_imported(p, 0, NULL) != NULL)
-	res = 1;
+    if (res == NULL && find_imported(p, 0, NULL) != NULL)
+	res = p;
 
     if (p != buffer)
 	vim_free(p);
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -3157,7 +3157,7 @@ append_command(char_u *cmd)
 find_ex_command(
 	exarg_T *eap,
 	int	*full UNUSED,
-	int	(*lookup)(char_u *, size_t, cctx_T *) UNUSED,
+	void	*(*lookup)(char_u *, size_t, cctx_T *) UNUSED,
 	cctx_T	*cctx UNUSED)
 {
     int		len;
@@ -3197,7 +3197,7 @@ find_ex_command(
 	    // "g:var = expr"
 	    // "var = expr"  where "var" is a local var name.
 	    if (((p - eap->cmd) > 2 && eap->cmd[1] == ':')
-		    || lookup(eap->cmd, p - eap->cmd, cctx) >= 0)
+		    || lookup(eap->cmd, p - eap->cmd, cctx) != NULL)
 	    {
 		eap->cmdidx = CMD_let;
 		return eap->cmd;
--- a/src/proto/evalvars.pro
+++ b/src/proto/evalvars.pro
@@ -55,7 +55,7 @@ int get_var_tv(char_u *name, int len, ty
 void check_vars(char_u *name, int len);
 dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload);
 dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload);
-int lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy);
+void *lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy);
 hashtab_T *find_var_ht(char_u *name, char_u **varname);
 char_u *get_var_value(char_u *name);
 void new_script_vars(scid_T id);
--- a/src/proto/ex_docmd.pro
+++ b/src/proto/ex_docmd.pro
@@ -7,7 +7,7 @@ void *getline_cookie(char_u *(*fgetline)
 int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only);
 int parse_cmd_address(exarg_T *eap, char **errormsg, int silent);
 int checkforcmd(char_u **pp, char *cmd, int len);
-char_u *find_ex_command(exarg_T *eap, int *full, int (*lookup)(char_u *, size_t, cctx_T *), cctx_T *cctx);
+char_u *find_ex_command(exarg_T *eap, int *full, void *((*lookup)(char_u *, size_t, cctx_T *)), cctx_T *cctx);
 int modifier_len(char_u *cmd);
 int cmd_exists(char_u *name);
 cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
@@ -19,7 +19,7 @@ void separate_nextcmd(exarg_T *eap);
 char_u *skip_cmd_arg(char_u *p, int rembs);
 int get_bad_opt(char_u *p, exarg_T *eap);
 int ends_excmd(int c);
-int ends_excmd2(char_u *before, char_u *cmd);
+int ends_excmd2(char_u *cmd_start, char_u *cmd);
 char_u *find_nextcmd(char_u *p);
 char_u *check_nextcmd(char_u *p);
 char_u *get_command_name(expand_T *xp, int idx);
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    675,
+/**/
     674,
 /**/
     673,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -97,6 +97,7 @@ struct scope_S {
 typedef struct {
     char_u	*lv_name;
     type_T	*lv_type;
+    int		lv_idx;	    // index of the variable on the stack
     int		lv_const;   // when TRUE cannot be assigned to
     int		lv_arg;	    // when TRUE this is an argument
 } lvar_T;
@@ -112,7 +113,7 @@ struct cctx_S {
     garray_T	ctx_instr;	    // generated instructions
 
     garray_T	ctx_locals;	    // currently visible local variables
-    int		ctx_max_local;	    // maximum number of locals at one time
+    int		ctx_locals_count;   // total number of local variables
 
     garray_T	ctx_imports;	    // imported items
 
@@ -120,6 +121,9 @@ struct cctx_S {
 				    // commands after "else"
     scope_T	*ctx_scope;	    // current scope, NULL at toplevel
 
+    cctx_T	*ctx_outer;	    // outer scope for lambda or nested
+				    // function
+
     garray_T	ctx_type_stack;	    // type of each item on the stack
     garray_T	*ctx_type_list;	    // list of pointers to allocated types
 };
@@ -135,24 +139,25 @@ static void arg_type_mismatch(type_T *ex
 static int check_type(type_T *expected, type_T *actual, int give_msg);
 
 /*
- * Lookup variable "name" in the local scope and return the index.
+ * Lookup variable "name" in the local scope and return it.
+ * Return NULL if not found.
  */
-    static int
+    static lvar_T *
 lookup_local(char_u *name, size_t len, cctx_T *cctx)
 {
     int	    idx;
 
     if (len == 0)
-	return -1;
+	return NULL;
     for (idx = 0; idx < cctx->ctx_locals.ga_len; ++idx)
     {
 	lvar_T *lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
 
 	if (STRNCMP(name, lvar->lv_name, len) == 0
 					       && STRLEN(lvar->lv_name) == len)
-	    return idx;
-    }
-    return -1;
+	    return lvar;
+    }
+    return NULL;
 }
 
 /*
@@ -217,7 +222,7 @@ check_defined(char_u *p, int len, cctx_T
 {
     if (lookup_script(p, len) == OK
 	    || (cctx != NULL
-		&& (lookup_local(p, len, cctx) >= 0
+		&& (lookup_local(p, len, cctx) != NULL
 		    || find_imported(p, len, cctx) != NULL)))
     {
 	semsg("E1073: imported name already defined: %s", p);
@@ -1458,33 +1463,34 @@ generate_EXECCONCAT(cctx_T *cctx, int co
 
 /*
  * Reserve space for a local variable.
- * Return the index or -1 if it failed.
+ * Return the variable or NULL if it failed.
  */
-    static int
+    static lvar_T *
 reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type)
 {
-    int	    idx;
     lvar_T  *lvar;
 
     if (lookup_arg(name, len, cctx) >= 0 || lookup_vararg(name, len, cctx))
     {
 	emsg_namelen(_("E1006: %s is used as an argument"), name, (int)len);
-	return -1;
+	return NULL;
     }
 
     if (ga_grow(&cctx->ctx_locals, 1) == FAIL)
-	return -1;
-    idx = cctx->ctx_locals.ga_len;
-    if (cctx->ctx_max_local < idx + 1)
-	cctx->ctx_max_local = idx + 1;
-    ++cctx->ctx_locals.ga_len;
-
-    lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
+	return NULL;
+    lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + cctx->ctx_locals.ga_len++;
+
+    // Every local variable uses the next entry on the stack.  We could re-use
+    // the last ones when leaving a scope, but then variables used in a closure
+    // might get overwritten.  To keep things simple do not re-use stack
+    // entries.  This is less efficient, but memory is cheap these days.
+    lvar->lv_idx = cctx->ctx_locals_count++;
+
     lvar->lv_name = vim_strnsave(name, (int)(len == 0 ? STRLEN(name) : len));
     lvar->lv_const = isConst;
     lvar->lv_type = type;
 
-    return idx;
+    return lvar;
 }
 
 /*
@@ -1511,7 +1517,7 @@ unwind_locals(cctx_T *cctx, int new_top)
  * Free all local variables.
  */
     static void
-free_local(cctx_T *cctx)
+free_locals(cctx_T *cctx)
 {
     unwind_locals(cctx, 0);
     ga_clear(&cctx->ctx_locals);
@@ -2331,10 +2337,12 @@ compile_load(char_u **arg, char_u *end_a
 	}
 	else
 	{
-	    idx = lookup_local(*arg, len, cctx);
-	    if (idx >= 0)
+	    lvar_T *lvar = lookup_local(*arg, len, cctx);
+
+	    if (lvar != NULL)
 	    {
-		type = (((lvar_T *)cctx->ctx_locals.ga_data) + idx)->lv_type;
+		type = lvar->lv_type;
+		idx = lvar->lv_idx;
 		gen_load = TRUE;
 	    }
 	    else
@@ -4040,7 +4048,6 @@ compile_assignment(char_u *arg, exarg_T 
     int		semicolon = 0;
     size_t	varlen;
     garray_T	*instr = &cctx->ctx_instr;
-    int		idx = -1;
     int		new_local = FALSE;
     char_u	*op;
     int		opt_type;
@@ -4050,7 +4057,7 @@ compile_assignment(char_u *arg, exarg_T 
     int		oplen = 0;
     int		heredoc = FALSE;
     type_T	*type = &t_any;
-    lvar_T	*lvar;
+    lvar_T	*lvar = NULL;
     char_u	*name;
     char_u	*sp;
     int		has_type = FALSE;
@@ -4203,6 +4210,8 @@ compile_assignment(char_u *arg, exarg_T 
 	}
 	else
 	{
+	    int idx;
+
 	    for (idx = 0; reserved[idx] != NULL; ++idx)
 		if (STRCMP(reserved[idx], name) == 0)
 		{
@@ -4210,8 +4219,8 @@ compile_assignment(char_u *arg, exarg_T 
 		    goto theend;
 		}
 
-	    idx = lookup_local(arg, varlen, cctx);
-	    if (idx >= 0)
+	    lvar = lookup_local(arg, varlen, cctx);
+	    if (lvar != NULL)
 	    {
 		if (is_decl)
 		{
@@ -4220,10 +4229,10 @@ compile_assignment(char_u *arg, exarg_T 
 		}
 		else
 		{
-		    lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
 		    if (lvar->lv_const)
 		    {
-			semsg(_("E1018: Cannot assign to a constant: %s"), name);
+			semsg(_("E1018: Cannot assign to a constant: %s"),
+									 name);
 			goto theend;
 		    }
 		}
@@ -4262,11 +4271,8 @@ compile_assignment(char_u *arg, exarg_T 
 	    type = parse_type(&p, cctx->ctx_type_list);
 	    has_type = TRUE;
 	}
-	else if (idx >= 0)
-	{
-	    lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
+	else if (lvar != NULL)
 	    type = lvar->lv_type;
-	}
     }
 
     sp = p;
@@ -4288,7 +4294,7 @@ compile_assignment(char_u *arg, exarg_T 
 	goto theend;
     }
 
-    if (idx < 0 && dest == dest_local && cctx->ctx_skip != TRUE)
+    if (lvar == NULL && dest == dest_local && cctx->ctx_skip != TRUE)
     {
 	if (oplen > 1 && !heredoc)
 	{
@@ -4301,8 +4307,8 @@ compile_assignment(char_u *arg, exarg_T 
 	// new local variable
 	if (type->tt_type == VAR_FUNC && var_check_func_name(name, TRUE))
 	    goto theend;
-	idx = reserve_local(cctx, arg, varlen, cmdidx == CMD_const, type);
-	if (idx < 0)
+	lvar = reserve_local(cctx, arg, varlen, cmdidx == CMD_const, type);
+	if (lvar == NULL)
 	    goto theend;
 	new_local = TRUE;
     }
@@ -4370,7 +4376,7 @@ compile_assignment(char_u *arg, exarg_T 
 		    generate_LOADV(cctx, name + 2, TRUE);
 		    break;
 		case dest_local:
-		    generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
+		    generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
 		    break;
 	    }
 	}
@@ -4392,9 +4398,8 @@ compile_assignment(char_u *arg, exarg_T 
 	    stack = &cctx->ctx_type_stack;
 	    stacktype = stack->ga_len == 0 ? &t_void
 			      : ((type_T **)stack->ga_data)[stack->ga_len - 1];
-	    if (idx >= 0 && (is_decl || !has_type))
+	    if (lvar != NULL && (is_decl || !has_type))
 	    {
-		lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
 		if (new_local && !has_type)
 		{
 		    if (stacktype->tt_type == VAR_VOID)
@@ -4546,6 +4551,7 @@ compile_assignment(char_u *arg, exarg_T 
 		char_u	    *rawname = name + (name[1] == ':' ? 2 : 0);
 		imported_T  *import = NULL;
 		int	    sid = current_sctx.sc_sid;
+		int	    idx;
 
 		if (name[1] != ':')
 		{
@@ -4581,6 +4587,7 @@ compile_assignment(char_u *arg, exarg_T 
 	    }
 	    break;
 	case dest_local:
+	    if (lvar != NULL)
 	    {
 		isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
 
@@ -4593,13 +4600,13 @@ compile_assignment(char_u *arg, exarg_T 
 		    garray_T	*stack = &cctx->ctx_type_stack;
 
 		    isn->isn_type = ISN_STORENR;
-		    isn->isn_arg.storenr.stnr_idx = idx;
+		    isn->isn_arg.storenr.stnr_idx = lvar->lv_idx;
 		    isn->isn_arg.storenr.stnr_val = val;
 		    if (stack->ga_len > 0)
 			--stack->ga_len;
 		}
 		else
-		    generate_STORE(cctx, ISN_STORE, idx, NULL);
+		    generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
 	    }
 	    break;
     }
@@ -5283,8 +5290,8 @@ compile_for(char_u *arg, cctx_T *cctx)
     garray_T	*instr = &cctx->ctx_instr;
     garray_T	*stack = &cctx->ctx_type_stack;
     scope_T	*scope;
-    int		loop_idx;	// index of loop iteration variable
-    int		var_idx;	// index of "var"
+    lvar_T	*loop_lvar;	// loop iteration variable
+    lvar_T	*var_lvar;	// variable for "var"
     type_T	*vartype;
 
     // TODO: list of variables: "for [key, value] in dict"
@@ -5292,8 +5299,8 @@ compile_for(char_u *arg, cctx_T *cctx)
     for (p = arg; eval_isnamec1(*p); ++p)
 	;
     varlen = p - arg;
-    var_idx = lookup_local(arg, varlen, cctx);
-    if (var_idx >= 0)
+    var_lvar = lookup_local(arg, varlen, cctx);
+    if (var_lvar != NULL)
     {
 	semsg(_("E1023: variable already defined: %s"), arg);
 	return NULL;
@@ -5314,23 +5321,24 @@ compile_for(char_u *arg, cctx_T *cctx)
 	return NULL;
 
     // Reserve a variable to store the loop iteration counter.
-    loop_idx = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
-    if (loop_idx < 0)
-    {
-	// only happens when out of memory
+    loop_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
+    if (loop_lvar == NULL)
+    {
+	// out of memory
 	drop_scope(cctx);
 	return NULL;
     }
 
     // Reserve a variable to store "var"
-    var_idx = reserve_local(cctx, arg, varlen, FALSE, &t_any);
-    if (var_idx < 0)
-    {
+    var_lvar = reserve_local(cctx, arg, varlen, FALSE, &t_any);
+    if (var_lvar == NULL)
+    {
+	// out of memory or used as an argument
 	drop_scope(cctx);
 	return NULL;
     }
 
-    generate_STORENR(cctx, loop_idx, -1);
+    generate_STORENR(cctx, loop_lvar->lv_idx, -1);
 
     // compile "expr", it remains on the stack until "endfor"
     arg = p;
@@ -5349,17 +5357,13 @@ compile_for(char_u *arg, cctx_T *cctx)
 	return NULL;
     }
     if (vartype->tt_member->tt_type != VAR_ANY)
-    {
-	lvar_T *lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + var_idx;
-
-	lvar->lv_type = vartype->tt_member;
-    }
+	var_lvar->lv_type = vartype->tt_member;
 
     // "for_end" is set when ":endfor" is found
     scope->se_u.se_for.fs_top_label = instr->ga_len;
 
-    generate_FOR(cctx, loop_idx);
-    generate_STORE(cctx, ISN_STORE, var_idx, NULL);
+    generate_FOR(cctx, loop_lvar->lv_idx);
+    generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL);
 
     return arg;
 }
@@ -6158,7 +6162,7 @@ compile_def_function(ufunc_T *ufunc, int
 			    || *ea.cmd == '$'
 			    || *ea.cmd == '@'
 			    || ((p - ea.cmd) > 2 && ea.cmd[1] == ':')
-			    || lookup_local(ea.cmd, p - ea.cmd, &cctx) >= 0
+			    || lookup_local(ea.cmd, p - ea.cmd, &cctx) != NULL
 			    || lookup_script(ea.cmd, p - ea.cmd) == OK
 			    || find_imported(ea.cmd, p - ea.cmd, &cctx) != NULL)
 		    {
@@ -6175,7 +6179,8 @@ compile_def_function(ufunc_T *ufunc, int
 	 * COMMAND after range
 	 */
 	ea.cmd = skip_range(ea.cmd, NULL);
-	p = find_ex_command(&ea, NULL, is_ex_command ? NULL : lookup_local,
+	p = find_ex_command(&ea, NULL, is_ex_command ? NULL
+		   : (void *(*)(char_u *, size_t, cctx_T *))lookup_local,
 									&cctx);
 
 	if (p == ea.cmd && ea.cmdidx != CMD_SIZE)
@@ -6349,7 +6354,7 @@ compile_def_function(ufunc_T *ufunc, int
 	dfunc->df_deleted = FALSE;
 	dfunc->df_instr = instr->ga_data;
 	dfunc->df_instr_count = instr->ga_len;
-	dfunc->df_varcount = cctx.ctx_max_local;
+	dfunc->df_varcount = cctx.ctx_locals_count;
     }
 
     {
@@ -6431,7 +6436,7 @@ erret:
 
     current_sctx = save_current_sctx;
     free_imported(&cctx);
-    free_local(&cctx);
+    free_locals(&cctx);
     ga_clear(&cctx.ctx_type_stack);
 }