patch 8.2.4682: Vim9: can use :unlockvar for const variable

Problem:    Vim9: can use :unlockvar for const variable. (Ernie Rael)
Solution:   Check whether the variable is a const.
diff --git a/src/eval.c b/src/eval.c
index 2cde642..b30577f 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1065,7 +1065,7 @@
 		&& lp->ll_tv == &v->di_tv
 		&& ht != NULL && ht == get_script_local_ht())
 	{
-	    svar_T  *sv = find_typval_in_script(lp->ll_tv, 0);
+	    svar_T  *sv = find_typval_in_script(lp->ll_tv, 0, TRUE);
 
 	    // Vim9 script local variable: get the type
 	    if (sv != NULL)
diff --git a/src/evalvars.c b/src/evalvars.c
index dd70427..c1ec5b0 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -1951,23 +1951,42 @@
 								  lp->ll_name);
 		ret = FAIL;
 	    }
-	    else if ((di->di_flags & DI_FLAGS_FIX)
-			    && di->di_tv.v_type != VAR_DICT
-			    && di->di_tv.v_type != VAR_LIST)
-	    {
-		// For historic reasons this error is not given for a list or
-		// dict.  E.g., the b: dict could be locked/unlocked.
-		semsg(_(e_cannot_lock_or_unlock_variable_str), lp->ll_name);
-		ret = FAIL;
-	    }
 	    else
 	    {
-		if (lock)
-		    di->di_flags |= DI_FLAGS_LOCK;
+		if ((di->di_flags & DI_FLAGS_FIX)
+			    && di->di_tv.v_type != VAR_DICT
+			    && di->di_tv.v_type != VAR_LIST)
+		{
+		    // For historic reasons this error is not given for a list
+		    // or dict.  E.g., the b: dict could be locked/unlocked.
+		    semsg(_(e_cannot_lock_or_unlock_variable_str), lp->ll_name);
+		    ret = FAIL;
+		}
 		else
-		    di->di_flags &= ~DI_FLAGS_LOCK;
-		if (deep != 0)
-		    item_lock(&di->di_tv, deep, lock, FALSE);
+		{
+		    if (in_vim9script())
+		    {
+			svar_T  *sv = find_typval_in_script(&di->di_tv,
+								     0, FALSE);
+
+			if (sv != NULL && sv->sv_const != 0)
+			{
+			    semsg(_(e_cannot_change_readonly_variable_str),
+								  lp->ll_name);
+			    ret = FAIL;
+			}
+		    }
+
+		    if (ret == OK)
+		    {
+			if (lock)
+			    di->di_flags |= DI_FLAGS_LOCK;
+			else
+			    di->di_flags &= ~DI_FLAGS_LOCK;
+			if (deep != 0)
+			    item_lock(&di->di_tv, deep, lock, FALSE);
+		    }
+		}
 	    }
 	}
 	*name_end = cc;
@@ -2812,7 +2831,7 @@
 	    if (ht != NULL && ht == get_script_local_ht()
 		    && tv != &SCRIPT_SV(current_sctx.sc_sid)->sv_var.di_tv)
 	    {
-		svar_T *sv = find_typval_in_script(tv, 0);
+		svar_T *sv = find_typval_in_script(tv, 0, TRUE);
 
 		if (sv != NULL)
 		    type = sv->sv_type;
@@ -3557,7 +3576,7 @@
 	    if (var_in_vim9script && (flags & ASSIGN_FOR_LOOP) == 0)
 	    {
 		where_T where = WHERE_INIT;
-		svar_T  *sv = find_typval_in_script(&di->di_tv, sid);
+		svar_T  *sv = find_typval_in_script(&di->di_tv, sid, TRUE);
 
 		if (sv != NULL)
 		{
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index c12f151..2d57524 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -303,7 +303,6 @@
 # define ex_throw		ex_ni
 # define ex_try			ex_ni
 # define ex_unlet		ex_ni
-# define ex_unlockvar		ex_ni
 # define ex_while		ex_ni
 # define ex_import		ex_ni
 # define ex_export		ex_ni
diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro
index bc1e232..04c07fc 100644
--- a/src/proto/vim9script.pro
+++ b/src/proto/vim9script.pro
@@ -16,7 +16,7 @@
 char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
 void update_vim9_script_var(int create, dictitem_T *di, char_u *name, int flags, typval_T *tv, type_T **type, int do_member);
 void hide_script_var(scriptitem_T *si, int idx, int func_defined);
-svar_T *find_typval_in_script(typval_T *dest, scid_T sid);
+svar_T *find_typval_in_script(typval_T *dest, scid_T sid, int must_find);
 int check_script_var_type(svar_T *sv, typval_T *value, char_u *name, where_T where);
 int check_reserved_name(char_u *name);
 /* vim: set ft=c : */
diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim
index 8dea71d..49ca29b 100644
--- a/src/testdir/test_vim9_cmd.vim
+++ b/src/testdir/test_vim9_cmd.vim
@@ -1650,6 +1650,23 @@
       LockIt()
   END
   v9.CheckScriptFailure(lines, 'E1246', 1)
+
+  lines =<< trim END
+      vim9script
+      const name = 'john'
+      unlockvar name
+  END
+  v9.CheckScriptFailure(lines, 'E46', 3)
+
+  lines =<< trim END
+      vim9script
+      const name = 'john'
+      def UnLockIt()
+        unlockvar name
+      enddef
+      UnLockIt()
+  END
+  v9.CheckScriptFailure(lines, 'E46', 1)
 enddef
 
 def Test_substitute_expr()
diff --git a/src/userfunc.c b/src/userfunc.c
index 052923e..c80a490 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -1697,7 +1697,7 @@
 	{
 	    if (!did_type && type != NULL && ht == get_script_local_ht())
 	    {
-		svar_T  *sv = find_typval_in_script(tv, 0);
+		svar_T  *sv = find_typval_in_script(tv, 0, TRUE);
 
 		if (sv != NULL)
 		    *type = sv->sv_type;
diff --git a/src/version.c b/src/version.c
index 79a3bad..8b7450c 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4682,
+/**/
     4681,
 /**/
     4680,
diff --git a/src/vim9script.c b/src/vim9script.c
index cd9ff92..adb01e8 100644
--- a/src/vim9script.c
+++ b/src/vim9script.c
@@ -956,7 +956,7 @@
     }
     else
     {
-	sv = find_typval_in_script(&di->di_tv, 0);
+	sv = find_typval_in_script(&di->di_tv, 0, TRUE);
     }
     if (sv != NULL)
     {
@@ -1053,10 +1053,11 @@
 /*
  * Find the script-local variable that links to "dest".
  * If "sid" is zero use the current script.
+ * if "must_find" is TRUE and "dest" cannot be found report an internal error.
  * Returns NULL if not found and give an internal error.
  */
     svar_T *
-find_typval_in_script(typval_T *dest, scid_T sid)
+find_typval_in_script(typval_T *dest, scid_T sid, int must_find)
 {
     scriptitem_T    *si = SCRIPT_ITEM(sid == 0 ? current_sctx.sc_sid : sid);
     int		    idx;
@@ -1076,7 +1077,8 @@
 	if (sv->sv_name != NULL && sv->sv_tv == dest)
 	    return sv;
     }
-    iemsg("find_typval_in_script(): not found");
+    if (must_find)
+	iemsg("find_typval_in_script(): not found");
     return NULL;
 }