changeset 19587:e61dc51ab9b4 v8.2.0350

patch 8.2.0350: Vim9: expression tests don't use recognized constants Commit: https://github.com/vim/vim/commit/080457c02d51f87e7d61ebd3e3aeef4468be939c Author: Bram Moolenaar <Bram@vim.org> Date: Tue Mar 3 21:53:32 2020 +0100 patch 8.2.0350: Vim9: expression tests don't use recognized constants Problem: Vim9: expression tests don't use recognized constants. Solution: Recognize "true" and "false" as constants. Make skipping work for assignment and expression evaluation.
author Bram Moolenaar <Bram@vim.org>
date Tue, 03 Mar 2020 22:00:04 +0100
parents 62ca09fadc5e
children 7368dce16a53
files src/version.c src/vim9compile.c
diffstat 2 files changed, 246 insertions(+), 150 deletions(-) [+]
line wrap: on
line diff
--- a/src/version.c
+++ b/src/version.c
@@ -739,6 +739,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    350,
+/**/
     349,
 /**/
     348,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -260,6 +260,9 @@ get_dict_type(type_T *member_type, garra
 /////////////////////////////////////////////////////////////////////
 // Following generate_ functions expect the caller to call ga_grow().
 
+#define RETURN_NULL_IF_SKIP(cctx) if (cctx->ctx_skip == TRUE) return NULL
+#define RETURN_OK_IF_SKIP(cctx) if (cctx->ctx_skip == TRUE) return OK
+
 /*
  * Generate an instruction without arguments.
  * Returns a pointer to the new instruction, NULL if failed.
@@ -270,6 +273,7 @@ generate_instr(cctx_T *cctx, isntype_T i
     garray_T	*instr = &cctx->ctx_instr;
     isn_T	*isn;
 
+    RETURN_NULL_IF_SKIP(cctx);
     if (ga_grow(instr, 1) == FAIL)
 	return NULL;
     isn = ((isn_T *)instr->ga_data) + instr->ga_len;
@@ -290,6 +294,7 @@ generate_instr_drop(cctx_T *cctx, isntyp
 {
     garray_T	*stack = &cctx->ctx_type_stack;
 
+    RETURN_NULL_IF_SKIP(cctx);
     stack->ga_len -= drop;
     return generate_instr(cctx, isn_type);
 }
@@ -364,6 +369,8 @@ generate_two_op(cctx_T *cctx, char_u *op
     vartype_T	vartype;
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
+
     // Get the known type of the two items on the stack.  If they are matching
     // use a type-specific instruction. Otherwise fall back to runtime type
     // checking.
@@ -461,6 +468,8 @@ generate_COMPARE(cctx_T *cctx, exptype_T
     vartype_T	type1;
     vartype_T	type2;
 
+    RETURN_OK_IF_SKIP(cctx);
+
     // Get the known type of the two items on the stack.  If they are matching
     // use a type-specific instruction. Otherwise fall back to runtime type
     // checking.
@@ -536,6 +545,7 @@ generate_2BOOL(cctx_T *cctx, int invert)
     isn_T	*isn;
     garray_T	*stack = &cctx->ctx_type_stack;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_2BOOL)) == NULL)
 	return FAIL;
     isn->isn_arg.number = invert;
@@ -552,6 +562,7 @@ generate_TYPECHECK(cctx_T *cctx, type_T 
     isn_T	*isn;
     garray_T	*stack = &cctx->ctx_type_stack;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_CHECKTYPE)) == NULL)
 	return FAIL;
     isn->isn_arg.type.ct_type = vartype->tt_type;  // TODO: whole type
@@ -571,6 +582,7 @@ generate_PUSHNR(cctx_T *cctx, varnumber_
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_type(cctx, ISN_PUSHNR, &t_number)) == NULL)
 	return FAIL;
     isn->isn_arg.number = number;
@@ -586,6 +598,7 @@ generate_PUSHBOOL(cctx_T *cctx, varnumbe
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_type(cctx, ISN_PUSHBOOL, &t_bool)) == NULL)
 	return FAIL;
     isn->isn_arg.number = number;
@@ -601,6 +614,7 @@ generate_PUSHSPEC(cctx_T *cctx, varnumbe
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_type(cctx, ISN_PUSHSPEC, &t_special)) == NULL)
 	return FAIL;
     isn->isn_arg.number = number;
@@ -617,6 +631,7 @@ generate_PUSHF(cctx_T *cctx, float_T fnu
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_type(cctx, ISN_PUSHF, &t_float)) == NULL)
 	return FAIL;
     isn->isn_arg.fnumber = fnumber;
@@ -634,6 +649,7 @@ generate_PUSHS(cctx_T *cctx, char_u *str
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_type(cctx, ISN_PUSHS, &t_string)) == NULL)
 	return FAIL;
     isn->isn_arg.string = str;
@@ -650,6 +666,7 @@ generate_PUSHCHANNEL(cctx_T *cctx, chann
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_type(cctx, ISN_PUSHCHANNEL, &t_channel)) == NULL)
 	return FAIL;
     isn->isn_arg.channel = channel;
@@ -666,6 +683,7 @@ generate_PUSHJOB(cctx_T *cctx, job_T *jo
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_type(cctx, ISN_PUSHJOB, &t_channel)) == NULL)
 	return FAIL;
     isn->isn_arg.job = job;
@@ -682,6 +700,7 @@ generate_PUSHBLOB(cctx_T *cctx, blob_T *
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_type(cctx, ISN_PUSHBLOB, &t_blob)) == NULL)
 	return FAIL;
     isn->isn_arg.blob = blob;
@@ -698,6 +717,7 @@ generate_PUSHFUNC(cctx_T *cctx, char_u *
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_type(cctx, ISN_PUSHFUNC, &t_func_void)) == NULL)
 	return FAIL;
     isn->isn_arg.string = name;
@@ -714,6 +734,7 @@ generate_PUSHPARTIAL(cctx_T *cctx, parti
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_type(cctx, ISN_PUSHPARTIAL,
 						      &t_partial_any)) == NULL)
 	return FAIL;
@@ -730,6 +751,7 @@ generate_STORE(cctx_T *cctx, isntype_T i
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_drop(cctx, isn_type, 1)) == NULL)
 	return FAIL;
     if (name != NULL)
@@ -748,6 +770,7 @@ generate_STORENR(cctx_T *cctx, int idx, 
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_STORENR)) == NULL)
 	return FAIL;
     isn->isn_arg.storenr.str_idx = idx;
@@ -764,6 +787,7 @@ generate_STOREOPT(cctx_T *cctx, char_u *
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_STOREOPT)) == NULL)
 	return FAIL;
     isn->isn_arg.storeopt.so_name = vim_strsave(name);
@@ -785,6 +809,7 @@ generate_LOAD(
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_type(cctx, isn_type, type)) == NULL)
 	return FAIL;
     if (name != NULL)
@@ -807,6 +832,7 @@ generate_LOADV(
     // load v:var
     int vidx = find_vim_var(name);
 
+    RETURN_OK_IF_SKIP(cctx);
     if (vidx < 0)
     {
 	if (error)
@@ -831,6 +857,7 @@ generate_OLDSCRIPT(
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if (isn_type == ISN_LOADS)
 	isn = generate_instr_type(cctx, isn_type, type);
     else
@@ -856,6 +883,7 @@ generate_VIM9SCRIPT(
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if (isn_type == ISN_LOADSCRIPT)
 	isn = generate_instr_type(cctx, isn_type, type);
     else
@@ -879,6 +907,7 @@ generate_NEWLIST(cctx_T *cctx, int count
     type_T	*type;
     type_T	*member;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_NEWLIST)) == NULL)
 	return FAIL;
     isn->isn_arg.number = count;
@@ -915,6 +944,7 @@ generate_NEWDICT(cctx_T *cctx, int count
     type_T	*type;
     type_T	*member;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_NEWDICT)) == NULL)
 	return FAIL;
     isn->isn_arg.number = count;
@@ -948,6 +978,7 @@ generate_FUNCREF(cctx_T *cctx, int dfunc
     isn_T	*isn;
     garray_T	*stack = &cctx->ctx_type_stack;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
 	return FAIL;
     isn->isn_arg.number = dfunc_idx;
@@ -970,6 +1001,7 @@ generate_JUMP(cctx_T *cctx, jumpwhen_T w
     isn_T	*isn;
     garray_T	*stack = &cctx->ctx_type_stack;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_JUMP)) == NULL)
 	return FAIL;
     isn->isn_arg.jump.jump_when = when;
@@ -987,6 +1019,7 @@ generate_FOR(cctx_T *cctx, int loop_idx)
     isn_T	*isn;
     garray_T	*stack = &cctx->ctx_type_stack;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_FOR)) == NULL)
 	return FAIL;
     isn->isn_arg.forloop.for_idx = loop_idx;
@@ -1012,6 +1045,7 @@ generate_BCALL(cctx_T *cctx, int func_id
     type_T	*argtypes[MAX_FUNC_ARGS];
     int		i;
 
+    RETURN_OK_IF_SKIP(cctx);
     if (check_internal_func(func_idx, argcount) == FAIL)
 	return FAIL;
 
@@ -1045,6 +1079,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufu
     int		regular_args = ufunc->uf_args.ga_len;
     int		argcount = pushed_argcount;
 
+    RETURN_OK_IF_SKIP(cctx);
     if (argcount > regular_args && !has_varargs(ufunc))
     {
 	semsg(_(e_toomanyarg), ufunc->uf_name);
@@ -1105,6 +1140,7 @@ generate_UCALL(cctx_T *cctx, char_u *nam
     isn_T	*isn;
     garray_T	*stack = &cctx->ctx_type_stack;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_UCALL)) == NULL)
 	return FAIL;
     isn->isn_arg.ufunc.cuf_name = vim_strsave(name);
@@ -1129,6 +1165,7 @@ generate_PCALL(cctx_T *cctx, int argcoun
     isn_T	*isn;
     garray_T	*stack = &cctx->ctx_type_stack;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_PCALL)) == NULL)
 	return FAIL;
     isn->isn_arg.pfunc.cpf_top = at_top;
@@ -1152,6 +1189,7 @@ generate_MEMBER(cctx_T *cctx, char_u *na
     garray_T	*stack = &cctx->ctx_type_stack;
     type_T	*type;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_MEMBER)) == NULL)
 	return FAIL;
     isn->isn_arg.string = vim_strnsave(name, (int)len);
@@ -1178,6 +1216,7 @@ generate_ECHO(cctx_T *cctx, int with_whi
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr_drop(cctx, ISN_ECHO, count)) == NULL)
 	return FAIL;
     isn->isn_arg.echo.echo_with_white = with_white;
@@ -1206,6 +1245,7 @@ generate_EXEC(cctx_T *cctx, char_u *line
 {
     isn_T	*isn;
 
+    RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_EXEC)) == NULL)
 	return FAIL;
     isn->isn_arg.string = vim_strsave(line);
@@ -2878,6 +2918,57 @@ compile_expr5(char_u **arg, cctx_T *cctx
     return OK;
 }
 
+    static exptype_T
+get_compare_type(char_u *p, int *len, int *type_is)
+{
+    exptype_T	type = EXPR_UNKNOWN;
+    int		i;
+
+    switch (p[0])
+    {
+	case '=':   if (p[1] == '=')
+			type = EXPR_EQUAL;
+		    else if (p[1] == '~')
+			type = EXPR_MATCH;
+		    break;
+	case '!':   if (p[1] == '=')
+			type = EXPR_NEQUAL;
+		    else if (p[1] == '~')
+			type = EXPR_NOMATCH;
+		    break;
+	case '>':   if (p[1] != '=')
+		    {
+			type = EXPR_GREATER;
+			*len = 1;
+		    }
+		    else
+			type = EXPR_GEQUAL;
+		    break;
+	case '<':   if (p[1] != '=')
+		    {
+			type = EXPR_SMALLER;
+			*len = 1;
+		    }
+		    else
+			type = EXPR_SEQUAL;
+		    break;
+	case 'i':   if (p[1] == 's')
+		    {
+			// "is" and "isnot"; but not a prefix of a name
+			if (p[2] == 'n' && p[3] == 'o' && p[4] == 't')
+			    *len = 5;
+			i = p[*len];
+			if (!isalnum(i) && i != '_')
+			{
+			    type = *len == 2 ? EXPR_IS : EXPR_ISNOT;
+			    *type_is = TRUE;
+			}
+		    }
+		    break;
+    }
+    return type;
+}
+
 /*
  * expr5a == expr5b
  * expr5a =~ expr5b
@@ -2901,7 +2992,6 @@ compile_expr4(char_u **arg, cctx_T *cctx
     exptype_T	type = EXPR_UNKNOWN;
     char_u	*p;
     int		len = 2;
-    int		i;
     int		type_is = FALSE;
 
     // get the first variable
@@ -2909,48 +2999,7 @@ compile_expr4(char_u **arg, cctx_T *cctx
 	return FAIL;
 
     p = skipwhite(*arg);
-    switch (p[0])
-    {
-	case '=':   if (p[1] == '=')
-			type = EXPR_EQUAL;
-		    else if (p[1] == '~')
-			type = EXPR_MATCH;
-		    break;
-	case '!':   if (p[1] == '=')
-			type = EXPR_NEQUAL;
-		    else if (p[1] == '~')
-			type = EXPR_NOMATCH;
-		    break;
-	case '>':   if (p[1] != '=')
-		    {
-			type = EXPR_GREATER;
-			len = 1;
-		    }
-		    else
-			type = EXPR_GEQUAL;
-		    break;
-	case '<':   if (p[1] != '=')
-		    {
-			type = EXPR_SMALLER;
-			len = 1;
-		    }
-		    else
-			type = EXPR_SEQUAL;
-		    break;
-	case 'i':   if (p[1] == 's')
-		    {
-			// "is" and "isnot"; but not a prefix of a name
-			if (p[2] == 'n' && p[3] == 'o' && p[4] == 't')
-			    len = 5;
-			i = p[len];
-			if (!isalnum(i) && i != '_')
-			{
-			    type = len == 2 ? EXPR_IS : EXPR_ISNOT;
-			    type_is = TRUE;
-			}
-		    }
-		    break;
-    }
+    type = get_compare_type(p, &len, &type_is);
 
     /*
      * If there is a comparative operator, use it.
@@ -3324,128 +3373,131 @@ compile_assignment(char_u *arg, exarg_T 
     if (name == NULL)
 	return NULL;
 
-    if (*arg == '&')
+    if (cctx->ctx_skip != TRUE)
     {
-	int	    cc;
-	long	    numval;
-	char_u	    *stringval = NULL;
-
-	dest = dest_option;
-	if (cmdidx == CMD_const)
-	{
-	    emsg(_(e_const_option));
-	    return NULL;
-	}
-	if (is_decl)
-	{
-	    semsg(_("E1052: Cannot declare an option: %s"), arg);
-	    goto theend;
-	}
-	p = arg;
-	p = find_option_end(&p, &opt_flags);
-	if (p == NULL)
+	if (*arg == '&')
 	{
-	    emsg(_(e_letunexp));
-	    return NULL;
-	}
-	cc = *p;
-	*p = NUL;
-	opt_type = get_option_value(arg + 1, &numval, &stringval, opt_flags);
-	*p = cc;
-	if (opt_type == -3)
-	{
-	    semsg(_(e_unknown_option), *arg);
-	    return NULL;
-	}
-	if (opt_type == -2 || opt_type == 0)
-	    type = &t_string;
-	else
-	    type = &t_number;	// both number and boolean option
-    }
-    else if (*arg == '$')
-    {
-	dest = dest_env;
-	if (is_decl)
-	{
-	    semsg(_("E1065: Cannot declare an environment variable: %s"), name);
-	    goto theend;
+	    int	    cc;
+	    long	    numval;
+	    char_u	    *stringval = NULL;
+
+	    dest = dest_option;
+	    if (cmdidx == CMD_const)
+	    {
+		emsg(_(e_const_option));
+		return NULL;
+	    }
+	    if (is_decl)
+	    {
+		semsg(_("E1052: Cannot declare an option: %s"), arg);
+		goto theend;
+	    }
+	    p = arg;
+	    p = find_option_end(&p, &opt_flags);
+	    if (p == NULL)
+	    {
+		emsg(_(e_letunexp));
+		return NULL;
+	    }
+	    cc = *p;
+	    *p = NUL;
+	    opt_type = get_option_value(arg + 1, &numval, &stringval, opt_flags);
+	    *p = cc;
+	    if (opt_type == -3)
+	    {
+		semsg(_(e_unknown_option), *arg);
+		return NULL;
+	    }
+	    if (opt_type == -2 || opt_type == 0)
+		type = &t_string;
+	    else
+		type = &t_number;	// both number and boolean option
 	}
-    }
-    else if (*arg == '@')
-    {
-	if (!valid_yank_reg(arg[1], TRUE))
-	{
-	    emsg_invreg(arg[1]);
-	    return FAIL;
-	}
-	dest = dest_reg;
-	if (is_decl)
+	else if (*arg == '$')
 	{
-	    semsg(_("E1066: Cannot declare a register: %s"), name);
-	    goto theend;
-	}
-    }
-    else if (STRNCMP(arg, "g:", 2) == 0)
-    {
-	dest = dest_global;
-	if (is_decl)
-	{
-	    semsg(_("E1016: Cannot declare a global variable: %s"), name);
-	    goto theend;
+	    dest = dest_env;
+	    if (is_decl)
+	    {
+		semsg(_("E1065: Cannot declare an environment variable: %s"), name);
+		goto theend;
+	    }
 	}
-    }
-    else if (STRNCMP(arg, "v:", 2) == 0)
-    {
-	vimvaridx = find_vim_var(name + 2);
-	if (vimvaridx < 0)
+	else if (*arg == '@')
 	{
-	    semsg(_(e_var_notfound), arg);
-	    goto theend;
-	}
-	dest = dest_vimvar;
-	if (is_decl)
-	{
-	    semsg(_("E1064: Cannot declare a v: variable: %s"), name);
-	    goto theend;
-	}
-    }
-    else
-    {
-	for (idx = 0; reserved[idx] != NULL; ++idx)
-	    if (STRCMP(reserved[idx], name) == 0)
+	    if (!valid_yank_reg(arg[1], TRUE))
 	    {
-		semsg(_("E1034: Cannot use reserved name %s"), name);
+		emsg_invreg(arg[1]);
+		return FAIL;
+	    }
+	    dest = dest_reg;
+	    if (is_decl)
+	    {
+		semsg(_("E1066: Cannot declare a register: %s"), name);
 		goto theend;
 	    }
-
-	idx = lookup_local(arg, varlen, cctx);
-	if (idx >= 0)
+	}
+	else if (STRNCMP(arg, "g:", 2) == 0)
 	{
+	    dest = dest_global;
 	    if (is_decl)
 	    {
-		semsg(_("E1017: Variable already declared: %s"), name);
+		semsg(_("E1016: Cannot declare a global variable: %s"), name);
+		goto theend;
+	    }
+	}
+	else if (STRNCMP(arg, "v:", 2) == 0)
+	{
+	    vimvaridx = find_vim_var(name + 2);
+	    if (vimvaridx < 0)
+	    {
+		semsg(_(e_var_notfound), arg);
+		goto theend;
+	    }
+	    dest = dest_vimvar;
+	    if (is_decl)
+	    {
+		semsg(_("E1064: Cannot declare a v: variable: %s"), name);
 		goto theend;
 	    }
-	    else
+	}
+	else
+	{
+	    for (idx = 0; reserved[idx] != NULL; ++idx)
+		if (STRCMP(reserved[idx], name) == 0)
+		{
+		    semsg(_("E1034: Cannot use reserved name %s"), name);
+		    goto theend;
+		}
+
+	    idx = lookup_local(arg, varlen, cctx);
+	    if (idx >= 0)
 	    {
-		lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
-		if (lvar->lv_const)
+		if (is_decl)
+		{
+		    semsg(_("E1017: Variable already declared: %s"), name);
+		    goto theend;
+		}
+		else
 		{
-		    semsg(_("E1018: Cannot assign to a constant: %s"), name);
-		    goto theend;
+		    lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
+		    if (lvar->lv_const)
+		    {
+			semsg(_("E1018: Cannot assign to a constant: %s"), name);
+			goto theend;
+		    }
 		}
 	    }
-	}
-	else if (STRNCMP(arg, "s:", 2) == 0
-		|| lookup_script(arg, varlen) == OK
-		|| find_imported(arg, varlen, cctx) != NULL)
-	{
-	    dest = dest_script;
-	    if (is_decl)
+	    else if (STRNCMP(arg, "s:", 2) == 0
+		    || lookup_script(arg, varlen) == OK
+		    || find_imported(arg, varlen, cctx) != NULL)
 	    {
-		semsg(_("E1054: Variable already declared in the script: %s"),
-									 name);
-		goto theend;
+		dest = dest_script;
+		if (is_decl)
+		{
+		    semsg(_("E1054: Variable already declared in the script: %s"),
+									     name);
+		    goto theend;
+		}
 	    }
 	}
     }
@@ -3493,7 +3545,7 @@ compile_assignment(char_u *arg, exarg_T 
     }
 
     // +=, /=, etc. require an existing variable
-    if (idx < 0 && dest == dest_local)
+    if (idx < 0 && dest == dest_local && cctx->ctx_skip != TRUE)
     {
 	if (oplen > 1 && !heredoc)
 	{
@@ -3840,8 +3892,23 @@ evaluate_const_expr7(char_u **arg, cctx_
     end_leader = *arg;
 
     /*
-     * Recognize only has() for now.
+     * Recognize only a few types of constants for now.
      */
+    if (STRNCMP("true", *arg, 4) == 0 && !ASCII_ISALNUM((*arg)[4]))
+    {
+	tv->v_type = VAR_SPECIAL;
+	tv->vval.v_number = VVAL_TRUE;
+	*arg += 4;
+	return OK;
+    }
+    if (STRNCMP("false", *arg, 5) == 0 && !ASCII_ISALNUM((*arg)[5]))
+    {
+	tv->v_type = VAR_SPECIAL;
+	tv->vval.v_number = VVAL_FALSE;
+	*arg += 5;
+	return OK;
+    }
+
     if (STRNCMP("has(", *arg, 4) != 0)
 	return FAIL;
     *arg = skipwhite(*arg + 4);
@@ -3881,6 +3948,33 @@ evaluate_const_expr7(char_u **arg, cctx_
     return OK;
 }
 
+    static int
+evaluate_const_expr4(char_u **arg, cctx_T *cctx UNUSED, typval_T *tv)
+{
+    exptype_T	type = EXPR_UNKNOWN;
+    char_u	*p;
+    int		len = 2;
+    int		type_is = FALSE;
+
+    // get the first variable
+    if (evaluate_const_expr7(arg, cctx, tv) == FAIL)
+	return FAIL;
+
+    p = skipwhite(*arg);
+    type = get_compare_type(p, &len, &type_is);
+
+    /*
+     * If there is a comparative operator, use it.
+     */
+    if (type != EXPR_UNKNOWN)
+    {
+	// TODO
+	return FAIL;
+    }
+
+    return OK;
+}
+
 static int evaluate_const_expr3(char_u **arg, cctx_T *cctx, typval_T *tv);
 
 /*
@@ -3911,7 +4005,7 @@ evaluate_const_and_or(char_u **arg, cctx
 	    tv2.v_type = VAR_UNKNOWN;
 	    tv2.v_lock = 0;
 	    if ((opchar == '|' ? evaluate_const_expr3(arg, cctx, &tv2)
-			       : evaluate_const_expr7(arg, cctx, &tv2)) == FAIL)
+			       : evaluate_const_expr4(arg, cctx, &tv2)) == FAIL)
 	    {
 		clear_tv(&tv2);
 		return FAIL;
@@ -3940,7 +4034,7 @@ evaluate_const_and_or(char_u **arg, cctx
 evaluate_const_expr3(char_u **arg, cctx_T *cctx, typval_T *tv)
 {
     // evaluate the first expression
-    if (evaluate_const_expr7(arg, cctx, tv) == FAIL)
+    if (evaluate_const_expr4(arg, cctx, tv) == FAIL)
 	return FAIL;
 
     // || and && work almost the same