patch 8.2.1744: Vim9: using ":const!" is weird

Problem:    Vim9: using ":const!" is weird.
Solution:   Use "var" - "final" - "const" like Dart.  "let" still works for
            now.
diff --git a/src/cmdexpand.c b/src/cmdexpand.c
index e7cf819..f3e19bc 100644
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -1513,8 +1513,10 @@
 	    break;
 #endif
 #ifdef FEAT_EVAL
+	case CMD_final:
 	case CMD_const:
 	case CMD_let:
+	case CMD_var:
 	case CMD_if:
 	case CMD_elseif:
 	case CMD_while:
diff --git a/src/errors.h b/src/errors.h
index 0246fa1..34c47ca 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -270,4 +270,8 @@
 	INIT(= N_("E1122: Variable is locked: %s"));
 EXTERN char e_missing_comma_before_argument_str[]
 	INIT(= N_("E1123: Missing comma before argument: %s"));
+EXTERN char e_str_cannot_be_used_in_legacy_vim_script[]
+	INIT(= N_("E1124: \"%s\" cannot be used in legacy Vim script"));
+EXTERN char e_final_requires_a_value[]
+	INIT(= N_("E1125: Final requires a value"));
 #endif
diff --git a/src/eval.c b/src/eval.c
index 6c37e70..c8c4f6e 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1213,7 +1213,7 @@
     char_u	*endp,
     typval_T	*rettv,
     int		copy,
