Mercurial > vim
comparison src/vim9compile.c @ 22596:107eae953b87 v8.2.1846
patch 8.2.1846: Vim9: block variables are not found in compiled function
Commit: https://github.com/vim/vim/commit/fbbcd00367e1a4fafd047d42ffce0e5dce88925c
Author: Bram Moolenaar <Bram@vim.org>
Date: Thu Oct 15 12:46:44 2020 +0200
patch 8.2.1846: Vim9: block variables are not found in compiled function
Problem: Vim9: variables declared in a local block are not found in
when a function is compiled.
Solution: Look for script variables in sn_all_vars.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Thu, 15 Oct 2020 13:00:05 +0200 |
parents | c271498e03b2 |
children | c7ef64b85e9b |
comparison
equal
deleted
inserted
replaced
22595:a42172375311 | 22596:107eae953b87 |
---|---|
193 * Returns the argument type in "type" | 193 * Returns the argument type in "type" |
194 * Sets "gen_load_outer" to TRUE if found in outer scope. | 194 * Sets "gen_load_outer" to TRUE if found in outer scope. |
195 * Returns OK when found, FAIL otherwise. | 195 * Returns OK when found, FAIL otherwise. |
196 */ | 196 */ |
197 static int | 197 static int |
198 lookup_arg( | 198 arg_exists( |
199 char_u *name, | 199 char_u *name, |
200 size_t len, | 200 size_t len, |
201 int *idxp, | 201 int *idxp, |
202 type_T **type, | 202 type_T **type, |
203 int *gen_load_outer, | 203 int *gen_load_outer, |
245 } | 245 } |
246 | 246 |
247 if (cctx->ctx_outer != NULL) | 247 if (cctx->ctx_outer != NULL) |
248 { | 248 { |
249 // Lookup the name for an argument of the outer function. | 249 // Lookup the name for an argument of the outer function. |
250 if (lookup_arg(name, len, idxp, type, gen_load_outer, cctx->ctx_outer) | 250 if (arg_exists(name, len, idxp, type, gen_load_outer, cctx->ctx_outer) |
251 == OK) | 251 == OK) |
252 { | 252 { |
253 *gen_load_outer = TRUE; | 253 *gen_load_outer = TRUE; |
254 return OK; | 254 return OK; |
255 } | 255 } |
257 | 257 |
258 return FAIL; | 258 return FAIL; |
259 } | 259 } |
260 | 260 |
261 /* | 261 /* |
262 * Lookup a script-local variable in the current script, possibly defined in a | |
263 * block that contains the function "cctx->ctx_ufunc". | |
264 * "cctx" is NULL at the script level. | |
265 * if "len" is <= 0 "name" must be NUL terminated. | |
266 * Return NULL when not found. | |
267 */ | |
268 static sallvar_T * | |
269 find_script_var(char_u *name, size_t len, cctx_T *cctx) | |
270 { | |
271 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); | |
272 hashitem_T *hi; | |
273 int cc; | |
274 sallvar_T *sav; | |
275 ufunc_T *ufunc; | |
276 | |
277 // Find the list of all script variables with the right name. | |
278 if (len > 0) | |
279 { | |
280 cc = name[len]; | |
281 name[len] = NUL; | |
282 } | |
283 hi = hash_find(&si->sn_all_vars.dv_hashtab, name); | |
284 if (len > 0) | |
285 name[len] = cc; | |
286 if (HASHITEM_EMPTY(hi)) | |
287 return NULL; | |
288 | |
289 sav = HI2SAV(hi); | |
290 if (sav->sav_block_id == 0 || cctx == NULL) | |
291 // variable defined in the script scope or not in a function. | |
292 return sav; | |
293 | |
294 // Go over the variables with this name and find one that was visible | |
295 // from the function. | |
296 ufunc = cctx->ctx_ufunc; | |
297 while (sav != NULL) | |
298 { | |
299 int idx; | |
300 | |
301 // Go over the blocks that this function was defined in. If the | |
302 // variable block ID matches it was visible to the function. | |
303 for (idx = 0; idx < ufunc->uf_block_depth; ++idx) | |
304 if (ufunc->uf_block_ids[idx] == sav->sav_block_id) | |
305 return sav; | |
306 sav = sav->sav_next; | |
307 } | |
308 | |
309 return NULL; | |
310 } | |
311 | |
312 /* | |
262 * Returnd TRUE if the script context is Vim9 script. | 313 * Returnd TRUE if the script context is Vim9 script. |
263 */ | 314 */ |
264 static int | 315 static int |
265 script_is_vim9() | 316 script_is_vim9() |
266 { | 317 { |
267 return SCRIPT_ITEM(current_sctx.sc_sid)->sn_version == SCRIPT_VERSION_VIM9; | 318 return SCRIPT_ITEM(current_sctx.sc_sid)->sn_version == SCRIPT_VERSION_VIM9; |
268 } | 319 } |
269 | 320 |
270 /* | 321 /* |
271 * Lookup a variable in the current script. | 322 * Lookup a variable (without s: prefix) in the current script. |
272 * If "vim9script" is TRUE the script must be Vim9 script. Used for "var" | 323 * If "vim9script" is TRUE the script must be Vim9 script. Used for "var" |
273 * without "s:". | 324 * without "s:". |
325 * "cctx" is NULL at the script level. | |
274 * Returns OK or FAIL. | 326 * Returns OK or FAIL. |
275 */ | 327 */ |
276 static int | 328 static int |
277 lookup_script(char_u *name, size_t len, int vim9script) | 329 script_var_exists(char_u *name, size_t len, int vim9script, cctx_T *cctx) |
278 { | 330 { |
279 int cc; | 331 int is_vim9_script; |
280 hashtab_T *ht; | |
281 dictitem_T *di; | |
282 | 332 |
283 if (current_sctx.sc_sid <= 0) | 333 if (current_sctx.sc_sid <= 0) |
284 return FAIL; | 334 return FAIL; |
285 ht = &SCRIPT_VARS(current_sctx.sc_sid); | 335 is_vim9_script = script_is_vim9(); |
286 if (vim9script && !script_is_vim9()) | 336 if (vim9script && !is_vim9_script) |
287 return FAIL; | 337 return FAIL; |
288 cc = name[len]; | 338 if (is_vim9_script) |
289 name[len] = NUL; | 339 { |
290 di = find_var_in_ht(ht, 0, name, TRUE); | 340 // Check script variables that were visible where the function was |
291 name[len] = cc; | 341 // defined. |
292 return di == NULL ? FAIL: OK; | 342 if (find_script_var(name, len, cctx) != NULL) |
343 return OK; | |
344 } | |
345 else | |
346 { | |
347 hashtab_T *ht = &SCRIPT_VARS(current_sctx.sc_sid); | |
348 dictitem_T *di; | |
349 int cc; | |
350 | |
351 // Check script variables that are currently visible | |
352 cc = name[len]; | |
353 name[len] = NUL; | |
354 di = find_var_in_ht(ht, 0, name, TRUE); | |
355 name[len] = cc; | |
356 if (di != NULL) | |
357 return OK; | |
358 } | |
359 | |
360 return FAIL; | |
293 } | 361 } |
294 | 362 |
295 /* | 363 /* |
296 * Check if "p[len]" is already defined, either in script "import_sid" or in | 364 * Check if "p[len]" is already defined, either in script "import_sid" or in |
297 * compilation context "cctx". | 365 * compilation context "cctx". "cctx" is NULL at the script level. |
298 * Does not check the global namespace. | 366 * Does not check the global namespace. |
299 * Return FAIL and give an error if it defined. | 367 * Return FAIL and give an error if it defined. |
300 */ | 368 */ |
301 int | 369 int |
302 check_defined(char_u *p, size_t len, cctx_T *cctx) | 370 check_defined(char_u *p, size_t len, cctx_T *cctx) |
303 { | 371 { |
304 int c = p[len]; | 372 int c = p[len]; |
305 ufunc_T *ufunc = NULL; | 373 ufunc_T *ufunc = NULL; |
306 | 374 |
307 p[len] = NUL; | 375 p[len] = NUL; |
308 if (lookup_script(p, len, FALSE) == OK | 376 if (script_var_exists(p, len, FALSE, cctx) == OK |
309 || (cctx != NULL | 377 || (cctx != NULL |
310 && (lookup_local(p, len, cctx) != NULL | 378 && (lookup_local(p, len, cctx) != NULL |
311 || lookup_arg(p, len, NULL, NULL, NULL, cctx) == OK)) | 379 || arg_exists(p, len, NULL, NULL, NULL, cctx) == OK)) |
312 || find_imported(p, len, cctx) != NULL | 380 || find_imported(p, len, cctx) != NULL |
313 || (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL) | 381 || (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL) |
314 { | 382 { |
315 // A local or script-local function can shadow a global function. | 383 // A local or script-local function can shadow a global function. |
316 if (ufunc == NULL || !func_is_global(ufunc) | 384 if (ufunc == NULL || !func_is_global(ufunc) |
1697 int isConst, | 1765 int isConst, |
1698 type_T *type) | 1766 type_T *type) |
1699 { | 1767 { |
1700 lvar_T *lvar; | 1768 lvar_T *lvar; |
1701 | 1769 |
1702 if (lookup_arg(name, len, NULL, NULL, NULL, cctx) == OK) | 1770 if (arg_exists(name, len, NULL, NULL, NULL, cctx) == OK) |
1703 { | 1771 { |
1704 emsg_namelen(_(e_str_is_used_as_argument), name, (int)len); | 1772 emsg_namelen(_(e_str_is_used_as_argument), name, (int)len); |
1705 return NULL; | 1773 return NULL; |
1706 } | 1774 } |
1707 | 1775 |
1758 * Returns the index in "sn_var_vals" if found. | 1826 * Returns the index in "sn_var_vals" if found. |
1759 * If found but not in "sn_var_vals" returns -1. | 1827 * If found but not in "sn_var_vals" returns -1. |
1760 * If not found returns -2. | 1828 * If not found returns -2. |
1761 */ | 1829 */ |
1762 int | 1830 int |
1763 get_script_item_idx(int sid, char_u *name, int check_writable) | 1831 get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx) |
1764 { | 1832 { |
1765 hashtab_T *ht; | 1833 hashtab_T *ht; |
1766 dictitem_T *di; | 1834 dictitem_T *di; |
1767 scriptitem_T *si = SCRIPT_ITEM(sid); | 1835 scriptitem_T *si = SCRIPT_ITEM(sid); |
1836 svar_T *sv; | |
1768 int idx; | 1837 int idx; |
1769 | 1838 |
1770 // First look the name up in the hashtable. | |
1771 if (!SCRIPT_ID_VALID(sid)) | 1839 if (!SCRIPT_ID_VALID(sid)) |
1772 return -1; | 1840 return -1; |
1841 if (sid == current_sctx.sc_sid) | |
1842 { | |
1843 sallvar_T *sav = find_script_var(name, (size_t)-1, cctx); | |
1844 | |
1845 if (sav == NULL) | |
1846 return -2; | |
1847 idx = sav->sav_var_vals_idx; | |
1848 sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; | |
1849 if (check_writable && sv->sv_const) | |
1850 semsg(_(e_readonlyvar), name); | |
1851 return idx; | |
1852 } | |
1853 | |
1854 // First look the name up in the hashtable. | |
1773 ht = &SCRIPT_VARS(sid); | 1855 ht = &SCRIPT_VARS(sid); |
1774 di = find_var_in_ht(ht, 0, name, TRUE); | 1856 di = find_var_in_ht(ht, 0, name, TRUE); |
1775 if (di == NULL) | 1857 if (di == NULL) |
1776 return -2; | 1858 return -2; |
1777 | 1859 |
1778 // Now find the svar_T index in sn_var_vals. | 1860 // Now find the svar_T index in sn_var_vals. |
1779 for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx) | 1861 for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx) |
1780 { | 1862 { |
1781 svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; | 1863 sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; |
1782 | |
1783 if (sv->sv_tv == &di->di_tv) | 1864 if (sv->sv_tv == &di->di_tv) |
1784 { | 1865 { |
1785 if (check_writable && sv->sv_const) | 1866 if (check_writable && sv->sv_const) |
1786 semsg(_(e_readonlyvar), name); | 1867 semsg(_(e_readonlyvar), name); |
1787 return idx; | 1868 return idx; |
2081 imported_T *import; | 2162 imported_T *import; |
2082 | 2163 |
2083 if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) | 2164 if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) |
2084 return FAIL; | 2165 return FAIL; |
2085 si = SCRIPT_ITEM(current_sctx.sc_sid); | 2166 si = SCRIPT_ITEM(current_sctx.sc_sid); |
2086 idx = get_script_item_idx(current_sctx.sc_sid, name, FALSE); | 2167 idx = get_script_item_idx(current_sctx.sc_sid, name, FALSE, cctx); |
2087 if (idx == -1 || si->sn_version != SCRIPT_VERSION_VIM9) | 2168 if (idx == -1 || si->sn_version != SCRIPT_VERSION_VIM9) |
2088 { | 2169 { |
2089 // variable is not in sn_var_vals: old style script. | 2170 // variable is not in sn_var_vals: old style script. |
2090 return generate_OLDSCRIPT(cctx, ISN_LOADS, name, current_sctx.sc_sid, | 2171 return generate_OLDSCRIPT(cctx, ISN_LOADS, name, current_sctx.sc_sid, |
2091 &t_any); | 2172 &t_any); |
2128 while (eval_isnamec(*p)) | 2209 while (eval_isnamec(*p)) |
2129 ++p; | 2210 ++p; |
2130 cc = *p; | 2211 cc = *p; |
2131 *p = NUL; | 2212 *p = NUL; |
2132 | 2213 |
2133 idx = find_exported(import->imp_sid, exp_name, &ufunc, &type); | 2214 idx = find_exported(import->imp_sid, exp_name, &ufunc, &type, cctx); |
2134 *p = cc; | 2215 *p = cc; |
2135 p = skipwhite(p); | 2216 p = skipwhite(p); |
2136 | 2217 |
2137 // TODO: what if it is a function? | 2218 // TODO: what if it is a function? |
2138 if (idx < 0) | 2219 if (idx < 0) |
2255 | 2336 |
2256 name = vim_strnsave(*arg, end - *arg); | 2337 name = vim_strnsave(*arg, end - *arg); |
2257 if (name == NULL) | 2338 if (name == NULL) |
2258 return FAIL; | 2339 return FAIL; |
2259 | 2340 |
2260 if (lookup_arg(*arg, len, &idx, &type, &gen_load_outer, cctx) == OK) | 2341 if (arg_exists(*arg, len, &idx, &type, &gen_load_outer, cctx) == OK) |
2261 { | 2342 { |
2262 if (!gen_load_outer) | 2343 if (!gen_load_outer) |
2263 gen_load = TRUE; | 2344 gen_load = TRUE; |
2264 } | 2345 } |
2265 else | 2346 else |
2277 } | 2358 } |
2278 else | 2359 else |
2279 { | 2360 { |
2280 // "var" can be script-local even without using "s:" if it | 2361 // "var" can be script-local even without using "s:" if it |
2281 // already exists in a Vim9 script or when it's imported. | 2362 // already exists in a Vim9 script or when it's imported. |
2282 if (lookup_script(*arg, len, TRUE) == OK | 2363 if (script_var_exists(*arg, len, TRUE, cctx) == OK |
2283 || find_imported(name, 0, cctx) != NULL) | 2364 || find_imported(name, 0, cctx) != NULL) |
2284 res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); | 2365 res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); |
2285 | 2366 |
2286 // When evaluating an expression and the name starts with an | 2367 // When evaluating an expression and the name starts with an |
2287 // uppercase letter or "x:" it can be a user defined function. | 2368 // uppercase letter or "x:" it can be a user defined function. |
4466 eap->getline = exarg_getline; | 4547 eap->getline = exarg_getline; |
4467 eap->cookie = cctx; | 4548 eap->cookie = cctx; |
4468 eap->skip = cctx->ctx_skip == SKIP_YES; | 4549 eap->skip = cctx->ctx_skip == SKIP_YES; |
4469 eap->forceit = FALSE; | 4550 eap->forceit = FALSE; |
4470 lambda_name = get_lambda_name(); | 4551 lambda_name = get_lambda_name(); |
4471 ufunc = def_function(eap, lambda_name); | 4552 ufunc = define_function(eap, lambda_name); |
4472 | 4553 |
4473 if (ufunc == NULL) | 4554 if (ufunc == NULL) |
4474 return eap->skip ? (char_u *)"" : NULL; | 4555 return eap->skip ? (char_u *)"" : NULL; |
4475 if (ufunc->uf_def_status == UF_TO_BE_COMPILED | 4556 if (ufunc->uf_def_status == UF_TO_BE_COMPILED |
4476 && compile_def_function(ufunc, TRUE, cctx) == FAIL) | 4557 && compile_def_function(ufunc, TRUE, cctx) == FAIL) |
4492 else | 4573 else |
4493 { | 4574 { |
4494 // Define a local variable for the function reference. | 4575 // Define a local variable for the function reference. |
4495 lvar_T *lvar = reserve_local(cctx, name_start, name_end - name_start, | 4576 lvar_T *lvar = reserve_local(cctx, name_start, name_end - name_start, |
4496 TRUE, ufunc->uf_func_type); | 4577 TRUE, ufunc->uf_func_type); |
4578 int block_depth = cctx->ctx_ufunc->uf_block_depth; | |
4497 | 4579 |
4498 if (lvar == NULL) | 4580 if (lvar == NULL) |
4499 return NULL; | 4581 return NULL; |
4500 if (generate_FUNCREF(cctx, ufunc) == FAIL) | 4582 if (generate_FUNCREF(cctx, ufunc) == FAIL) |
4501 return NULL; | 4583 return NULL; |
4502 r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL); | 4584 r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL); |
4585 | |
4586 // copy over the block scope IDs | |
4587 if (block_depth > 0) | |
4588 { | |
4589 ufunc->uf_block_ids = ALLOC_MULT(int, block_depth); | |
4590 if (ufunc->uf_block_ids != NULL) | |
4591 { | |
4592 mch_memmove(ufunc->uf_block_ids, cctx->ctx_ufunc->uf_block_ids, | |
4593 sizeof(int) * block_depth); | |
4594 ufunc->uf_block_depth = block_depth; | |
4595 } | |
4596 } | |
4503 } | 4597 } |
4504 | 4598 |
4505 // TODO: warning for trailing text? | 4599 // TODO: warning for trailing text? |
4506 return r == FAIL ? NULL : (char_u *)""; | 4600 return r == FAIL ? NULL : (char_u *)""; |
4507 } | 4601 } |
4898 | 4992 |
4899 lvar = lookup_local(var_start, varlen, cctx); | 4993 lvar = lookup_local(var_start, varlen, cctx); |
4900 if (lvar == NULL) | 4994 if (lvar == NULL) |
4901 { | 4995 { |
4902 CLEAR_FIELD(arg_lvar); | 4996 CLEAR_FIELD(arg_lvar); |
4903 if (lookup_arg(var_start, varlen, | 4997 if (arg_exists(var_start, varlen, |
4904 &arg_lvar.lv_idx, &arg_lvar.lv_type, | 4998 &arg_lvar.lv_idx, &arg_lvar.lv_type, |
4905 &arg_lvar.lv_from_outer, cctx) == OK) | 4999 &arg_lvar.lv_from_outer, cctx) == OK) |
4906 { | 5000 { |
4907 if (is_decl) | 5001 if (is_decl) |
4908 { | 5002 { |
4923 else | 5017 else |
4924 { | 5018 { |
4925 int script_namespace = varlen > 1 | 5019 int script_namespace = varlen > 1 |
4926 && STRNCMP(var_start, "s:", 2) == 0; | 5020 && STRNCMP(var_start, "s:", 2) == 0; |
4927 int script_var = (script_namespace | 5021 int script_var = (script_namespace |
4928 ? lookup_script(var_start + 2, varlen - 2, FALSE) | 5022 ? script_var_exists(var_start + 2, varlen - 2, |
4929 : lookup_script(var_start, varlen, TRUE)) == OK; | 5023 FALSE, cctx) |
5024 : script_var_exists(var_start, varlen, | |
5025 TRUE, cctx)) == OK; | |
4930 imported_T *import = | 5026 imported_T *import = |
4931 find_imported(var_start, varlen, cctx); | 5027 find_imported(var_start, varlen, cctx); |
4932 | 5028 |
4933 if (script_namespace || script_var || import != NULL) | 5029 if (script_namespace || script_var || import != NULL) |
4934 { | 5030 { |
4960 if (import != NULL) | 5056 if (import != NULL) |
4961 scriptvar_sid = import->imp_sid; | 5057 scriptvar_sid = import->imp_sid; |
4962 if (SCRIPT_ID_VALID(scriptvar_sid)) | 5058 if (SCRIPT_ID_VALID(scriptvar_sid)) |
4963 { | 5059 { |
4964 scriptvar_idx = get_script_item_idx(scriptvar_sid, | 5060 scriptvar_idx = get_script_item_idx(scriptvar_sid, |
4965 rawname, TRUE); | 5061 rawname, TRUE, cctx); |
4966 if (scriptvar_idx >= 0) | 5062 if (scriptvar_idx >= 0) |
4967 { | 5063 { |
4968 scriptitem_T *si = SCRIPT_ITEM(scriptvar_sid); | 5064 scriptitem_T *si = SCRIPT_ITEM(scriptvar_sid); |
4969 svar_T *sv = | 5065 svar_T *sv = |
4970 ((svar_T *)si->sn_var_vals.ga_data) | 5066 ((svar_T *)si->sn_var_vals.ga_data) |
6962 if (*ea.cmd == '&' | 7058 if (*ea.cmd == '&' |
6963 || *ea.cmd == '$' | 7059 || *ea.cmd == '$' |
6964 || *ea.cmd == '@' | 7060 || *ea.cmd == '@' |
6965 || ((len) > 2 && ea.cmd[1] == ':') | 7061 || ((len) > 2 && ea.cmd[1] == ':') |
6966 || lookup_local(ea.cmd, len, &cctx) != NULL | 7062 || lookup_local(ea.cmd, len, &cctx) != NULL |
6967 || lookup_arg(ea.cmd, len, NULL, NULL, | 7063 || arg_exists(ea.cmd, len, NULL, NULL, |
6968 NULL, &cctx) == OK | 7064 NULL, &cctx) == OK |
6969 || lookup_script(ea.cmd, len, FALSE) == OK | 7065 || script_var_exists(ea.cmd, len, |
7066 FALSE, &cctx) == OK | |
6970 || find_imported(ea.cmd, len, &cctx) != NULL) | 7067 || find_imported(ea.cmd, len, &cctx) != NULL) |
6971 { | 7068 { |
6972 line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx); | 7069 line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx); |
6973 if (line == NULL || line == ea.cmd) | 7070 if (line == NULL || line == ea.cmd) |
6974 goto erret; | 7071 goto erret; |