Patch 8.2.0719
Problem: Vim9: more expressions can be evaluated at compile time
Solution: Recognize has('name').
Files: src/vim9compile.c, src/testdir/test_vim9_disassemble.vim,
src/testdir/test_vim9_expr.vim
*** ../vim-8.2.0718/src/vim9compile.c 2020-05-08 19:10:30.782336716 +0200
--- src/vim9compile.c 2020-05-09 15:34:32.888630958 +0200
***************
*** 136,144 ****
static char e_var_notfound[] = N_("E1001: variable not found: %s");
static char e_syntax_at[] = N_("E1002: Syntax error at %s");
! static int compile_expr1(char_u **arg, cctx_T *cctx);
! static int compile_expr2(char_u **arg, cctx_T *cctx);
! static int compile_expr3(char_u **arg, cctx_T *cctx);
static void delete_def_function_contents(dfunc_T *dfunc);
static void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
static int check_type(type_T *expected, type_T *actual, int give_msg);
--- 136,142 ----
static char e_var_notfound[] = N_("E1001: variable not found: %s");
static char e_syntax_at[] = N_("E1002: Syntax error at %s");
! static int compile_expr0(char_u **arg, cctx_T *cctx);
static void delete_def_function_contents(dfunc_T *dfunc);
static void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
static int check_type(type_T *expected, type_T *actual, int give_msg);
***************
*** 744,767 ****
}
/*
! * Generate an ISN_COMPARE* instruction with a boolean result.
*/
! static int
! generate_COMPARE(cctx_T *cctx, exptype_T exptype, int ic)
{
isntype_T isntype = ISN_DROP;
- isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
- 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.
- type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2]->tt_type;
- type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1]->tt_type;
if (type1 == VAR_UNKNOWN)
type1 = VAR_ANY;
if (type2 == VAR_UNKNOWN)
--- 742,755 ----
}
/*
! * Get the instruction to use for comparing "type1" with "type2"
! * Return ISN_DROP when failed.
*/
! static isntype_T
! get_compare_isn(exptype_T exptype, vartype_T type1, vartype_T type2)
{
isntype_T isntype = ISN_DROP;
if (type1 == VAR_UNKNOWN)
type1 = VAR_ANY;
if (type2 == VAR_UNKNOWN)
***************
*** 796,802 ****
{
semsg(_("E1037: Cannot use \"%s\" with %s"),
exptype == EXPR_IS ? "is" : "isnot" , vartype_name(type1));
! return FAIL;
}
if (isntype == ISN_DROP
|| ((exptype != EXPR_EQUAL && exptype != EXPR_NEQUAL
--- 784,790 ----
{
semsg(_("E1037: Cannot use \"%s\" with %s"),
exptype == EXPR_IS ? "is" : "isnot" , vartype_name(type1));
! return ISN_DROP;
}
if (isntype == ISN_DROP
|| ((exptype != EXPR_EQUAL && exptype != EXPR_NEQUAL
***************
*** 809,816 ****
{
semsg(_("E1072: Cannot compare %s with %s"),
vartype_name(type1), vartype_name(type2));
! return FAIL;
}
if ((isn = generate_instr(cctx, isntype)) == NULL)
return FAIL;
--- 797,829 ----
{
semsg(_("E1072: Cannot compare %s with %s"),
vartype_name(type1), vartype_name(type2));
! return ISN_DROP;
}
+ return isntype;
+ }
+
+ /*
+ * Generate an ISN_COMPARE* instruction with a boolean result.
+ */
+ static int
+ generate_COMPARE(cctx_T *cctx, exptype_T exptype, int ic)
+ {
+ isntype_T isntype;
+ isn_T *isn;
+ garray_T *stack = &cctx->ctx_type_stack;
+ 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.
+ type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2]->tt_type;
+ type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1]->tt_type;
+ isntype = get_compare_isn(exptype, type1, type2);
+ if (isntype == ISN_DROP)
+ return FAIL;
if ((isn = generate_instr(cctx, isntype)) == NULL)
return FAIL;
***************
*** 2340,2345 ****
--- 2353,2443 ----
return OK;
}
+ // Structure passed between the compile_expr* functions to keep track of
+ // constants that have been parsed but for which no code was produced yet. If
+ // possible expressions on these constants are applied at compile time. If
+ // that is not possible, the code to push the constants needs to be generated
+ // before other instructions.
+ typedef struct {
+ typval_T pp_tv[10]; // stack of ppconst constants
+ int pp_used; // active entries in pp_tv[]
+ } ppconst_T;
+
+ /*
+ * Generate a PUSH instruction for "tv".
+ * "tv" will be consumed or cleared.
+ * Nothing happens if "tv" is NULL or of type VAR_UNKNOWN;
+ */
+ static int
+ generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
+ {
+ if (tv != NULL)
+ {
+ switch (tv->v_type)
+ {
+ case VAR_UNKNOWN:
+ break;
+ case VAR_BOOL:
+ generate_PUSHBOOL(cctx, tv->vval.v_number);
+ break;
+ case VAR_SPECIAL:
+ generate_PUSHSPEC(cctx, tv->vval.v_number);
+ break;
+ case VAR_NUMBER:
+ generate_PUSHNR(cctx, tv->vval.v_number);
+ break;
+ #ifdef FEAT_FLOAT
+ case VAR_FLOAT:
+ generate_PUSHF(cctx, tv->vval.v_float);
+ break;
+ #endif
+ case VAR_BLOB:
+ generate_PUSHBLOB(cctx, tv->vval.v_blob);
+ tv->vval.v_blob = NULL;
+ break;
+ case VAR_STRING:
+ generate_PUSHS(cctx, tv->vval.v_string);
+ tv->vval.v_string = NULL;
+ break;
+ default:
+ iemsg("constant type not supported");
+ clear_tv(tv);
+ return FAIL;
+ }
+ tv->v_type = VAR_UNKNOWN;
+ }
+ return OK;
+ }
+
+ /*
+ * Generate code for any ppconst entries.
+ */
+ static int
+ generate_ppconst(cctx_T *cctx, ppconst_T *ppconst)
+ {
+ int i;
+ int ret = OK;
+
+ for (i = 0; i < ppconst->pp_used; ++i)
+ if (generate_tv_PUSH(cctx, &ppconst->pp_tv[i]) == FAIL)
+ ret = FAIL;
+ ppconst->pp_used = 0;
+ return ret;
+ }
+
+ /*
+ * Clear ppconst constants. Used when failing.
+ */
+ static void
+ clear_ppconst(ppconst_T *ppconst)
+ {
+ int i;
+
+ for (i = 0; i < ppconst->pp_used; ++i)
+ clear_tv(&ppconst->pp_tv[i]);
+ ppconst->pp_used = 0;
+ }
+
/*
* Generate an instruction to load script-local variable "name", without the
* leading "s:".
***************
*** 2526,2550 ****
}
else
{
! if ((len == 4 && STRNCMP("true", *arg, 4) == 0)
! || (len == 5 && STRNCMP("false", *arg, 5) == 0))
! res = generate_PUSHBOOL(cctx, **arg == 't'
! ? VVAL_TRUE : VVAL_FALSE);
! else
! {
! // "var" can be script-local even without using "s:" if it
! // already exists.
! if (SCRIPT_ITEM(current_sctx.sc_sid)->sn_version
! == SCRIPT_VERSION_VIM9
! || lookup_script(*arg, len) == OK)
! res = compile_load_scriptvar(cctx, name, *arg, &end,
! FALSE);
!
! // When the name starts with an uppercase letter or "x:" it
! // can be a user defined function.
! if (res == FAIL && (ASCII_ISUPPER(*name) || name[1] == ':'))
! res = generate_funcref(cctx, name);
! }
}
}
if (gen_load)
--- 2624,2641 ----
}
else
{
! // "var" can be script-local even without using "s:" if it
! // already exists.
! if (SCRIPT_ITEM(current_sctx.sc_sid)->sn_version
! == SCRIPT_VERSION_VIM9
! || lookup_script(*arg, len) == OK)
! res = compile_load_scriptvar(cctx, name, *arg, &end,
! FALSE);
!
! // When the name starts with an uppercase letter or "x:" it
! // can be a user defined function.
! if (res == FAIL && (ASCII_ISUPPER(*name) || name[1] == ':'))
! res = generate_funcref(cctx, name);
}
}
if (gen_load)
***************
*** 2588,2594 ****
return OK;
}
! if (compile_expr1(&p, cctx) == FAIL)
return FAIL;
++*argcount;
--- 2679,2685 ----
return OK;
}
! if (compile_expr0(&p, cctx) == FAIL)
return FAIL;
++*argcount;
***************
*** 2621,2627 ****
* BCALL / DCALL / UCALL
*/
static int
! compile_call(char_u **arg, size_t varlen, cctx_T *cctx, int argcount_init)
{
char_u *name = *arg;
char_u *p;
--- 2712,2723 ----
* BCALL / DCALL / UCALL
*/
static int
! compile_call(
! char_u **arg,
! size_t varlen,
! cctx_T *cctx,
! ppconst_T *ppconst,
! int argcount_init)
{
char_u *name = *arg;
char_u *p;
***************
*** 2633,2638 ****
--- 2729,2764 ----
ufunc_T *ufunc;
int res = FAIL;
+ // we can evaluate "has('name')" at compile time
+ if (varlen == 3 && STRNCMP(*arg, "has", 3) == 0)
+ {
+ char_u *s = skipwhite(*arg + varlen + 1);
+ typval_T argvars[2];
+
+ argvars[0].v_type = VAR_UNKNOWN;
+ if (*s == '"')
+ (void)get_string_tv(&s, &argvars[0], TRUE);
+ else if (*s == '\'')
+ (void)get_lit_string_tv(&s, &argvars[0], TRUE);
+ s = skipwhite(s);
+ if (*s == ')' && argvars[0].v_type == VAR_STRING)
+ {
+ typval_T *tv = &ppconst->pp_tv[ppconst->pp_used];
+
+ *arg = s + 1;
+ argvars[1].v_type = VAR_UNKNOWN;
+ tv->v_type = VAR_NUMBER;
+ tv->vval.v_number = 0;
+ f_has(argvars, tv);
+ clear_tv(&argvars[0]);
+ ++ppconst->pp_used;
+ return OK;
+ }
+ }
+
+ if (generate_ppconst(cctx, ppconst) == FAIL)
+ return FAIL;
+
if (varlen >= sizeof(namebuf))
{
semsg(_("E1011: name too long: %s"), name);
***************
*** 2791,2797 ****
p += STRLEN(p);
break;
}
! if (compile_expr1(&p, cctx) == FAIL)
break;
++count;
if (*p == ',')
--- 2917,2923 ----
p += STRLEN(p);
break;
}
! if (compile_expr0(&p, cctx) == FAIL)
break;
++count;
if (*p == ',')
***************
*** 2929,2935 ****
{
isn_T *isn;
! if (compile_expr1(arg, cctx) == FAIL)
return FAIL;
// TODO: check type is string
isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
--- 3055,3061 ----
{
isn_T *isn;
! if (compile_expr0(arg, cctx) == FAIL)
return FAIL;
// TODO: check type is string
isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
***************
*** 2974,2980 ****
*arg = skipwhite(*arg);
}
! if (compile_expr1(arg, cctx) == FAIL)
return FAIL;
++count;
--- 3100,3106 ----
*arg = skipwhite(*arg);
}
! if (compile_expr0(arg, cctx) == FAIL)
return FAIL;
++count;
***************
*** 3626,3715 ****
return OK;
}
! // Structure passed between the compile_expr* functions to keep track of
! // constants that have been parsed but for which no code was produced yet. If
! // possible expressions on these constants are applied at compile time. If
! // that is not possible, the code to push the constants needs to be generated
! // before other instructions.
! typedef struct {
! typval_T pp_tv[10]; // stack of ppconst constants
! int pp_used; // active entries in pp_tv[]
! } ppconst_T;
!
! /*
! * Generate a PUSH instruction for "tv".
! * "tv" will be consumed or cleared.
! * Nothing happens if "tv" is NULL or of type VAR_UNKNOWN;
! */
! static int
! generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
! {
! if (tv != NULL)
! {
! switch (tv->v_type)
! {
! case VAR_UNKNOWN:
! break;
! case VAR_BOOL:
! generate_PUSHBOOL(cctx, tv->vval.v_number);
! break;
! case VAR_SPECIAL:
! generate_PUSHSPEC(cctx, tv->vval.v_number);
! break;
! case VAR_NUMBER:
! generate_PUSHNR(cctx, tv->vval.v_number);
! break;
! #ifdef FEAT_FLOAT
! case VAR_FLOAT:
! generate_PUSHF(cctx, tv->vval.v_float);
! break;
! #endif
! case VAR_BLOB:
! generate_PUSHBLOB(cctx, tv->vval.v_blob);
! tv->vval.v_blob = NULL;
! break;
! case VAR_STRING:
! generate_PUSHS(cctx, tv->vval.v_string);
! tv->vval.v_string = NULL;
! break;
! default:
! iemsg("constant type not supported");
! clear_tv(tv);
! return FAIL;
! }
! tv->v_type = VAR_UNKNOWN;
! }
! return OK;
! }
!
! /*
! * Generate code for any ppconst entries.
! */
! static int
! generate_ppconst(cctx_T *cctx, ppconst_T *ppconst)
! {
! int i;
! int ret = OK;
!
! for (i = 0; i < ppconst->pp_used; ++i)
! if (generate_tv_PUSH(cctx, &ppconst->pp_tv[i]) == FAIL)
! ret = FAIL;
! ppconst->pp_used = 0;
! return ret;
! }
!
! /*
! * Clear ppconst constants. Used when failing.
! */
! static void
! clear_ppconst(ppconst_T *ppconst)
! {
! int i;
!
! for (i = 0; i < ppconst->pp_used; ++i)
! clear_tv(&ppconst->pp_tv[i]);
! ppconst->pp_used = 0;
! }
/*
* Compile code to apply '-', '+' and '!'.
--- 3752,3758 ----
return OK;
}
! static int compile_expr3(char_u **arg, cctx_T *cctx, ppconst_T *ppconst);
/*
* Compile code to apply '-', '+' and '!'.
***************
*** 3825,3831 ****
return FAIL;
}
// TODO: base value may not be the first argument
! if (compile_call(arg, p - *arg, cctx, 1) == FAIL)
return FAIL;
}
}
--- 3868,3874 ----
return FAIL;
}
// TODO: base value may not be the first argument
! if (compile_call(arg, p - *arg, cctx, ppconst, 1) == FAIL)
return FAIL;
}
}
***************
*** 3841,3847 ****
// TODO: more arguments
// TODO: dict member dict['name']
*arg = skipwhite(*arg + 1);
! if (compile_expr1(arg, cctx) == FAIL)
return FAIL;
if (**arg != ']')
--- 3884,3890 ----
// TODO: more arguments
// TODO: dict member dict['name']
*arg = skipwhite(*arg + 1);
! if (compile_expr0(arg, cctx) == FAIL)
return FAIL;
if (**arg != ']')
***************
*** 3991,3996 ****
--- 4034,4067 ----
break;
/*
+ * "true" constant
+ */
+ case 't': if (STRNCMP(*arg, "true", 4) == 0
+ && !eval_isnamec((*arg)[4]))
+ {
+ *arg += 4;
+ rettv->v_type = VAR_BOOL;
+ rettv->vval.v_number = VVAL_TRUE;
+ }
+ else
+ ret = NOTDONE;
+ break;
+
+ /*
+ * "false" constant
+ */
+ case 'f': if (STRNCMP(*arg, "false", 5) == 0
+ && !eval_isnamec((*arg)[5]))
+ {
+ *arg += 5;
+ rettv->v_type = VAR_BOOL;
+ rettv->vval.v_number = VVAL_FALSE;
+ }
+ else
+ ret = NOTDONE;
+ break;
+
+ /*
* List: [expr, expr]
*/
case '[': ret = compile_list(arg, cctx);
***************
*** 4047,4053 ****
* nested expression: (expression).
*/
case '(': *arg = skipwhite(*arg + 1);
! ret = compile_expr1(arg, cctx); // recursive!
*arg = skipwhite(*arg);
if (**arg == ')')
++*arg;
--- 4118,4124 ----
* nested expression: (expression).
*/
case '(': *arg = skipwhite(*arg + 1);
! ret = compile_expr0(arg, cctx); // recursive!
*arg = skipwhite(*arg);
if (**arg == ')')
++*arg;
***************
*** 4074,4091 ****
}
start_leader = end_leader; // don't apply again below
! // A constant expression can possibly be handled compile time, return
! // the value instead of generating code.
! ++ppconst->pp_used;
}
else if (ret == NOTDONE)
{
char_u *p;
int r;
- if (generate_ppconst(cctx, ppconst) == FAIL)
- return FAIL;
-
if (!eval_isnamec1(**arg))
{
semsg(_("E1015: Name expected: %s"), *arg);
--- 4145,4162 ----
}
start_leader = end_leader; // don't apply again below
! if (cctx->ctx_skip == TRUE)
! clear_tv(rettv);
! else
! // A constant expression can possibly be handled compile time,
! // return the value instead of generating code.
! ++ppconst->pp_used;
}
else if (ret == NOTDONE)
{
char_u *p;
int r;
if (!eval_isnamec1(**arg))
{
semsg(_("E1015: Name expected: %s"), *arg);
***************
*** 4095,4103 ****
// "name" or "name()"
p = to_name_end(*arg, TRUE);
if (*p == '(')
! r = compile_call(arg, p - *arg, cctx, 0);
else
r = compile_load(arg, p, cctx, TRUE);
if (r == FAIL)
return FAIL;
}
--- 4166,4180 ----
// "name" or "name()"
p = to_name_end(*arg, TRUE);
if (*p == '(')
! {
! r = compile_call(arg, p - *arg, cctx, ppconst, 0);
! }
else
+ {
+ if (generate_ppconst(cctx, ppconst) == FAIL)
+ return FAIL;
r = compile_load(arg, p, cctx, TRUE);
+ }
if (r == FAIL)
return FAIL;
}
***************
*** 4105,4112 ****
// Handle following "[]", ".member", etc.
// Then deal with prefixed '-', '+' and '!', if not done already.
if (compile_subscript(arg, cctx, &start_leader, end_leader,
! ppconst) == FAIL
! || compile_leader(cctx, start_leader, end_leader) == FAIL)
return FAIL;
return OK;
}
--- 4182,4198 ----
// Handle following "[]", ".member", etc.
// Then deal with prefixed '-', '+' and '!', if not done already.
if (compile_subscript(arg, cctx, &start_leader, end_leader,
! ppconst) == FAIL)
! return FAIL;
! if (ppconst->pp_used > 0)
! {
! // apply the '!', '-' and '+' before the constant
! rettv = &ppconst->pp_tv[ppconst->pp_used - 1];
! if (apply_leader(rettv, start_leader, end_leader) == FAIL)
! return FAIL;
! return OK;
! }
! if (compile_leader(cctx, start_leader, end_leader) == FAIL)
return FAIL;
return OK;
}
***************
*** 4117,4126 ****
* % number modulo
*/
static int
! compile_expr6(
! char_u **arg,
! cctx_T *cctx,
! ppconst_T *ppconst)
{
char_u *op;
int ppconst_used = ppconst->pp_used;
--- 4203,4209 ----
* % number modulo
*/
static int
! compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
{
char_u *op;
int ppconst_used = ppconst->pp_used;
***************
*** 4191,4211 ****
* .. string concatenation
*/
static int
! compile_expr5(char_u **arg, cctx_T *cctx)
{
char_u *op;
int oplen;
! ppconst_T ppconst;
! int ppconst_used = 0;
!
! CLEAR_FIELD(ppconst);
// get the first variable
! if (compile_expr6(arg, cctx, &ppconst) == FAIL)
! {
! clear_ppconst(&ppconst);
return FAIL;
- }
/*
* Repeat computing, until no "+", "-" or ".." is following.
--- 4274,4288 ----
* .. string concatenation
*/
static int
! compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
{
char_u *op;
int oplen;
! int ppconst_used = ppconst->pp_used;
// get the first variable
! if (compile_expr6(arg, cctx, ppconst) == FAIL)
return FAIL;
/*
* Repeat computing, until no "+", "-" or ".." is following.
***************
*** 4221,4227 ****
{
char_u buf[3];
- clear_ppconst(&ppconst);
vim_strncpy(buf, op, oplen);
semsg(_(e_white_both), buf);
return FAIL;
--- 4298,4303 ----
***************
*** 4229,4255 ****
*arg = skipwhite(op + oplen);
if (may_get_next_line(op + oplen, arg, cctx) == FAIL)
- {
- clear_ppconst(&ppconst);
return FAIL;
- }
// get the second expression
! if (compile_expr6(arg, cctx, &ppconst) == FAIL)
! {
! clear_ppconst(&ppconst);
return FAIL;
- }
! if (ppconst.pp_used == ppconst_used + 2
&& (*op == '.'
! ? (ppconst.pp_tv[ppconst_used].v_type == VAR_STRING
! && ppconst.pp_tv[ppconst_used + 1].v_type == VAR_STRING)
! : (ppconst.pp_tv[ppconst_used].v_type == VAR_NUMBER
! && ppconst.pp_tv[ppconst_used + 1].v_type == VAR_NUMBER)))
{
! typval_T *tv1 = &ppconst.pp_tv[ppconst_used];
! typval_T *tv2 = &ppconst.pp_tv[ppconst_used + 1];
// concat/subtract/add constant numbers
if (*op == '+')
--- 4305,4325 ----
*arg = skipwhite(op + oplen);
if (may_get_next_line(op + oplen, arg, cctx) == FAIL)
return FAIL;
// get the second expression
! if (compile_expr6(arg, cctx, ppconst) == FAIL)
return FAIL;
! if (ppconst->pp_used == ppconst_used + 2
&& (*op == '.'
! ? (ppconst->pp_tv[ppconst_used].v_type == VAR_STRING
! && ppconst->pp_tv[ppconst_used + 1].v_type == VAR_STRING)
! : (ppconst->pp_tv[ppconst_used].v_type == VAR_NUMBER
! && ppconst->pp_tv[ppconst_used + 1].v_type == VAR_NUMBER)))
{
! typval_T *tv1 = &ppconst->pp_tv[ppconst_used];
! typval_T *tv2 = &ppconst->pp_tv[ppconst_used + 1];
// concat/subtract/add constant numbers
if (*op == '+')
***************
*** 4266,4272 ****
tv1->vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1));
if (tv1->vval.v_string == NULL)
{
! clear_ppconst(&ppconst);
return FAIL;
}
mch_memmove(tv1->vval.v_string, s1, len1);
--- 4336,4342 ----
tv1->vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1));
if (tv1->vval.v_string == NULL)
{
! clear_ppconst(ppconst);
return FAIL;
}
mch_memmove(tv1->vval.v_string, s1, len1);
***************
*** 4274,4292 ****
vim_free(s1);
vim_free(s2);
}
! --ppconst.pp_used;
}
else
{
! generate_ppconst(cctx, &ppconst);
if (*op == '.')
{
if (may_generate_2STRING(-2, cctx) == FAIL
|| may_generate_2STRING(-1, cctx) == FAIL)
- {
- clear_ppconst(&ppconst);
return FAIL;
- }
generate_instr_drop(cctx, ISN_CONCAT, 1);
}
else
--- 4344,4359 ----
vim_free(s1);
vim_free(s2);
}
! --ppconst->pp_used;
}
else
{
! generate_ppconst(cctx, ppconst);
if (*op == '.')
{
if (may_generate_2STRING(-2, cctx) == FAIL
|| may_generate_2STRING(-1, cctx) == FAIL)
return FAIL;
generate_instr_drop(cctx, ISN_CONCAT, 1);
}
else
***************
*** 4294,4302 ****
}
}
- // TODO: move to caller
- generate_ppconst(cctx, &ppconst);
-
return OK;
}
--- 4361,4366 ----
***************
*** 4318,4332 ****
* COMPARE one of the compare instructions
*/
static int
! compile_expr4(char_u **arg, cctx_T *cctx)
{
exptype_T type = EXPR_UNKNOWN;
char_u *p;
int len = 2;
int type_is = FALSE;
// get the first variable
! if (compile_expr5(arg, cctx) == FAIL)
return FAIL;
p = skipwhite(*arg);
--- 4382,4397 ----
* COMPARE one of the compare instructions
*/
static int
! compile_expr4(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
{
exptype_T type = EXPR_UNKNOWN;
char_u *p;
int len = 2;
int type_is = FALSE;
+ int ppconst_used = ppconst->pp_used;
// get the first variable
! if (compile_expr5(arg, cctx, ppconst) == FAIL)
return FAIL;
p = skipwhite(*arg);
***************
*** 4369,4378 ****
if (may_get_next_line(p + len, arg, cctx) == FAIL)
return FAIL;
! if (compile_expr5(arg, cctx) == FAIL)
return FAIL;
! generate_COMPARE(cctx, type, ic);
}
return OK;
--- 4434,4467 ----
if (may_get_next_line(p + len, arg, cctx) == FAIL)
return FAIL;
! if (compile_expr5(arg, cctx, ppconst) == FAIL)
return FAIL;
! if (ppconst->pp_used == ppconst_used + 2)
! {
! typval_T * tv1 = &ppconst->pp_tv[ppconst->pp_used - 2];
! typval_T *tv2 = &ppconst->pp_tv[ppconst->pp_used - 1];
! int ret;
!
! // Both sides are a constant, compute the result now.
! // First check for a valid combination of types, this is more
! // strict than typval_compare().
! if (get_compare_isn(type, tv1->v_type, tv2->v_type) == ISN_DROP)
! ret = FAIL;
! else
! {
! ret = typval_compare(tv1, tv2, type, ic);
! tv1->v_type = VAR_BOOL;
! tv1->vval.v_number = tv1->vval.v_number
! ? VVAL_TRUE : VVAL_FALSE;
! clear_tv(tv2);
! --ppconst->pp_used;
! }
! return ret;
! }
!
! generate_ppconst(cctx, ppconst);
! return generate_COMPARE(cctx, type, ic);
}
return OK;
***************
*** 4382,4388 ****
* Compile || or &&.
*/
static int
! compile_and_or(char_u **arg, cctx_T *cctx, char *op)
{
char_u *p = skipwhite(*arg);
int opchar = *op;
--- 4471,4482 ----
* Compile || or &&.
*/
static int
! compile_and_or(
! char_u **arg,
! cctx_T *cctx,
! char *op,
! ppconst_T *ppconst,
! int ppconst_used UNUSED)
{
char_u *p = skipwhite(*arg);
int opchar = *op;
***************
*** 4404,4409 ****
--- 4498,4506 ----
return FAIL;
}
+ // TODO: use ppconst if the value is a constant
+ generate_ppconst(cctx, ppconst);
+
if (ga_grow(&end_ga, 1) == FAIL)
{
ga_clear(&end_ga);
***************
*** 4419,4432 ****
if (may_get_next_line(p + 2, arg, cctx) == FAIL)
return FAIL;
! if ((opchar == '|' ? compile_expr3(arg, cctx)
! : compile_expr4(arg, cctx)) == FAIL)
{
ga_clear(&end_ga);
return FAIL;
}
p = skipwhite(*arg);
}
// Fill in the end label in all jumps.
while (end_ga.ga_len > 0)
--- 4516,4530 ----
if (may_get_next_line(p + 2, arg, cctx) == FAIL)
return FAIL;
! if ((opchar == '|' ? compile_expr3(arg, cctx, ppconst)
! : compile_expr4(arg, cctx, ppconst)) == FAIL)
{
ga_clear(&end_ga);
return FAIL;
}
p = skipwhite(*arg);
}
+ generate_ppconst(cctx, ppconst);
// Fill in the end label in all jumps.
while (end_ga.ga_len > 0)
***************
*** 4456,4469 ****
* end:
*/
static int
! compile_expr3(char_u **arg, cctx_T *cctx)
{
// get the first variable
! if (compile_expr4(arg, cctx) == FAIL)
return FAIL;
// || and && work almost the same
! return compile_and_or(arg, cctx, "&&");
}
/*
--- 4554,4569 ----
* end:
*/
static int
! compile_expr3(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
{
+ int ppconst_used = ppconst->pp_used;
+
// get the first variable
! if (compile_expr4(arg, cctx, ppconst) == FAIL)
return FAIL;
// || and && work almost the same
! return compile_and_or(arg, cctx, "&&", ppconst, ppconst_used);
}
/*
***************
*** 4478,4491 ****
* end:
*/
static int
! compile_expr2(char_u **arg, cctx_T *cctx)
{
// eval the first expression
! if (compile_expr3(arg, cctx) == FAIL)
return FAIL;
// || and && work almost the same
! return compile_and_or(arg, cctx, "||");
}
/*
--- 4578,4593 ----
* end:
*/
static int
! compile_expr2(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
{
+ int ppconst_used = ppconst->pp_used;
+
// eval the first expression
! if (compile_expr3(arg, cctx, ppconst) == FAIL)
return FAIL;
// || and && work almost the same
! return compile_and_or(arg, cctx, "||", ppconst, ppconst_used);
}
/*
***************
*** 4500,4511 ****
* end:
*/
static int
! compile_expr1(char_u **arg, cctx_T *cctx)
{
char_u *p;
// Evaluate the first expression.
! if (compile_expr2(arg, cctx) == FAIL)
return FAIL;
p = skipwhite(*arg);
--- 4602,4614 ----
* end:
*/
static int
! compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
{
char_u *p;
+ int ppconst_used = ppconst->pp_used;
// Evaluate the first expression.
! if (compile_expr2(arg, cctx, ppconst) == FAIL)
return FAIL;
p = skipwhite(*arg);
***************
*** 4518,4523 ****
--- 4621,4629 ----
isn_T *isn;
type_T *type1;
type_T *type2;
+ int has_const_expr = FALSE;
+ int const_value = FALSE;
+ int save_skip = cctx->ctx_skip;
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(p[1]))
{
***************
*** 4525,4550 ****
return FAIL;
}
! generate_JUMP(cctx, JUMP_IF_FALSE, 0);
// evaluate the second expression; any type is accepted
*arg = skipwhite(p + 1);
if (may_get_next_line(p + 1, arg, cctx) == FAIL)
return FAIL;
!
! if (compile_expr1(arg, cctx) == FAIL)
return FAIL;
! // remember the type and drop it
! --stack->ga_len;
! type1 = ((type_T **)stack->ga_data)[stack->ga_len];
! end_idx = instr->ga_len;
! generate_JUMP(cctx, JUMP_ALWAYS, 0);
! // jump here from JUMP_IF_FALSE
! isn = ((isn_T *)instr->ga_data) + alt_idx;
! isn->isn_arg.jump.jump_where = instr->ga_len;
// Check for the ":".
p = skipwhite(*arg);
--- 4631,4674 ----
return FAIL;
}
! if (ppconst->pp_used == ppconst_used + 1)
! {
! // the condition is a constant, we know whether the ? or the :
! // expression is to be evaluated.
! has_const_expr = TRUE;
! const_value = tv2bool(&ppconst->pp_tv[ppconst_used]);
! clear_tv(&ppconst->pp_tv[ppconst_used]);
! --ppconst->pp_used;
! cctx->ctx_skip = save_skip == TRUE || !const_value;
! }
! else
! {
! generate_ppconst(cctx, ppconst);
! generate_JUMP(cctx, JUMP_IF_FALSE, 0);
! }
// evaluate the second expression; any type is accepted
*arg = skipwhite(p + 1);
if (may_get_next_line(p + 1, arg, cctx) == FAIL)
return FAIL;
! if (compile_expr1(arg, cctx, ppconst) == FAIL)
return FAIL;
! if (!has_const_expr)
! {
! generate_ppconst(cctx, ppconst);
! // remember the type and drop it
! --stack->ga_len;
! type1 = ((type_T **)stack->ga_data)[stack->ga_len];
! end_idx = instr->ga_len;
! generate_JUMP(cctx, JUMP_ALWAYS, 0);
!
! // jump here from JUMP_IF_FALSE
! isn = ((isn_T *)instr->ga_data) + alt_idx;
! isn->isn_arg.jump.jump_where = instr->ga_len;
! }
// Check for the ":".
p = skipwhite(*arg);
***************
*** 4560,4584 ****
}
// evaluate the third expression
*arg = skipwhite(p + 1);
if (may_get_next_line(p + 1, arg, cctx) == FAIL)
return FAIL;
!
! if (compile_expr1(arg, cctx) == FAIL)
return FAIL;
! // If the types differ, the result has a more generic type.
! type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1];
! common_type(type1, type2, &type2, cctx->ctx_type_list);
! // jump here from JUMP_ALWAYS
! isn = ((isn_T *)instr->ga_data) + end_idx;
! isn->isn_arg.jump.jump_where = instr->ga_len;
}
return OK;
}
/*
* compile "return [expr]"
*/
static char_u *
--- 4684,4735 ----
}
// evaluate the third expression
+ if (has_const_expr)
+ cctx->ctx_skip = save_skip == TRUE || const_value;
*arg = skipwhite(p + 1);
if (may_get_next_line(p + 1, arg, cctx) == FAIL)
return FAIL;
! if (compile_expr1(arg, cctx, ppconst) == FAIL)
return FAIL;
! if (!has_const_expr)
! {
! generate_ppconst(cctx, ppconst);
!
! // If the types differ, the result has a more generic type.
! type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1];
! common_type(type1, type2, &type2, cctx->ctx_type_list);
! // jump here from JUMP_ALWAYS
! isn = ((isn_T *)instr->ga_data) + end_idx;
! isn->isn_arg.jump.jump_where = instr->ga_len;
! }
!
! cctx->ctx_skip = save_skip;
}
return OK;
}
/*
+ * Toplevel expression.
+ */
+ static int
+ compile_expr0(char_u **arg, cctx_T *cctx)
+ {
+ ppconst_T ppconst;
+
+ CLEAR_FIELD(ppconst);
+ if (compile_expr1(arg, cctx, &ppconst) == FAIL)
+ {
+ clear_ppconst(&ppconst);
+ return FAIL;
+ }
+ if (generate_ppconst(cctx, &ppconst) == FAIL)
+ return FAIL;
+ return OK;
+ }
+
+ /*
* compile "return [expr]"
*/
static char_u *
***************
*** 4591,4597 ****
if (*p != NUL && *p != '|' && *p != '\n')
{
// compile return argument into instructions
! if (compile_expr1(&p, cctx) == FAIL)
return NULL;
stack_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
--- 4742,4748 ----
if (*p != NUL && *p != '|' && *p != '\n')
{
// compile return argument into instructions
! if (compile_expr0(&p, cctx) == FAIL)
return NULL;
stack_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
***************
*** 5075,5081 ****
--cctx->ctx_locals.ga_len;
instr_count = instr->ga_len;
p = skipwhite(p + oplen);
! r = compile_expr1(&p, cctx);
if (new_local)
++cctx->ctx_locals.ga_len;
if (r == FAIL)
--- 5226,5232 ----
--cctx->ctx_locals.ga_len;
instr_count = instr->ga_len;
p = skipwhite(p + oplen);
! r = compile_expr0(&p, cctx);
if (new_local)
++cctx->ctx_locals.ga_len;
if (r == FAIL)
***************
*** 5526,5545 ****
{
char_u *p = arg;
garray_T *instr = &cctx->ctx_instr;
scope_T *scope;
! typval_T tv;
! // compile "expr"; if we know it evaluates to FALSE skip the block
! tv.v_type = VAR_UNKNOWN;
! if (evaluate_const_expr1(&p, cctx, &tv) == OK)
! cctx->ctx_skip = tv2bool(&tv) ? FALSE : TRUE;
else
- cctx->ctx_skip = MAYBE;
- clear_tv(&tv);
- if (cctx->ctx_skip == MAYBE)
{
! p = arg;
! if (compile_expr1(&p, cctx) == FAIL)
return NULL;
}
--- 5677,5704 ----
{
char_u *p = arg;
garray_T *instr = &cctx->ctx_instr;
+ int instr_count = instr->ga_len;
scope_T *scope;
! ppconst_T ppconst;
! CLEAR_FIELD(ppconst);
! if (compile_expr1(&p, cctx, &ppconst) == FAIL)
! {
! clear_ppconst(&ppconst);
! return NULL;
! }
! if (instr->ga_len == instr_count && ppconst.pp_used == 1)
! {
! // The expression results in a constant.
! // TODO: how about nesting?
! cctx->ctx_skip = tv2bool(&ppconst.pp_tv[0]) ? FALSE : TRUE;
! clear_ppconst(&ppconst);
! }
else
{
! // Not a constant, generate instructions for the expression.
! cctx->ctx_skip = MAYBE;
! if (generate_ppconst(cctx, &ppconst) == FAIL)
return NULL;
}
***************
*** 5595,5601 ****
if (cctx->ctx_skip == MAYBE)
{
p = arg;
! if (compile_expr1(&p, cctx) == FAIL)
return NULL;
// "where" is set when ":elseif", "else" or ":endif" is found
--- 5754,5760 ----
if (cctx->ctx_skip == MAYBE)
{
p = arg;
! if (compile_expr0(&p, cctx) == FAIL)
return NULL;
// "where" is set when ":elseif", "else" or ":endif" is found
***************
*** 5754,5760 ****
// compile "expr", it remains on the stack until "endfor"
arg = p;
! if (compile_expr1(&arg, cctx) == FAIL)
{
drop_scope(cctx);
return NULL;
--- 5913,5919 ----
// compile "expr", it remains on the stack until "endfor"
arg = p;
! if (compile_expr0(&arg, cctx) == FAIL)
{
drop_scope(cctx);
return NULL;
***************
*** 5844,5850 ****
scope->se_u.se_while.ws_top_label = instr->ga_len;
// compile "expr"
! if (compile_expr1(&p, cctx) == FAIL)
return NULL;
// "while_end" is set when ":endwhile" is found
--- 6003,6009 ----
scope->se_u.se_while.ws_top_label = instr->ga_len;
// compile "expr"
! if (compile_expr0(&p, cctx) == FAIL)
return NULL;
// "while_end" is set when ":endwhile" is found
***************
*** 6219,6225 ****
{
char_u *p = skipwhite(arg);
! if (compile_expr1(&p, cctx) == FAIL)
return NULL;
if (may_generate_2STRING(-1, cctx) == FAIL)
return NULL;
--- 6378,6384 ----
{
char_u *p = skipwhite(arg);
! if (compile_expr0(&p, cctx) == FAIL)
return NULL;
if (may_generate_2STRING(-1, cctx) == FAIL)
return NULL;
***************
*** 6243,6249 ****
for (;;)
{
! if (compile_expr1(&p, cctx) == FAIL)
return NULL;
++count;
p = skipwhite(p);
--- 6402,6408 ----
for (;;)
{
! if (compile_expr0(&p, cctx) == FAIL)
return NULL;
++count;
p = skipwhite(p);
***************
*** 6305,6311 ****
++count;
}
p += 2;
! if (compile_expr1(&p, cctx) == FAIL)
return NULL;
may_generate_2STRING(-1, cctx);
++count;
--- 6464,6470 ----
++count;
}
p += 2;
! if (compile_expr0(&p, cctx) == FAIL)
return NULL;
may_generate_2STRING(-1, cctx);
++count;
***************
*** 6423,6429 ****
ufunc->uf_def_arg_idx[i] = instr->ga_len;
arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
! if (compile_expr1(&arg, &cctx) == FAIL)
goto erret;
// If no type specified use the type of the default value.
--- 6582,6588 ----
ufunc->uf_def_arg_idx[i] = instr->ga_len;
arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
! if (compile_expr0(&arg, &cctx) == FAIL)
goto erret;
// If no type specified use the type of the default value.
***************
*** 6609,6615 ****
if (ea.cmdidx == CMD_eval)
{
p = ea.cmd;
! if (compile_expr1(&p, &cctx) == FAIL)
goto erret;
// drop the return value
--- 6768,6774 ----
if (ea.cmdidx == CMD_eval)
{
p = ea.cmd;
! if (compile_expr0(&p, &cctx) == FAIL)
goto erret;
// drop the return value
*** ../vim-8.2.0718/src/testdir/test_vim9_disassemble.vim 2020-05-08
19:10:30.782336716 +0200
--- src/testdir/test_vim9_disassemble.vim 2020-05-09 15:21:35.170848668
+0200
***************
*** 814,859 ****
enddef
def Test_disassemble_compare()
- " TODO: COMPAREFUNC
let cases = [
! ['true == false', 'COMPAREBOOL =='],
! ['true != false', 'COMPAREBOOL !='],
! ['v:none == v:null', 'COMPARESPECIAL =='],
! ['v:none != v:null', 'COMPARESPECIAL !='],
!
! ['111 == 222', 'COMPARENR =='],
! ['111 != 222', 'COMPARENR !='],
! ['111 > 222', 'COMPARENR >'],
! ['111 < 222', 'COMPARENR <'],
! ['111 >= 222', 'COMPARENR >='],
! ['111 <= 222', 'COMPARENR <='],
! ['111 =~ 222', 'COMPARENR =\~'],
! ['111 !~ 222', 'COMPARENR !\~'],
!
! ['"xx" != "yy"', 'COMPARESTRING !='],
! ['"xx" > "yy"', 'COMPARESTRING >'],
! ['"xx" < "yy"', 'COMPARESTRING <'],
! ['"xx" >= "yy"', 'COMPARESTRING >='],
! ['"xx" <= "yy"', 'COMPARESTRING <='],
! ['"xx" =~ "yy"', 'COMPARESTRING =\~'],
! ['"xx" !~ "yy"', 'COMPARESTRING !\~'],
! ['"xx" is "yy"', 'COMPARESTRING is'],
! ['"xx" isnot "yy"', 'COMPARESTRING isnot'],
!
! ['0z11 == 0z22', 'COMPAREBLOB =='],
! ['0z11 != 0z22', 'COMPAREBLOB !='],
! ['0z11 is 0z22', 'COMPAREBLOB is'],
! ['0z11 isnot 0z22', 'COMPAREBLOB isnot'],
!
! ['[1,2] == [3,4]', 'COMPARELIST =='],
! ['[1,2] != [3,4]', 'COMPARELIST !='],
! ['[1,2] is [3,4]', 'COMPARELIST is'],
! ['[1,2] isnot [3,4]', 'COMPARELIST isnot'],
!
! ['#{a:1} == #{x:2}', 'COMPAREDICT =='],
! ['#{a:1} != #{x:2}', 'COMPAREDICT !='],
! ['#{a:1} is #{x:2}', 'COMPAREDICT is'],
! ['#{a:1} isnot #{x:2}', 'COMPAREDICT isnot'],
['{->33} == {->44}', 'COMPAREFUNC =='],
['{->33} != {->44}', 'COMPAREFUNC !='],
--- 814,858 ----
enddef
def Test_disassemble_compare()
let cases = [
! ['true == isFalse', 'COMPAREBOOL =='],
! ['true != isFalse', 'COMPAREBOOL !='],
! ['v:none == isNull', 'COMPARESPECIAL =='],
! ['v:none != isNull', 'COMPARESPECIAL !='],
!
! ['111 == aNumber', 'COMPARENR =='],
! ['111 != aNumber', 'COMPARENR !='],
! ['111 > aNumber', 'COMPARENR >'],
! ['111 < aNumber', 'COMPARENR <'],
! ['111 >= aNumber', 'COMPARENR >='],
! ['111 <= aNumber', 'COMPARENR <='],
! ['111 =~ aNumber', 'COMPARENR =\~'],
! ['111 !~ aNumber', 'COMPARENR !\~'],
!
! ['"xx" != aString', 'COMPARESTRING !='],
! ['"xx" > aString', 'COMPARESTRING >'],
! ['"xx" < aString', 'COMPARESTRING <'],
! ['"xx" >= aString', 'COMPARESTRING >='],
! ['"xx" <= aString', 'COMPARESTRING <='],
! ['"xx" =~ aString', 'COMPARESTRING =\~'],
! ['"xx" !~ aString', 'COMPARESTRING !\~'],
! ['"xx" is aString', 'COMPARESTRING is'],
! ['"xx" isnot aString', 'COMPARESTRING isnot'],
!
! ['0z11 == aBlob', 'COMPAREBLOB =='],
! ['0z11 != aBlob', 'COMPAREBLOB !='],
! ['0z11 is aBlob', 'COMPAREBLOB is'],
! ['0z11 isnot aBlob', 'COMPAREBLOB isnot'],
!
! ['[1, 2] == aList', 'COMPARELIST =='],
! ['[1, 2] != aList', 'COMPARELIST !='],
! ['[1, 2] is aList', 'COMPARELIST is'],
! ['[1, 2] isnot aList', 'COMPARELIST isnot'],
!
! ['#{a: 1} == aDict', 'COMPAREDICT =='],
! ['#{a: 1} != aDict', 'COMPAREDICT !='],
! ['#{a: 1} is aDict', 'COMPAREDICT is'],
! ['#{a: 1} isnot aDict', 'COMPAREDICT isnot'],
['{->33} == {->44}', 'COMPAREFUNC =='],
['{->33} != {->44}', 'COMPAREFUNC !='],
***************
*** 871,892 ****
['77 is g:xx', 'COMPAREANY is'],
['77 isnot g:xx', 'COMPAREANY isnot'],
]
if has('float')
cases->extend([
! ['1.1 == 2.2', 'COMPAREFLOAT =='],
! ['1.1 != 2.2', 'COMPAREFLOAT !='],
! ['1.1 > 2.2', 'COMPAREFLOAT >'],
! ['1.1 < 2.2', 'COMPAREFLOAT <'],
! ['1.1 >= 2.2', 'COMPAREFLOAT >='],
! ['1.1 <= 2.2', 'COMPAREFLOAT <='],
! ['1.1 =~ 2.2', 'COMPAREFLOAT =\~'],
! ['1.1 !~ 2.2', 'COMPAREFLOAT !\~'],
])
endif
let nr = 1
for case in cases
writefile(['def TestCase' .. nr .. '()',
' if ' .. case[0],
' echo 42'
' endif',
--- 870,902 ----
['77 is g:xx', 'COMPAREANY is'],
['77 isnot g:xx', 'COMPAREANY isnot'],
]
+ let floatDecl = ''
if has('float')
cases->extend([
! ['1.1 == aFloat', 'COMPAREFLOAT =='],
! ['1.1 != aFloat', 'COMPAREFLOAT !='],
! ['1.1 > aFloat', 'COMPAREFLOAT >'],
! ['1.1 < aFloat', 'COMPAREFLOAT <'],
! ['1.1 >= aFloat', 'COMPAREFLOAT >='],
! ['1.1 <= aFloat', 'COMPAREFLOAT <='],
! ['1.1 =~ aFloat', 'COMPAREFLOAT =\~'],
! ['1.1 !~ aFloat', 'COMPAREFLOAT !\~'],
])
+ floatDecl = 'let aFloat = 2.2'
endif
let nr = 1
for case in cases
+ " declare local variables to get a non-constant with the right type
writefile(['def TestCase' .. nr .. '()',
+ ' let isFalse = false',
+ ' let isNull = v:null',
+ ' let aNumber = 222',
+ ' let aString = "yy"',
+ ' let aBlob = 0z22',
+ ' let aList = [3, 4]',
+ ' let aDict = #{x: 2}',
+ floatDecl,
' if ' .. case[0],
' echo 42'
' endif',
***************
*** 896,902 ****
assert_match('TestCase' .. nr .. '.*' ..
'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
'\d \(PUSH\|FUNCREF\).*' ..
! '\d \(PUSH\|FUNCREF\|LOADG\).*' ..
'\d ' .. case[1] .. '.*' ..
'\d JUMP_IF_FALSE -> \d\+.*',
instr)
--- 906,912 ----
assert_match('TestCase' .. nr .. '.*' ..
'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
'\d \(PUSH\|FUNCREF\).*' ..
! '\d \(PUSH\|FUNCREF\|LOAD\).*' ..
'\d ' .. case[1] .. '.*' ..
'\d JUMP_IF_FALSE -> \d\+.*',
instr)
*** ../vim-8.2.0718/src/testdir/test_vim9_expr.vim 2020-05-07
22:18:58.260836097 +0200
--- src/testdir/test_vim9_expr.vim 2020-05-09 15:36:59.004186370 +0200
***************
*** 429,435 ****
call CheckDefFailure(["let x = 1 == '2'"], 'Cannot compare number with
string')
call CheckDefFailure(["let x = '1' == 2"], 'Cannot compare string with
number')
! call CheckDefFailure(["let x = 1 == RetVoid()"], 'Cannot use void value')
call CheckDefFailure(["let x = RetVoid() == 1"], 'Cannot compare void with
number')
call CheckDefFailure(["let x = true > false"], 'Cannot compare bool with
bool')
--- 429,435 ----
call CheckDefFailure(["let x = 1 == '2'"], 'Cannot compare number with
string')
call CheckDefFailure(["let x = '1' == 2"], 'Cannot compare string with
number')
! call CheckDefFailure(["let x = 1 == RetVoid()"], 'Cannot compare number
with void')
call CheckDefFailure(["let x = RetVoid() == 1"], 'Cannot compare void with
number')
call CheckDefFailure(["let x = true > false"], 'Cannot compare bool with
bool')
*** ../vim-8.2.0718/src/version.c 2020-05-09 13:06:20.224712254 +0200
--- src/version.c 2020-05-09 15:07:36.069628668 +0200
***************
*** 748,749 ****
--- 748,751 ----
{ /* Add new patch number below this line */
+ /**/
+ 719,
/**/
--
Q: How many legs does a giraffe have?
A: Eight: two in front, two behind, two on the left and two on the right
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/202005091345.049Dj4P9005393%40masaka.moolenaar.net.