-    int		flags,    // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
+    int		flags,    // ASSIGN_CONST, ASSIGN_NO_DECL
     char_u	*op)
 {
     int		cc;
@@ -1281,7 +1281,7 @@
 	{
 	    typval_T tv;
 
-	    if (flags & LET_IS_CONST)
+	    if (flags & ASSIGN_CONST)
 	    {
 		emsg(_(e_cannot_mod));
 		*endp = cc;
@@ -1319,7 +1319,7 @@
 	listitem_T *ll_li = lp->ll_li;
 	int	    ll_n1 = lp->ll_n1;
 
-	if (flags & LET_IS_CONST)
+	if (flags & ASSIGN_CONST)
 	{
 	    emsg(_("E996: Cannot lock a range"));
 	    return;
@@ -1378,7 +1378,7 @@
 	/*
 	 * Assign to a List or Dictionary item.
 	 */
-	if (flags & LET_IS_CONST)
+	if (flags & ASSIGN_CONST)
 	{
 	    emsg(_("E996: Cannot lock a list or dict"));
 	    return;
@@ -1688,7 +1688,7 @@
 {
     forinfo_T	*fi = (forinfo_T *)fi_void;
     int		result;
-    int		flag = in_vim9script() ?  LET_NO_COMMAND : 0;
+    int		flag = in_vim9script() ?  ASSIGN_NO_DECL : 0;
     listitem_T	*item;
 
     if (fi->fi_blob != NULL)
@@ -1741,11 +1741,12 @@
     char_u	*arg,
     cmdidx_T	cmdidx)
 {
-    int		got_eq = FALSE;
+    int		has_expr = cmdidx != CMD_let && cmdidx != CMD_var;
     int		c;
     char_u	*p;
 
-    if (cmdidx == CMD_let || cmdidx == CMD_const)
+    if (cmdidx == CMD_let || cmdidx == CMD_var
+				 || cmdidx == CMD_const || cmdidx == CMD_final)
     {
 	xp->xp_context = EXPAND_USER_VARS;
 	if (vim_strpbrk(arg, (char_u *)"\"'+-*/%.=!?~|&$([<>,#") == NULL)
@@ -1774,8 +1775,7 @@
 	    if (c == '&')
 	    {
 		++xp->xp_pattern;
-		xp->xp_context = cmdidx != CMD_let || got_eq
-					 ? EXPAND_EXPRESSION : EXPAND_NOTHING;
+		xp->xp_context = has_expr ? EXPAND_EXPRESSION : EXPAND_NOTHING;
 	    }
 	    else if (c != ' ')
 	    {
@@ -1792,7 +1792,7 @@
 	}
 	else if (c == '=')
 	{
-	    got_eq = TRUE;
+	    has_expr = TRUE;
 	    xp->xp_context = EXPAND_EXPRESSION;
 	}
 	else if (c == '#'
@@ -1808,7 +1808,7 @@
 	    // Function name can start with "<SNR>" and contain '#'.
 	    break;
 	}
-	else if (cmdidx != CMD_let || got_eq)
+	else if (has_expr)
 	{
 	    if (c == '"')	    // string
 	    {
diff --git a/src/evalvars.c b/src/evalvars.c
index 5ceaca4..087a0cb 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -669,6 +669,25 @@
 }
 
 /*
+ * Vim9 variable declaration:
+ * ":var name"
+ * ":var name: type"
+ * ":var name = expr"
+ * ":var name: type = expr"
+ * etc.
+ */
+    void
+ex_var(exarg_T *eap)
+{
+    if (!in_vim9script())
+    {
+	semsg(_(e_str_cannot_be_used_in_legacy_vim_script), ":var");
+	return;
+    }
+    ex_let(eap);
+}
+
+/*
  * ":let"			list all variable values
  * ":let var1 var2"		list variable values
  * ":let var = expr"		assignment command.
@@ -683,6 +702,9 @@
  * ":let var =<< ..."		heredoc
  * ":let var: string"		Vim9 declaration
  *
+ * ":final var = expr"		assignment command.
+ * ":final [var1, var2] = expr"	unpack list.
+ *
  * ":const"			list all variable values
  * ":const var1 var2"		list variable values
  * ":const var = expr"		assignment command.
@@ -702,14 +724,22 @@
     int		first = TRUE;
     int		concat;
     int		has_assign;
-    int		flags = eap->cmdidx == CMD_const ? LET_IS_CONST : 0;
+    int		flags = eap->cmdidx == CMD_const ? ASSIGN_CONST : 0;
     int		vim9script = in_vim9script();
 
+    if (eap->cmdidx == CMD_final && !vim9script)
+    {
+	    // In legacy Vim script ":final" is short for ":finally".
+	    ex_finally(eap);
+	    return;
+    }
+    if (eap->cmdidx == CMD_const && !vim9script && !eap->forceit)
+	// In legacy Vim script ":const" works like ":final".
+	eap->cmdidx = CMD_final;
+
     // detect Vim9 assignment without ":let" or ":const"
     if (eap->arg == eap->cmd)
-	flags |= LET_NO_COMMAND;
-    if (eap->forceit)
-	flags |= LET_FORCEIT;
+	flags |= ASSIGN_NO_DECL;
 
     argend = skip_var_list(arg, TRUE, &var_count, &semicolon, FALSE);
     if (argend == NULL)
@@ -787,7 +817,7 @@
 	    op[1] = NUL;
 	    if (*expr != '=')
 	    {
-		if (vim9script && (flags & LET_NO_COMMAND) == 0)
+		if (vim9script && (flags & ASSIGN_NO_DECL) == 0)
 		{
 		    // +=, /=, etc. require an existing variable
 		    semsg(_(e_cannot_use_operator_on_new_variable), eap->arg);
@@ -860,7 +890,7 @@
     int		copy,		// copy values from "tv", don't move
     int		semicolon,	// from skip_var_list()
     int		var_count,	// from skip_var_list()
-    int		flags,		// LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
+    int		flags,		// ASSIGN_CONST, ASSIGN_NO_DECL
     char_u	*op)
 {
     char_u	*arg = arg_start;
@@ -1215,7 +1245,7 @@
     char_u	*arg,		// points to variable name
     typval_T	*tv,		// value to assign to variable
     int		copy,		// copy value from "tv"
-    int		flags,		// LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
+    int		flags,		// ASSIGN_CONST, ASSIGN_NO_DECL
     char_u	*endchars,	// valid chars after variable name  or NULL
     char_u	*op)		// "+", "-", "."  or NULL
 {
@@ -1227,7 +1257,7 @@
     int		opt_flags;
     char_u	*tofree = NULL;
 
-    if (in_vim9script() && (flags & LET_NO_COMMAND) == 0
+    if (in_vim9script() && (flags & ASSIGN_NO_DECL) == 0
 				  && vim_strchr((char_u *)"$@&", *arg) != NULL)
     {
 	vim9_declare_error(arg);
@@ -1237,7 +1267,7 @@
     // ":let $VAR = expr": Set environment variable.
     if (*arg == '$')
     {
-	if (flags & LET_IS_CONST)
+	if (flags & ASSIGN_CONST)
 	{
 	    emsg(_("E996: Cannot lock an environment variable"));
 	    return NULL;
@@ -1289,7 +1319,7 @@
     // ":let &g:option = expr": Set global option value.
     else if (*arg == '&')
     {
-	if (flags & LET_IS_CONST)
+	if (flags & ASSIGN_CONST)
 	{
 	    emsg(_(e_const_option));
 	    return NULL;
@@ -1373,7 +1403,7 @@
     // ":let @r = expr": Set register contents.
     else if (*arg == '@')
     {
-	if (flags & LET_IS_CONST)
+	if (flags & ASSIGN_CONST)
 	{
 	    emsg(_("E996: Cannot lock a register"));
 	    return NULL;
@@ -2926,7 +2956,7 @@
     typval_T	*tv,
     int		copy)	    // make copy of value in "tv"
 {
-    set_var_const(name, NULL, tv, copy, LET_NO_COMMAND);
+    set_var_const(name, NULL, tv, copy, ASSIGN_NO_DECL);
 }
 
 /*
@@ -2940,7 +2970,7 @@
     type_T	*type,
     typval_T	*tv_arg,
     int		copy,	    // make copy of value in "tv"
-    int		flags)	    // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND
+    int		flags)	    // ASSIGN_CONST, ASSIGN_NO_DECL
 {
     typval_T	*tv = tv_arg;
     typval_T	bool_tv;
@@ -2960,7 +2990,7 @@
 
     if (vim9script
 	    && !is_script_local
-	    && (flags & LET_NO_COMMAND) == 0
+	    && (flags & ASSIGN_NO_DECL) == 0
 	    && name[1] == ':')
     {
 	vim9_declare_error(name);
@@ -2990,7 +3020,7 @@
     {
 	if ((di->di_flags & DI_FLAGS_RELOAD) == 0)
 	{
-	    if (flags & LET_IS_CONST)
+	    if (flags & ASSIGN_CONST)
 	    {
 		emsg(_(e_cannot_mod));
 		goto failed;
@@ -2998,7 +3028,7 @@
 
 	    if (is_script_local && vim9script)
 	    {
-		if ((flags & LET_NO_COMMAND) == 0)
+		if ((flags & ASSIGN_NO_DECL) == 0)
 		{
 		    semsg(_(e_redefining_script_item_str), name);
 		    goto failed;
@@ -3094,7 +3124,7 @@
 	    goto failed;
 	}
 	di->di_flags = DI_FLAGS_ALLOC;
-	if (flags & LET_IS_CONST)
+	if (flags & ASSIGN_CONST)
 	    di->di_flags |= DI_FLAGS_LOCK;
 
 	if (is_script_local && vim9script)
@@ -3113,7 +3143,7 @@
 		    sv->sv_type = typval2type(tv, &si->sn_type_list);
 		else
 		    sv->sv_type = type;
-		sv->sv_const = (flags & LET_IS_CONST);
+		sv->sv_const = (flags & ASSIGN_CONST);
 		sv->sv_export = is_export;
 		++si->sn_var_vals.ga_len;
 
@@ -3132,8 +3162,8 @@
 	init_tv(tv);
     }
 
-    // ":const var = val" locks the value; in Vim9 script only with ":const!"
-    if ((flags & LET_IS_CONST) && (!vim9script || (flags & LET_FORCEIT)))
+    // ":const var = val" locks the value
+    if (flags & ASSIGN_CONST)
 	// Like :lockvar! name: lock the value and what it contains, but only
 	// if the reference count is up to one.  That locks only literal
 	// values.
diff --git a/src/ex_cmdidxs.h b/src/ex_cmdidxs.h
index 53081d0..0f9eb39 100644
--- a/src/ex_cmdidxs.h
+++ b/src/ex_cmdidxs.h
@@ -11,26 +11,26 @@
   /* d */ 108,
   /* e */ 133,
   /* f */ 156,
-  /* g */ 172,
-  /* h */ 178,
-  /* i */ 187,
-  /* j */ 206,
-  /* k */ 208,
-  /* l */ 213,
-  /* m */ 275,
-  /* n */ 293,
-  /* o */ 313,
-  /* p */ 325,
-  /* q */ 364,
-  /* r */ 367,
-  /* s */ 387,
-  /* t */ 456,
-  /* u */ 501,
-  /* v */ 512,
-  /* w */ 531,
-  /* x */ 545,
-  /* y */ 555,
-  /* z */ 556
+  /* g */ 173,
+  /* h */ 179,
+  /* i */ 188,
+  /* j */ 207,
+  /* k */ 209,
+  /* l */ 214,
+  /* m */ 276,
+  /* n */ 294,
+  /* o */ 314,
+  /* p */ 326,
+  /* q */ 365,
+  /* r */ 368,
+  /* s */ 388,
+  /* t */ 457,
+  /* u */ 502,
+  /* v */ 513,
+  /* w */ 533,
+  /* x */ 547,
+  /* y */ 557,
+  /* z */ 558
 };
 
 /*
@@ -46,7 +46,7 @@
   /* c */ {  3, 12, 16, 18, 20, 22, 25,  0,  0,  0,  0, 33, 37, 40, 46, 56, 58, 59, 60,  0, 62,  0, 65,  0,  0,  0 },
   /* d */ {  0,  0,  0,  0,  0,  0,  0,  0,  8, 18,  0, 19,  0,  0, 20,  0,  0, 22, 23,  0,  0,  0,  0,  0,  0,  0 },
   /* e */ {  1,  0,  2,  0,  0,  0,  0,  0,  0,  0,  0,  7,  9, 10,  0,  0,  0,  0,  0,  0,  0, 17,  0, 18,  0,  0 },
-  /* f */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  9,  0,  0,  0,  0,  0, 15,  0,  0,  0,  0,  0 },
+  /* f */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 10,  0,  0,  0,  0,  0, 16,  0,  0,  0,  0,  0 },
   /* g */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  0,  0,  2,  0,  0,  4,  5,  0,  0,  0,  0 },
   /* h */ {  5,  0,  0,  0,  0,  0,  0,  0,  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* i */ {  1,  0,  0,  0,  0,  3,  0,  0,  0,  4,  0,  5,  6,  0,  0,  0,  0,  0, 14,  0, 16,  0,  0,  0,  0,  0 },
@@ -62,11 +62,11 @@
   /* s */ {  2,  6, 15,  0, 19, 23,  0, 25, 26,  0,  0, 29, 31, 35, 39, 41,  0, 50,  0, 51,  0, 63, 64,  0, 65,  0 },
   /* t */ {  2,  0, 19,  0, 24, 26,  0, 27,  0, 28,  0, 29, 33, 36, 38, 39,  0, 40, 42,  0, 43,  0,  0,  0,  0,  0 },
   /* u */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
-  /* v */ {  0,  0,  0,  0,  1,  0,  0,  0,  4,  0,  0,  0, 10, 13,  0,  0,  0,  0, 16,  0, 17,  0,  0,  0,  0,  0 },
+  /* v */ {  1,  0,  0,  0,  2,  0,  0,  0,  5,  0,  0,  0, 11, 14,  0,  0,  0,  0, 17,  0, 18,  0,  0,  0,  0,  0 },
   /* w */ {  2,  0,  0,  0,  0,  0,  0,  3,  4,  0,  0,  0,  0,  8,  0,  9, 10,  0,  0,  0, 12, 13,  0,  0,  0,  0 },
   /* x */ {  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  2,  5,  0,  0,  0,  7,  0,  0,  8,  0,  0,  0,  0,  0 },
   /* y */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* z */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }
 };
 
-static const int command_count = 569;
+static const int command_count = 571;
diff --git a/src/ex_cmds.h b/src/ex_cmds.h
index 8ef9eb4..0c83f52 100644
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -592,6 +592,9 @@
 EXCMD(CMD_find,		"find",		ex_find,
 	EX_RANGE|EX_BANG|EX_FILE1|EX_CMDARG|EX_ARGOPT|EX_TRLBAR|EX_NEEDARG,
 	ADDR_OTHER),
+EXCMD(CMD_final,	"final",	ex_let,
+	EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
+	ADDR_NONE),
 EXCMD(CMD_finally,	"finally",	ex_finally,
 	EX_TRLBAR|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
 	ADDR_NONE),
@@ -1648,6 +1651,9 @@
 EXCMD(CMD_vglobal,	"vglobal",	ex_global,
 	EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_DFLALL|EX_CMDWIN|EX_LOCK_OK,
 	ADDR_LINES),
+EXCMD(CMD_var,		"var",		ex_var,
+	EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
+	ADDR_NONE),
 EXCMD(CMD_version,	"version",	ex_version,
 	EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
 	ADDR_NONE),
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 7f96388..f584153 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -2421,6 +2421,7 @@
 	    case CMD_eval:
 	    case CMD_execute:
 	    case CMD_filter:
+	    case CMD_final:
 	    case CMD_help:
 	    case CMD_hide:
 	    case CMD_ijump:
@@ -2442,9 +2443,9 @@
 	    case CMD_noswapfile:
 	    case CMD_perl:
 	    case CMD_psearch:
-	    case CMD_python:
 	    case CMD_py3:
 	    case CMD_python3:
+	    case CMD_python:
 	    case CMD_return:
 	    case CMD_rightbelow:
 	    case CMD_ruby:
@@ -2460,6 +2461,7 @@
 	    case CMD_topleft:
 	    case CMD_unlet:
 	    case CMD_unlockvar:
+	    case CMD_var:
 	    case CMD_verbose:
 	    case CMD_vertical:
 	    case CMD_wincmd:
@@ -3244,7 +3246,7 @@
 		if (skip_expr(&after) == OK
 				  && (*after == '='
 				      || (*after != NUL && after[1] == '=')))
-		    eap->cmdidx = CMD_let;
+		    eap->cmdidx = CMD_var;
 		else
 		    eap->cmdidx = CMD_eval;
 		--emsg_silent;
@@ -3268,7 +3270,7 @@
 		}
 		if (p > eap->cmd && *skipwhite(p) == '=')
 		{
-		    eap->cmdidx = CMD_let;
+		    eap->cmdidx = CMD_var;
 		    return eap->cmd;
 		}
 	    }
@@ -3287,7 +3289,7 @@
 			|| *eap->cmd == '@'
 			|| lookup(eap->cmd, p - eap->cmd, cctx) != NULL)
 		{
-		    eap->cmdidx = CMD_let;
+		    eap->cmdidx = CMD_var;
 		    return eap->cmd;
 		}
 	    }
