patch 8.2.3314: behavior of exists() in a :def function is unpredictable

Problem:    Behavior of exists() in a :def function is unpredictable.
Solution:   Add exists_compiled().
diff --git a/src/errors.h b/src/errors.h
index 24420e4..3b15d10 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -646,3 +646,7 @@
 	INIT(= N_("E1230: Encryption: sodium_mlock() failed"));
 EXTERN char e_cannot_use_bar_to_separate_commands_here_str[]
 	INIT(= N_("E1231: Cannot use a bar to separate commands here: %s"));
+EXTERN char e_argument_of_exists_compiled_must_be_literal_string[]
+	INIT(= N_("E1232: Argument of exists_compiled() must be a literal string"));
+EXTERN char e_exists_compiled_can_only_be_used_in_def_function[]
+	INIT(= N_("E1233: exists_compiled() can only be used in a :def function"));
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 7006afa..95ae211 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -49,6 +49,7 @@
 static void f_eval(typval_T *argvars, typval_T *rettv);
 static void f_eventhandler(typval_T *argvars, typval_T *rettv);
 static void f_execute(typval_T *argvars, typval_T *rettv);
+static void f_exists_compiled(typval_T *argvars, typval_T *rettv);
 static void f_expand(typval_T *argvars, typval_T *rettv);
 static void f_expandcmd(typval_T *argvars, typval_T *rettv);
 static void f_feedkeys(typval_T *argvars, typval_T *rettv);
@@ -1329,6 +1330,8 @@
 			ret_string,	    f_exepath},
     {"exists",		1, 1, FEARG_1,	    arg1_string,
 			ret_number_bool,    f_exists},
+    {"exists_compiled",	1, 1, FEARG_1,	    arg1_string,
+			ret_number_bool,    f_exists_compiled},
     {"exp",		1, 1, FEARG_1,	    arg1_float_or_nr,
 			ret_float,	    FLOAT_FUNC(f_exp)},
     {"expand",		1, 3, FEARG_1,	    arg3_string_bool_bool,
@@ -3626,6 +3629,12 @@
     rettv->vval.v_number = n;
 }
 
+    static void
+f_exists_compiled(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
+{
+    emsg(_(e_exists_compiled_can_only_be_used_in_def_function));
+}
+
 /*
  * "expand()" function
  */
diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim
index 4fb1df3..6ca6c7b 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -793,42 +793,57 @@
   CheckDefAndScriptFailure2(['exists(10)'], 'E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1')
   call assert_equal(1, exists('&tabstop'))
 
-  if exists('+newoption')
+  var lines =<< trim END
+    if exists('+newoption')
+      if &newoption == 'ok'
+      endif
+    endif
+  END
+  CheckDefFailure(lines, 'E113:')
+  CheckScriptSuccess(lines)
+enddef
+
+def Test_exists_compiled()
+  call assert_equal(1, exists_compiled('&tabstop'))
+  CheckDefAndScriptFailure2(['exists_compiled(10)'], 'E1232:', 'E1233:')
+  CheckDefAndScriptFailure2(['exists_compiled(v:progname)'], 'E1232:', 'E1233:')
+
+  if exists_compiled('+newoption')
     if &newoption == 'ok'
     endif
   endif
-  if exists('&newoption')
+  if exists_compiled('&newoption')
     if &newoption == 'ok'
     endif
   endif
-  if exists('+tabstop')
+  if exists_compiled('+tabstop')
     assert_equal(8, &tabstop)
   else
     assert_report('tabstop option not existing?')
   endif
-  if exists('&tabstop')
+  if exists_compiled('&tabstop')
     assert_equal(8, &tabstop)
   else
     assert_report('tabstop option not existing?')
   endif
 
-  if exists(':DoSomeCommand') >= 2
+  if exists_compiled(':DoSomeCommand') >= 2
     DoSomeCommand
   endif
   assert_equal(4, g:didSomeCommand)
-  if exists(':NoSuchCommand') >= 2
+  if exists_compiled(':NoSuchCommand') >= 2
     NoSuchCommand
   endif
 
   var found = false
-  if exists('*CheckScriptSuccess')
+  if exists_compiled('*CheckScriptSuccess')
     found = true
   endif
   assert_true(found)
-  if exists('*NoSuchFunction')
+  if exists_compiled('*NoSuchFunction')
     NoSuchFunction()
   endif
-  if exists('*no_such_function')
+  if exists_compiled('*no_such_function')
     no_such_function()
   endif
 enddef
diff --git a/src/version.c b/src/version.c
index db75af7..06c89b3 100644
--- a/src/version.c
+++ b/src/version.c
@@ -756,6 +756,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3314,
+/**/
     3313,
 /**/
     3312,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 5effa5c..c08aad6 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -3415,9 +3415,9 @@
     int		is_searchpair;
 
     // We can evaluate "has('name')" at compile time.
-    // We can evaluate some "exists()" values at compile time.
+    // We always evaluate "exists_compiled()" at compile time.
     if ((varlen == 3 && STRNCMP(*arg, "has", 3) == 0)
-	    || (varlen == 6 && STRNCMP(*arg, "exists", 6) == 0))
+	    || (varlen == 15 && STRNCMP(*arg, "exists_compiled", 6) == 0))
     {
 	char_u	    *s = skipwhite(*arg + varlen + 1);
 	typval_T    argvars[2];
@@ -3431,8 +3431,7 @@
 	s = skipwhite(s);
 	if (*s == ')' && argvars[0].v_type == VAR_STRING
 	       && ((is_has && !dynamic_feature(argvars[0].vval.v_string))
-		    || (!is_has && vim_strchr((char_u *)"+&:*",
-						  *argvars[0].vval.v_string))))
+		    || !is_has))
 	{
 	    typval_T	*tv = &ppconst->pp_tv[ppconst->pp_used];
 
@@ -3449,6 +3448,11 @@
 	    return OK;
 	}
 	clear_tv(&argvars[0]);
+	if (!is_has)
+	{
+	    emsg(_(e_argument_of_exists_compiled_must_be_literal_string));
+	    return FAIL;
+	}
     }
 
     if (generate_ppconst(cctx, ppconst) == FAIL)