summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-03-08 13:18:55 +0000
committerBram Moolenaar <Bram@vim.org>2022-03-08 13:18:55 +0000
commit8acb9cc6209768ca7ec75c9f7af8c389312ea8d6 (patch)
tree8a5f92b1b10f53f7a3f0e95ffbdcb519ce9ecb09 /src
parent08238045e769fce2d5bf1e17167e26eafc3d72b8 (diff)
patch 8.2.4526: Vim9: cannot set variables to a null valuev8.2.4526
Problem: Vim9: cannot set variables to a null value. Solution: Add null_list, null_job, etc.
Diffstat (limited to 'src')
-rw-r--r--src/eval.c120
-rw-r--r--src/evalvars.c5
-rw-r--r--src/proto/eval.pro1
-rw-r--r--src/testdir/test_expr.vim20
-rw-r--r--src/testdir/test_vim9_assign.vim34
-rw-r--r--src/testdir/test_vim9_disassemble.vim52
-rw-r--r--src/testdir/test_vim9_func.vim2
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h1
-rw-r--r--src/vim9compile.c6
-rw-r--r--src/vim9execute.c14
-rw-r--r--src/vim9expr.c18
-rw-r--r--src/vim9instr.c37
-rw-r--r--src/vim9script.c8
-rw-r--r--src/vim9type.c21
15 files changed, 297 insertions, 44 deletions
diff --git a/src/eval.c b/src/eval.c
index c4c403c19d..a48b8f8abb 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -943,6 +943,7 @@ get_lval(
type_list = &SCRIPT_ITEM(current_sctx.sc_sid)->sn_type_list;
else
{
+ // TODO: should we give an error here?
type_list = &tmp_type_list;
ga_init2(type_list, sizeof(type_T), 10);
}
@@ -3483,6 +3484,100 @@ eval_leader(char_u **arg, int vim9)
}
/*
+ * Check for a predefined value "true", "false" and "null.*".
+ * Return OK when recognized.
+ */
+ int
+handle_predefined(char_u *s, int len, typval_T *rettv)
+{
+ switch (len)
+ {
+ case 4: if (STRNCMP(s, "true", 4) == 0)
+ {
+ rettv->v_type = VAR_BOOL;
+ rettv->vval.v_number = VVAL_TRUE;
+ return OK;
+ }
+ if (STRNCMP(s, "null", 4) == 0)
+ {
+ rettv->v_type = VAR_SPECIAL;
+ rettv->vval.v_number = VVAL_NULL;
+ return OK;
+ }
+ break;
+ case 5: if (STRNCMP(s, "false", 5) == 0)
+ {
+ rettv->v_type = VAR_BOOL;
+ rettv->vval.v_number = VVAL_FALSE;
+ return OK;
+ }
+ break;
+#ifdef FEAT_JOB_CHANNEL
+ case 8: if (STRNCMP(s, "null_job", 8) == 0)
+ {
+ rettv->v_type = VAR_JOB;
+ rettv->vval.v_job = NULL;
+ return OK;
+ }
+ break;
+#endif
+ case 9:
+ if (STRNCMP(s, "null_", 5) != 0)
+ break;
+ if (STRNCMP(s + 5, "list", 4) == 0)
+ {
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = NULL;
+ return OK;
+ }
+ if (STRNCMP(s + 5, "dict", 4) == 0)
+ {
+ rettv->v_type = VAR_DICT;
+ rettv->vval.v_dict = NULL;
+ return OK;
+ }
+ if (STRNCMP(s + 5, "blob", 4) == 0)
+ {
+ rettv->v_type = VAR_BLOB;
+ rettv->vval.v_blob = NULL;
+ return OK;
+ }
+ break;
+ case 11: if (STRNCMP(s, "null_string", 11) == 0)
+ {
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ return OK;
+ }
+ break;
+ case 12:
+#ifdef FEAT_JOB_CHANNEL
+ if (STRNCMP(s, "null_channel", 12) == 0)
+ {
+ rettv->v_type = VAR_CHANNEL;
+ rettv->vval.v_channel = NULL;
+ return OK;
+ }
+#endif
+ if (STRNCMP(s, "null_partial", 12) == 0)
+ {
+ rettv->v_type = VAR_PARTIAL;
+ rettv->vval.v_partial = NULL;
+ return OK;
+ }
+ break;
+ case 13: if (STRNCMP(s, "null_function", 13) == 0)
+ {
+ rettv->v_type = VAR_FUNC;
+ rettv->vval.v_string = NULL;
+ return OK;
+ }
+ break;
+ }
+ return FAIL;
+}
+
+/*
* Handle sixth level expression:
* number number constant
* 0zFFFFFFFF Blob constant
@@ -3757,26 +3852,11 @@ eval7(
ret = FAIL;
else if (evaluate)
{
- // get the value of "true", "false" or a variable
- if (len == 4 && vim9script && STRNCMP(s, "true", 4) == 0)
- {
- rettv->v_type = VAR_BOOL;
- rettv->vval.v_number = VVAL_TRUE;
- ret = OK;
- }
- else if (len == 5 && vim9script && STRNCMP(s, "false", 5) == 0)
- {
- rettv->v_type = VAR_BOOL;
- rettv->vval.v_number = VVAL_FALSE;
- ret = OK;
- }
- else if (len == 4 && vim9script && STRNCMP(s, "null", 4) == 0)
- {
- rettv->v_type = VAR_SPECIAL;
- rettv->vval.v_number = VVAL_NULL;
- ret = OK;
- }
- else
+ // get the value of "true", "false", etc. or a variable
+ ret = FAIL;
+ if (vim9script)
+ ret = handle_predefined(s, len, rettv);
+ if (ret == FAIL)
{
name_start = s;
ret = eval_variable(s, len, 0, rettv, NULL,
diff --git a/src/evalvars.c b/src/evalvars.c
index 44882cae2f..4f7252c5c6 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -999,6 +999,11 @@ ex_let_vars(
listitem_T *item;
typval_T ltv;
+ if (tv->v_type == VAR_VOID)
+ {
+ emsg(_(e_cannot_use_void_value));
+ return FAIL;
+ }
if (*arg != '[')
{
// ":let var = expr" or ":for var in list"
diff --git a/src/proto/eval.pro b/src/proto/eval.pro
index 8be58855d7..49dc65b8a8 100644
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -42,6 +42,7 @@ int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
void eval_addblob(typval_T *tv1, typval_T *tv2);
int eval_addlist(typval_T *tv1, typval_T *tv2);
int eval_leader(char_u **arg, int vim9);
+int handle_predefined(char_u *s, int len, typval_T *rettv);
int check_can_index(typval_T *rettv, int evaluate, int verbose);
void f_slice(typval_T *argvars, typval_T *rettv);
int eval_index_inner(typval_T *rettv, int is_range, typval_T *var1, typval_T *var2, int exclusive, char_u *key, int keylen, int verbose);
diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim
index 40c1701df7..5f2020db42 100644
--- a/src/testdir/test_expr.vim
+++ b/src/testdir/test_expr.vim
@@ -157,12 +157,28 @@ endfunc
func Test_loop_over_null_list()
let lines =<< trim END
- VAR null_list = test_null_list()
- for i in null_list
+ VAR nulllist = test_null_list()
+ for i in nulllist
call assert_report('should not get here')
endfor
END
call v9.CheckLegacyAndVim9Success(lines)
+
+ let lines =<< trim END
+ var nulllist = null_list
+ for i in nulllist
+ call assert_report('should not get here')
+ endfor
+ END
+ call v9.CheckDefAndScriptSuccess(lines)
+
+ let lines =<< trim END
+ let nulllist = null_list
+ for i in nulllist
+ call assert_report('should not get here')
+ endfor
+ END
+ call v9.CheckScriptFailure(lines, 'E121: Undefined variable: null_list')
endfunc
func Test_setreg_null_list()
diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim
index 6f6433493b..abc3a15b9a 100644
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -306,12 +306,44 @@ def Test_assign_register()
enddef
def Test_reserved_name()
- for name in ['true', 'false', 'null']
+ var more_names = ['null_job', 'null_channel']
+ if !has('job')
+ more_names = []
+ endif
+
+ for name in ['true',
+ 'false',
+ 'null',
+ 'null_blob',
+ 'null_dict',
+ 'null_function',
+ 'null_list',
+ 'null_partial',
+ 'null_string',
+ ] + more_names
v9.CheckDefExecAndScriptFailure(['var ' .. name .. ' = 0'], 'E1034:')
v9.CheckDefExecAndScriptFailure(['var ' .. name .. ': bool'], 'E1034:')
endfor
enddef
+def Test_null_values()
+ var lines =<< trim END
+ var b: blob = null_blob
+ var dn: dict<number> = null_dict
+ var ds: dict<string> = null_dict
+ var ln: list<number> = null_list
+ var ls: list<string> = null_list
+ var Ff: func(string): string = null_function
+ var Fp: func(number): number = null_partial
+ var s: string = null_string
+ if has('job')
+ var j: job = null_job
+ var c: channel = null_channel
+ endif
+ END
+ v9.CheckDefAndScriptSuccess(lines)
+enddef
+
def Test_skipped_assignment()
var lines =<< trim END
for x in []
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index 028e1a760e..26ecddedb2 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -415,6 +415,58 @@ def Test_disassemble_store_member()
res)
enddef
+if has('job')
+ def s:StoreNull()
+ var ss = null_string
+ var bb = null_blob
+ var dd = null_dict
+ var ll = null_list
+ var Ff = null_function
+ var Pp = null_partial
+ var jj = null_job
+ var cc = null_channel
+ enddef
+
+ def Test_disassemble_assign_null()
+ var res = execute('disass s:StoreNull')
+ assert_match('<SNR>\d*_StoreNull\_s*' ..
+ 'var ss = null_string\_s*' ..
+ '\d\+ PUSHS "\[NULL\]"\_s*' ..
+ '\d\+ STORE $\d\_s*' ..
+
+ 'var bb = null_blob\_s*' ..
+ '\d\+ PUSHBLOB 0z\_s*' ..
+ '\d\+ STORE $\d\_s*' ..
+
+ 'var dd = null_dict\_s*' ..
+ '\d\+ NEWDICT size 0\_s*' ..
+ '\d\+ STORE $\d\_s*' ..
+
+ 'var ll = null_list\_s*' ..
+ '\d\+ NEWLIST size 0\_s*' ..
+ '\d\+ STORE $\d\_s*' ..
+
+ 'var Ff = null_function\_s*' ..
+ '\d\+ PUSHFUNC "\[none\]"\_s*' ..
+ '\d\+ STORE $\d\_s*' ..
+
+ 'var Pp = null_partial\_s*' ..
+ '\d\+ NEWPARTIAL\_s*' ..
+ '\d\+ STORE $\d\_s*' ..
+
+ 'var jj = null_job\_s*' ..
+ '\d\+ PUSHJOB "no process"\_s*' ..
+ '\d\+ STORE $\d\_s*' ..
+
+ 'var cc = null_channel\_s*' ..
+ '\d\+ PUSHCHANNEL 0\_s*' ..
+ '\d\+ STORE $\d\_s*' ..
+
+ '\d\+ RETURN void',
+ res)
+ enddef
+endif
+
def s:ScriptFuncStoreIndex()
var d = {dd: {}}
d.dd[0] = 0
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index 866cf4cc7f..a47dbae27b 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -3326,7 +3326,7 @@ def Test_partial_call()
var Expr: func(dict<any>): dict<any>
const Call = Foo(Expr)
END
- v9.CheckScriptFailure(lines, 'E1235:')
+ v9.CheckScriptFailure(lines, 'E1031:')
enddef
def Test_partial_double_nested()
diff --git a/src/version.c b/src/version.c
index 9693ad35cf..c089b02aa3 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 4526,
+/**/
4525,
/**/
4524,
diff --git a/src/vim9.h b/src/vim9.h
index 07dd6392f3..a4fcc9b32a 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -91,6 +91,7 @@ typedef enum {
ISN_PUSHJOB, // push channel isn_arg.job
ISN_NEWLIST, // push list from stack items, size is isn_arg.number
ISN_NEWDICT, // push dict from stack items, size is isn_arg.number
+ ISN_NEWPARTIAL, // push NULL partial
ISN_AUTOLOAD, // get item from autoload import, function or variable
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 634f24e1ec..6a3c32aad0 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -403,8 +403,12 @@ need_type_where(
if (ret == OK)
return OK;
+ // If actual a constant a runtime check makes no sense. If it's
+ // null_function it is OK.
+ if (actual_is_const && ret == MAYBE && actual == &t_func_unknown)
+ return OK;
+
// If the actual type can be the expected type add a runtime check.
- // If it's a constant a runtime check makes no sense.
if (!actual_is_const && ret == MAYBE && use_typecheck(actual, expected))
{
generate_TYPECHECK(cctx, expected, offset, where.wt_index);
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 56220f862e..9f6cc5d91c 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -3440,6 +3440,17 @@ exec_instructions(ectx_T *ectx)
}
break;
+ // create a partial with NULL value
+ case ISN_NEWPARTIAL:
+ if (GA_GROW_FAILS(&ectx->ec_stack, 1))
+ goto theend;
+ ++ectx->ec_stack.ga_len;
+ tv = STACK_TV_BOT(-1);
+ tv->v_type = VAR_PARTIAL;
+ tv->v_lock = 0;
+ tv->vval.v_partial = NULL;
+ break;
+
// call a :def function
case ISN_DCALL:
SOURCING_LNUM = iptr->isn_lnum;
@@ -5720,6 +5731,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
smsg("%s%4d NEWDICT size %lld", pfx, current,
(varnumber_T)(iptr->isn_arg.number));
break;
+ case ISN_NEWPARTIAL:
+ smsg("%s%4d NEWPARTIAL", pfx, current);
+ break;
// function call
case ISN_BCALL:
diff --git a/src/vim9expr.c b/src/vim9expr.c
index 1485bd93cf..19cd55d3c8 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -2107,14 +2107,20 @@ compile_expr8(
break;
/*
- * "null" constant
+ * "null" or "null_*" constant
*/
- case 'n': if (STRNCMP(*arg, "null", 4) == 0
- && !eval_isnamec((*arg)[4]))
+ case 'n': if (STRNCMP(*arg, "null", 4) == 0)
{
- *arg += 4;
- rettv->v_type = VAR_SPECIAL;
- rettv->vval.v_number = VVAL_NULL;
+ char_u *p = *arg + 4;
+ int len;
+
+ for (len = 0; eval_isnamec(p[len]); ++len)
+ ;
+ ret = handle_predefined(*arg, len + 4, rettv);
+ if (ret == FAIL)
+ ret = NOTDONE;
+ else
+ *arg += len + 4;
}
else
ret = NOTDONE;
diff --git a/src/vim9instr.c b/src/vim9instr.c
index 2c76fadc3a..7fbf529a64 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -570,6 +570,40 @@ generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
generate_PUSHBLOB(cctx, tv->vval.v_blob);
tv->vval.v_blob = NULL;
break;
+ case VAR_LIST:
+ if (tv->vval.v_list != NULL)
+ iemsg("non-empty list constant not supported");
+ generate_NEWLIST(cctx, 0);
+ break;
+ case VAR_DICT:
+ if (tv->vval.v_dict != NULL)
+ iemsg("non-empty dict constant not supported");
+ generate_NEWDICT(cctx, 0);
+ break;
+#ifdef FEAT_JOB_CHANNEL
+ case VAR_JOB:
+ if (tv->vval.v_job != NULL)
+ iemsg("non-null job constant not supported");
+ generate_PUSHJOB(cctx, NULL);
+ break;
+ case VAR_CHANNEL:
+ if (tv->vval.v_channel != NULL)
+ iemsg("non-null channel constant not supported");
+ generate_PUSHCHANNEL(cctx, NULL);
+ break;
+#endif
+ case VAR_FUNC:
+ if (tv->vval.v_string != NULL)
+ iemsg("non-null function constant not supported");
+ generate_PUSHFUNC(cctx, NULL, &t_func_unknown);
+ break;
+ case VAR_PARTIAL:
+ if (tv->vval.v_partial != NULL)
+ iemsg("non-null partial constant not supported");
+ if (generate_instr_type(cctx, ISN_NEWPARTIAL, &t_func_unknown)
+ == NULL)
+ return FAIL;
+ break;
case VAR_STRING:
generate_PUSHS(cctx, &tv->vval.v_string);
tv->vval.v_string = NULL;
@@ -706,7 +740,7 @@ generate_PUSHJOB(cctx_T *cctx, job_T *job)
isn_T *isn;
RETURN_OK_IF_SKIP(cctx);
- if ((isn = generate_instr_type(cctx, ISN_PUSHJOB, &t_channel)) == NULL)
+ if ((isn = generate_instr_type(cctx, ISN_PUSHJOB, &t_job)) == NULL)
return FAIL;
isn->isn_arg.job = job;
@@ -2185,6 +2219,7 @@ delete_instr(isn_T *isn)
case ISN_NEGATENR:
case ISN_NEWDICT:
case ISN_NEWLIST:
+ case ISN_NEWPARTIAL:
case ISN_OPANY:
case ISN_OPFLOAT:
case ISN_OPNR:
diff --git a/src/vim9script.c b/src/vim9script.c
index 74f9325891..6dce5722ae 100644
--- a/src/vim9script.c
+++ b/src/vim9script.c
@@ -1062,6 +1062,14 @@ static char *reserved[] = {
"true",
"false",
"null",
+ "null_blob",
+ "null_dict",
+ "null_function",
+ "null_list",
+ "null_partial",
+ "null_string",
+ "null_channel",
+ "null_job",
"this",
NULL
};
diff --git a/src/vim9type.c b/src/vim9type.c
index dcfc998c57..fe4dec59f0 100644
--- a/src/vim9type.c
+++ b/src/vim9type.c
@@ -567,22 +567,19 @@ check_typval_type(type_T *expected, typval_T *actual_tv, where_T where)
if (expected == NULL)
return OK; // didn't expect anything.
+ //
+ ga_init2(&type_list, sizeof(type_T *), 10);
- // For some values there is no type, assume an error will be given later
- // for an invalid value.
+ // A null_function and null_partial are special cases, they can be used to
+ // clear a variable.
if ((actual_tv->v_type == VAR_FUNC && actual_tv->vval.v_string == NULL)
|| (actual_tv->v_type == VAR_PARTIAL
&& actual_tv->vval.v_partial == NULL))
- {
- emsg(_(e_function_reference_is_not_set));
- return FAIL;
- }
-
- ga_init2(&type_list, sizeof(type_T *), 10);
-
- // When the actual type is list<any> or dict<any> go through the values to
- // possibly get a more specific type.
- actual_type = typval2type(actual_tv, get_copyID(), &type_list,
+ actual_type = &t_func_unknown;
+ else
+ // When the actual type is list<any> or dict<any> go through the values
+ // to possibly get a more specific type.
+ actual_type = typval2type(actual_tv, get_copyID(), &type_list,
TVTT_DO_MEMBER | TVTT_MORE_SPECIFIC);
if (actual_type != NULL)
{