diff --git a/src/proto/evalvars.pro b/src/proto/evalvars.pro
index 569a73a..6965508 100644
--- a/src/proto/evalvars.pro
+++ b/src/proto/evalvars.pro
@@ -14,6 +14,7 @@
 void prepare_vimvar(int idx, typval_T *save_tv);
 void restore_vimvar(int idx, typval_T *save_tv);
 list_T *heredoc_get(exarg_T *eap, char_u *cmd, int script_get);
+void ex_var(exarg_T *eap);
 void ex_let(exarg_T *eap);
 int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon, int var_count, int flags, char_u *op);
 char_u *skip_var_list(char_u *arg, int include_type, int *var_count, int *semicolon, int silent);
diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim
index 4c825e8..94861e5 100644
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -12,30 +12,30 @@
 let g:astring = 'text'
 
 def Test_assignment_bool()
-  let bool1: bool = true
+  var bool1: bool = true
   assert_equal(v:true, bool1)
-  let bool2: bool = false
+  var bool2: bool = false
   assert_equal(v:false, bool2)
 
-  let bool3: bool = 0
+  var bool3: bool = 0
   assert_equal(false, bool3)
-  let bool4: bool = 1
+  var bool4: bool = 1
   assert_equal(true, bool4)
 
-  let bool5: bool = 'yes' && 'no'
+  var bool5: bool = 'yes' && 'no'
   assert_equal(true, bool5)
