mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 8.2.2936: Vim9: converting number to bool uses wrong stack offset
Problem: Vim9: converting number to bool uses wrong stack offset. (Salman Halim) Solution: Include the offset in the 2BOOL command.
This commit is contained in:
@@ -1650,11 +1650,11 @@ def Test_disassemble_invert_bool()
|
|||||||
'\d STORE $0\_s*' ..
|
'\d STORE $0\_s*' ..
|
||||||
'var invert = !flag\_s*' ..
|
'var invert = !flag\_s*' ..
|
||||||
'\d LOAD $0\_s*' ..
|
'\d LOAD $0\_s*' ..
|
||||||
'\d INVERT (!val)\_s*' ..
|
'\d INVERT -1 (!val)\_s*' ..
|
||||||
'\d STORE $1\_s*' ..
|
'\d STORE $1\_s*' ..
|
||||||
'var res = !!flag\_s*' ..
|
'var res = !!flag\_s*' ..
|
||||||
'\d LOAD $0\_s*' ..
|
'\d LOAD $0\_s*' ..
|
||||||
'\d 2BOOL (!!val)\_s*' ..
|
'\d 2BOOL -1 (!!val)\_s*' ..
|
||||||
'\d STORE $2\_s*',
|
'\d STORE $2\_s*',
|
||||||
instr)
|
instr)
|
||||||
assert_equal(true, InvertBool())
|
assert_equal(true, InvertBool())
|
||||||
|
@@ -2480,6 +2480,25 @@ def Test_expr7_dict_vim9script()
|
|||||||
endif
|
endif
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_expr7_call_2bool()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
def BrokenCall(nr: number, mode: bool, use: string): void
|
||||||
|
assert_equal(3, nr)
|
||||||
|
assert_equal(false, mode)
|
||||||
|
assert_equal('ab', use)
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def TestBrokenCall(): void
|
||||||
|
BrokenCall(3, 0, 'ab')
|
||||||
|
enddef
|
||||||
|
|
||||||
|
TestBrokenCall()
|
||||||
|
END
|
||||||
|
CheckScriptSuccess(lines)
|
||||||
|
enddef
|
||||||
|
|
||||||
let g:oneString = 'one'
|
let g:oneString = 'one'
|
||||||
|
|
||||||
def Test_expr_member()
|
def Test_expr_member()
|
||||||
|
@@ -750,6 +750,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
2936,
|
||||||
/**/
|
/**/
|
||||||
2935,
|
2935,
|
||||||
/**/
|
/**/
|
||||||
|
18
src/vim9.h
18
src/vim9.h
@@ -148,9 +148,9 @@ typedef enum {
|
|||||||
ISN_GETITEM, // push list item, isn_arg.number is the index
|
ISN_GETITEM, // push list item, isn_arg.number is the index
|
||||||
ISN_MEMBER, // dict[member]
|
ISN_MEMBER, // dict[member]
|
||||||
ISN_STRINGMEMBER, // dict.member using isn_arg.string
|
ISN_STRINGMEMBER, // dict.member using isn_arg.string
|
||||||
ISN_2BOOL, // falsy/truthy to bool, invert if isn_arg.number != 0
|
ISN_2BOOL, // falsy/truthy to bool, uses isn_arg.tobool
|
||||||
ISN_COND2BOOL, // convert value to bool
|
ISN_COND2BOOL, // convert value to bool
|
||||||
ISN_2STRING, // convert value to string at isn_arg.number on stack
|
ISN_2STRING, // convert value to string at isn_arg.tostring on stack
|
||||||
ISN_2STRING_ANY, // like ISN_2STRING but check type
|
ISN_2STRING_ANY, // like ISN_2STRING but check type
|
||||||
ISN_NEGATENR, // apply "-" to number
|
ISN_NEGATENR, // apply "-" to number
|
||||||
|
|
||||||
@@ -369,6 +369,18 @@ typedef struct {
|
|||||||
cexprref_T *cexpr_ref;
|
cexprref_T *cexpr_ref;
|
||||||
} cexpr_T;
|
} cexpr_T;
|
||||||
|
|
||||||
|
// arguments to ISN_2STRING and ISN_2STRING_ANY
|
||||||
|
typedef struct {
|
||||||
|
int offset;
|
||||||
|
int tolerant;
|
||||||
|
} tostring_T;
|
||||||
|
|
||||||
|
// arguments to ISN_2BOOL
|
||||||
|
typedef struct {
|
||||||
|
int offset;
|
||||||
|
int invert;
|
||||||
|
} tobool_T;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Instruction
|
* Instruction
|
||||||
*/
|
*/
|
||||||
@@ -414,6 +426,8 @@ struct isn_S {
|
|||||||
subs_T subs;
|
subs_T subs;
|
||||||
cexpr_T cexpr;
|
cexpr_T cexpr;
|
||||||
isn_T *instr;
|
isn_T *instr;
|
||||||
|
tostring_T tostring;
|
||||||
|
tobool_T tobool;
|
||||||
} isn_arg;
|
} isn_arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -577,9 +577,10 @@ generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type)
|
|||||||
/*
|
/*
|
||||||
* If type at "offset" isn't already VAR_STRING then generate ISN_2STRING.
|
* If type at "offset" isn't already VAR_STRING then generate ISN_2STRING.
|
||||||
* But only for simple types.
|
* But only for simple types.
|
||||||
|
* When "tolerant" is TRUE convert most types to string, e.g. a List.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
may_generate_2STRING(int offset, cctx_T *cctx)
|
may_generate_2STRING(int offset, int tolerant, cctx_T *cctx)
|
||||||
{
|
{
|
||||||
isn_T *isn;
|
isn_T *isn;
|
||||||
isntype_T isntype = ISN_2STRING;
|
isntype_T isntype = ISN_2STRING;
|
||||||
@@ -606,12 +607,20 @@ may_generate_2STRING(int offset, cctx_T *cctx)
|
|||||||
isntype = ISN_2STRING_ANY;
|
isntype = ISN_2STRING_ANY;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// conversion possible when tolerant
|
||||||
|
case VAR_LIST:
|
||||||
|
if (tolerant)
|
||||||
|
{
|
||||||
|
isntype = ISN_2STRING_ANY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// FALLTHROUGH
|
||||||
|
|
||||||
// conversion not possible
|
// conversion not possible
|
||||||
case VAR_VOID:
|
case VAR_VOID:
|
||||||
case VAR_BLOB:
|
case VAR_BLOB:
|
||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
case VAR_PARTIAL:
|
case VAR_PARTIAL:
|
||||||
case VAR_LIST:
|
|
||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
case VAR_JOB:
|
case VAR_JOB:
|
||||||
case VAR_CHANNEL:
|
case VAR_CHANNEL:
|
||||||
@@ -623,7 +632,8 @@ may_generate_2STRING(int offset, cctx_T *cctx)
|
|||||||
*type = &t_string;
|
*type = &t_string;
|
||||||
if ((isn = generate_instr(cctx, isntype)) == NULL)
|
if ((isn = generate_instr(cctx, isntype)) == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
isn->isn_arg.number = offset;
|
isn->isn_arg.tostring.offset = offset;
|
||||||
|
isn->isn_arg.tostring.tolerant = tolerant;
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@@ -886,9 +896,10 @@ generate_COMPARE(cctx_T *cctx, exprtype_T exprtype, int ic)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate an ISN_2BOOL instruction.
|
* Generate an ISN_2BOOL instruction.
|
||||||
|
* "offset" is the offset in the type stack.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
generate_2BOOL(cctx_T *cctx, int invert)
|
generate_2BOOL(cctx_T *cctx, int invert, int offset)
|
||||||
{
|
{
|
||||||
isn_T *isn;
|
isn_T *isn;
|
||||||
garray_T *stack = &cctx->ctx_type_stack;
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
@@ -896,10 +907,11 @@ generate_2BOOL(cctx_T *cctx, int invert)
|
|||||||
RETURN_OK_IF_SKIP(cctx);
|
RETURN_OK_IF_SKIP(cctx);
|
||||||
if ((isn = generate_instr(cctx, ISN_2BOOL)) == NULL)
|
if ((isn = generate_instr(cctx, ISN_2BOOL)) == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
isn->isn_arg.number = invert;
|
isn->isn_arg.tobool.invert = invert;
|
||||||
|
isn->isn_arg.tobool.offset = offset;
|
||||||
|
|
||||||
// type becomes bool
|
// type becomes bool
|
||||||
((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_bool;
|
((type_T **)stack->ga_data)[stack->ga_len + offset] = &t_bool;
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@@ -1008,7 +1020,7 @@ need_type(
|
|||||||
{
|
{
|
||||||
// Using "0", "1" or the result of an expression with "&&" or "||" as a
|
// Using "0", "1" or the result of an expression with "&&" or "||" as a
|
||||||
// boolean is OK but requires a conversion.
|
// boolean is OK but requires a conversion.
|
||||||
generate_2BOOL(cctx, FALSE);
|
generate_2BOOL(cctx, FALSE, offset);
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2782,7 +2794,7 @@ compile_member(int is_slice, cctx_T *cctx)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
*typep = &t_any;
|
*typep = &t_any;
|
||||||
}
|
}
|
||||||
if (may_generate_2STRING(-1, cctx) == FAIL)
|
if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
|
if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -3625,7 +3637,7 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
|||||||
}
|
}
|
||||||
if (isn->isn_type == ISN_PUSHS)
|
if (isn->isn_type == ISN_PUSHS)
|
||||||
key = isn->isn_arg.string;
|
key = isn->isn_arg.string;
|
||||||
else if (may_generate_2STRING(-1, cctx) == FAIL)
|
else if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
*arg = skipwhite(*arg);
|
*arg = skipwhite(*arg);
|
||||||
if (**arg != ']')
|
if (**arg != ']')
|
||||||
@@ -4026,7 +4038,7 @@ compile_leader(cctx_T *cctx, int numeric_only, char_u *start, char_u **end)
|
|||||||
invert = !invert;
|
invert = !invert;
|
||||||
--p;
|
--p;
|
||||||
}
|
}
|
||||||
if (generate_2BOOL(cctx, invert) == FAIL)
|
if (generate_2BOOL(cctx, invert, -1) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4849,8 +4861,8 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
|||||||
ppconst->pp_is_const = FALSE;
|
ppconst->pp_is_const = FALSE;
|
||||||
if (*op == '.')
|
if (*op == '.')
|
||||||
{
|
{
|
||||||
if (may_generate_2STRING(-2, cctx) == FAIL
|
if (may_generate_2STRING(-2, FALSE, cctx) == FAIL
|
||||||
|| may_generate_2STRING(-1, cctx) == FAIL)
|
|| may_generate_2STRING(-1, FALSE, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
generate_instr_drop(cctx, ISN_CONCAT, 1);
|
generate_instr_drop(cctx, ISN_CONCAT, 1);
|
||||||
}
|
}
|
||||||
@@ -6420,7 +6432,8 @@ compile_assign_unlet(
|
|||||||
emsg(e_cannot_use_range_with_dictionary);
|
emsg(e_cannot_use_range_with_dictionary);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
if (dest_type == VAR_DICT && may_generate_2STRING(-1, cctx) == FAIL)
|
if (dest_type == VAR_DICT
|
||||||
|
&& may_generate_2STRING(-1, FALSE, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
if (dest_type == VAR_LIST || dest_type == VAR_BLOB)
|
if (dest_type == VAR_LIST || dest_type == VAR_BLOB)
|
||||||
{
|
{
|
||||||
@@ -8383,7 +8396,7 @@ compile_throw(char_u *arg, cctx_T *cctx UNUSED)
|
|||||||
return NULL;
|
return NULL;
|
||||||
if (cctx->ctx_skip == SKIP_YES)
|
if (cctx->ctx_skip == SKIP_YES)
|
||||||
return p;
|
return p;
|
||||||
if (may_generate_2STRING(-1, cctx) == FAIL)
|
if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (generate_instr_drop(cctx, ISN_THROW, 1) == NULL)
|
if (generate_instr_drop(cctx, ISN_THROW, 1) == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -8618,7 +8631,7 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx)
|
|||||||
p += 2;
|
p += 2;
|
||||||
if (compile_expr0(&p, cctx) == FAIL)
|
if (compile_expr0(&p, cctx) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
may_generate_2STRING(-1, cctx);
|
may_generate_2STRING(-1, TRUE, cctx);
|
||||||
++count;
|
++count;
|
||||||
p = skipwhite(p);
|
p = skipwhite(p);
|
||||||
if (*p != '`')
|
if (*p != '`')
|
||||||
|
@@ -980,7 +980,7 @@ store_var(char_u *name, typval_T *tv)
|
|||||||
* Return FAIL if not allowed.
|
* Return FAIL if not allowed.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
do_2string(typval_T *tv, int is_2string_any)
|
do_2string(typval_T *tv, int is_2string_any, int tolerant)
|
||||||
{
|
{
|
||||||
if (tv->v_type != VAR_STRING)
|
if (tv->v_type != VAR_STRING)
|
||||||
{
|
{
|
||||||
@@ -995,6 +995,22 @@ do_2string(typval_T *tv, int is_2string_any)
|
|||||||
case VAR_NUMBER:
|
case VAR_NUMBER:
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
case VAR_BLOB: break;
|
case VAR_BLOB: break;
|
||||||
|
|
||||||
|
case VAR_LIST:
|
||||||
|
if (tolerant)
|
||||||
|
{
|
||||||
|
char_u *p;
|
||||||
|
|
||||||
|
str = typval2string(tv, TRUE);
|
||||||
|
clear_tv(tv);
|
||||||
|
tv->v_type = VAR_STRING;
|
||||||
|
tv->vval.v_string = str;
|
||||||
|
// TODO: escaping
|
||||||
|
while ((p = vim_strchr(str, '\n')) != NULL)
|
||||||
|
*p = ' ';
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
// FALLTHROUGH
|
||||||
default: to_string_error(tv->v_type);
|
default: to_string_error(tv->v_type);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
@@ -2055,7 +2071,7 @@ exec_instructions(ectx_T *ectx)
|
|||||||
{
|
{
|
||||||
dest_type = tv_dest->v_type;
|
dest_type = tv_dest->v_type;
|
||||||
if (dest_type == VAR_DICT)
|
if (dest_type == VAR_DICT)
|
||||||
status = do_2string(tv_idx, TRUE);
|
status = do_2string(tv_idx, TRUE, FALSE);
|
||||||
else if (dest_type == VAR_LIST
|
else if (dest_type == VAR_LIST
|
||||||
&& tv_idx->v_type != VAR_NUMBER)
|
&& tv_idx->v_type != VAR_NUMBER)
|
||||||
{
|
{
|
||||||
@@ -3770,15 +3786,16 @@ exec_instructions(ectx_T *ectx)
|
|||||||
int n;
|
int n;
|
||||||
int error = FALSE;
|
int error = FALSE;
|
||||||
|
|
||||||
tv = STACK_TV_BOT(-1);
|
|
||||||
if (iptr->isn_type == ISN_2BOOL)
|
if (iptr->isn_type == ISN_2BOOL)
|
||||||
{
|
{
|
||||||
|
tv = STACK_TV_BOT(iptr->isn_arg.tobool.offset);
|
||||||
n = tv2bool(tv);
|
n = tv2bool(tv);
|
||||||
if (iptr->isn_arg.number) // invert
|
if (iptr->isn_arg.tobool.invert)
|
||||||
n = !n;
|
n = !n;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
tv = STACK_TV_BOT(-1);
|
||||||
SOURCING_LNUM = iptr->isn_lnum;
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
n = tv_get_bool_chk(tv, &error);
|
n = tv_get_bool_chk(tv, &error);
|
||||||
if (error)
|
if (error)
|
||||||
@@ -3793,8 +3810,9 @@ exec_instructions(ectx_T *ectx)
|
|||||||
case ISN_2STRING:
|
case ISN_2STRING:
|
||||||
case ISN_2STRING_ANY:
|
case ISN_2STRING_ANY:
|
||||||
SOURCING_LNUM = iptr->isn_lnum;
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
if (do_2string(STACK_TV_BOT(iptr->isn_arg.number),
|
if (do_2string(STACK_TV_BOT(iptr->isn_arg.tostring.offset),
|
||||||
iptr->isn_type == ISN_2STRING_ANY) == FAIL)
|
iptr->isn_type == ISN_2STRING_ANY,
|
||||||
|
iptr->isn_arg.tostring.tolerant) == FAIL)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -5122,26 +5140,30 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ISN_COND2BOOL: smsg("%s%4d COND2BOOL", pfx, current); break;
|
case ISN_COND2BOOL: smsg("%s%4d COND2BOOL", pfx, current); break;
|
||||||
case ISN_2BOOL: if (iptr->isn_arg.number)
|
case ISN_2BOOL: if (iptr->isn_arg.tobool.invert)
|
||||||
smsg("%s%4d INVERT (!val)", pfx, current);
|
smsg("%s%4d INVERT %d (!val)", pfx, current,
|
||||||
|
iptr->isn_arg.tobool.offset);
|
||||||
else
|
else
|
||||||
smsg("%s%4d 2BOOL (!!val)", pfx, current);
|
smsg("%s%4d 2BOOL %d (!!val)", pfx, current,
|
||||||
|
iptr->isn_arg.tobool.offset);
|
||||||
break;
|
break;
|
||||||
case ISN_2STRING: smsg("%s%4d 2STRING stack[%lld]", pfx, current,
|
case ISN_2STRING: smsg("%s%4d 2STRING stack[%lld]", pfx, current,
|
||||||
(varnumber_T)(iptr->isn_arg.number));
|
(varnumber_T)(iptr->isn_arg.tostring.offset));
|
||||||
break;
|
break;
|
||||||
case ISN_2STRING_ANY: smsg("%s%4d 2STRING_ANY stack[%lld]", pfx, current,
|
case ISN_2STRING_ANY: smsg("%s%4d 2STRING_ANY stack[%lld]",
|
||||||
(varnumber_T)(iptr->isn_arg.number));
|
pfx, current,
|
||||||
|
(varnumber_T)(iptr->isn_arg.tostring.offset));
|
||||||
break;
|
break;
|
||||||
case ISN_RANGE: smsg("%s%4d RANGE %s", pfx, current, iptr->isn_arg.string);
|
case ISN_RANGE: smsg("%s%4d RANGE %s", pfx, current,
|
||||||
|
iptr->isn_arg.string);
|
||||||
break;
|
break;
|
||||||
case ISN_PUT:
|
case ISN_PUT:
|
||||||
if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE_ABOVE)
|
if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE_ABOVE)
|
||||||
smsg("%s%4d PUT %c above range",
|
smsg("%s%4d PUT %c above range",
|
||||||
pfx, current, iptr->isn_arg.put.put_regname);
|
pfx, current, iptr->isn_arg.put.put_regname);
|
||||||
else if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE)
|
else if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE)
|
||||||
smsg("%s%4d PUT %c range",
|
smsg("%s%4d PUT %c range",
|
||||||
pfx, current, iptr->isn_arg.put.put_regname);
|
pfx, current, iptr->isn_arg.put.put_regname);
|
||||||
else
|
else
|
||||||
smsg("%s%4d PUT %c %ld", pfx, current,
|
smsg("%s%4d PUT %c %ld", pfx, current,
|
||||||
iptr->isn_arg.put.put_regname,
|
iptr->isn_arg.put.put_regname,
|
||||||
|
Reference in New Issue
Block a user