# HG changeset patch # User Bram Moolenaar # Date 1589489104 -7200 # Node ID c225be44692abd93633df66065de576da5eb51ed # Parent 89228c88b5c4ebe8aed38096cb03df28c8407aa9 patch 8.2.0753: Vim9: expressions are evaluated in the discovery phase Commit: https://github.com/vim/vim/commit/32e351179eacfc84f64cd5029e221582d400bb38 Author: Bram Moolenaar Date: Thu May 14 22:41:15 2020 +0200 patch 8.2.0753: Vim9: expressions are evaluated in the discovery phase Problem: Vim9: expressions are evaluated in the discovery phase. Solution: Bail out if an expression is not a constant. Require a type for declared constants. diff --git a/src/dict.c b/src/dict.c --- a/src/dict.c +++ b/src/dict.c @@ -791,8 +791,9 @@ get_literal_key(char_u **arg, typval_T * * Return OK or FAIL. Returns NOTDONE for {expr}. */ int -eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal) +eval_dict(char_u **arg, typval_T *rettv, int flags, int literal) { + int evaluate = flags & EVAL_EVALUATE; dict_T *d = NULL; typval_T tvkey; typval_T tv; @@ -800,6 +801,7 @@ eval_dict(char_u **arg, typval_T *rettv, dictitem_T *item; char_u *start = skipwhite(*arg + 1); char_u buf[NUMBUFLEN]; + int vim9script = current_sctx.sc_version == SCRIPT_VERSION_VIM9; /* * First check if it's not a curly-braces thing: {expr}. @@ -808,9 +810,9 @@ eval_dict(char_u **arg, typval_T *rettv, * first item. * But {} is an empty Dictionary. */ - if (*start != '}') + if (!vim9script && *start != '}') { - if (eval1(&start, &tv, FALSE) == FAIL) // recursive! + if (eval1(&start, &tv, 0) == FAIL) // recursive! return FAIL; if (*start == '}') return NOTDONE; @@ -830,7 +832,7 @@ eval_dict(char_u **arg, typval_T *rettv, { if ((literal ? get_literal_key(arg, &tvkey) - : eval1(arg, &tvkey, evaluate)) == FAIL) // recursive! + : eval1(arg, &tvkey, flags)) == FAIL) // recursive! goto failret; if (**arg != ':') @@ -852,7 +854,7 @@ eval_dict(char_u **arg, typval_T *rettv, } *arg = skipwhite(*arg + 1); - if (eval1(arg, &tv, evaluate) == FAIL) // recursive! + if (eval1(arg, &tv, flags) == FAIL) // recursive! { if (evaluate) clear_tv(&tvkey); diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -48,12 +48,12 @@ typedef struct } forinfo_T; static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op); -static int eval2(char_u **arg, typval_T *rettv, int evaluate); -static int eval3(char_u **arg, typval_T *rettv, int evaluate); -static int eval4(char_u **arg, typval_T *rettv, int evaluate); -static int eval5(char_u **arg, typval_T *rettv, int evaluate); -static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string); -static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string); +static int eval2(char_u **arg, typval_T *rettv, int flags); +static int eval3(char_u **arg, typval_T *rettv, int flags); +static int eval4(char_u **arg, typval_T *rettv, int flags); +static int eval5(char_u **arg, typval_T *rettv, int flags); +static int eval6(char_u **arg, typval_T *rettv, int flags, int want_string); +static int eval7(char_u **arg, typval_T *rettv, int flags, int want_string); static int eval7_leader(typval_T *rettv, char_u *start_leader, char_u **end_leaderp); static int free_unref_items(int copyID); @@ -173,7 +173,7 @@ eval_to_bool( if (skip) ++emsg_skip; - if (eval0(arg, &tv, nextcmd, !skip) == FAIL) + if (eval0(arg, &tv, nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL) *error = TRUE; else { @@ -201,7 +201,7 @@ eval1_emsg(char_u **arg, typval_T *rettv int did_emsg_before = did_emsg; int called_emsg_before = called_emsg; - ret = eval1(arg, rettv, evaluate); + ret = eval1(arg, rettv, evaluate ? EVAL_EVALUATE : 0); if (ret == FAIL) { // Report the invalid expression unless the expression evaluation has @@ -315,7 +315,7 @@ eval_to_string_skip( if (skip) ++emsg_skip; - if (eval0(arg, &tv, nextcmd, !skip) == FAIL || skip) + if (eval0(arg, &tv, nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL || skip) retval = NULL; else { @@ -338,7 +338,7 @@ skip_expr(char_u **pp) typval_T rettv; *pp = skipwhite(*pp); - return eval1(pp, &rettv, FALSE); + return eval1(pp, &rettv, 0); } /* @@ -360,7 +360,7 @@ eval_to_string( char_u numbuf[NUMBUFLEN]; #endif - if (eval0(arg, &tv, nextcmd, TRUE) == FAIL) + if (eval0(arg, &tv, nextcmd, EVAL_EVALUATE) == FAIL) retval = NULL; else { @@ -430,7 +430,7 @@ eval_to_number(char_u *expr) ++emsg_off; - if (eval1(&p, &rettv, TRUE) == FAIL) + if (eval1(&p, &rettv, EVAL_EVALUATE) == FAIL) retval = -1; else { @@ -453,7 +453,7 @@ eval_expr(char_u *arg, char_u **nextcmd) typval_T *tv; tv = ALLOC_ONE(typval_T); - if (tv != NULL && eval0(arg, tv, nextcmd, TRUE) == FAIL) + if (tv != NULL && eval0(arg, tv, nextcmd, EVAL_EVALUATE) == FAIL) VIM_CLEAR(tv); return tv; @@ -578,7 +578,7 @@ eval_foldexpr(char_u *arg, int *cp) ++sandbox; ++textwinlock; *cp = NUL; - if (eval0(arg, &tv, NULL, TRUE) == FAIL) + if (eval0(arg, &tv, NULL, EVAL_EVALUATE) == FAIL) retval = 0; else { @@ -766,7 +766,7 @@ get_lval( else { empty1 = FALSE; - if (eval1(&p, &var1, TRUE) == FAIL) // recursive! + if (eval1(&p, &var1, EVAL_EVALUATE) == FAIL) // recursive! return NULL; if (tv_get_string_chk(&var1) == NULL) { @@ -803,7 +803,8 @@ get_lval( else { lp->ll_empty2 = FALSE; - if (eval1(&p, &var2, TRUE) == FAIL) // recursive! + // recursive! + if (eval1(&p, &var2, EVAL_EVALUATE) == FAIL) { clear_tv(&var1); return NULL; @@ -1433,7 +1434,8 @@ eval_for_line( if (skip) ++emsg_skip; - if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) + if (eval0(skipwhite(expr + 2), &tv, nextcmdp, skip ? 0 : EVAL_EVALUATE) + == OK) { *errp = FALSE; if (!skip) @@ -1694,9 +1696,10 @@ eval_func( char_u *name, int name_len, typval_T *rettv, - int evaluate, + int flags, typval_T *basetv) // "expr" for "expr->name(arg)" { + int evaluate = flags & EVAL_EVALUATE; char_u *s = name; int len = name_len; partial_T *partial; @@ -1712,7 +1715,7 @@ eval_func( // Need to make a copy, in case evaluating the arguments makes // the name invalid. s = vim_strsave(s); - if (s == NULL) + if (s == NULL || (flags & EVAL_CONSTANT)) ret = FAIL; else { @@ -1761,6 +1764,7 @@ eval_func( * This calls eval1() and handles error message and nextcmd. * Put the result in "rettv" when returning OK and "evaluate" is TRUE. * Note: "rettv.v_lock" is not set. + * "flags" has EVAL_EVALUATE and similar flags. * Return OK or FAIL. */ int @@ -1768,7 +1772,7 @@ eval0( char_u *arg, typval_T *rettv, char_u **nextcmd, - int evaluate) + int flags) { int ret; char_u *p; @@ -1776,7 +1780,7 @@ eval0( int called_emsg_before = called_emsg; p = skipwhite(arg); - ret = eval1(&p, rettv, evaluate); + ret = eval1(&p, rettv, flags); if (ret == FAIL || !ends_excmd2(arg, p)) { if (ret != FAIL) @@ -1787,8 +1791,10 @@ eval0( * exception, or we already gave a more specific error. * Also check called_emsg for when using assert_fails(). */ - if (!aborting() && did_emsg == did_emsg_before - && called_emsg == called_emsg_before) + if (!aborting() + && did_emsg == did_emsg_before + && called_emsg == called_emsg_before + && (flags & EVAL_CONSTANT) == 0) semsg(_(e_invexpr2), arg); ret = FAIL; } @@ -1810,7 +1816,7 @@ eval0( * Return OK or FAIL. */ int -eval1(char_u **arg, typval_T *rettv, int evaluate) +eval1(char_u **arg, typval_T *rettv, int flags) { int result; typval_T var2; @@ -1818,13 +1824,15 @@ eval1(char_u **arg, typval_T *rettv, int /* * Get the first variable. */ - if (eval2(arg, rettv, evaluate) == FAIL) + if (eval2(arg, rettv, flags) == FAIL) return FAIL; if ((*arg)[0] == '?') { + int evaluate = flags & EVAL_EVALUATE; + result = FALSE; - if (evaluate) + if (flags & EVAL_EVALUATE) { int error = FALSE; @@ -1836,10 +1844,10 @@ eval1(char_u **arg, typval_T *rettv, int } /* - * Get the second variable. + * Get the second variable. Recursive! */ *arg = skipwhite(*arg + 1); - if (eval1(arg, rettv, evaluate && result) == FAIL) // recursive! + if (eval1(arg, rettv, result ? flags : flags & ~EVAL_EVALUATE) == FAIL) return FAIL; /* @@ -1854,10 +1862,10 @@ eval1(char_u **arg, typval_T *rettv, int } /* - * Get the third variable. + * Get the third variable. Recursive! */ *arg = skipwhite(*arg + 1); - if (eval1(arg, &var2, evaluate && !result) == FAIL) // recursive! + if (eval1(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE) == FAIL) { if (evaluate && result) clear_tv(rettv); @@ -1880,7 +1888,7 @@ eval1(char_u **arg, typval_T *rettv, int * Return OK or FAIL. */ static int -eval2(char_u **arg, typval_T *rettv, int evaluate) +eval2(char_u **arg, typval_T *rettv, int flags) { typval_T var2; long result; @@ -1890,7 +1898,7 @@ eval2(char_u **arg, typval_T *rettv, int /* * Get the first variable. */ - if (eval3(arg, rettv, evaluate) == FAIL) + if (eval3(arg, rettv, flags) == FAIL) return FAIL; /* @@ -1900,6 +1908,8 @@ eval2(char_u **arg, typval_T *rettv, int result = FALSE; while ((*arg)[0] == '|' && (*arg)[1] == '|') { + int evaluate = flags & EVAL_EVALUATE; + if (evaluate && first) { if (tv_get_number_chk(rettv, &error) != 0) @@ -1914,7 +1924,8 @@ eval2(char_u **arg, typval_T *rettv, int * Get the second variable. */ *arg = skipwhite(*arg + 2); - if (eval3(arg, &var2, evaluate && !result) == FAIL) + if (eval3(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE) + == FAIL) return FAIL; /* @@ -1948,7 +1959,7 @@ eval2(char_u **arg, typval_T *rettv, int * Return OK or FAIL. */ static int -eval3(char_u **arg, typval_T *rettv, int evaluate) +eval3(char_u **arg, typval_T *rettv, int flags) { typval_T var2; long result; @@ -1958,7 +1969,7 @@ eval3(char_u **arg, typval_T *rettv, int /* * Get the first variable. */ - if (eval4(arg, rettv, evaluate) == FAIL) + if (eval4(arg, rettv, flags) == FAIL) return FAIL; /* @@ -1968,6 +1979,8 @@ eval3(char_u **arg, typval_T *rettv, int result = TRUE; while ((*arg)[0] == '&' && (*arg)[1] == '&') { + int evaluate = flags & EVAL_EVALUATE; + if (evaluate && first) { if (tv_get_number_chk(rettv, &error) == 0) @@ -1982,7 +1995,7 @@ eval3(char_u **arg, typval_T *rettv, int * Get the second variable. */ *arg = skipwhite(*arg + 2); - if (eval4(arg, &var2, evaluate && result) == FAIL) + if (eval4(arg, &var2, result ? flags : flags & ~EVAL_EVALUATE) == FAIL) return FAIL; /* @@ -2025,7 +2038,7 @@ eval3(char_u **arg, typval_T *rettv, int * Return OK or FAIL. */ static int -eval4(char_u **arg, typval_T *rettv, int evaluate) +eval4(char_u **arg, typval_T *rettv, int flags) { typval_T var2; char_u *p; @@ -2037,7 +2050,7 @@ eval4(char_u **arg, typval_T *rettv, int /* * Get the first variable. */ - if (eval5(arg, rettv, evaluate) == FAIL) + if (eval5(arg, rettv, flags) == FAIL) return FAIL; p = *arg; @@ -2105,12 +2118,12 @@ eval4(char_u **arg, typval_T *rettv, int * Get the second variable. */ *arg = skipwhite(p + len); - if (eval5(arg, &var2, evaluate) == FAIL) + if (eval5(arg, &var2, flags) == FAIL) { clear_tv(rettv); return FAIL; } - if (evaluate) + if (flags & EVAL_EVALUATE) { int ret = typval_compare(rettv, &var2, type, ic); @@ -2172,7 +2185,7 @@ eval_addlist(typval_T *tv1, typval_T *tv * Return OK or FAIL. */ static int -eval5(char_u **arg, typval_T *rettv, int evaluate) +eval5(char_u **arg, typval_T *rettv, int flags) { typval_T var2; int op; @@ -2188,7 +2201,7 @@ eval5(char_u **arg, typval_T *rettv, int /* * Get the first variable. */ - if (eval6(arg, rettv, evaluate, FALSE) == FAIL) + if (eval6(arg, rettv, flags, FALSE) == FAIL) return FAIL; /* @@ -2217,7 +2230,7 @@ eval5(char_u **arg, typval_T *rettv, int // we know that the first operand needs to be a string or number // without evaluating the 2nd operand. So check before to avoid // side effects after an error. - if (evaluate && tv_get_string_chk(rettv) == NULL) + if ((flags & EVAL_EVALUATE) && tv_get_string_chk(rettv) == NULL) { clear_tv(rettv); return FAIL; @@ -2230,13 +2243,13 @@ eval5(char_u **arg, typval_T *rettv, int if (op == '.' && *(*arg + 1) == '.') // .. string concatenation ++*arg; *arg = skipwhite(*arg + 1); - if (eval6(arg, &var2, evaluate, op == '.') == FAIL) + if (eval6(arg, &var2, flags, op == '.') == FAIL) { clear_tv(rettv); return FAIL; } - if (evaluate) + if (flags & EVAL_EVALUATE) { /* * Compute the result. @@ -2358,7 +2371,7 @@ eval5(char_u **arg, typval_T *rettv, int eval6( char_u **arg, typval_T *rettv, - int evaluate, + int flags, int want_string) // after "." operator { typval_T var2; @@ -2373,7 +2386,7 @@ eval6( /* * Get the first variable. */ - if (eval7(arg, rettv, evaluate, want_string) == FAIL) + if (eval7(arg, rettv, flags, want_string) == FAIL) return FAIL; /* @@ -2385,7 +2398,7 @@ eval6( if (op != '*' && op != '/' && op != '%') break; - if (evaluate) + if (flags & EVAL_EVALUATE) { #ifdef FEAT_FLOAT if (rettv->v_type == VAR_FLOAT) @@ -2408,10 +2421,10 @@ eval6( * Get the second variable. */ *arg = skipwhite(*arg + 1); - if (eval7(arg, &var2, evaluate, FALSE) == FAIL) + if (eval7(arg, &var2, flags, FALSE) == FAIL) return FAIL; - if (evaluate) + if (flags & EVAL_EVALUATE) { #ifdef FEAT_FLOAT if (var2.v_type == VAR_FLOAT) @@ -2528,9 +2541,10 @@ eval6( eval7( char_u **arg, typval_T *rettv, - int evaluate, + int flags, int want_string) // after "." operator { + int evaluate = flags & EVAL_EVALUATE; int len; char_u *s; char_u *start_leader, *end_leader; @@ -2595,7 +2609,7 @@ eval7( /* * List: [expr, expr] */ - case '[': ret = get_list_tv(arg, rettv, evaluate, TRUE); + case '[': ret = get_list_tv(arg, rettv, flags, TRUE); break; /* @@ -2604,7 +2618,7 @@ eval7( case '#': if ((*arg)[1] == '{') { ++*arg; - ret = eval_dict(arg, rettv, evaluate, TRUE); + ret = eval_dict(arg, rettv, flags, TRUE); } else ret = NOTDONE; @@ -2616,7 +2630,7 @@ eval7( */ case '{': ret = get_lambda_tv(arg, rettv, evaluate); if (ret == NOTDONE) - ret = eval_dict(arg, rettv, evaluate, FALSE); + ret = eval_dict(arg, rettv, flags, FALSE); break; /* @@ -2649,7 +2663,7 @@ eval7( * nested expression: (expression). */ case '(': *arg = skipwhite(*arg + 1); - ret = eval1(arg, rettv, evaluate); // recursive! + ret = eval1(arg, rettv, flags); // recursive! if (**arg == ')') ++*arg; else if (ret == OK) @@ -2680,7 +2694,7 @@ eval7( else { if (**arg == '(') // recursive! - ret = eval_func(arg, s, len, rettv, evaluate, NULL); + ret = eval_func(arg, s, len, rettv, flags, NULL); else if (evaluate) ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE); else @@ -2697,7 +2711,7 @@ eval7( // Handle following '[', '(' and '.' for expr[expr], expr.name, // expr(expr), expr->name(expr) if (ret == OK) - ret = handle_subscript(arg, rettv, evaluate, TRUE, + ret = handle_subscript(arg, rettv, flags, TRUE, start_leader, &end_leader); /* @@ -2919,7 +2933,8 @@ eval_method( ret = FAIL; } else - ret = eval_func(arg, name, len, rettv, evaluate, &base); + ret = eval_func(arg, name, len, rettv, + evaluate ? EVAL_EVALUATE : 0, &base); } // Clear the funcref afterwards, so that deleting it while @@ -2939,9 +2954,10 @@ eval_method( eval_index( char_u **arg, typval_T *rettv, - int evaluate, + int flags, int verbose) // give error messages { + int evaluate = flags & EVAL_EVALUATE; int empty1 = FALSE, empty2 = FALSE; typval_T var1, var2; long i; @@ -3010,7 +3026,7 @@ eval_index( *arg = skipwhite(*arg + 1); if (**arg == ':') empty1 = TRUE; - else if (eval1(arg, &var1, evaluate) == FAIL) // recursive! + else if (eval1(arg, &var1, flags) == FAIL) // recursive! return FAIL; else if (evaluate && tv_get_string_chk(&var1) == NULL) { @@ -3028,7 +3044,7 @@ eval_index( *arg = skipwhite(*arg + 1); if (**arg == ']') empty2 = TRUE; - else if (eval1(arg, &var2, evaluate) == FAIL) // recursive! + else if (eval1(arg, &var2, flags) == FAIL) // recursive! { if (!empty1) clear_tv(&var1); @@ -5310,11 +5326,12 @@ eval_isnamec1(int c) handle_subscript( char_u **arg, typval_T *rettv, - int evaluate, // do more than finding the end + int flags, // do more than finding the end int verbose, // give error messages char_u *start_leader, // start of '!' and '-' prefixes char_u **end_leaderp) // end of '!' and '-' prefixes { + int evaluate = flags & EVAL_EVALUATE; int ret = OK; dict_T *selfdict = NULL; @@ -5374,7 +5391,7 @@ handle_subscript( } else selfdict = NULL; - if (eval_index(arg, rettv, evaluate, verbose) == FAIL) + if (eval_index(arg, rettv, flags, verbose) == FAIL) { clear_tv(rettv); ret = FAIL; @@ -6108,7 +6125,7 @@ ex_echo(exarg_T *eap) need_clr_eos = needclr; p = arg; - if (eval1(&arg, &rettv, !eap->skip) == FAIL) + if (eval1(&arg, &rettv, eap->skip ? 0 : EVAL_EVALUATE) == FAIL) { /* * Report the invalid expression unless the expression evaluation diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -2132,7 +2132,7 @@ f_eval(typval_T *argvars, typval_T *rett s = skipwhite(s); p = s; - if (s == NULL || eval1(&s, rettv, TRUE) == FAIL) + if (s == NULL || eval1(&s, rettv, EVAL_EVALUATE) == FAIL) { if (p != NULL && !aborting()) semsg(_(e_invexpr2), p); diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -433,7 +433,7 @@ eval_spell_expr(char_u *badword, char_u if (p_verbose == 0) ++emsg_off; - if (eval1(&p, &rettv, TRUE) == OK) + if (eval1(&p, &rettv, EVAL_EVALUATE) == OK) { if (rettv.v_type != VAR_LIST) clear_tv(&rettv); @@ -701,11 +701,14 @@ ex_const(exarg_T *eap) } /* - * When "redefine" is TRUE the command will be executed again, redefining the - * variable is OK then. + * When "discovery" is TRUE the ":let" or ":const" is encountered during the + * discovery phase of vim9script: + * - The command will be executed again, redefining the variable is OK then. + * - The expresion argument must be a constant. + * - If no constant expression a type must be specified. */ void -ex_let_const(exarg_T *eap, int redefine) +ex_let_const(exarg_T *eap, int discovery) { char_u *arg = eap->arg; char_u *expr = NULL; @@ -717,13 +720,14 @@ ex_let_const(exarg_T *eap, int redefine) char_u *argend; int first = TRUE; int concat; + int has_assign; int flags = eap->cmdidx == CMD_const ? LET_IS_CONST : 0; // detect Vim9 assignment without ":let" or ":const" if (eap->arg == eap->cmd) flags |= LET_NO_COMMAND; - if (redefine) - flags |= LET_REDEFINE; + if (discovery) + flags |= LET_DISCOVERY; argend = skip_var_list(arg, TRUE, &var_count, &semicolon); if (argend == NULL) @@ -734,8 +738,9 @@ ex_let_const(exarg_T *eap, int redefine) concat = expr[0] == '.' && ((expr[1] == '=' && current_sctx.sc_version < 2) || (expr[1] == '.' && expr[2] == '=')); - if (*expr != '=' && !((vim_strchr((char_u *)"+-*/%", *expr) != NULL - && expr[1] == '=') || concat)) + has_assign = *expr == '=' || (vim_strchr((char_u *)"+-*/%", *expr) != NULL + && expr[1] == '='); + if (!has_assign && !concat && !discovery) { // ":let" without "=": list variables if (*arg == '[') @@ -779,32 +784,45 @@ ex_let_const(exarg_T *eap, int redefine) } else { - op[0] = '='; - op[1] = NUL; - if (*expr != '=') + int eval_flags; + int save_called_emsg = called_emsg; + + rettv.v_type = VAR_UNKNOWN; + i = FAIL; + if (has_assign || concat) { - if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL) + op[0] = '='; + op[1] = NUL; + if (*expr != '=') { - op[0] = *expr; // +=, -=, *=, /=, %= or .= - if (expr[0] == '.' && expr[1] == '.') // ..= - ++expr; + if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL) + { + op[0] = *expr; // +=, -=, *=, /=, %= or .= + if (expr[0] == '.' && expr[1] == '.') // ..= + ++expr; + } + expr = skipwhite(expr + 2); } - expr = skipwhite(expr + 2); + else + expr = skipwhite(expr + 1); + + if (eap->skip) + ++emsg_skip; + eval_flags = eap->skip ? 0 : EVAL_EVALUATE; + if (discovery) + eval_flags |= EVAL_CONSTANT; + i = eval0(expr, &rettv, &eap->nextcmd, eval_flags); } - else - expr = skipwhite(expr + 1); - - if (eap->skip) - ++emsg_skip; - i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); if (eap->skip) { if (i != FAIL) clear_tv(&rettv); --emsg_skip; } - else if (i != FAIL) + else if (i != FAIL || (discovery && save_called_emsg == called_emsg)) { + // In Vim9 script discovery "let v: bool = Func()" fails but is + // still a valid declaration. (void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count, flags, op); clear_tv(&rettv); @@ -1112,7 +1130,7 @@ list_arg_vars(exarg_T *eap, char_u *arg, { // handle d.key, l[idx], f(expr) arg_subsc = arg; - if (handle_subscript(&arg, &tv, TRUE, TRUE, + if (handle_subscript(&arg, &tv, EVAL_EVALUATE, TRUE, name, &name) == FAIL) error = TRUE; else @@ -1353,7 +1371,12 @@ ex_let_one( lval_T lv; p = get_lval(arg, tv, &lv, FALSE, FALSE, 0, FNE_CHECK_START); - if (p != NULL && lv.ll_name != NULL) + if ((flags & LET_DISCOVERY) && tv->v_type == VAR_UNKNOWN + && lv.ll_type == NULL) + { + semsg(_("E1091: type missing for %s"), arg); + } + else if (p != NULL && lv.ll_name != NULL) { if (endchars != NULL && vim_strchr(endchars, *skipwhite(lv.ll_name_end)) == NULL) @@ -2981,7 +3004,7 @@ set_var_const( if (flags & LET_IS_CONST) di->di_tv.v_lock |= VAR_LOCKED; - if (flags & LET_REDEFINE) + if (flags & LET_DISCOVERY) di->di_flags |= DI_FLAGS_RELOAD; } @@ -3288,7 +3311,8 @@ var_exists(char_u *var) if (n) { // handle d.key, l[idx], f(expr) - n = (handle_subscript(&var, &tv, TRUE, FALSE, name, &name) == OK); + n = (handle_subscript(&var, &tv, EVAL_EVALUATE, + FALSE, name, &name) == OK); if (n) clear_tv(&tv); } diff --git a/src/ex_eval.c b/src/ex_eval.c --- a/src/ex_eval.c +++ b/src/ex_eval.c @@ -879,7 +879,8 @@ ex_eval(exarg_T *eap) { typval_T tv; - if (eval0(eap->arg, &tv, &eap->nextcmd, !eap->skip) == OK) + if (eval0(eap->arg, &tv, &eap->nextcmd, eap->skip ? 0 : EVAL_EVALUATE) + == OK) clear_tv(&tv); } diff --git a/src/list.c b/src/list.c --- a/src/list.c +++ b/src/list.c @@ -1046,8 +1046,9 @@ f_join(typval_T *argvars, typval_T *rett * Return OK or FAIL. */ int -get_list_tv(char_u **arg, typval_T *rettv, int evaluate, int do_error) +get_list_tv(char_u **arg, typval_T *rettv, int flags, int do_error) { + int evaluate = flags & EVAL_EVALUATE; list_T *l = NULL; typval_T tv; listitem_T *item; @@ -1062,7 +1063,7 @@ get_list_tv(char_u **arg, typval_T *rett *arg = skipwhite(*arg + 1); while (**arg != ']' && **arg != NUL) { - if (eval1(arg, &tv, evaluate) == FAIL) // recursive! + if (eval1(arg, &tv, flags) == FAIL) // recursive! goto failret; if (evaluate) { diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -1054,7 +1054,7 @@ def Test_vim9script_forward_func() def FuncTwo(): string return 'two' enddef - let g:res_FuncOne = execute('disass FuncOne') + let g:res_FuncOne: string = execute('disass FuncOne') END writefile(lines, 'Xdisassemble') source Xdisassemble diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -494,7 +494,7 @@ let s:export_script_lines =<< trim END def Concat(arg: string): string return name .. arg enddef - let g:result = Concat('bie') + let g:result: string = Concat('bie') let g:localname = name export const CONST = 1234 @@ -1633,7 +1633,7 @@ def Test_vim9_comment_not_compiled() CheckScriptFailure([ 'vim9script', 'let g:var = 123', - 'unlet g:var# comment', + 'unlet g:var# comment1', ], 'E108:') CheckScriptFailure([ @@ -1643,7 +1643,7 @@ def Test_vim9_comment_not_compiled() CheckScriptSuccess([ 'vim9script', - 'if 1 # comment', + 'if 1 # comment2', ' echo "yes"', 'elseif 2 #comment', ' echo "no"', @@ -1652,14 +1652,14 @@ def Test_vim9_comment_not_compiled() CheckScriptFailure([ 'vim9script', - 'if 1# comment', + 'if 1# comment3', ' echo "yes"', 'endif', ], 'E15:') CheckScriptFailure([ 'vim9script', - 'if 0 # comment', + 'if 0 # comment4', ' echo "yes"', 'elseif 2#comment', ' echo "no"', @@ -1668,23 +1668,18 @@ def Test_vim9_comment_not_compiled() CheckScriptSuccess([ 'vim9script', - 'let # comment', + 'let v = 1 # comment5', ]) CheckScriptFailure([ 'vim9script', - 'let# comment', - ], 'E121:') - - CheckScriptSuccess([ - 'vim9script', - 'let v:version # comment', - ]) + 'let v = 1# comment6', + ], 'E15:') CheckScriptFailure([ 'vim9script', - 'let v:version# comment', - ], 'E121:') + 'let v:version', + ], 'E1091:') CheckScriptSuccess([ 'vim9script', @@ -1722,6 +1717,41 @@ def Test_finish() delete('Xfinished') enddef +def Test_let_func_call() + let lines =<< trim END + vim9script + func GetValue() + if exists('g:count') + let g:count += 1 + else + let g:count = 1 + endif + return 'this' + endfunc + let val: string = GetValue() + END + writefile(lines, 'Xfinished') + source Xfinished + assert_equal(1, g:count) + + unlet g:count + delete('Xfinished') +enddef + +def Test_let_missing_type() + let lines =<< trim END + vim9script + func GetValue() + return 'this' + endfunc + let val = GetValue() + END + writefile(lines, 'Xfinished') + assert_fails('source Xfinished', 'E1091:') + + delete('Xfinished') +enddef + " Keep this last, it messes up highlighting. def Test_substitute_cmd() new diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -239,7 +239,7 @@ get_function_args( whitep = p; p = skipwhite(p); expr = p; - if (eval1(&p, &rettv, FALSE) != FAIL) + if (eval1(&p, &rettv, 0) != FAIL) { if (ga_grow(default_args, 1) == FAIL) goto err_ret; @@ -572,7 +572,8 @@ get_func_tv( argp = skipwhite(argp + 1); // skip the '(' or ',' if (*argp == ')' || *argp == ',' || *argp == NUL) break; - if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) + if (eval1(&argp, &argvars[argcount], + funcexe->evaluate ? EVAL_EVALUATE : 0) == FAIL) { ret = FAIL; break; @@ -1223,7 +1224,7 @@ call_user_func( default_expr = ((char_u **)(fp->uf_def_args.ga_data)) [ai + fp->uf_def_args.ga_len]; - if (eval1(&default_expr, &def_rettv, TRUE) == FAIL) + if (eval1(&default_expr, &def_rettv, EVAL_EVALUATE) == FAIL) { default_arg_err = 1; break; @@ -1368,7 +1369,7 @@ call_user_func( // A Lambda always has the command "return {expr}". It is much faster // to evaluate {expr} directly. ++ex_nesting_level; - (void)eval1(&p, rettv, TRUE); + (void)eval1(&p, rettv, EVAL_EVALUATE); --ex_nesting_level; } else @@ -3623,7 +3624,8 @@ ex_return(exarg_T *eap) eap->nextcmd = NULL; if ((*arg != NUL && *arg != '|' && *arg != '\n') - && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) + && eval0(arg, &rettv, &eap->nextcmd, eap->skip ? 0 : EVAL_EVALUATE) + != FAIL) { if (!eap->skip) returning = do_return(eap, FALSE, TRUE, &rettv); @@ -3680,7 +3682,7 @@ ex_call(exarg_T *eap) // instead to skip to any following command, e.g. for: // :if 0 | call dict.foo().bar() | endif ++emsg_skip; - if (eval0(eap->arg, &rettv, &eap->nextcmd, FALSE) != FAIL) + if (eval0(eap->arg, &rettv, &eap->nextcmd, 0) != FAIL) clear_tv(&rettv); --emsg_skip; return; @@ -3768,8 +3770,8 @@ ex_call(exarg_T *eap) dbg_check_breakpoint(eap); // Handle a function returning a Funcref, Dictionary or List. - if (handle_subscript(&arg, &rettv, !eap->skip, TRUE, - name, &name) == FAIL) + if (handle_subscript(&arg, &rettv, eap->skip ? 0 : EVAL_EVALUATE, + TRUE, name, &name) == FAIL) { failed = TRUE; break; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 753, +/**/ 752, /**/ 751, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -2133,7 +2133,7 @@ typedef enum { // Flags for assignment functions. #define LET_IS_CONST 1 // ":const" #define LET_NO_COMMAND 2 // "var = expr" without ":let" or ":const" -#define LET_REDEFINE 4 // variable can be redefined later +#define LET_DISCOVERY 4 // discovery phase: variable can be redefined later #include "ex_cmds.h" // Ex command defines #include "spell.h" // spell checking stuff @@ -2662,4 +2662,8 @@ long elapsed(DWORD start_tick); #define REPTERM_SPECIAL 4 #define REPTERM_NO_SIMPLIFY 8 +// Flags for expression evaluation. +#define EVAL_EVALUATE 1 // when missing don't actually evaluate +#define EVAL_CONSTANT 2 // when not a constant return FAIL + #endif // VIM__H diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2869,14 +2869,14 @@ to_name_const_end(char_u *arg) { // Can be "[1, 2, 3]->Func()". - if (get_list_tv(&p, &rettv, FALSE, FALSE) == FAIL) + if (get_list_tv(&p, &rettv, 0, FALSE) == FAIL) p = arg; } else if (p == arg && *arg == '#' && arg[1] == '{') { // Can be "#{a: 1}->Func()". ++p; - if (eval_dict(&p, &rettv, FALSE, TRUE) == FAIL) + if (eval_dict(&p, &rettv, 0, TRUE) == FAIL) p = arg; } else if (p == arg && *arg == '{') @@ -2886,7 +2886,7 @@ to_name_const_end(char_u *arg) // Can be "{x -> ret}()". // Can be "{'a': 1}->Func()". if (ret == NOTDONE) - ret = eval_dict(&p, &rettv, FALSE, FALSE); + ret = eval_dict(&p, &rettv, 0, FALSE); if (ret != OK) p = arg; }