-  let bool6: bool = [] && 99
+  var bool6: bool = [] && 99
   assert_equal(false, bool6)
-  let bool7: bool = [] || #{a: 1} && 99
+  var bool7: bool = [] || #{a: 1} && 99
   assert_equal(true, bool7)
 
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     def GetFlag(): bool
-      let flag: bool = 1
+      var flag: bool = 1
       return flag
     enddef
-    let flag: bool = GetFlag()
+    var flag: bool = GetFlag()
     assert_equal(true, flag)
     flag = 0
     assert_equal(false, flag)
@@ -47,41 +47,42 @@
     assert_equal(false, flag)
   END
   CheckScriptSuccess(lines)
-  CheckDefAndScriptFailure(['let x: bool = 2'], 'E1012:')
-  CheckDefAndScriptFailure(['let x: bool = -1'], 'E1012:')
-  CheckDefAndScriptFailure(['let x: bool = [1]'], 'E1012:')
-  CheckDefAndScriptFailure(['let x: bool = {}'], 'E1012:')
-  CheckDefAndScriptFailure(['let x: bool = "x"'], 'E1012:')
+  CheckDefAndScriptFailure(['var x: bool = 2'], 'E1012:')
+  CheckDefAndScriptFailure(['var x: bool = -1'], 'E1012:')
+  CheckDefAndScriptFailure(['var x: bool = [1]'], 'E1012:')
+  CheckDefAndScriptFailure(['var x: bool = {}'], 'E1012:')
+  CheckDefAndScriptFailure(['var x: bool = "x"'], 'E1012:')
 enddef
 
 def Test_syntax()
-  let var = 234
-  let other: list<string> = ['asdf']
+  var var = 234
+  var other: list<string> = ['asdf']
 enddef
 
 def Test_assignment()
-  CheckDefFailure(['let x:string'], 'E1069:')
-  CheckDefFailure(['let x:string = "x"'], 'E1069:')
-  CheckDefFailure(['let a:string = "x"'], 'E1069:')
-  CheckDefFailure(['let lambda = {-> "lambda"}'], 'E704:')
+  CheckDefFailure(['var x:string'], 'E1069:')
+  CheckDefFailure(['var x:string = "x"'], 'E1069:')
+  CheckDefFailure(['var a:string = "x"'], 'E1069:')
+  CheckDefFailure(['var lambda = {-> "lambda"}'], 'E704:')
+  CheckScriptFailure(['var x = "x"'], 'E1124:')
 
-  let nr: number = 1234
-  CheckDefFailure(['let nr: number = "asdf"'], 'E1012:')
+  var nr: number = 1234
+  CheckDefFailure(['var nr: number = "asdf"'], 'E1012:')
 
-  let a: number = 6 #comment
+  var a: number = 6 #comment
   assert_equal(6, a)
 
   if has('channel')
-    let chan1: channel
-    let job1: job
-    let job2: job = job_start('willfail')
+    var chan1: channel
+    var job1: job
+    var job2: job = job_start('willfail')
   endif
   if has('float')
-    let float1: float = 3.4
+    var float1: float = 3.4
   endif
-  let Funky1: func
-  let Funky2: func = function('len')
-  let Party2: func = funcref('g:Test_syntax')
+  var Funky1: func
+  var Funky2: func = function('len')
+  var Party2: func = funcref('g:Test_syntax')
 
   g:newvar = 'new'  #comment
   assert_equal('new', g:newvar)
@@ -97,7 +98,7 @@
   assert_equal('foobar', $ENVVAR)
   $ENVVAR = ''
 
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     $ENVVAR = 'barfoo'
     assert_equal('barfoo', $ENVVAR)
@@ -126,15 +127,15 @@
   assert_equal(2, &ts)
 
   if has('float')
-    let f100: float = 100.0
+    var f100: float = 100.0
     f100 /= 5
     assert_equal(20.0, f100)
 
-    let f200: float = 200.0
+    var f200: float = 200.0
     f200 /= 5.0
     assert_equal(40.0, f200)
 
-    CheckDefFailure(['let nr: number = 200', 'nr /= 5.0'], 'E1012:')
+    CheckDefFailure(['var nr: number = 200', 'nr /= 5.0'], 'E1012:')
   endif
 
   lines =<< trim END
@@ -163,11 +164,11 @@
   CheckDefFailure(['&path += 3'], 'E1012:')
   CheckDefExecFailure(['&bs = "asdf"'], 'E474:')
   # test freeing ISN_STOREOPT
-  CheckDefFailure(['&ts = 3', 'let asdf'], 'E1022:')
+  CheckDefFailure(['&ts = 3', 'var asdf'], 'E1022:')
   &ts = 8
 
   lines =<< trim END
-    let save_TI = &t_TI
+    var save_TI = &t_TI
     &t_TI = ''
     assert_equal('', &t_TI)
     &t_TI = 'xxx'
@@ -179,8 +180,8 @@
   CheckDefFailure(['&t_TI = 123'], 'E1012:')
   CheckScriptFailure(['vim9script', '&t_TI = 123'], 'E928:')
 
-  CheckDefFailure(['let s:var = 123'], 'E1101:')
-  CheckDefFailure(['let s:var: number'], 'E1101:')
+  CheckDefFailure(['var s:var = 123'], 'E1101:')
+  CheckDefFailure(['var s:var: number'], 'E1101:')
 
   lines =<< trim END
     vim9script
@@ -217,20 +218,20 @@
 
   # this should not leak
   if 0
-    let text =<< trim END
+    var text =<< trim END
       some text
     END
   endif
 enddef
 
 def Test_extend_list()
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
-      let l: list<number>
+      var l: list<number>
       l += [123]
       assert_equal([123], l)
 
