]> granicus.if.org Git - vim/commitdiff
patch 8.2.0753: Vim9: expressions are evaluated in the discovery phase v8.2.0753
authorBram Moolenaar <Bram@vim.org>
Thu, 14 May 2020 20:41:15 +0000 (22:41 +0200)
committerBram Moolenaar <Bram@vim.org>
Thu, 14 May 2020 20:41:15 +0000 (22:41 +0200)
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.

12 files changed:
src/dict.c
src/eval.c
src/evalfunc.c
src/evalvars.c
src/ex_eval.c
src/list.c
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_script.vim
src/userfunc.c
src/version.c
src/vim.h
src/vim9compile.c

index 54d3110b026b64af0cdca16570e404294c1bb7cc..3824f407cbada923edd6d3ab26e0b6061f59c652 100644 (file)
@@ -791,8 +791,9 @@ get_literal_key(char_u **arg, typval_T *tv)
  * 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, int evaluate, int literal)
     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, int evaluate, int literal)
      * 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, int evaluate, int literal)
     {
        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, int evaluate, int literal)
        }
 
        *arg = skipwhite(*arg + 1);
-       if (eval1(arg, &tv, evaluate) == FAIL)  // recursive!
+       if (eval1(arg, &tv, flags) == FAIL)     // recursive!
        {
            if (evaluate)
                clear_tv(&tvkey);
index 2100d8039feb115a3f14bcf248f5dc120744af02..b3fe650ee88858ddf12640191c96c7f110432f50 100644 (file)
@@ -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 evaluate)
     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 evaluate)
     /*
      * 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 evaluate)
        }
 
        /*
-        * 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 evaluate)
        }
 
        /*
-        * 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 evaluate)
  * 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 evaluate)
     /*
      * 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 evaluate)
     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 evaluate)
         * 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 evaluate)
  * 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 evaluate)
     /*
      * 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 evaluate)
     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 evaluate)
         * 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 evaluate)
  * 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 evaluate)
     /*
      * 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 evaluate)
         * 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 *tv2)
  * 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 evaluate)
     /*
      * 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 evaluate)
            // 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 evaluate)
        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 evaluate)
 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
index 7579208359b20e3614898ab2eac368ec08306a1f..297961184a77d3c0db54bc68ff004f2744ebd173 100644 (file)
@@ -2132,7 +2132,7 @@ f_eval(typval_T *argvars, typval_T *rettv)
        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);
index e747230033330ee4a785c0efa9b4f2e0b245532b..fd652ae1096abddc2920b3fe0c262b3cc1bbe089 100644 (file)
@@ -433,7 +433,7 @@ eval_spell_expr(char_u *badword, char_u *expr)
     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, int *first)
                {
                    // 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);
        }
index 4aa265836123015f896f4bbe7236fd88b1dc4048..f7253438d7dfd2aef61a53f76b81cb0ace9f3208 100644 (file)
@@ -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);
 }
 
index 6242e5243805f7d08f198cb32b2b9b57f5befd63..b9e7e340d7275a6fb8708dfe32e13447fc9702e0 100644 (file)
@@ -1046,8 +1046,9 @@ f_join(typval_T *argvars, typval_T *rettv)
  * 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 *rettv, int evaluate, int do_error)
     *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)
        {
index 7213ee4ff781e462f8b70e308eb6a20c0a23a00a..2df28e220f68f9335f0089d6f39c6688a30f7dd2 100644 (file)
@@ -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
index e85da5a8001be245610485010c404141fedfd347..c528b8009b0aae9a3794239752e3348e96a5bd9a 100644 (file)
@@ -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
index 5a64ccb75ffe2b3b6fc53d6a765d1a505f92e569..b3c4f900a1c5094b12ea16f8497b50452064b0d5 100644 (file)
@@ -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;
index 3f67db4511390fa11a96d2f65aa6352afc56cad8..ad56b7af19d809ece01d074c837b9dd343e828e0 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    753,
 /**/
     752,
 /**/
index 9e4c61db37c3aca50d8e6aaa051b2b752fdfbd62..667e290299c43f3f0e822e6343624068b1261f18 100644 (file)
--- 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
index 99246e07dfdbffd595e75bb9923237b8a0456d83..4b294df2988a61526a332dc90b858389f799a1c8 100644 (file)
@@ -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;
     }