mirror of
https://github.com/vim/vim.git
synced 2025-08-31 20:53:42 -04:00
patch 8.2.4526: Vim9: cannot set variables to a null value
Problem: Vim9: cannot set variables to a null value. Solution: Add null_list, null_job, etc.
This commit is contained in:
parent
08238045e7
commit
8acb9cc620
@ -94,8 +94,20 @@ script and `:def` functions; details are below:
|
||||
def CallMe(count: number, message: string): bool
|
||||
- Call functions without `:call`: >
|
||||
writefile(['done'], 'file.txt')
|
||||
- You cannot use old Ex commands `:xit`, `:t`, `:k`, `:append`, `:change`,
|
||||
`:insert`, `:open`, and `:s` or `:d` with only flags.
|
||||
- You cannot use old Ex commands:
|
||||
`:Print`
|
||||
`:append`
|
||||
`:change`
|
||||
`:d` directly followed by 'd' or 'p'.
|
||||
`:insert`
|
||||
`:k`
|
||||
`:mode`
|
||||
`:open`
|
||||
`:s` with only flags
|
||||
`:t`
|
||||
`:xit`
|
||||
- Some commands, especially those used for flow control, cannot be shortened.
|
||||
E.g., `:throw` cannot be written as `:th`. *E839*
|
||||
- You cannot use curly-braces names.
|
||||
- A range before a command must be prefixed with a colon: >
|
||||
:%s/this/that
|
||||
@ -305,7 +317,7 @@ function, the function does not need to be defined more than once: >
|
||||
|
||||
|
||||
Variable declarations with :var, :final and :const ~
|
||||
*vim9-declaration* *:var*
|
||||
*vim9-declaration* *:var* *E1079*
|
||||
*E1017* *E1020* *E1054* *E1087* *E1108* *E1124*
|
||||
Local variables need to be declared with `:var`. Local constants need to be
|
||||
declared with `:final` or `:const`. We refer to both as "variables" in this
|
||||
@ -375,6 +387,9 @@ In Vim9 script `:let` cannot be used. An existing variable is assigned to
|
||||
without any command. The same for global, window, tab, buffer and Vim
|
||||
variables, because they are not really declared. Those can also be deleted
|
||||
with `:unlet`.
|
||||
*E1065*
|
||||
You cannot use `:va` to declare a variable, it must be written with the full
|
||||
name `:var`. Just to make sure it is easy to read.
|
||||
*E1178*
|
||||
`:lockvar` does not work on local variables. Use `:const` and `:final`
|
||||
instead.
|
||||
@ -952,10 +967,37 @@ always converted to string: >
|
||||
Simple types are Number, Float, Special and Bool. For other types |string()|
|
||||
should be used.
|
||||
*false* *true* *null* *E1034*
|
||||
In Vim9 script one can use "true" for v:true, "false" for v:false and "null"
|
||||
for v:null. When converting a boolean to a string "false" and "true" are
|
||||
used, not "v:false" and "v:true" like in legacy script. "v:none" is not
|
||||
changed, it is only used in JSON and has no equivalent in other languages.
|
||||
In Vim9 script one can use the following predefined values: >
|
||||
true
|
||||
false
|
||||
null
|
||||
null_blob
|
||||
null_channel
|
||||
null_dict
|
||||
null_function
|
||||
null_job
|
||||
null_list
|
||||
null_partial
|
||||
null_string
|
||||
`true` is the same as `v:true`, `false` the same as `v:false`, `null` the same
|
||||
as `v:null`.
|
||||
|
||||
While `null` has the type "special", the other "null_" types have the type
|
||||
indicated by their name. Quite often a null value is handled the same as an
|
||||
empty value, but not always. The values can be useful to clear a script-local
|
||||
variable, since they cannot be deleted with `:unlet`. E.g.: >
|
||||
var theJob = job_start(...)
|
||||
# let the job do its work
|
||||
theJob = null_job
|
||||
|
||||
The values can also be useful as the default value for an argument: >
|
||||
def MyFunc(b: blob = null_blob)
|
||||
if b == null_blob
|
||||
# b argument was not given
|
||||
|
||||
When converting a boolean to a string `false` and `true` are used, not
|
||||
`v:false` and `v:true` like in legacy script. `v:none` has no `none`
|
||||
replacement, it has no equivalent in other languages.
|
||||
|
||||
Indexing a string with [idx] or taking a slice with [idx : idx] uses character
|
||||
indexes instead of byte indexes. Composing characters are included.
|
||||
|
120
src/eval.c
120
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);
|
||||
}
|
||||
@ -3482,6 +3483,100 @@ eval_leader(char_u **arg, int vim9)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
@ -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,
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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 []
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -750,6 +750,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
4526,
|
||||
/**/
|
||||
4525,
|
||||
/**/
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user