-      let d: dict<number>
+      var d: dict<number>
       d['one'] = 1
       assert_equal(#{one: 1}, d)
   END
@@ -239,41 +240,41 @@
 
 def Test_single_letter_vars()
   # single letter variables
-  let a: number = 123
+  var a: number = 123
   a = 123
   assert_equal(123, a)
-  let b: number
+  var b: number
   b = 123
   assert_equal(123, b)
-  let g: number
+  var g: number
   g = 123
   assert_equal(123, g)
-  let s: number
+  var s: number
   s = 123
   assert_equal(123, s)
-  let t: number
+  var t: number
   t = 123
   assert_equal(123, t)
-  let v: number
+  var v: number
   v = 123
   assert_equal(123, v)
-  let w: number
+  var w: number
   w = 123
   assert_equal(123, w)
 enddef
 
 def Test_vim9_single_char_vars()
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
 
       # single character variable declarations work
-      let a: string
-      let b: number
-      let l: list<any>
-      let s: string
-      let t: number
-      let v: number
-      let w: number
+      var a: string
+      var b: number
+      var l: list<any>
+      var s: string
+      var t: number
+      var v: number
+      var w: number
 
       # script-local variables can be used without s: prefix
       a = 'script-a'
@@ -298,14 +299,14 @@
 enddef
 
 def Test_assignment_list()
-  let list1: list<bool> = [false, true, false]
-  let list2: list<number> = [1, 2, 3]
-  let list3: list<string> = ['sdf', 'asdf']
-  let list4: list<any> = ['yes', true, 1234]
-  let list5: list<blob> = [0z01, 0z02]
+  var list1: list<bool> = [false, true, false]
+  var list2: list<number> = [1, 2, 3]
+  var list3: list<string> = ['sdf', 'asdf']
+  var list4: list<any> = ['yes', true, 1234]
+  var list5: list<blob> = [0z01, 0z02]
 
-  let listS: list<string> = []
-  let listN: list<number> = []
+  var listS: list<string> = []
+  var listN: list<number> = []
 
   assert_equal([1, 2, 3], list2)
   list2[-1] = 99
@@ -320,19 +321,19 @@
   list3 += ['end']
   assert_equal(['sdf', 'asdf', 'end'], list3)
 
-  CheckDefExecFailure(['let ll = [1, 2, 3]', 'll[-4] = 6'], 'E684:')
-  CheckDefExecFailure(['let [v1, v2] = [1, 2]'], 'E1092:')
+  CheckDefExecFailure(['var ll = [1, 2, 3]', 'll[-4] = 6'], 'E684:')
+  CheckDefExecFailure(['var [v1, v2] = [1, 2]'], 'E1092:')
 
   # type becomes list<any>
-  let somelist = rand() > 0 ? [1, 2, 3] : ['a', 'b', 'c']
+  var somelist = rand() > 0 ? [1, 2, 3] : ['a', 'b', 'c']
 enddef
 
 def Test_assignment_list_vim9script()
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
-    let v1: number
-    let v2: number
-    let v3: number
+    var v1: number
+    var v2: number
+    var v3: number
     [v1, v2, v3] = [1, 2, 3]
     assert_equal([1, 2, 3], [v1, v2, v3])
   END
@@ -340,27 +341,27 @@
 enddef
 
 def Test_assignment_dict()
-  let dict1: dict<bool> = #{one: false, two: true}
-  let dict2: dict<number> = #{one: 1, two: 2}
-  let dict3: dict<string> = #{key: 'value'}
-  let dict4: dict<any> = #{one: 1, two: '2'}
-  let dict5: dict<blob> = #{one: 0z01, two: 0z02}
+  var dict1: dict<bool> = #{one: false, two: true}
+  var dict2: dict<number> = #{one: 1, two: 2}
+  var dict3: dict<string> = #{key: 'value'}
+  var dict4: dict<any> = #{one: 1, two: '2'}
+  var dict5: dict<blob> = #{one: 0z01, two: 0z02}
 
   # overwrite
   dict3['key'] = 'another'
 
   # empty key can be used
-  let dd = {}
+  var dd = {}
   dd[""] = 6
   assert_equal({'': 6}, dd)
 
   # type becomes dict<any>
-  let somedict = rand() > 0 ? #{a: 1, b: 2} : #{a: 'a', b: 'b'}
+  var somedict = rand() > 0 ? #{a: 1, b: 2} : #{a: 'a', b: 'b'}
 
   # assignment to script-local dict
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
-    let test: dict<any> = {}
+    var test: dict<any> = {}
     def FillDict(): dict<any>
       test['a'] = 43
       return test
@@ -371,7 +372,7 @@
 
   lines =<< trim END
     vim9script
-    let test: dict<any>
+    var test: dict<any>
     def FillDict(): dict<any>
       test['a'] = 43
       return test
@@ -408,7 +409,7 @@
 def Test_assignment_local()
   # Test in a separated file in order not to the current buffer/window/tab is
   # changed.
-  let script_lines: list<string> =<< trim END
+  var script_lines: list<string> =<< trim END
     let b:existing = 'yes'
     let w:existing = 'yes'
     let t:existing = 'yes'
@@ -446,37 +447,37 @@
 def Test_assignment_default()
 
   # Test default values.
-  let thebool: bool
+  var thebool: bool
   assert_equal(v:false, thebool)
 
-  let thenumber: number
+  var thenumber: number
   assert_equal(0, thenumber)
 
   if has('float')
-    let thefloat: float
+    var thefloat: float
     assert_equal(0.0, thefloat)
   endif
 
-  let thestring: string
+  var thestring: string
   assert_equal('', thestring)
 
-  let theblob: blob
+  var theblob: blob
   assert_equal(0z, theblob)
 
-  let Thefunc: func
+  var Thefunc: func
   assert_equal(test_null_function(), Thefunc)
 
-  let thelist: list<any>
+  var thelist: list<any>
   assert_equal([], thelist)
 
-  let thedict: dict<any>
+  var thedict: dict<any>
   assert_equal({}, thedict)
 
   if has('channel')
-    let thejob: job
+    var thejob: job
     assert_equal(test_null_job(), thejob)
 
-    let thechannel: channel
+    var thechannel: channel
     assert_equal(test_null_channel(), thechannel)
 
     if has('unix') && executable('cat')
@@ -487,14 +488,14 @@
     endif
   endif
 
-  let nr = 1234 | nr = 5678
+  var nr = 1234 | nr = 5678
   assert_equal(5678, nr)
 enddef
 
 def Test_assignment_var_list()
-  let v1: string
-  let v2: string
-  let vrem: list<string>
+  var v1: string
+  var v2: string
+  var vrem: list<string>
   [v1] = ['aaa']
   assert_equal('aaa', v1)
 
@@ -519,18 +520,18 @@
 enddef
 
 def Test_assignment_vim9script()
-  let lines =<< trim END
+  var lines =<< trim END
     vim9script
     def Func(): list<number>
       return [1, 2]
     enddef
-    let var1: number
-    let var2: number
+    var var1: number
+    var var2: number
     [var1, var2] =
           Func()
     assert_equal(1, var1)
     assert_equal(2, var2)
-    let ll =
+    var ll =
           Func()
     assert_equal([1, 2], ll)
 
@@ -551,15 +552,15 @@
       assert_equal('plus', @+)
     endif
 
-    let a: number = 123
+    var a: number = 123
     assert_equal(123, a)
-    let s: string = 'yes'
+    var s: string = 'yes'
     assert_equal('yes', s)
-    let b: number = 42
+    var b: number = 42
     assert_equal(42, b)
-    let w: number = 43
+    var w: number = 43
     assert_equal(43, w)
-    let t: number = 44
+    var t: number = 44
     assert_equal(44, t)
   END
   CheckScriptSuccess(lines)
@@ -571,80 +572,80 @@
 enddef
 
 def Test_assignment_failure()
-  CheckDefFailure(['let var=234'], 'E1004:')
-  CheckDefFailure(['let var =234'], 'E1004:')
-  CheckDefFailure(['let var= 234'], 'E1004:')
+  CheckDefFailure(['var var=234'], 'E1004:')
+  CheckDefFailure(['var var =234'], 'E1004:')
+  CheckDefFailure(['var var= 234'], 'E1004:')
 
-  CheckScriptFailure(['vim9script', 'let var=234'], 'E1004:')
-  CheckScriptFailure(['vim9script', 'let var=234'], "before and after '='")
-  CheckScriptFailure(['vim9script', 'let var =234'], 'E1004:')
-  CheckScriptFailure(['vim9script', 'let var= 234'], 'E1004:')
-  CheckScriptFailure(['vim9script', 'let var = 234', 'var+=234'], 'E1004:')
-  CheckScriptFailure(['vim9script', 'let var = 234', 'var+=234'], "before and after '+='")
-  CheckScriptFailure(['vim9script', 'let var = "x"', 'var..="y"'], 'E1004:')
-  CheckScriptFailure(['vim9script', 'let var = "x"', 'var..="y"'], "before and after '..='")
+  CheckScriptFailure(['vim9script', 'var var=234'], 'E1004:')
+  CheckScriptFailure(['vim9script', 'var var=234'], "before and after '='")
+  CheckScriptFailure(['vim9script', 'var var =234'], 'E1004:')
+  CheckScriptFailure(['vim9script', 'var var= 234'], 'E1004:')
+  CheckScriptFailure(['vim9script', 'var var = 234', 'var+=234'], 'E1004:')
+  CheckScriptFailure(['vim9script', 'var var = 234', 'var+=234'], "before and after '+='")
+  CheckScriptFailure(['vim9script', 'var var = "x"', 'var..="y"'], 'E1004:')
+  CheckScriptFailure(['vim9script', 'var var = "x"', 'var..="y"'], "before and after '..='")
 
-  CheckDefFailure(['let true = 1'], 'E1034:')
-  CheckDefFailure(['let false = 1'], 'E1034:')
+  CheckDefFailure(['var true = 1'], 'E1034:')
+  CheckDefFailure(['var false = 1'], 'E1034:')
 
   CheckDefFailure(['[a; b; c] = g:list'], 'E452:')
-  CheckDefExecFailure(['let a: number',
+  CheckDefExecFailure(['var a: number',
                        '[a] = test_null_list()'], 'E1093:')
-  CheckDefExecFailure(['let a: number',
+  CheckDefExecFailure(['var a: number',
                        '[a] = []'], 'E1093:')
-  CheckDefExecFailure(['let x: number',
-                       'let y: number',
+  CheckDefExecFailure(['var x: number',
+                       'var y: number',
                        '[x, y] = [1]'], 'E1093:')
-  CheckDefExecFailure(['let x: number',
-                       'let y: number',
-                       'let z: list<number>',
+  CheckDefExecFailure(['var x: number',
+                       'var y: number',
+                       'var z: list<number>',
                        '[x, y; z] = [1]'], 'E1093:')
 
-  CheckDefFailure(['let somevar'], "E1022:")
-  CheckDefFailure(['let &tabstop = 4'], 'E1052:')
+  CheckDefFailure(['var somevar'], "E1022:")
+  CheckDefFailure(['var &tabstop = 4'], 'E1052:')
   CheckDefFailure(['&g:option = 5'], 'E113:')
-  CheckScriptFailure(['vim9script', 'let &tabstop = 4'], 'E1052:')
+  CheckScriptFailure(['vim9script', 'var &tabstop = 4'], 'E1052:')
 
-  CheckDefFailure(['let $VAR = 5'], 'E1016: Cannot declare an environment variable:')
-  CheckScriptFailure(['vim9script', 'let $ENV = "xxx"'], 'E1016:')
+  CheckDefFailure(['var $VAR = 5'], 'E1016: Cannot declare an environment variable:')
+  CheckScriptFailure(['vim9script', 'var $ENV = "xxx"'], 'E1016:')
 
   if has('dnd')
-    CheckDefFailure(['let @~ = 5'], 'E1066:')
+    CheckDefFailure(['var @~ = 5'], 'E1066:')
   else
-    CheckDefFailure(['let @~ = 5'], 'E354:')
+    CheckDefFailure(['var @~ = 5'], 'E354:')
     CheckDefFailure(['@~ = 5'], 'E354:')
   endif
-  CheckDefFailure(['let @a = 5'], 'E1066:')
-  CheckDefFailure(['let @/ = "x"'], 'E1066:')
-  CheckScriptFailure(['vim9script', 'let @a = "abc"'], 'E1066:')
+  CheckDefFailure(['var @a = 5'], 'E1066:')
+  CheckDefFailure(['var @/ = "x"'], 'E1066:')
+  CheckScriptFailure(['vim9script', 'var @a = "abc"'], 'E1066:')
 
-  CheckDefFailure(['let g:var = 5'], 'E1016: Cannot declare a global variable:')
-  CheckDefFailure(['let w:var = 5'], 'E1016: Cannot declare a window variable:')
-  CheckDefFailure(['let b:var = 5'], 'E1016: Cannot declare a buffer variable:')
-  CheckDefFailure(['let t:var = 5'], 'E1016: Cannot declare a tab variable:')
+  CheckDefFailure(['var g:var = 5'], 'E1016: Cannot declare a global variable:')
+  CheckDefFailure(['var w:var = 5'], 'E1016: Cannot declare a window variable:')
+  CheckDefFailure(['var b:var = 5'], 'E1016: Cannot declare a buffer variable:')
+  CheckDefFailure(['var t:var = 5'], 'E1016: Cannot declare a tab variable:')
 
-  CheckDefFailure(['let anr = 4', 'anr ..= "text"'], 'E1019:')
-  CheckDefFailure(['let xnr += 4'], 'E1020:', 1)
-  CheckScriptFailure(['vim9script', 'let xnr += 4'], 'E1020:')
-  CheckDefFailure(["let xnr = xnr + 1"], 'E1001:', 1)
-  CheckScriptFailure(['vim9script', 'let xnr = xnr + 4'], 'E121:')
+  CheckDefFailure(['var anr = 4', 'anr ..= "text"'], 'E1019:')
+  CheckDefFailure(['var xnr += 4'], 'E1020:', 1)
+  CheckScriptFailure(['vim9script', 'var xnr += 4'], 'E1020:')
+  CheckDefFailure(["var xnr = xnr + 1"], 'E1001:', 1)
+  CheckScriptFailure(['vim9script', 'var xnr = xnr + 4'], 'E121:')
 
-  CheckScriptFailure(['vim9script', 'def Func()', 'let dummy = s:notfound', 'enddef', 'defcompile'], 'E1108:')
+  CheckScriptFailure(['vim9script', 'def Func()', 'var dummy = s:notfound', 'enddef', 'defcompile'], 'E1108:')
 
-  CheckDefFailure(['let var: list<string> = [123]'], 'expected list<string> but got list<number>')
-  CheckDefFailure(['let var: list<number> = ["xx"]'], 'expected list<number> but got list<string>')
+  CheckDefFailure(['var var: list<string> = [123]'], 'expected list<string> but got list<number>')
+  CheckDefFailure(['var var: list<number> = ["xx"]'], 'expected list<number> but got list<string>')
 
-  CheckDefFailure(['let var: dict<string> = #{key: 123}'], 'expected dict<string> but got dict<number>')
-  CheckDefFailure(['let var: dict<number> = #{key: "xx"}'], 'expected dict<number> but got dict<string>')
+  CheckDefFailure(['var var: dict<string> = #{key: 123}'], 'expected dict<string> but got dict<number>')
+  CheckDefFailure(['var var: dict<number> = #{key: "xx"}'], 'expected dict<number> but got dict<string>')
 
-  CheckDefFailure(['let var = feedkeys("0")'], 'E1031:')
-  CheckDefFailure(['let var: number = feedkeys("0")'], 'expected number but got void')
+  CheckDefFailure(['var var = feedkeys("0")'], 'E1031:')
+  CheckDefFailure(['var var: number = feedkeys("0")'], 'expected number but got void')
 
-  CheckDefFailure(['let var: dict <number>'], 'E1068:')
-  CheckDefFailure(['let var: dict<number'], 'E1009:')
+  CheckDefFailure(['var var: dict <number>'], 'E1068:')
+  CheckDefFailure(['var var: dict<number'], 'E1009:')
 
   assert_fails('s/^/\=Mess()/n', 'E794:')
-  CheckDefFailure(['let var: dict<number'], 'E1009:')
+  CheckDefFailure(['var var: dict<number'], 'E1009:')
 
   CheckDefFailure(['w:foo: number = 10'],
                   'E488: Trailing characters: : number = 1')
@@ -657,7 +658,7 @@
 enddef
 
 def Test_assign_list()
-  let l: list<string> = []
+  var l: list<string> = []
   l[0] = 'value'
   assert_equal('value', l[0])
 
@@ -667,7 +668,7 @@
   assert_equal('asdf', l[-1])
   assert_equal('value', l[-2])
 
-  let nrl: list<number> = []
+  var nrl: list<number> = []
   for i in range(5)
     nrl[i] = i
   endfor
@@ -675,7 +676,7 @@
 enddef
 
 def Test_assign_dict()
-  let d: dict<string> = {}
+  var d: dict<string> = {}
   d['key'] = 'value'
   assert_equal('value', d['key'])
 
@@ -683,7 +684,7 @@
   assert_equal('qwerty', d[123])
   assert_equal('qwerty', d['123'])
 
-  let nrd: dict<number> = {}
+  var nrd: dict<number> = {}
   for i in range(3)
     nrd[i] = i
   endfor
@@ -691,12 +692,12 @@
 enddef
 
 def Test_assign_dict_unknown_type()
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
-      let mylist = []
+      var mylist = []
       mylist += [#{one: 'one'}]
       def Func()
-        let dd = mylist[0]
+        var dd = mylist[0]
         assert_equal('one', dd.one)
       enddef
       Func()
@@ -706,10 +707,10 @@
   # doesn't work yet
   #lines =<< trim END
   #    vim9script
-  #    let mylist = [[]]
+  #    var mylist = [[]]
   #    mylist[0] += [#{one: 'one'}]
   #    def Func()
-  #      let dd = mylist[0][0]
+  #      var dd = mylist[0][0]
   #      assert_equal('one', dd.one)
   #    enddef
   #    Func()
@@ -719,13 +720,13 @@
 
 def Test_assign_lambda()
   # check if assign a lambda to a variable which type is func or any.
-  let lines =<< trim END
+  var lines =<< trim END
       vim9script
-      let FuncRef = {->123}
+      var FuncRef = {->123}
       assert_equal(123, FuncRef())
-      let FuncRef_Func: func = {->123}
+      var FuncRef_Func: func = {->123}
       assert_equal(123, FuncRef_Func())
-      let FuncRef_Any: any = {->123}
+      var FuncRef_Any: any = {->123}
       assert_equal(123, FuncRef_Any())
   END
   CheckScriptSuccess(lines)
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index f63ecfc..2f82b8d 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -193,21 +193,21 @@
 enddef
 
 def Test_const()
-  CheckDefFailure(['const var = 234', 'var = 99'], 'E1018:')
-  CheckDefFailure(['const one = 234', 'let one = 99'], 'E1017:')
-  CheckDefFailure(['const list = [1, 2]', 'let list = [3, 4]'], 'E1017:')
-  CheckDefFailure(['const two'], 'E1021:')
-  CheckDefFailure(['const &option'], 'E996:')
+  CheckDefFailure(['final var = 234', 'var = 99'], 'E1018:')
+  CheckDefFailure(['final one = 234', 'let one = 99'], 'E1017:')
+  CheckDefFailure(['final list = [1, 2]', 'let list = [3, 4]'], 'E1017:')
+  CheckDefFailure(['final two'], 'E1125:')
+  CheckDefFailure(['final &option'], 'E996:')
 
   let lines =<< trim END
-    const list = [1, 2, 3]
+    final list = [1, 2, 3]
     list[0] = 4
     list->assert_equal([4, 2, 3])
-    const! other = [5, 6, 7]
+    const other = [5, 6, 7]
     other->assert_equal([5, 6, 7])
 
     let varlist = [7, 8]
-    const! constlist = [1, varlist, 3]
+    const constlist = [1, varlist, 3]
     varlist[0] = 77
     # TODO: does not work yet
     # constlist[1][1] = 88
@@ -216,7 +216,7 @@
     constlist->assert_equal([1, [77, 88], 3])
 
     let vardict = #{five: 5, six: 6}
-    const! constdict = #{one: 1, two: vardict, three: 3}
+    const constdict = #{one: 1, two: vardict, three: 3}
     vardict['five'] = 55
     # TODO: does not work yet
     # constdict['two']['six'] = 66
@@ -229,35 +229,35 @@
 
 def Test_const_bang()
   let lines =<< trim END
-      const! var = 234
+      const var = 234
       var = 99
   END
   CheckDefExecFailure(lines, 'E1018:', 2)
   CheckScriptFailure(['vim9script'] + lines, 'E46:', 3)
 
   lines =<< trim END
-      const! ll = [2, 3, 4]
+      const ll = [2, 3, 4]
       ll[0] = 99
   END
   CheckDefExecFailure(lines, 'E1119:', 2)
   CheckScriptFailure(['vim9script'] + lines, 'E741:', 3)
 
   lines =<< trim END
-      const! ll = [2, 3, 4]
+      const ll = [2, 3, 4]
       ll[3] = 99
   END
   CheckDefExecFailure(lines, 'E1118:', 2)
   CheckScriptFailure(['vim9script'] + lines, 'E684:', 3)
 
   lines =<< trim END
-      const! dd = #{one: 1, two: 2}
+      const dd = #{one: 1, two: 2}
       dd["one"] = 99
   END
   CheckDefExecFailure(lines, 'E1121:', 2)
   CheckScriptFailure(['vim9script'] + lines, 'E741:', 3)
 
   lines =<< trim END
-      const! dd = #{one: 1, two: 2}
+      const dd = #{one: 1, two: 2}
       dd["three"] = 99
   END
   CheckDefExecFailure(lines, 'E1120:')
@@ -2532,6 +2532,12 @@
 def Test_let_declaration_fails()
   let lines =<< trim END
     vim9script
+    final var: string
+  END
+  CheckScriptFailure(lines, 'E1125:')
+
+  lines =<< trim END
+    vim9script
     const var: string
   END
   CheckScriptFailure(lines, 'E1021:')
diff --git a/src/version.c b/src/version.c
index 282c04e..03a5581 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1744,
+/**/
     1743,
 /**/
     1742,
diff --git a/src/vim.h b/src/vim.h
index 208128e..9da878e 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -2135,9 +2135,9 @@
 } estack_arg_T;
 
 // Flags for assignment functions.
-#define LET_IS_CONST	1   // ":const"
-#define LET_FORCEIT	2   // ":const!" (LET_IS_CONST is also set)
-#define LET_NO_COMMAND	4   // "var = expr" without ":let" or ":const"
+#define ASSIGN_FINAL	1   // ":final"
+#define ASSIGN_CONST	2   // ":const"
+#define ASSIGN_NO_DECL	4   // "name = expr" without ":let" or ":const"
 
 #include "ex_cmds.h"	    // Ex command defines
 #include "spell.h"	    // spell checking stuff
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 5d0ccb6..2d64015 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -4562,8 +4562,12 @@
 
 /*
  * Compile declaration and assignment:
- * "let var", "let var = expr", "const var = expr" and "var = expr"
- * "arg" points to "var".
+ * "let name"
+ * "var name = expr"
+ * "final name = expr"
+ * "const name = expr"
+ * "name = expr"
+ * "arg" points to "name".
  * Return NULL for an error.
  * Return "arg" if it does not look like a variable list.
  */
@@ -4588,7 +4592,8 @@
     type_T	*member_type = &t_any;
     char_u	*name = NULL;
     char_u	*sp;
-    int		is_decl = cmdidx == CMD_let || cmdidx == CMD_const;
+    int		is_decl = cmdidx == CMD_let || cmdidx == CMD_var
+				 || cmdidx == CMD_final || cmdidx == CMD_const;
 
     // Skip over the "var" or "[var, var]" to get to any "=".
     p = skip_var_list(arg, TRUE, &var_count, &semicolon, TRUE);
@@ -4729,7 +4734,7 @@
 		long	    numval;
 
 		dest = dest_option;
-		if (cmdidx == CMD_const)
+		if (cmdidx == CMD_final || cmdidx == CMD_const)
 		{
 		    emsg(_(e_const_option));
 		    goto theend;
@@ -4968,7 +4973,7 @@
 					    && var_wrong_func_name(name, TRUE))
 		goto theend;
 	    lvar = reserve_local(cctx, var_start, varlen,
-						    cmdidx == CMD_const, type);
+			     cmdidx == CMD_final || cmdidx == CMD_const, type);
 	    if (lvar == NULL)
 		goto theend;
 	    new_local = TRUE;
@@ -5119,6 +5124,11 @@
 							  cctx, FALSE) == FAIL)
 		    goto theend;
 	    }
+	    else if (cmdidx == CMD_final)
+	    {
+		emsg(_(e_final_requires_a_value));
+		goto theend;
+	    }
 	    else if (cmdidx == CMD_const)
 	    {
 		emsg(_(e_const_requires_a_value));
@@ -5283,9 +5293,9 @@
 	}
 	else
 	{
-	    if (is_decl && eap->forceit && cmdidx == CMD_const
-		    && (dest == dest_script || dest == dest_local))
-		// ":const! var": lock the value, but not referenced variables
+	    if (is_decl && cmdidx == CMD_const
+				&& (dest == dest_script || dest == dest_local))
+		// ":const var": lock the value, but not referenced variables
 		generate_LOCKCONST(cctx);
 
 	    switch (dest)
@@ -6915,7 +6925,7 @@
 	    // Expression or function call.
 	    if (ea.cmdidx != CMD_eval)
 	    {
-		// CMD_let cannot happen, compile_assignment() above is used
+		// CMD_var cannot happen, compile_assignment() above is used
 		iemsg("Command from find_ex_command() not handled");
 		goto erret;
 	    }
@@ -6967,6 +6977,8 @@
 		    break;
 
 	    case CMD_let:
+	    case CMD_var:
+	    case CMD_final:
 	    case CMD_const:
 		    line = compile_assignment(p, &ea, ea.cmdidx, &cctx);
 		    if (line == p)
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 02d2c30..955c3aa 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -729,7 +729,7 @@
     funccal_entry_T entry;
 
     save_funccal(&entry);
-    set_var_const(name, NULL, tv, FALSE, LET_NO_COMMAND);
+    set_var_const(name, NULL, tv, FALSE, ASSIGN_NO_DECL);
     restore_funccal();
 }
 
diff --git a/src/vim9script.c b/src/vim9script.c
index 74d0579..399c4a7 100644
--- a/src/vim9script.c
+++ b/src/vim9script.c
@@ -97,6 +97,8 @@
     switch (eap->cmdidx)
     {
 	case CMD_let:
+	case CMD_var:
+	case CMD_final:
 	case CMD_const:
 	case CMD_def:
 	// case CMD_class:
@@ -508,9 +510,12 @@
     int		    called_emsg_before = called_emsg;
     typval_T	    init_tv;
 
-    if (eap->cmdidx == CMD_const)
+    if (eap->cmdidx == CMD_final || eap->cmdidx == CMD_const)
     {
-	emsg(_(e_const_requires_a_value));
+	if (eap->cmdidx == CMD_final)
+	    emsg(_(e_final_requires_a_value));
+	else
+	    emsg(_(e_const_requires_a_value));
 	return arg + STRLEN(arg);
     }