patch 9.0.2170: Vim9: no support for const/final class/objects vars

Problem:  Vim9: no support for const/final class/objects vars
Solution: Support final and const object and class variables

closes: #13655

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/vim9class.c b/src/vim9class.c
index 9d17140..a1f4aa2 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -152,6 +152,19 @@
     return OK;
 }
 
+typedef struct oc_newmember_S oc_newmember_T;
+struct oc_newmember_S
+{
+    garray_T	*gap;
+    char_u	*varname;
+    char_u	*varname_end;
+    int		has_public;
+    int		has_final;
+    int		has_type;
+    type_T	*type;
+    char_u	*init_expr;
+};
+
 /*
  * Add a member to an object or a class.
  * Returns OK when successful, "init_expr" will be consumed then.
@@ -163,6 +176,8 @@
     char_u	*varname,
     char_u	*varname_end,
     int		has_public,
+    int		has_final,
+    int		has_const,
     int		has_type,
     type_T	*type,
     char_u	*init_expr)
@@ -173,7 +188,12 @@
     m->ocm_name = vim_strnsave(varname, varname_end - varname);
     m->ocm_access = has_public ? VIM_ACCESS_ALL
 		      : *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
-    m->ocm_has_type = has_type;
+    if (has_final)
+	m->ocm_flags |= OCMFLAG_FINAL;
+    if (has_const)
+	m->ocm_flags |= OCMFLAG_CONST;
+    if (has_type)
+	m->ocm_flags |= OCMFLAG_HAS_TYPE;
     m->ocm_type = type;
     if (init_expr != NULL)
 	m->ocm_init = init_expr;
@@ -1132,7 +1152,7 @@
 	    if (etv != NULL)
 	    {
 		if (m->ocm_type->tt_type == VAR_ANY
-			&& !m->ocm_has_type
+			&& !(m->ocm_flags & OCMFLAG_HAS_TYPE)
 			&& etv->v_type != VAR_SPECIAL)
 		    // If the member variable type is not yet set, then use
 		    // the initialization expression type.
@@ -1149,6 +1169,8 @@
 	    tv->v_type = m->ocm_type->tt_type;
 	    tv->vval.v_string = NULL;
 	}
+	if (m->ocm_flags & OCMFLAG_CONST)
+	    item_lock(tv, DICT_MAXNEST, TRUE, TRUE);
     }
 }
 
@@ -1558,9 +1580,10 @@
 	    has_public = TRUE;
 	    p = skipwhite(line + 6);
 
-	    if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "static", 6) != 0)
+	    if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "static", 6) != 0
+		&& STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
 	    {
-		emsg(_(e_public_must_be_followed_by_var_or_static));
+		emsg(_(e_public_must_be_followed_by_var_static_final_or_const));
 		break;
 	    }
 	}
@@ -1616,22 +1639,60 @@
 	    has_static = TRUE;
 	    p = skipwhite(ps + 6);
 
-	    if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "def", 3) != 0)
+	    if (STRNCMP(p, "var", 3) != 0 && STRNCMP(p, "def", 3) != 0
+		&& STRNCMP(p, "final", 5) != 0 && STRNCMP(p, "const", 5) != 0)
 	    {
-		emsg(_(e_static_must_be_followed_by_var_or_def));
+		emsg(_(e_static_must_be_followed_by_var_def_final_or_const));
 		break;
 	    }
 	}
 
+	int has_final = FALSE;
+	int has_var = FALSE;
+	int has_const = FALSE;
+	if (checkforcmd(&p, "var", 3))
+	    has_var = TRUE;
+	else if (checkforcmd(&p, "final", 5))
+	{
+	    if (!is_class)
+	    {
+		emsg(_(e_final_variable_not_supported_in_interface));
+		break;
+	    }
+	    has_final = TRUE;
+	}
+	else if (checkforcmd(&p, "const", 5))
+	{
+	    if (!is_class)
+	    {
+		emsg(_(e_const_variable_not_supported_in_interface));
+		break;
+	    }
+	    has_const = TRUE;
+	}
+	p = skipwhite(p);
+
 	// object members (public, read access, private):
 	//	"var _varname"
 	//	"var varname"
 	//	"public var varname"
+	//	"final _varname"
+	//	"final varname"
+	//	"public final varname"
+	//	"const _varname"
+	//	"const varname"
+	//	"public const varname"
 	// class members (public, read access, private):
 	//	"static var _varname"
 	//	"static var varname"
 	//	"public static var varname"
-	if (checkforcmd(&p, "var", 3))
+	//	"static final _varname"
+	//	"static final varname"
+	//	"public static final varname"
+	//	"static const _varname"
+	//	"static const varname"
+	//	"public static const varname"
+	if (has_var || has_final || has_const)
 	{
 	    char_u *varname = p;
 	    char_u *varname_end = NULL;
@@ -1671,8 +1732,9 @@
 		vim_free(init_expr);
 		break;
 	    }
-	    if (add_member(has_static ? &classmembers : &objmembers, varname, varname_end,
-				has_public, has_type, type, init_expr) == FAIL)
+	    if (add_member(has_static ? &classmembers : &objmembers, varname,
+				varname_end, has_public, has_final, has_const,
+				has_type, type, init_expr) == FAIL)
 	    {
 		vim_free(init_expr);
 		break;
@@ -2780,6 +2842,40 @@
 }
 
 /*
+ * Return TRUE if object/class variable "m" is read-only.
+ * Also give an error message.
+ */
+    int
+oc_var_check_ro(class_T *cl, ocmember_T *m)
+{
+    if (m->ocm_flags & (OCMFLAG_FINAL | OCMFLAG_CONST))
+    {
+	semsg(_(e_cannot_change_readonly_variable_str_in_class_str),
+		m->ocm_name, cl->class_name);
+	return TRUE;
+    }
+    return FALSE;
+}
+
+/*
+ * Lock all the constant object variables.  Called after creating and
+ * initializing a new object.
+ */
+    void
+obj_lock_const_vars(object_T *obj)
+{
+    for (int i = 0; i < obj->obj_class->class_obj_member_count; i++)
+    {
+	ocmember_T *ocm = &obj->obj_class->class_obj_members[i];
+	if (ocm->ocm_flags & OCMFLAG_CONST)
+	{
+	    typval_T *mtv = ((typval_T *)(obj + 1)) + i;
+	    item_lock(mtv, DICT_MAXNEST, TRUE, TRUE);
+	}
+    }
+}
+
+/*
  * Make a copy of an object.
  */
     void