changeset 34676:5b25ec43f208 v9.1.0219

patch 9.1.0219: Vim9: No enum support Commit: https://github.com/vim/vim/commit/3164cf8f12f14b725b918e3170bb0a9085af8298 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Thu Mar 28 10:36:42 2024 +0100 patch 9.1.0219: Vim9: No enum support Problem: No enum support Solution: Implement enums for Vim9 script (Yegappan Lakshmanan) closes: #14224 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Thu, 28 Mar 2024 10:45:06 +0100
parents 42bca55140ec
children ed56f31f1054
files runtime/doc/builtin.txt runtime/doc/eval.txt runtime/doc/tags runtime/doc/todo.txt runtime/doc/version9.txt runtime/doc/vim9class.txt src/errors.h src/eval.c src/evalfunc.c src/evalvars.c src/ex_cmds.h src/proto/vim9class.pro src/structs.h src/testdir/Make_all.mak src/testdir/test_vim9_class.vim src/testdir/test_vim9_enum.vim src/testing.c src/typval.c src/version.c src/vim.h src/vim9class.c src/vim9compile.c src/vim9execute.c src/vim9expr.c src/vim9type.c
diffstat 25 files changed, 2325 insertions(+), 141 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -1,4 +1,4 @@
-*builtin.txt*	For Vim version 9.1.  Last change: 2024 Mar 23
+*builtin.txt*	For Vim version 9.1.  Last change: 2024 Mar 28
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -9598,6 +9598,8 @@ string({expr})	Return {expr} converted t
 			Dictionary	{key: value, key: value}
 			Class		class SomeName
 			Object		object of SomeName {lnum: 1, col: 3}
+			Enum		enum EnumName
+			EnumValue	enum.value
 
 		When a |List| or |Dictionary| has a recursive reference it is
 		replaced by "[...]" or "{...}".  Using eval() on the result
@@ -10461,6 +10463,8 @@ type({expr})	The result is a Number repr
 			Class:	   12  |v:t_class|
 			Object:	   13  |v:t_object|
 			Typealias: 14  |v:t_typealias|
+			Enum:	   15  |v:t_enum|
+			EnumValue: 16  |v:t_enumvalue|
 		For backward compatibility, this method can be used: >
 			:if type(myvar) == type(0)
 			:if type(myvar) == type("")
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1,4 +1,4 @@
-*eval.txt*	For Vim version 9.1.  Last change: 2024 Mar 20
+*eval.txt*	For Vim version 9.1.  Last change: 2024 Mar 28
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -2601,6 +2601,10 @@ v:t_class	Value of |class| type.  Read-o
 v:t_object	Value of |object| type.  Read-only.  See: |type()|
 					*v:t_typealias* *t_typealias-variable*
 v:t_typealias	Value of |typealias| type.  Read-only.  See: |type()|
+					*v:t_enum* *t_enum-variable*
+v:t_enum	Value of |enum| type.  Read-only.  See: |type()|
+					*v:t_enumvalue* *t_enumvalue-variable*
+v:t_enumvalue	Value of |enumvalue| type.  Read-only.  See: |type()|
 
 				*v:termresponse* *termresponse-variable*
 v:termresponse	The escape sequence returned by the terminal for the |t_RV|
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -4520,7 +4520,20 @@ E1410	vim9class.txt	/*E1410*
 E1411	vim9class.txt	/*E1411*
 E1412	vim9class.txt	/*E1412*
 E1413	vim9class.txt	/*E1413*
+E1414	vim9class.txt	/*E1414*
+E1415	vim9class.txt	/*E1415*
+E1416	vim9class.txt	/*E1416*
+E1417	vim9class.txt	/*E1417*
+E1418	vim9class.txt	/*E1418*
+E1419	vim9class.txt	/*E1419*
 E142	message.txt	/*E142*
+E1420	vim9class.txt	/*E1420*
+E1421	vim9class.txt	/*E1421*
+E1422	vim9class.txt	/*E1422*
+E1423	vim9class.txt	/*E1423*
+E1424	vim9class.txt	/*E1424*
+E1425	vim9class.txt	/*E1425*
+E1426	vim9class.txt	/*E1426*
 E143	autocmd.txt	/*E143*
 E144	various.txt	/*E144*
 E145	starting.txt	/*E145*
@@ -6874,6 +6887,12 @@ encryption	editing.txt	/*encryption*
 end	intro.txt	/*end*
 end-of-file	pattern.txt	/*end-of-file*
 enlightened-terminal	syntax.txt	/*enlightened-terminal*
+enum	vim9class.txt	/*enum*
+enum-constructor	vim9class.txt	/*enum-constructor*
+enum-name	vim9class.txt	/*enum-name*
+enum-ordinal	vim9class.txt	/*enum-ordinal*
+enum-values	vim9class.txt	/*enum-values*
+enumvalue	vim9class.txt	/*enumvalue*
 environ()	builtin.txt	/*environ()*
 eol-and-eof	editing.txt	/*eol-and-eof*
 erlang.vim	syntax.txt	/*erlang.vim*
@@ -10290,6 +10309,8 @@ t_dl	term.txt	/*t_dl*
 t_ds	term.txt	/*t_ds*
 t_ed	version4.txt	/*t_ed*
 t_el	version4.txt	/*t_el*
+t_enum-variable	eval.txt	/*t_enum-variable*
+t_enumvalue-variable	eval.txt	/*t_enumvalue-variable*
 t_f1	version4.txt	/*t_f1*
 t_f10	version4.txt	/*t_f10*
 t_f2	version4.txt	/*t_f2*
@@ -10863,6 +10884,8 @@ v:t_bool	eval.txt	/*v:t_bool*
 v:t_channel	eval.txt	/*v:t_channel*
 v:t_class	eval.txt	/*v:t_class*
 v:t_dict	eval.txt	/*v:t_dict*
+v:t_enum	eval.txt	/*v:t_enum*
+v:t_enumvalue	eval.txt	/*v:t_enumvalue*
 v:t_float	eval.txt	/*v:t_float*
 v:t_func	eval.txt	/*v:t_func*
 v:t_job	eval.txt	/*v:t_job*
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -1,4 +1,4 @@
-*todo.txt*      For Vim version 9.1.  Last change: 2024 Mar 03
+*todo.txt*      For Vim version 9.1.  Last change: 2024 Mar 28
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -144,7 +144,6 @@ Further Vim9 improvements:
   - More efficient way for interface member index than iterating over list?
   - a variant of type() that returns a different type for each class?
       list<number> and list<string> should also differ.
-- implement :enum
 - Promise class, could be used to wait on a popup close callback?
 - class local to a function
 - Use Vim9 for more runtime files.
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -41548,6 +41548,8 @@ Vim9 script
 Add support for internal builtin functions with vim9 objects, see
 |builtin-object-methods|
 
+Enum support for Vim9 script |:enum|
+
 Other improvements				*new-other-9.2*
 ------------------
 
--- a/runtime/doc/vim9class.txt
+++ b/runtime/doc/vim9class.txt
@@ -1,4 +1,4 @@
-*vim9class.txt*	For Vim version 9.1.  Last change: 2024 Mar 03
+*vim9class.txt*	For Vim version 9.1.  Last change: 2024 Mar 28
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -904,19 +904,125 @@ aliased: >
 
 8.  Enum					*Vim9-enum* *:enum* *:endenum*
 
-{not implemented yet}
-
+						*enum* *E1418* *E1419* *E1420*
 An enum is a type that can have one of a list of values.  Example: >
 
-	:enum Color
-		White
-		Red
-		Green
-		Blue
-		Black
-	:endenum
+    :enum Color
+	White,
+	Red,
+	Green, Blue, Black
+    :endenum
+<
+						*enumvalue* *E1422*
+The enum values are separated by commas.  More than one enum value can be
+listed in a single line.  The final enum value should not be followed by a
+comma.
+
+An enum value is accessed using the enum name followed by the value name: >
+
+    var a: Color = Color.Blue
+<
+Enums are treated as classes, where each enum value is essentially an instance
+of that class.  Unlike typical object instantiation with the |new()| method,
+enum instances cannot be created this way.
+
+An enum can only be defined in a |Vim9| script file.	*E1414*
+An enum cannot be defined inside a function.
+
+							*E1415*
+An enum name must start with an uppercase letter.  The name of an enum value
+in an enum can start with an upper or lowercase letter.
+
+							*E1416*
+An enum can implement an interface but cannot extend a class: >
+
+    enum MyEnum implements MyIntf
+	Value1,
+	Value2
+
+	def SomeMethod()
+	enddef
+    endenum
+<
+							*enum-constructor*
+The enum value objects in an enum are constructed like any other objects using
+the |new()| method.  Arguments can be passed to the enum constructor by
+specifying them after the enum value name, just like calling a function.  The
+default constructor doesn't have any arguments.
+
+							*E1417*
+An enum can contain class variables, class methods, object variables and
+object methods.  The methods in an enum cannot be |:abstract| methods.
+
+The following example shows an enum with object variables and methods: >
+
+    vim9script
+    enum Planet
+	Earth(1, false),
+	Jupiter(95, true),
+	Saturn(146, true)
 
+	var moons: number
+	var has_rings: bool
+	def GetMoons(): number
+	    return this.moons
+	enddef
+    endenum
+    echo Planet.Jupiter.GetMoons()
+    echo Planet.Earth.has_rings
+<
+						*E1421* *E1423* *E1424* *E1425*
+Enums and their values are immutable. They cannot be modified after
+declaration and cannot be utilized as numerical or string types.
 
+						*enum-name*
+Each enum value object has a "name" instance variable which contains the name
+of the enum value.  This is a readonly variable.
+
+						*enum-ordinal* *E1426*
+Each enum value has an associated ordinal number starting with 0.  The ordinal
+number of an enum value can be accessed using the "ordinal" instance variable.
+This is a readonly variable.  Note that if the ordering of the enum values in
+an enum is changed, then their ordinal values will also change.
+
+						*enum-values*
+All the values in an enum can be accessed using the "values" class variable
+which is a List of the enum objects.  This is a readonly variable.
+
+Example: >
+    enum Planet
+	Mercury,
+	Venus,
+	Earth
+    endenum
+
+    echo Planet.Mercury
+    echo Planet.Venus.name
+    echo Planet.Venus.ordinal
+    for p in Planet.values
+	# ...
+    endfor
+<
+An enum is a class with class variables for the enum value objects and object
+variables for the enum value name and the enum value ordinal: >
+
+    enum Planet
+	Mercury,
+	Venus
+    endenum
+<
+The above enum definition is equivalent to the following class definition: >
+
+    class Planet
+      public static final Mercury: Planet = Planet.new('Mercury', 0)
+      public static final Venus: Planet = Planet.new('Venus', 1)
+
+      public static const values: list<Planet> = [Planet.Mercury, Planet.Venus]
+
+      public const name: string
+      public const ordinal: number
+    endclass
+<
 ==============================================================================
 
 9.  Rationale
--- a/src/errors.h
+++ b/src/errors.h
@@ -3585,8 +3585,38 @@ EXTERN char e_builtin_object_method_str_
 	INIT(= N_("E1412: Builtin object method \"%s\" not supported"));
 EXTERN char e_builtin_class_method_not_supported[]
 	INIT(= N_("E1413: Builtin class method not supported"));
-#endif
-// E1415 - E1499 unused (reserved for Vim9 class support)
+EXTERN char e_enum_can_only_be_defined_in_vim9_script[]
+       INIT(= N_("E1414: Enum can only be defined in Vim9 script"));
+EXTERN char e_enum_name_must_start_with_uppercase_letter_str[]
+	INIT(= N_("E1415: Enum name must start with an uppercase letter: %s"));
+EXTERN char e_enum_cannot_extend_class[]
+	INIT(= N_("E1416: Enum cannot extend a class or enum"));
+EXTERN char e_abstract_cannot_be_used_in_enum[]
+	INIT(= N_("E1417: Abstract cannot be used in an Enum"));
+EXTERN char e_invalid_enum_value_declaration_str[]
+	INIT(= N_("E1418: Invalid enum value declaration: %s"));
+EXTERN char e_not_valid_command_in_enum_str[]
+	INIT(= N_("E1419: Not a valid command in an Enum: %s"));
+EXTERN char e_missing_endenum[]
+	INIT(= N_("E1420: Missing :endenum"));
+EXTERN char e_using_enum_as_value_str[]
+	INIT(= N_("E1421: Enum \"%s\" cannot be used as a value"));
+EXTERN char e_enum_value_str_not_found_in_enum_str[]
+	INIT(= N_("E1422: Enum value \"%s\" not found in enum \"%s\""));
+EXTERN char e_enumvalue_str_cannot_be_modified[]
+	INIT(= N_("E1423: Enum value \"%s.%s\" cannot be modified"));
+EXTERN char e_using_enum_str_as_number[]
+	INIT(= N_("E1424: Using an Enum \"%s\" as a Number"));
+EXTERN char e_using_enum_str_as_string[]
+	INIT(= N_("E1425: Using an Enum \"%s\" as a String"));
+EXTERN char e_enum_str_ordinal_cannot_be_modified[]
+	INIT(= N_("E1426: Enum \"%s\" ordinal value cannot be modified"));
+EXTERN char e_enum_str_name_cannot_be_modified[]
+	INIT(= N_("E1427: Enum \"%s\" name cannot be modified"));
+EXTERN char e_duplicate_enum_str[]
+	INIT(= N_("E1428: Duplicate enum value: %s"));
+#endif
+// E1429 - E1499 unused (reserved for Vim9 class support)
 EXTERN char e_cannot_mix_positional_and_non_positional_str[]
 	INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s"));
 EXTERN char e_fmt_arg_nr_unused_str[]
--- a/src/eval.c
+++ b/src/eval.c
@@ -1119,7 +1119,18 @@ get_lval_check_access(
 		if (*p == '[' || *p == '.')
 		    break;
 		if ((flags & GLV_READ_ONLY) == 0)
-		    msg = e_variable_is_not_writable_str;
+		{
+		    if (IS_ENUM(cl))
+		    {
+			if (om->ocm_type->tt_type == VAR_OBJECT)
+			    semsg(_(e_enumvalue_str_cannot_be_modified),
+				    cl->class_name, om->ocm_name);
+			else
+			    msg = e_variable_is_not_writable_str;
+		    }
+		    else
+			msg = e_variable_is_not_writable_str;
+		}
 		break;
 	    case VIM_ACCESS_ALL:
 		break;
@@ -6310,9 +6321,15 @@ echo_string_core(
 	case VAR_CLASS:
 	    {
 		class_T *cl = tv->vval.v_class;
-		size_t len = 6 + (cl == NULL ? 9 : STRLEN(cl->class_name)) + 1;
+		char *s = "class";
+		if (IS_INTERFACE(cl))
+		    s = "interface";
+		else if (IS_ENUM(cl))
+		    s = "enum";
+		size_t len = STRLEN(s) + 1 +
+		    (cl == NULL ? 9 : STRLEN(cl->class_name)) + 1;
 		r = *tofree = alloc(len);
-		vim_snprintf((char *)r, len, "class %s",
+		vim_snprintf((char *)r, len, "%s %s", s,
 			    cl == NULL ? "[unknown]" : (char *)cl->class_name);
 	    }
 	    break;
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -11486,15 +11486,31 @@ f_type(typval_T *argvars, typval_T *rett
 	case VAR_CHANNEL: n = VAR_TYPE_CHANNEL; break;
 	case VAR_BLOB:    n = VAR_TYPE_BLOB; break;
 	case VAR_INSTR:   n = VAR_TYPE_INSTR; break;
-	case VAR_CLASS:   n = VAR_TYPE_CLASS; break;
-	case VAR_OBJECT:  n = VAR_TYPE_OBJECT; break;
 	case VAR_TYPEALIAS: n = VAR_TYPE_TYPEALIAS; break;
+	case VAR_CLASS:
+	    {
+		class_T *cl = argvars[0].vval.v_class;
+		if (IS_ENUM(cl))
+		    n = VAR_TYPE_ENUM;
+		else
+		    n = VAR_TYPE_CLASS;
+		break;
+	    }
+	case VAR_OBJECT:
+	    {
+		class_T *cl = argvars[0].vval.v_object->obj_class;
+		if (IS_ENUM(cl))
+		    n = VAR_TYPE_ENUMVALUE;
+		else
+		    n = VAR_TYPE_OBJECT;
+		break;
+	    }
 	case VAR_UNKNOWN:
 	case VAR_ANY:
 	case VAR_VOID:
-	     internal_error_no_abort("f_type(UNKNOWN)");
-	     n = -1;
-	     break;
+	    internal_error_no_abort("f_type(UNKNOWN)");
+	    n = -1;
+	    break;
     }
     rettv->vval.v_number = n;
 }
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -159,6 +159,8 @@ static struct vimvar
     {VV_NAME("maxcol",		 VAR_NUMBER), NULL, VV_RO},
     {VV_NAME("python3_version",	 VAR_NUMBER), NULL, VV_RO},
     {VV_NAME("t_typealias",	 VAR_NUMBER), NULL, VV_RO},
+    {VV_NAME("t_enum",		 VAR_NUMBER), NULL, VV_RO},
+    {VV_NAME("t_enumvalue",	 VAR_NUMBER), NULL, VV_RO},
 };
 
 // shorthand
@@ -262,6 +264,8 @@ evalvars_init(void)
     set_vim_var_nr(VV_TYPE_CLASS,   VAR_TYPE_CLASS);
     set_vim_var_nr(VV_TYPE_OBJECT,  VAR_TYPE_OBJECT);
     set_vim_var_nr(VV_TYPE_TYPEALIAS,  VAR_TYPE_TYPEALIAS);
+    set_vim_var_nr(VV_TYPE_ENUM,  VAR_TYPE_ENUM);
+    set_vim_var_nr(VV_TYPE_ENUMVALUE,  VAR_TYPE_ENUMVALUE);
 
     set_vim_var_nr(VV_ECHOSPACE,    sc_col - 1);
 
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -595,7 +595,7 @@ EXCMD(CMD_endwhile,	"endwhile",	ex_endwh
 EXCMD(CMD_enew,		"enew",		ex_edit,
 	EX_BANG|EX_TRLBAR,
 	ADDR_NONE),
-EXCMD(CMD_enum,		"enum",		ex_enum,
+EXCMD(CMD_enum,		"enum",		ex_class,
 	EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_EXPORT,
 	ADDR_NONE),
 EXCMD(CMD_eval,		"eval",		ex_eval,
--- a/src/proto/vim9class.pro
+++ b/src/proto/vim9class.pro
@@ -3,6 +3,7 @@ int object_index_from_itf_index(class_T 
 int is_valid_builtin_obj_methodname(char_u *funcname);
 ufunc_T *class_get_builtin_method(class_T *cl, class_builtin_T builtin_method, int *method_idx);
 void ex_class(exarg_T *eap);
+void enum_set_internal_obj_vars(class_T *en, object_T *enval);
 type_T *oc_member_type(class_T *cl, int is_object, char_u *name, char_u *name_end, int *member_idx);
 type_T *oc_member_type_by_idx(class_T *cl, int is_object, int member_idx);
 void ex_enum(exarg_T *eap);
--- a/src/structs.h
+++ b/src/structs.h
@@ -1562,9 +1562,10 @@ struct itf2class_S {
     // array with ints follows
 };
 
-#define CLASS_INTERFACE	    1
-#define CLASS_EXTENDED	    2	    // another class extends this one
-#define CLASS_ABSTRACT	    4	    // abstract class
+#define CLASS_INTERFACE	    0x1
+#define CLASS_EXTENDED	    0x2	    // another class extends this one
+#define CLASS_ABSTRACT	    0x4	    // abstract class
+#define CLASS_ENUM	    0x8	    // enum
 
 // "class_T": used for v_class of typval of VAR_CLASS
 // Also used for an interface (class_flags has CLASS_INTERFACE).
@@ -1613,6 +1614,9 @@ struct class_S
     type_T	class_object_type;	// same as class_type but VAR_OBJECT
 };
 
+#define IS_INTERFACE(cl)	((cl)->class_flags & CLASS_INTERFACE)
+#define IS_ENUM(cl)		((cl)->class_flags & CLASS_ENUM)
+
 // Used for v_object of typval of VAR_OBJECT.
 // The member variables follow in an array of typval_T.
 struct object_S
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -40,6 +40,7 @@ TEST_VIM9 = \
 	test_vim9_class \
 	test_vim9_cmd \
 	test_vim9_disassemble \
+	test_vim9_enum \
 	test_vim9_expr \
 	test_vim9_fails \
 	test_vim9_func \
@@ -53,6 +54,7 @@ TEST_VIM9_RES = \
 	test_vim9_class.res \
 	test_vim9_cmd.res \
 	test_vim9_disassemble.res \
+	test_vim9_enum.res \
 	test_vim9_expr.res \
 	test_vim9_fails.res \
 	test_vim9_func.res \
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -2275,6 +2275,18 @@ def Test_interface_basics()
   v9.CheckScriptSuccess(lines)
 enddef
 
+" Test for using string() with an interface
+def Test_interface_to_string()
+  var lines =<< trim END
+    vim9script
+    interface Intf
+      def Method(nr: number)
+    endinterface
+    assert_equal("interface Intf", string(Intf))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
 def Test_class_implements_interface()
   var lines =<< trim END
     vim9script
@@ -10391,6 +10403,23 @@ def Test_compound_op_in_objmethod_lambda
   v9.CheckScriptSuccess(lines)
 enddef
 
+" Test for using test_refcount() with a class and an object
+def Test_class_object_refcount()
+  var lines =<< trim END
+    vim9script
+    class A
+    endclass
+    var a: A = A.new()
+    assert_equal(2, test_refcount(A))
+    assert_equal(1, test_refcount(a))
+    var b = a
+    assert_equal(2, test_refcount(A))
+    assert_equal(2, test_refcount(a))
+    assert_equal(2, test_refcount(b))
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 " call a lambda function in one object from another object
 def Test_lambda_invocation_across_classes()
   var lines =<< trim END
@@ -10420,4 +10449,18 @@ def Test_lambda_invocation_across_classe
   v9.CheckScriptSuccess(lines)
 enddef
 
+" Test for using a class member which is an object of the current class
+def Test_current_class_object_class_member()
+  var lines =<< trim END
+    vim9script
+    class A
+      public static var obj1: A = A.new(10)
+      var n: number
+    endclass
+    defcompile
+    assert_equal(10, A.obj1.n)
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
new file mode 100644
--- /dev/null
+++ b/src/testdir/test_vim9_enum.vim
@@ -0,0 +1,1476 @@
+" Test Vim9 enums
+
+source check.vim
+import './vim9.vim' as v9
+
+" Test for parsing an enum definition
+def Test_enum_parse()
+  # enum supported only in a Vim9 script
+  var lines =<< trim END
+    enum Foo
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1414: Enum can only be defined in Vim9 script', 1)
+
+  # First character in an enum name should be capitalized.
+  lines =<< trim END
+    vim9script
+    enum foo
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1415: Enum name must start with an uppercase letter: foo', 2)
+
+  # Only alphanumeric characters are supported in an enum name
+  lines =<< trim END
+    vim9script
+    enum Foo@bar
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1315: White space required after name: Foo@bar', 2)
+
+  # Unsupported keyword (instead of enum)
+  lines =<< trim END
+    vim9script
+    noenum Something
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E492: Not an editor command: noenum Something', 2)
+
+  # Only the complete word "enum" should be recognized
+  lines =<< trim END
+    vim9script
+    enums Something
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E492: Not an editor command: enums Something', 2)
+
+  # The complete "endenum" should be specified.
+  lines =<< trim END
+    vim9script
+    enum Something
+    enden
+  END
+  v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: enden', 3)
+
+  # Only the complete word "endenum" should be recognized
+  lines =<< trim END
+    vim9script
+    enum Something
+    endenums
+  END
+  v9.CheckSourceFailure(lines, 'E1420: Missing :endenum', 4)
+
+  # all lower case should be used for "enum"
+  lines =<< trim END
+    vim9script
+    Enum Something
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E492: Not an editor command: Enum Something', 2)
+
+  # all lower case should be used for "endenum"
+  lines =<< trim END
+    vim9script
+    enum Something
+    Endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1420: Missing :endenum', 4)
+
+  # Additional words after "endenum"
+  lines =<< trim END
+    vim9script
+    enum Something
+    endenum school's out
+  END
+  v9.CheckSourceFailure(lines, "E488: Trailing characters: school's out", 3)
+
+  # Additional commands after "endenum"
+  lines =<< trim END
+    vim9script
+    enum Something
+    endenum | echo 'done'
+  END
+  v9.CheckSourceFailure(lines, "E488: Trailing characters: | echo 'done'", 3)
+
+  # Try to define enum in a single command
+  lines =<< trim END
+    vim9script
+    enum Something | endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1420: Missing :endenum', 3)
+
+  # Try to define an enum with the same name as an existing variable
+  lines =<< trim END
+    vim9script
+    var Something: list<number> = [1]
+    enum Something
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "Something"', 3)
+
+  # Unsupported special character following enum name
+  lines =<< trim END
+    vim9script
+    enum Foo
+      first,
+      second : 20
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: : 20', 4)
+
+  # Try initializing an enum item with a number
+  lines =<< trim END
+    vim9script
+    enum Foo
+      first,
+      second = 2
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: = 2', 4)
+
+  # Try initializing an enum item with a String
+  lines =<< trim END
+    vim9script
+    enum Foo
+      first,
+      second = 'second'
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1123: Missing comma before argument: = 'second'", 4)
+
+  # Try initializing an enum item with a List
+  lines =<< trim END
+    vim9script
+    enum Foo
+      first,
+      second = []
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: = []', 4)
+
+  # Use a colon after name
+  lines =<< trim END
+    vim9script
+    enum Foo
+
+      # first
+      first:
+      second
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: :', 5)
+
+  # Use a '=='
+  lines =<< trim END
+    vim9script
+    enum Foo
+      first == 1
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: == 1', 3)
+
+  # Missing comma after an enum item
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury
+      venus
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1419: Not a valid command in an Enum: venus', 4)
+
+  # Comma at the beginning of an item
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury
+      ,venus
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1419: Not a valid command in an Enum: ,venus', 4)
+  # Space before comma
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury ,
+      venus
+    endenum
+  END
+  v9.CheckSourceFailure(lines, "E1068: No white space allowed before ','", 3)
+
+  # No space after comma
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury,venus
+    endenum
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ',': mercury,venus", 3)
+
+  # no comma between items in the same line
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury venus earth
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: venus earth', 3)
+
+  # No space after an item and comment between items
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury
+
+      # Venus
+      venus
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1419: Not a valid command in an Enum: venus', 6)
+
+  # Comma is supported for the last item
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury,
+      venus,
+    endenum
+    var p: Planet
+    p = Planet.mercury
+    p = Planet.venus
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # invalid enum value declaration
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      $%@
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1418: Invalid enum value declaration: $%@', 4)
+
+  # Duplicate enum value
+  lines =<< trim END
+    vim9script
+    enum A
+      Foo,
+      Bar,
+      Foo
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1428: Duplicate enum value: Foo', 5)
+
+  # Duplicate enum value in the same line
+  lines =<< trim END
+    vim9script
+    enum A
+      Foo, Bar, Foo,
+      Bar
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1428: Duplicate enum value: Foo', 3)
+
+  # Try extending a class when defining an enum
+  lines =<< trim END
+    vim9script
+    class Foo
+    endclass
+    enum Bar extends Foo
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1416: Enum cannot extend a class or enum', 4)
+
+  # Try extending an enum
+  lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    enum Bar extends Foo
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1416: Enum cannot extend a class or enum', 4)
+
+  # Try extending an enum using a class
+  lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    class Bar extends Foo
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1354: Cannot extend Foo', 5)
+
+  # Try implementing an enum using a class
+  lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    class Bar implements Foo
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: Foo', 5)
+
+  # abstract method is not supported in an enum
+  lines =<< trim END
+    vim9script
+    enum Foo
+      Apple
+      abstract def Bar()
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1417: Abstract cannot be used in an Enum', 4)
+
+  # Define an enum without any enum values but only with an object variable
+  lines =<< trim END
+    vim9script
+    enum Foo
+      final n: number = 10
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: n: number = 10', 3)
+enddef
+
+def Test_basic_enum()
+  # Declare a simple enum
+  var lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    var a: Foo = Foo.apple
+    var b: Foo = Foo.orange
+    assert_equal(a, Foo.apple)
+    assert_equal(b, Foo.orange)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Multiple enums in a single line
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple, orange
+    endenum
+    assert_equal('enum<Foo>', typename(Foo.apple))
+    assert_equal('enum<Foo>', typename(Foo.orange))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Comments and empty lines are supported between enum items
+  lines =<< trim END
+    vim9script
+    enum Foo
+      # Apple
+      apple,
+
+      # Orange
+      orange
+    endenum
+    def Fn()
+      var a: Foo = Foo.apple
+      var b: Foo = Foo.orange
+      assert_equal(a, Foo.apple)
+      assert_equal(b, Foo.orange)
+    enddef
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Try using a non-existing enum value
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    var a: Foo = Foo.pear
+  END
+  v9.CheckSourceFailure(lines, 'E1422: Enum value "pear" not found in enum "Foo"', 6)
+
+  # Enum function argument
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    def Fn(a: Foo): Foo
+      return a
+    enddef
+    assert_equal(Foo.apple, Fn(Foo.apple))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Enum function argument
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    def Fn(a: Foo): Foo
+      return a
+    enddef
+    Fn({})
+  END
+  v9.CheckSourceFailure(lines, 'E1013: Argument 1: type mismatch, expected enum<Foo> but got dict<any>', 9)
+
+  # Returning an enum in a function returning number
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    def Fn(): number
+      return Foo.orange
+    enddef
+    Fn()
+  END
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got enum<Foo>', 1)
+
+  # Returning a number in a function returning enum
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    def Fn(): Foo
+      return 20
+    enddef
+    Fn()
+  END
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected enum<Foo> but got number', 1)
+
+  # Use a List of enums
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus,
+      Earth
+    endenum
+    var l1: list<Planet> = [Planet.Mercury, Planet.Venus]
+    assert_equal(Planet.Venus, l1[1])
+    def Fn()
+      var l2: list<Planet> = [Planet.Mercury, Planet.Venus]
+      assert_equal(Planet.Venus, l2[1])
+    enddef
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Try using an enum as a value
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+    endenum
+    var a = Fruit
+  END
+  v9.CheckSourceFailure(lines, 'E1421: Enum "Fruit" cannot be used as a value', 6)
+enddef
+
+" Test for type() and typename() of an enum
+def Test_enum_type()
+  var lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+    endenum
+    assert_equal('enum<Fruit>', typename(Fruit))
+    assert_equal('enum<Fruit>', typename(Fruit.Apple))
+    assert_equal(v:t_enum, type(Fruit))
+    assert_equal(v:t_enumvalue, type(Fruit.Apple))
+    assert_equal(15, type(Fruit))
+    assert_equal(16, type(Fruit.Apple))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Assign an enum to a variable with any type
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+    endenum
+    var a = Fruit.Apple
+    var b: any = Fruit.Orange
+    assert_equal('enum<Fruit>', typename(a))
+    assert_equal('enum<Fruit>', typename(b))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Try modifying an enum or an enum item
+def Test_enum_modify()
+  # Try assigning an unsupported value to an enum
+  var lines =<< trim END
+    vim9script
+    enum Foo
+      apple
+    endenum
+    var a: Foo = 30
+  END
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected enum<Foo> but got number', 5)
+
+  # Try assigning an unsupported value to an enum in a function
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple
+    endenum
+    def Fn()
+      var a: Foo = 30
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected enum<Foo> but got number', 1)
+
+  # Try assigning a number to an enum
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    Foo = 10
+  END
+  v9.CheckSourceFailure(lines, 'E1421: Enum "Foo" cannot be used as a value', 6)
+
+  # Try assigning a number to an enum in a function
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple
+    endenum
+    def Fn()
+      Foo = 10
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected enum<Foo> but got number', 1)
+
+  # Try assigning a number to an enum value
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple
+    endenum
+    Foo.apple = 20
+  END
+  v9.CheckSourceFailure(lines, 'E1423: Enum value "Foo.apple" cannot be modified', 5)
+
+  # Try assigning a number to an enum value in a function
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple
+    endenum
+    def Fn()
+      Foo.apple = 20
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1423: Enum value "Foo.apple" cannot be modified', 1)
+
+  # Try assigning one enum to another
+  lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    enum Bar
+    endenum
+    Foo = Bar
+  END
+  v9.CheckSourceFailure(lines, 'E1421: Enum "Bar" cannot be used as a value', 6)
+
+  # Try assigning one enum to another in a function
+  lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    enum Bar
+    endenum
+    def Fn()
+      Foo = Bar
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1421: Enum "Bar" cannot be used as a value', 1)
+
+  # Try assigning one enum item to another enum item
+  lines =<< trim END
+    vim9script
+    enum Foo
+      Apple
+    endenum
+    enum Bar
+      Orange
+    endenum
+    Foo.Apple = Bar.Orange
+  END
+  v9.CheckSourceFailure(lines, 'E1423: Enum value "Foo.Apple" cannot be modified', 8)
+
+  # Try assigning one enum item to another enum item in a function
+  lines =<< trim END
+    vim9script
+    enum Foo
+      Apple
+    endenum
+    enum Bar
+      Orange
+    endenum
+    def Fn()
+      Foo.Apple = Bar.Orange
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1423: Enum value "Foo.Apple" cannot be modified', 1)
+enddef
+
+" Test for using enum in an expression
+def Test_enum_expr()
+  var lines =<< trim END
+    vim9script
+    enum Color
+      Red, Blue, Green
+    endenum
+    var a: number = 1 + Color
+  END
+  v9.CheckSourceFailure(lines, 'E1421: Enum "Color" cannot be used as a value', 5)
+
+  lines =<< trim END
+    vim9script
+    enum Color
+      Red, Blue, Green
+    endenum
+    var a: number = 1 + Color.Red
+  END
+  v9.CheckSourceFailure(lines, 'E1424: Using an Enum "Color" as a Number', 5)
+
+  lines =<< trim END
+    vim9script
+    enum Color
+      Red, Blue, Green
+    endenum
+    var s: string = "abc" .. Color
+  END
+  v9.CheckSourceFailure(lines, 'E1421: Enum "Color" cannot be used as a value', 5)
+
+  lines =<< trim END
+    vim9script
+    enum Color
+      Red, Blue, Green
+    endenum
+    var s: string = "abc" .. Color.Red
+  END
+  v9.CheckSourceFailure(lines, 'E1425: Using an Enum "Color" as a String', 5)
+enddef
+
+" Using an enum in a lambda function
+def Test_enum_lambda()
+  var lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus,
+      Earth,
+    endenum
+    var Fn = (p: Planet): Planet => p
+    for [idx, v] in items([Planet.Mercury, Planet.Venus, Planet.Earth])
+      assert_equal(idx, Fn(v).ordinal)
+    endfor
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Comparison using enums
+def Test_enum_compare()
+  var lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus,
+      Earth,
+    endenum
+    enum Fruit
+      Apple,
+      Orange
+    endenum
+
+    var p: Planet = Planet.Venus
+    var f: Fruit = Fruit.Orange
+    assert_equal(true, p == Planet.Venus)
+    assert_equal(false, p == Planet.Earth)
+    assert_equal(false, p == f)
+    assert_equal(true, Planet.Mercury == Planet.Mercury)
+    assert_equal(true, Planet.Venus != Planet.Earth)
+    assert_equal(true, Planet.Mercury != Fruit.Apple)
+
+    def Fn1()
+      var p2: Planet = Planet.Venus
+      var f2: Fruit = Fruit.Orange
+      assert_equal(true, p2 == Planet.Venus)
+      assert_equal(false, p2 == Planet.Earth)
+      assert_equal(false, p2 == f2)
+    enddef
+    Fn1()
+
+    # comparison using "is" and "isnot"
+    assert_equal(true, p is Planet.Venus)
+    assert_equal(true, p isnot Planet.Earth)
+    assert_equal(false, p is Fruit.Orange)
+    assert_equal(true, p isnot Fruit.Orange)
+    def Fn2(arg: Planet)
+      assert_equal(true, arg is Planet.Venus)
+      assert_equal(true, arg isnot Planet.Earth)
+      assert_equal(false, arg is Fruit.Orange)
+      assert_equal(true, arg isnot Fruit.Orange)
+    enddef
+    Fn2(p)
+
+    class A
+    endclass
+    var o: A = A.new()
+    assert_equal(false, p == o)
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using an enum as a default argument to a function
+def Test_enum_default_arg()
+  var lines =<< trim END
+    vim9script
+    enum Day
+      Monday, Tuesday, Wednesday
+    endenum
+    def Fn(d: Day = Day.Tuesday): Day
+      return d
+    enddef
+    assert_equal(Day.Tuesday, Fn())
+    assert_equal(Day.Wednesday, Fn(Day.Wednesday))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for enum garbage collection
+func Test_enum_garbagecollect()
+  let lines =<< trim END
+    vim9script
+    enum Car
+      Honda, Ford, Tesla
+    endenum
+    assert_equal(1, Car.Ford.ordinal)
+    call test_garbagecollect_now()
+    assert_equal(1, Car.Ford.ordinal)
+    var c: Car = Car.Tesla
+    assert_equal(2, c.ordinal)
+    call test_garbagecollect_now()
+    assert_equal(2, c.ordinal)
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  " garbage collection with a variable of type any
+  let lines =<< trim END
+    vim9script
+    enum Car
+      Honda, Ford, Tesla
+    endenum
+    call test_garbagecollect_now()
+    var c: any = Car.Tesla
+    call test_garbagecollect_now()
+    assert_equal(Car.Tesla, c)
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  " garbage collection with function arguments and return types
+  let lines =<< trim END
+    vim9script
+    enum Car
+      Honda, Ford, Tesla
+    endenum
+    def Fn(a: Car): Car
+      assert_equal(Car.Ford, a)
+      return Car.Tesla
+    enddef
+    call test_garbagecollect_now()
+    var b: Car = Car.Ford
+    call test_garbagecollect_now()
+    assert_equal(Car.Tesla, Fn(b))
+    call test_garbagecollect_now()
+  END
+  call v9.CheckSourceSuccess(lines)
+endfunc
+
+" Test for the enum values class variable
+def Test_enum_values()
+  var lines =<< trim END
+    vim9script
+    enum Car
+      Honda, Ford, Tesla
+    endenum
+    var l: list<Car> = Car.values
+    assert_equal(Car.Ford, l[1])
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # empty enum
+  lines =<< trim END
+    vim9script
+    enum Car
+    endenum
+    assert_equal([], Car.values)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # single value
+  lines =<< trim END
+    vim9script
+    enum Car
+      Honda
+    endenum
+    assert_equal([Car.Honda], Car.values)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+    enum A
+      Red,
+      Blue
+      static def GetValues(): list<A>
+	return values
+      enddef
+    endenum
+    assert_equal([A.Red, A.Blue], A.GetValues())
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Other class variables in an enum should not be added to 'values'
+  lines =<< trim END
+    vim9script
+    enum LogLevel
+      Error, Warn
+      static const x: number = 22
+    endenum
+    assert_equal([LogLevel.Error, LogLevel.Warn], LogLevel.values)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Other class variable of enum type should not be added to 'values'
+  lines =<< trim END
+    vim9script
+    enum LogLevel
+      Error, Warn
+      static const x: LogLevel = LogLevel.Warn
+    endenum
+    assert_equal([LogLevel.Error, LogLevel.Warn], LogLevel.values)
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test comments in enums
+def Test_enum_comments()
+  var lines =<< trim END
+    vim9script
+    enum Car  # cars
+      # before enum
+      Honda,  # honda
+      # before enum
+      Ford    # ford
+    endenum
+    assert_equal(1, Car.Ford.ordinal)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Test for using an unsupported comment
+  lines =<< trim END
+    vim9script
+    enum Car
+      Honda,
+      Ford,
+      #{
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1170: Cannot use #{ to start a comment', 4)
+enddef
+
+" Test string() with enums
+def Test_enum_string()
+  var lines =<< trim END
+    vim9script
+    enum Car
+      Honda,
+      Ford
+    endenum
+    assert_equal("enum Car", string(Car))
+    assert_equal("Car.Honda", string(Car.Honda))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for importing an enum
+def Test_enum_import()
+  var lines =<< trim END
+    vim9script
+    export enum Star
+      Gemini,
+      Orion,
+      Pisces
+    endenum
+  END
+  writefile(lines, 'Xenumexport.vim', 'D')
+
+  lines =<< trim END
+    vim9script
+    import './Xenumexport.vim' as mod
+
+    var s1: mod.Star = mod.Star.Orion
+    assert_equal(true, s1 == mod.Star.Orion)
+    assert_equal(2, mod.Star.Pisces.ordinal)
+    var l1: list<mod.Star> = mod.Star.values
+    assert_equal("Star.Orion", string(l1[1]))
+    assert_equal(s1, l1[1])
+
+    def Fn()
+      var s2: mod.Star = mod.Star.Orion
+      assert_equal(true, s2 == mod.Star.Orion)
+      assert_equal(2, mod.Star.Pisces.ordinal)
+      var l2: list<mod.Star> = mod.Star.values
+      assert_equal("Star.Orion", string(l2[1]))
+      assert_equal(s2, l2[1])
+    enddef
+    Fn()
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for using test_refcount() with enum
+def Test_enum_refcount()
+  var lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    assert_equal(1, test_refcount(Foo))
+
+    enum Star
+      Gemini,
+      Orion,
+    endenum
+    assert_equal(3, test_refcount(Star))
+    assert_equal(2, test_refcount(Star.Gemini))
+    assert_equal(2, test_refcount(Star.Orion))
+
+    var s: Star
+    assert_equal(3, test_refcount(Star))
+    assert_equal(-1, test_refcount(s))
+    s = Star.Orion
+    assert_equal(3, test_refcount(Star))
+    assert_equal(3, test_refcount(s))
+    assert_equal(2, test_refcount(Star.Gemini))
+    var t = s
+    assert_equal(3, test_refcount(Star))
+    assert_equal(4, test_refcount(s))
+    assert_equal(4, test_refcount(Star.Orion))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for defining an enum with additional object variables and methods
+def Test_enum_enhanced()
+  var lines =<< trim END
+    vim9script
+    enum Vehicle
+      car(4, 5, 400),
+      bus(6, 50, 800),
+      bicycle(2, 1, 0)
+
+      final tires: number
+      final passengers: number
+      final carbonPerKilometer: number
+
+      def new(t: number, p: number, cpk: number)
+        this.tires = t
+        this.passengers = p
+        this.carbonPerKilometer = cpk
+      enddef
+
+      def CarbonFootprint(): float
+        return round(this.carbonPerKilometer / this.passengers)
+      enddef
+
+      def IsTwoWheeled(): bool
+        return this == Vehicle.bicycle
+      enddef
+
+      def CompareTo(other: Vehicle): float
+         return this.CarbonFootprint() - other.CarbonFootprint()
+      enddef
+    endenum
+
+    var v: Vehicle = Vehicle.bus
+    assert_equal([6, 50, 800], [v.tires, v.passengers, v.carbonPerKilometer])
+    assert_equal(true, Vehicle.bicycle.IsTwoWheeled())
+    assert_equal(false, Vehicle.car.IsTwoWheeled())
+    assert_equal(16.0, Vehicle.bus.CarbonFootprint())
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for the enum value 'name' variable
+def Test_enum_name()
+  # Check the names of enum values
+  var lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus,
+      Earth
+    endenum
+    assert_equal('Mercury', Planet.Mercury.name)
+    assert_equal('Venus', Planet.Venus.name)
+    assert_equal('Earth', Planet.Earth.name)
+    assert_equal('string', typename(Planet.Earth.name))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Check the name of enum items in the constructor
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury("Mercury"),
+      Venus("Venus"),
+      Earth("Earth")
+
+      def new(s: string)
+        assert_equal(s, this.name)
+      enddef
+    endenum
+    defcompile
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Try assigning to the name of an enum
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple
+    endenum
+    Fruit.Apple.name = 'foo'
+  END
+  v9.CheckSourceFailure(lines, 'E1335: Variable "name" in class "Fruit" is not writable', 5)
+
+  # Try assigning to the name of an enum in a function
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple
+    endenum
+    def Fn()
+      Fruit.Apple.name = 'bar'
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1423: Enum value "Fruit.name" cannot be modified', 1)
+
+  # Try to overwrite an enum value name in the enum constructor
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus
+
+      def new()
+        this.name = 'foo'
+      enddef
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1427: Enum "Planet" name cannot be modified', 1)
+
+  # Try to declare an object variable named 'name'
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury
+      var name: string
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: name', 4)
+enddef
+
+" Test for the enum value 'ordinal' variable
+def Test_enum_ordinal()
+  # Check the ordinal values of enum items
+  var lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus,
+      Earth
+    endenum
+    assert_equal(0, Planet.Mercury.ordinal)
+    assert_equal(1, Planet.Venus.ordinal)
+    assert_equal(2, Planet.Earth.ordinal)
+    assert_equal('number', typename(Planet.Earth.ordinal))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Check the ordinal value of enum items in the constructor
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury(0),
+      Venus(1),
+      Earth(2)
+
+      def new(v: number)
+        assert_equal(v, this.ordinal)
+      enddef
+    endenum
+    defcompile
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Try assigning to the ordinal value of an enum
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple
+    endenum
+    Fruit.Apple.ordinal = 20
+  END
+  v9.CheckSourceFailure(lines, 'E1335: Variable "ordinal" in class "Fruit" is not writable', 5)
+
+  # Try assigning to the ordinal value of an enum in a function
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple
+    endenum
+    def Fn()
+      Fruit.Apple.ordinal = 20
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1423: Enum value "Fruit.ordinal" cannot be modified', 1)
+
+  # Try to overwrite an enum value ordinal in the enum constructor
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus
+
+      def new()
+        this.ordinal = 20
+      enddef
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1426: Enum "Planet" ordinal value cannot be modified', 1)
+
+  # Try to declare an object variable named 'ordinal'
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury
+      var ordinal: number
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: ordinal', 4)
+enddef
+
+" Test for trying to create a new enum object using the constructor
+def Test_enum_invoke_constructor()
+  var lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    var f: Foo = Foo.new()
+  END
+  v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "Foo"', 4)
+
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+    endenum
+    var f: Fruit = Fruit.new()
+  END
+  v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "Fruit"', 6)
+
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+      def newFruit()
+      enddef
+    endenum
+    var f: Fruit = Fruit.newFruit()
+  END
+  v9.CheckSourceFailure(lines, 'E1325: Method "newFruit" not found in class "Fruit"', 8)
+
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+    endenum
+    def Fn()
+      var f: Fruit = Fruit.new()
+    enddef
+    Fn()
+  END
+  v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "Fruit"', 1)
+
+  # error in the enum constructor
+  lines =<< trim END
+    vim9script
+    enum Planet
+      earth
+      def new()
+        x = 123
+      enddef
+    endenum
+  END
+  v9.CheckSourceFailureList(lines, ['E1100:', 'E1100:'], 1)
+enddef
+
+" Test for checking "this" in an enum constructor
+def Test_enum_this_in_constructor()
+  var lines =<< trim END
+    vim9script
+    enum A
+      Red("A.Red"),
+      Blue("A.Blue"),
+      Green("A.Green")
+
+      def new(s: string)
+        assert_equal(s, string(this))
+      enddef
+    endenum
+    defcompile
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using member variables in an enum object
+def Test_enum_object_variable()
+  var lines =<< trim END
+    vim9script
+    enum Planet
+      Jupiter(95),
+      Saturn(146)
+
+      var moons: number
+    endenum
+    assert_equal(95, Planet.Jupiter.moons)
+    assert_equal(146, Planet.Saturn.moons)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Use a final object variable
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Jupiter(95),
+      Saturn(146)
+
+      final moons: number
+      def new(n: number)
+        this.moons = n
+      enddef
+    endenum
+    assert_equal(95, Planet.Jupiter.moons)
+    assert_equal(146, Planet.Saturn.moons)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Use a const object variable
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mars(false),
+      Jupiter(true)
+
+      const has_ring: bool
+      def new(r: bool)
+        this.has_ring = r
+      enddef
+    endenum
+    assert_equal(false, Planet.Mars.has_ring)
+    assert_equal(true, Planet.Jupiter.has_ring)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Use a regular object variable
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+
+      final farm: string = 'SunValley'
+    endenum
+    assert_equal('SunValley', Fruit.Apple.farm)
+    assert_equal('SunValley', Fruit.Apple.farm)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Invoke the default constructor with an object variable
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple('foo'),
+      Orange('bar')
+
+      final t: string
+    endenum
+    assert_equal('foo', Fruit.Apple.t)
+    assert_equal('bar', Fruit.Orange.t)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Invoke the default constructor with an argument but without the object
+  # variable
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange('bar')
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E118: Too many arguments for function: new', 5)
+
+  # Define a default constructor with an argument, but don't pass it in when
+  # defining the enum value
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple(5),
+      Orange
+
+      def new(t: number)
+      enddef
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: new', 8)
+enddef
+
+" Test for using a custom constructor with an enum
+def Test_enum_custom_constructor()
+  # space before "("
+  var lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple(10),
+      Orange (20)
+
+      def new(t: number)
+      enddef
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1068: No white space allowed before '(': Orange (20)", 4)
+
+  # no closing ")"
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple(10),
+      Orange (20
+
+      def new(t: number)
+      enddef
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1068: No white space allowed before '(': Orange (20", 4)
+
+  # Specify constructor arguments split across multiple lines
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple(10,
+            'foo'), Orange(20,
+            'bar'),
+      Pear(30,
+           'baz'), Mango(40,
+           'qux')
+
+      final n: number
+      final s: string
+      def new(t: number, str: string)
+        this.n = t
+        this.s = str
+      enddef
+    endenum
+    defcompile
+    assert_equal([10, 'foo'], [Fruit.Apple.n, Fruit.Apple.s])
+    assert_equal([20, 'bar'], [Fruit.Orange.n, Fruit.Orange.s])
+    assert_equal([30, 'baz'], [Fruit.Pear.n, Fruit.Pear.s])
+    assert_equal([40, 'qux'], [Fruit.Mango.n, Fruit.Mango.s])
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # specify multiple enums with constructor arguments in a single line
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple(10, 'foo'), Orange(20, 'bar'), Pear(30, 'baz'), Mango(40, 'qux')
+      const n: number
+      const s: string
+    endenum
+    defcompile
+    assert_equal([10, 'foo'], [Fruit.Apple.n, Fruit.Apple.s])
+    assert_equal([20, 'bar'], [Fruit.Orange.n, Fruit.Orange.s])
+    assert_equal([30, 'baz'], [Fruit.Pear.n, Fruit.Pear.s])
+    assert_equal([40, 'qux'], [Fruit.Mango.n, Fruit.Mango.s])
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using class variables in an enum class
+def Test_enum_class_variable()
+  var lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+
+      static var farm: string = 'SunValley'
+    endenum
+    assert_equal('SunValley', Fruit.farm)
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for converting an enum value to a string and then back to an enum value
+def Test_enum_eval()
+  var lines =<< trim END
+    vim9script
+    enum Color
+      Red,
+      Blue
+    endenum
+    var s: string = string(Color.Blue)
+    var e = eval(s)
+    assert_equal(Color.Blue, e)
+    assert_equal(1, e.ordinal)
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
--- a/src/testing.c
+++ b/src/testing.c
@@ -1091,9 +1091,8 @@ f_test_refcount(typval_T *argvars, typva
 	case VAR_SPECIAL:
 	case VAR_STRING:
 	case VAR_INSTR:
-	case VAR_CLASS:
-	case VAR_OBJECT:
 	    break;
+
 	case VAR_JOB:
 #ifdef FEAT_JOB_CHANNEL
 	    if (argvars[0].vval.v_job != NULL)
@@ -1132,6 +1131,14 @@ f_test_refcount(typval_T *argvars, typva
 	    if (argvars[0].vval.v_dict != NULL)
 		retval = argvars[0].vval.v_dict->dv_refcount - 1;
 	    break;
+	case VAR_CLASS:
+	    if (argvars[0].vval.v_class != NULL)
+		retval = argvars[0].vval.v_class->class_refcount - 1;
+	    break;
+	case VAR_OBJECT:
+	    if (argvars[0].vval.v_object != NULL)
+		retval = argvars[0].vval.v_object->obj_refcount - 1;
+	    break;
 	case VAR_TYPEALIAS:
 	    if (argvars[0].vval.v_typealias != NULL)
 		retval = argvars[0].vval.v_typealias->ta_refcount - 1;
--- a/src/typval.c
+++ b/src/typval.c
@@ -266,7 +266,13 @@ tv_get_bool_or_number_chk(
 	    check_typval_is_value(varp);
 	    break;
 	case VAR_OBJECT:
-	    emsg(_(e_using_object_as_number));
+	    {
+		class_T *cl = varp->vval.v_object->obj_class;
+		if (cl != NULL && IS_ENUM(cl))
+		    semsg(_(e_using_enum_str_as_number), cl->class_name);
+		else
+		    emsg(_(e_using_object_as_number));
+	    }
 	    break;
 	case VAR_VOID:
 	    emsg(_(e_cannot_use_void_value));
@@ -1139,7 +1145,13 @@ tv_get_string_buf_chk_strict(typval_T *v
 	    check_typval_is_value(varp);
 	    break;
 	case VAR_OBJECT:
-	    emsg(_(e_using_object_as_string));
+	    {
+		class_T *cl = varp->vval.v_object->obj_class;
+		if (cl != NULL && IS_ENUM(cl))
+		    semsg(_(e_using_enum_str_as_string), cl->class_name);
+		else
+		    emsg(_(e_using_object_as_string));
+	    }
 	    break;
 	case VAR_JOB:
 #ifdef FEAT_JOB_CHANNEL
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    219,
+/**/
     218,
 /**/
     217,
--- a/src/vim.h
+++ b/src/vim.h
@@ -2164,7 +2164,9 @@ typedef int sock_T;
 #define VV_MAXCOL	105
 #define VV_PYTHON3_VERSION 106
 #define VV_TYPE_TYPEALIAS 107
-#define VV_LEN		108	// number of v: vars
+#define VV_TYPE_ENUM	  108
+#define VV_TYPE_ENUMVALUE 109
+#define VV_LEN		110	// number of v: vars
 
 // used for v_number in VAR_BOOL and VAR_SPECIAL
 #define VVAL_FALSE	0L	// VAR_BOOL
@@ -2188,6 +2190,8 @@ typedef int sock_T;
 #define VAR_TYPE_CLASS	    12
 #define VAR_TYPE_OBJECT	    13
 #define VAR_TYPE_TYPEALIAS  14
+#define VAR_TYPE_ENUM	    15
+#define VAR_TYPE_ENUMVALUE  16
 
 #define DICT_MAXNEST 100	// maximum nesting of lists and dicts
 
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -168,7 +168,8 @@ struct oc_newmember_S
 /*
  * Add a member to an object or a class.
  * Returns OK when successful, "init_expr" will be consumed then.
- * Returns FAIL otherwise, caller might need to free "init_expr".
+ * Returns OK on success and FAIL on memory allocation failure (caller might
+ * need to free "init_expr").
  */
     static int
 add_member(
@@ -323,13 +324,15 @@ validate_extends_class(
     }
 
     if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
-	    || (is_class
-		&& (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
-	    || (!is_class
-		&& (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0))
-	// a interface cannot extend a class and a class cannot extend an
-	// interface.
+	    || (is_class && IS_INTERFACE(tv.vval.v_class))
+	    || (!is_class && !IS_INTERFACE(tv.vval.v_class))
+	    || (is_class && IS_ENUM(tv.vval.v_class)))
+    {
+	// a class cannot extend an interface
+	// an interface cannot extend a class
+	// a class cannot extend an enum.
 	semsg(_(e_cannot_extend_str), extends_name);
+    }
     else
     {
 	class_T *extends_cl = tv.vval.v_class;
@@ -793,7 +796,7 @@ validate_implements_classes(
 
 	if (tv.v_type != VAR_CLASS
 		|| tv.vval.v_class == NULL
-		|| (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
+		|| !IS_INTERFACE(tv.vval.v_class))
 	{
 	    semsg(_(e_not_valid_interface_str), impl);
 	    success = FALSE;
@@ -1234,14 +1237,14 @@ add_lookup_tables(class_T *cl, class_T *
  * Add class members to a new class.  Allocate a typval for each class member
  * and initialize it.
  */
-    static void
+    static int
 add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
 {
     // Allocate a typval for each class member and initialize it.
     cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
 					    cl->class_class_member_count);
     if (cl->class_members_tv == NULL)
-	return;
+	return FAIL;
 
     for (int i = 0; i < cl->class_class_member_count; ++i)
     {
@@ -1250,19 +1253,19 @@ add_class_members(class_T *cl, exarg_T *
 	if (m->ocm_init != NULL)
 	{
 	    typval_T *etv = eval_expr(m->ocm_init, eap);
-	    if (etv != NULL)
-	    {
-		if (m->ocm_type->tt_type == VAR_ANY
-			&& !(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.
-		    m->ocm_type = typval2type(etv, get_copyID(),
-					type_list_gap,
-					TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
-		*tv = *etv;
-		vim_free(etv);
-	    }
+	    if (etv == NULL)
+		return FAIL;
+
+	    if (m->ocm_type->tt_type == VAR_ANY
+		    && !(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.
+		m->ocm_type = typval2type(etv, get_copyID(),
+			type_list_gap,
+			TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
+	    *tv = *etv;
+	    vim_free(etv);
 	}
 	else
 	{
@@ -1273,6 +1276,8 @@ add_class_members(class_T *cl, exarg_T *
 	if (m->ocm_flags & OCMFLAG_CONST)
 	    item_lock(tv, DICT_MAXNEST, TRUE, TRUE);
     }
+
+    return OK;
 }
 
 /*
@@ -1284,13 +1289,21 @@ add_default_constructor(
     garray_T	*classfunctions_gap,
     garray_T	*type_list_gap)
 {
-    garray_T fga;
+    garray_T	fga;
+    int		is_enum = IS_ENUM(cl);
 
     ga_init2(&fga, 1, 1000);
     ga_concat(&fga, (char_u *)"new(");
     for (int i = 0; i < cl->class_obj_member_count; ++i)
     {
-	if (i > 0)
+	if (i < 2 && is_enum)
+	    // The first two object variables in an enum are the enum value
+	    // name and ordinal.  Don't initialize these object variables in
+	    // the default constructor as they are already initialized right
+	    // after creating the object.
+	    continue;
+
+	if (i > (is_enum ? 2 : 0))
 	    ga_concat(&fga, (char_u *)", ");
 	ga_concat(&fga, (char_u *)"this.");
 	ocmember_T *m = cl->class_obj_members + i;
@@ -1336,6 +1349,7 @@ add_default_constructor(
  * Add the class methods and object methods to the new class "cl".
  * When extending a class "extends_cl", add the instance methods from the
  * parent class also.
+ * Returns OK on success and FAIL on memory allocation failure.
  */
     static int
 add_classfuncs_objmethods(
@@ -1373,7 +1387,7 @@ add_classfuncs_objmethods(
 
 	if (gap->ga_len != 0)
 	    mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
-	vim_free(gap->ga_data);
+	VIM_CLEAR(gap->ga_data);
 	if (loop == 1)
 	    cl->class_class_function_count_child = gap->ga_len;
 	else
@@ -1422,6 +1436,8 @@ add_classfuncs_objmethods(
 	    if (loop == 2)
 		fp->uf_flags |= FC_OBJECT;
 	}
+
+	ga_clear(gap);
     }
 
     return OK;
@@ -1471,6 +1487,246 @@ find_class_name_end(char_u *arg)
     return end;
 }
 
+/*
+ * Returns TRUE if the enum value "varname" is already defined.
+ */
+    static int
+is_duplicate_enum(
+    garray_T	*enum_gap,
+    char_u	*varname,
+    char_u	*varname_end)
+{
+    char_u	*name = vim_strnsave(varname, varname_end - varname);
+    int		dup = FALSE;
+
+    for (int i = 0; i < enum_gap->ga_len; ++i)
+    {
+	ocmember_T *m = ((ocmember_T *)enum_gap->ga_data) + i;
+	if (STRCMP(name, m->ocm_name) == 0)
+	{
+	    semsg(_(e_duplicate_enum_str), name);
+	    dup = TRUE;
+	    break;
+	}
+    }
+
+    vim_free(name);
+    return dup;
+}
+
+/*
+ * Parse the enum values in "line" separated by comma and add them to "gap".
+ * If the last enum value is found, then "enum_end" is set to TRUE.
+ */
+    static int
+enum_parse_values(
+    exarg_T	*eap,
+    class_T	*en,
+    char_u	*line,
+    garray_T	*gap,
+    int		*num_enum_values,
+    int		*enum_end)
+{
+    evalarg_T	evalarg;
+    char_u	*p = line;
+    char	initexpr_buf[1024];
+    char_u	last_char = NUL;
+    int		rc = OK;
+
+    fill_evalarg_from_eap(&evalarg, eap, FALSE);
+
+    int		did_emsg_before = did_emsg;
+    while (*p != NUL)
+    {
+	// ignore comment
+	if (*p == '#')
+	    break;
+
+	if (!eval_isnamec1(*p))
+	{
+	    semsg(_(e_invalid_enum_value_declaration_str), p);
+	    break;
+	}
+
+	char_u *eni_name_start = p;
+	char_u *eni_name_end = to_name_end(p, FALSE);
+
+	if (is_duplicate_enum(gap, eni_name_start, eni_name_end))
+	    break;
+
+	p = skipwhite(eni_name_end);
+
+	char_u	*init_expr = NULL;
+	if (*p == '(')
+	{
+	    if (VIM_ISWHITE(p[-1]))
+	    {
+		semsg(_(e_no_white_space_allowed_before_str_str), "(", line);
+		break;
+	    }
+
+	    char_u *expr_start, *expr_end;
+
+	    p = eni_name_start;
+	    (void)skip_expr_concatenate(&p, &expr_start, &expr_end, &evalarg);
+
+	    while (*expr_start && *expr_start != '(')
+		expr_start++;
+
+	    if (expr_end > expr_start)
+		init_expr = vim_strnsave(expr_start, expr_end - expr_start);
+	}
+
+	if (init_expr == NULL)
+	    vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new()",
+						    en->class_name);
+	else
+	{
+	    vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new%s",
+					    en->class_name, init_expr);
+	    vim_free(init_expr);
+	}
+	if (add_member(gap, eni_name_start, eni_name_end, FALSE,
+				TRUE, TRUE, TRUE, &en->class_object_type,
+				vim_strsave((char_u *)initexpr_buf)) == FAIL)
+	    break;
+
+	++*num_enum_values;
+
+	if (*p != '#')
+	    last_char = *p;
+
+	if (*p != NUL && *p != ',')
+	    break;
+
+	if (*p == ',')
+	{
+	    if (!IS_WHITE_OR_NUL(p[1]))
+	    {
+		semsg(_(e_white_space_required_after_str_str), ",", line);
+		break;
+	    }
+	    if (VIM_ISWHITE(p[-1]))
+	    {
+		semsg(_(e_no_white_space_allowed_before_str_str), ",", line);
+		break;
+	    }
+	    p = skipwhite(p + 1);
+	}
+    }
+
+    if (*p != NUL && *p != '#')
+    {
+	if (did_emsg == did_emsg_before)
+	    semsg(_(e_missing_comma_before_argument_str), p);
+	rc = FAIL;
+    }
+
+    if (last_char != ',')
+	// last enum value should not be terminated by ","
+	*enum_end = TRUE;
+
+    // Free the memory pointed by expr_start.
+    clear_evalarg(&evalarg, NULL);
+
+    return rc;
+}
+
+/*
+ * Add the "values" class variable (List of enum value objects) to the enum
+ * class "en"
+ */
+    static int
+enum_add_values_member(
+    class_T	*en,
+    garray_T	*gap,
+    int		num_enum_values,
+    garray_T	*type_list_gap)
+{
+    garray_T	fga;
+    int		rc = FAIL;
+
+    ga_init2(&fga, 1, 1000);
+    ga_concat(&fga, (char_u *)"[");
+    for (int i = 0; i < num_enum_values; ++i)
+    {
+	ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
+
+	if (i > 0)
+	    ga_concat(&fga, (char_u *)", ");
+	ga_concat(&fga, en->class_name);
+	ga_concat(&fga, (char_u *)".");
+	ga_concat(&fga, (char_u *)m->ocm_name);
+    }
+    ga_concat(&fga, (char_u *)"]");
+    ga_append(&fga, NUL);
+
+    char_u *varname = (char_u *)"values";
+
+    type_T *type = get_type_ptr(type_list_gap);
+    if (type == NULL)
+	goto done;
+
+    type->tt_type = VAR_LIST;
+    type->tt_member = get_type_ptr(type_list_gap);
+    if (type->tt_member != NULL)
+    {
+	type->tt_member->tt_type = VAR_OBJECT;
+	type->tt_member->tt_class = en;
+    }
+
+    rc = add_member(gap, varname, varname + 6, FALSE, FALSE, TRUE, TRUE, type,
+					vim_strsave((char_u *)fga.ga_data));
+
+done:
+    vim_free(fga.ga_data);
+
+    return rc;
+}
+
+/*
+ * Clear the constructor method names in a enum class, so that an enum class
+ * cannot be instantiated.
+ */
+    static void
+enum_clear_constructors(class_T *en)
+{
+    for (int i = 0; i < en->class_class_function_count; ++i)
+    {
+	ufunc_T *fp = en->class_class_functions[i];
+
+	if (fp->uf_flags & FC_NEW)
+	    *fp->uf_name = NUL;
+    }
+}
+
+/*
+ * Initialize the name and ordinal object variable in the enum value "enval" in
+ * the enum "en".  These values are set during the enum value object creation.
+ */
+    void
+enum_set_internal_obj_vars(class_T *en, object_T *enval)
+{
+    int	i;
+
+    for (i = 0; i < en->class_class_member_count; ++i)
+    {
+	typval_T *en_tv = en->class_members_tv + i;
+	if (en_tv != NULL && en_tv->v_type == VAR_UNKNOWN)
+	    break;
+    }
+
+    // First object variable is the name
+    ocmember_T *value_ocm = en->class_class_members + i;
+    typval_T *name_tv = (typval_T *)(enval + 1);
+    name_tv->v_type = VAR_STRING;
+    name_tv->vval.v_string = vim_strsave(value_ocm->ocm_name);
+
+    // Second object variable is the ordinal
+    typval_T *ord_tv = (typval_T *)(name_tv + 1);
+    ord_tv->v_type = VAR_NUMBER;
+    ord_tv->vval.v_number = i;
+}
 
 /*
  * Handle ":class" and ":abstract class" up to ":endclass".
@@ -1479,10 +1735,12 @@ find_class_name_end(char_u *arg)
     void
 ex_class(exarg_T *eap)
 {
-    int		is_class = eap->cmdidx == CMD_class;  // FALSE for :interface
+    int		is_class = eap->cmdidx == CMD_class;
+    int		is_abstract = eap->cmdidx == CMD_abstract;
+    int		is_enum = eap->cmdidx == CMD_enum;
+    int		is_interface;
     long	start_lnum = SOURCING_LNUM;
     char_u	*arg = eap->arg;
-    int		is_abstract = eap->cmdidx == CMD_abstract;
 
     if (is_abstract)
     {
@@ -1495,12 +1753,16 @@ ex_class(exarg_T *eap)
 	is_class = TRUE;
     }
 
+    is_interface = !is_class && !is_enum;
+
     if (!current_script_is_vim9()
 		|| (cmdmod.cmod_flags & CMOD_LEGACY)
 		|| !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
     {
 	if (is_class)
 	    emsg(_(e_class_can_only_be_defined_in_vim9_script));
+	else if (is_enum)
+	    emsg(_(e_enum_can_only_be_defined_in_vim9_script));
 	else
 	    emsg(_(e_interface_can_only_be_defined_in_vim9_script));
 	return;
@@ -1510,6 +1772,8 @@ ex_class(exarg_T *eap)
     {
 	if (is_class)
 	    semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
+	else if (is_enum)
+	    semsg(_(e_enum_name_must_start_with_uppercase_letter_str), arg);
 	else
 	    semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
 									  arg);
@@ -1523,11 +1787,6 @@ ex_class(exarg_T *eap)
     }
     char_u *name_start = arg;
 
-    // "export class" gets used when creating the class, don't use "is_export"
-    // for the items inside the class.
-    int class_export = is_export;
-    is_export = FALSE;
-
     // TODO:
     //    generics: <Tkey, Tentry>
 
@@ -1545,6 +1804,11 @@ ex_class(exarg_T *eap)
 	//    specifies SomeInterface
 	if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
 	{
+	    if (is_enum)
+	    {
+		emsg(_(e_enum_cannot_extend_class));
+		goto early_ret;
+	    }
 	    if (extends != NULL)
 	    {
 		emsg(_(e_duplicate_extends));
@@ -1567,7 +1831,7 @@ ex_class(exarg_T *eap)
 	else if (STRNCMP(arg, "implements", 10) == 0
 						   && IS_WHITE_OR_NUL(arg[10]))
 	{
-	    if (!is_class)
+	    if (is_interface)
 	    {
 		emsg(_(e_interface_cannot_use_implements));
 		goto early_ret;
@@ -1652,11 +1916,15 @@ early_ret:
     class_T *cl = NULL;
     class_T *extends_cl = NULL;  // class from "extends" argument
     class_T **intf_classes = NULL;
+    int	    num_enum_values = 0;
 
     cl = ALLOC_CLEAR_ONE(class_T);
     if (cl == NULL)
 	goto cleanup;
-    if (!is_class)
+
+    if (is_enum)
+	cl->class_flags = CLASS_ENUM;
+    else if (is_interface)
 	cl->class_flags = CLASS_INTERFACE;
     else if (is_abstract)
 	cl->class_flags = CLASS_ABSTRACT;
@@ -1666,22 +1934,48 @@ early_ret:
     if (cl->class_name == NULL)
 	goto cleanup;
 
+    cl->class_type.tt_type = VAR_CLASS;
+    cl->class_type.tt_class = cl;
+    cl->class_object_type.tt_type = VAR_OBJECT;
+    cl->class_object_type.tt_class = cl;
+
     // Add the class to the script-local variables.
     // TODO: handle other context, e.g. in a function
     // TODO: does uf_hash need to be cleared?
     typval_T tv;
     tv.v_type = VAR_CLASS;
     tv.vval.v_class = cl;
-    is_export = class_export;
     SOURCING_LNUM = start_lnum;
     int rc = set_var_const(cl->class_name, current_sctx.sc_sid,
 						NULL, &tv, FALSE, 0, 0);
     if (rc == FAIL)
 	goto cleanup;
 
+    if (is_enum)
+    {
+	// All the enum classes have the name and ordinal object variables.
+	char_u *varname = (char_u *)"name";
+	if (add_member(&objmembers, varname, varname + 4, FALSE, FALSE, TRUE,
+		    TRUE, &t_string, NULL) == FAIL)
+	    goto cleanup;
+
+	varname = (char_u *)"ordinal";
+	if (add_member(&objmembers, varname, varname + 7, FALSE, FALSE, TRUE,
+		    TRUE, &t_number, NULL) == FAIL)
+	    goto cleanup;
+    }
+
+    // "export class" gets used when creating the class, don't use "is_export"
+    // for the items inside the class.
+    is_export = FALSE;
+
+    // When parsing an enum definition, this denotes whether all the enumerated
+    // values are parsed or not.
+    int		enum_end = FALSE;
+
     /*
      * Go over the body of the class/interface until "endclass" or
-     * "endinterface" is found.
+     * "endinterface" or "endenum" is found.
      */
     char_u *theline = NULL;
     int success = FALSE;
@@ -1704,10 +1998,32 @@ early_ret:
 	}
 
 	char_u *p = line;
-	char *end_name = is_class ? "endclass" : "endinterface";
-	if (checkforcmd(&p, end_name, is_class ? 4 : 5))
+
+	char	*end_name;
+	int	shortlen;
+	int	fullen;
+	if (is_class)
+	{
+	    end_name = "endclass";
+	    shortlen = 4;
+	    fullen = 8;
+	}
+	else if (is_enum)
 	{
-	    if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
+	    end_name = "endenum";
+	    shortlen = 4;
+	    fullen = 7;
+	}
+	else
+	{
+	    end_name = "endinterface";
+	    shortlen = 5;
+	    fullen = 12;
+	}
+
+	if (checkforcmd(&p, end_name, shortlen))
+	{
+	    if (STRNCMP(line, end_name, fullen) != 0)
 		semsg(_(e_command_cannot_be_shortened_str), line);
 	    else if (*p == '|' || !ends_excmd2(line, p))
 		semsg(_(e_trailing_characters_str), p);
@@ -1715,13 +2031,34 @@ early_ret:
 		success = TRUE;
 	    break;
 	}
-	char *wrong_name = is_class ? "endinterface" : "endclass";
-	if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
+
+	int	wrong_endname = FALSE;
+	if (is_class)
+	    wrong_endname = checkforcmd(&p, "endinterface", 5)
+					|| checkforcmd(&p, "endenum", 4);
+	else if (is_enum)
+	    wrong_endname = checkforcmd(&p, "endclass", 4)
+					|| checkforcmd(&p, "endinterface", 5);
+	else
+	    wrong_endname = checkforcmd(&p, "endclass", 4)
+					|| checkforcmd(&p, "endenum", 4);
+	if (wrong_endname)
 	{
 	    semsg(_(e_invalid_command_str_expected_str), line, end_name);
 	    break;
 	}
 
+	if (is_enum && !enum_end)
+	{
+	    // In an enum, all the enumerated values are at the beginning
+	    // separated by comma.  The class and object variables/methods
+	    // follow the values.
+	    if (enum_parse_values(eap, cl, line, &classmembers,
+					&num_enum_values, &enum_end) == FAIL)
+		break;
+	    continue;
+	}
+
 	int has_public = FALSE;
 	if (checkforcmd(&p, "public", 3))
 	{
@@ -1730,7 +2067,7 @@ early_ret:
 		semsg(_(e_command_cannot_be_shortened_str), line);
 		break;
 	    }
-	    if (!is_class)
+	    if (is_interface)
 	    {
 		emsg(_(e_public_variable_not_supported_in_interface));
 		break;
@@ -1756,7 +2093,14 @@ early_ret:
 		break;
 	    }
 
-	    if (!is_class)
+	    if (is_enum)
+	    {
+		// "abstract" not supported in an enum
+		emsg(_(e_abstract_cannot_be_used_in_enum));
+		break;
+	    }
+
+	    if (is_interface)
 	    {
 		// "abstract" not supported in an interface
 		emsg(_(e_abstract_cannot_be_used_in_interface));
@@ -1789,7 +2133,7 @@ early_ret:
 		break;
 	    }
 
-	    if (!is_class)
+	    if (is_interface)
 	    {
 		emsg(_(e_static_member_not_supported_in_interface));
 		break;
@@ -1812,7 +2156,7 @@ early_ret:
 	    has_var = TRUE;
 	else if (checkforcmd(&p, "final", 5))
 	{
-	    if (!is_class)
+	    if (is_interface)
 	    {
 		emsg(_(e_final_variable_not_supported_in_interface));
 		break;
@@ -1821,7 +2165,7 @@ early_ret:
 	}
 	else if (checkforcmd(&p, "const", 5))
 	{
-	    if (!is_class)
+	    if (is_interface)
 	    {
 		emsg(_(e_const_variable_not_supported_in_interface));
 		break;
@@ -1867,7 +2211,7 @@ early_ret:
 		break;
 	    }
 
-	    if (!is_class && *varname == '_')
+	    if (is_interface && *varname == '_')
 	    {
 		// private variables are not supported in an interface
 		semsg(_(e_protected_variable_not_supported_in_interface),
@@ -1877,7 +2221,7 @@ early_ret:
 
 	    if (parse_member(eap, line, varname, has_public,
 			  &varname_end, &has_type, &type_list, &type,
-			  is_class ? &init_expr: NULL) == FAIL)
+			  !is_interface ? &init_expr: NULL) == FAIL)
 		break;
 
 	    if (is_reserved_varname(varname, varname_end)
@@ -1930,7 +2274,7 @@ early_ret:
 		break;
 	    }
 
-	    if (!is_class && *p == '_')
+	    if (is_interface && *p == '_')
 	    {
 		// private methods are not supported in an interface
 		semsg(_(e_protected_method_not_supported_in_interface), p);
@@ -1953,10 +2297,10 @@ early_ret:
 
 	    ga_init2(&lines_to_free, sizeof(char_u *), 50);
 	    int class_flags;
-	    if (is_class)
+	    if (is_interface)
+		class_flags = CF_INTERFACE;
+	    else
 		class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
-	    else
-		class_flags = CF_INTERFACE;
 	    ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
 			class_flags, objmembers.ga_data, objmembers.ga_len);
 	    ga_clear_strings(&lines_to_free);
@@ -2011,15 +2355,25 @@ early_ret:
 	{
 	    if (is_class)
 		semsg(_(e_not_valid_command_in_class_str), line);
+	    else if (is_enum)
+		semsg(_(e_not_valid_command_in_enum_str), line);
 	    else
 		semsg(_(e_not_valid_command_in_interface_str), line);
 	    break;
 	}
     }
+
+    if (theline == NULL && !success && is_enum)
+	emsg(_(e_missing_endenum));
+
     vim_free(theline);
 
+    if (success && is_enum)
+	// Add the enum "values" class variable.
+	enum_add_values_member(cl, &classmembers, num_enum_values, &type_list);
+
     /*
-     * Check a few things before defining the class.
+     * Check a few things
      */
 
     // Check the "extends" class is valid.
@@ -2067,7 +2421,8 @@ early_ret:
 
     if (success)
     {
-	// "endclass" encountered without failures: Create the class.
+	// "endclass" or "endinterface" or "endenum" encountered without any
+	// failures
 
 	if (extends_cl != NULL)
 	{
@@ -2114,10 +2469,6 @@ early_ret:
 		goto cleanup;
 	}
 
-	// Allocate a typval for each class member and initialize it.
-	if (is_class && cl->class_class_member_count > 0)
-	    add_class_members(cl, eap, &type_list);
-
 	int	have_new = FALSE;
 	ufunc_T	*class_func = NULL;
 	for (int i = 0; i < classfunctions.ga_len; ++i)
@@ -2133,7 +2484,7 @@ early_ret:
 	if (have_new)
 	    // The return type of new() is an object of class "cl"
 	    class_func->uf_ret_type->tt_class = cl;
-	else if (is_class && !is_abstract && !have_new)
+	else if ((is_class || is_enum) && !is_abstract && !have_new)
 	    // No new() method was defined, add the default constructor.
 	    add_default_constructor(cl, &classfunctions, &type_list);
 
@@ -2144,13 +2495,21 @@ early_ret:
 
 	update_builtin_method_index(cl);
 
-	cl->class_type.tt_type = VAR_CLASS;
-	cl->class_type.tt_class = cl;
-	cl->class_object_type.tt_type = VAR_OBJECT;
-	cl->class_object_type.tt_class = cl;
+	class_created(cl);
+
+	// Allocate a typval for each class member and initialize it.
+	if ((is_class || is_enum) && cl->class_class_member_count > 0)
+	    if (add_class_members(cl, eap, &type_list) == FAIL)
+		goto cleanup;
+
 	cl->class_type_list = type_list;
 
-	class_created(cl);
+	if (is_enum)
+	{
+	    // clear the constructor method names, so that an enum class cannot
+	    // be instantiated
+	    enum_clear_constructors(cl);
+	}
 
 	// TODO:
 	// - Fill hashtab with object members and methods ?
@@ -2265,15 +2624,6 @@ oc_member_type_by_idx(
 }
 
 /*
- * Handle ":enum" up to ":endenum".
- */
-    void
-ex_enum(exarg_T *eap UNUSED)
-{
-    // TODO
-}
-
-/*
  * Type aliases (:type)
  */
 
@@ -3334,8 +3684,14 @@ member_not_found_msg(class_T *cl, vartyp
 	    semsg(_(e_object_variable_str_accessible_only_using_object_str),
 		    varname, cl->class_name);
 	else
-	    semsg(_(e_class_variable_str_not_found_in_class_str),
-		    varname, cl->class_name);
+	{
+	    if (IS_ENUM(cl))
+		semsg(_(e_enum_value_str_not_found_in_enum_str),
+			varname, cl->class_name);
+	    else
+		semsg(_(e_class_variable_str_not_found_in_class_str),
+			varname, cl->class_name);
+	}
     }
     vim_free(varname);
 }
@@ -3480,8 +3836,17 @@ object_string(
 	garray_T ga;
 	ga_init2(&ga, 1, 50);
 
+	class_T *cl = obj == NULL ? NULL : obj->obj_class;
+	if (cl != NULL && IS_ENUM(cl))
+	{
+	    ga_concat(&ga, cl->class_name);
+	    char_u *name = ((typval_T *)(obj + 1))->vval.v_string;
+	    ga_concat(&ga, (char_u *)".");
+	    ga_concat(&ga, name);
+	    return ga.ga_data;
+	}
+
 	ga_concat(&ga, (char_u *)"object of ");
-	class_T *cl = obj == NULL ? NULL : obj->obj_class;
 	ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
 		: cl->class_name);
 	if (cl != NULL)
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1614,6 +1614,13 @@ lhs_class_member_modifiable(lhs_T *lhs, 
 	return FALSE;
     }
 
+    if (IS_ENUM(cl))
+    {
+	semsg(_(e_enumvalue_str_cannot_be_modified), cl->class_name,
+		m->ocm_name);
+	return FALSE;
+    }
+
     // If it is private member variable, then accessing it outside the
     // class is not allowed.
     // If it is a read only class variable, then it can be modified
@@ -2037,6 +2044,25 @@ compile_lhs(
 		return FAIL;
 	    }
 
+	    if (IS_ENUM(cl))
+	    {
+		if (!inside_class(cctx, cl))
+		{
+		    semsg(_(e_enumvalue_str_cannot_be_modified),
+			    cl->class_name, m->ocm_name);
+		    return FALSE;
+		}
+		if (lhs->lhs_type->tt_type == VAR_OBJECT &&
+			lhs->lhs_member_idx < 2)
+		{
+		    char *msg = lhs->lhs_member_idx == 0 ?
+			e_enum_str_name_cannot_be_modified :
+			e_enum_str_ordinal_cannot_be_modified;
+		    semsg(_(msg), cl->class_name);
+		    return FALSE;
+		}
+	    }
+
 	    // If it is private member variable, then accessing it outside the
 	    // class is not allowed.
 	    // If it is a read only class variable, then it can be modified
@@ -2304,7 +2330,7 @@ compile_load_lhs_with_index(lhs_T *lhs, 
 	    if (compile_load_lhs(lhs, var_start, lhs->lhs_type, cctx) == FAIL)
 		return FAIL;
 	}
-	if (cl->class_flags & CLASS_INTERFACE)
+	if (IS_INTERFACE(cl))
 	    return generate_GET_ITF_MEMBER(cctx, cl, lhs->lhs_member_idx, type);
 	return generate_GET_OBJ_MEMBER(cctx, lhs->lhs_member_idx, type);
     }
@@ -2453,7 +2479,7 @@ compile_assign_unlet(
 		{
 		    class_T *cl = lhs->lhs_type->tt_class;
 
-		    if (cl->class_flags & CLASS_INTERFACE)
+		    if (IS_INTERFACE(cl))
 		    {
 			// "this.value": load "this" object and get the value
 			// at index for an object or class member get the type
@@ -3375,6 +3401,14 @@ compile_def_function(
 	    for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i)
 	    {
 		ocmember_T *m = &ufunc->uf_class->class_obj_members[i];
+
+		if (i < 2 && IS_ENUM(ufunc->uf_class))
+		    // The first two object variables in an enum are the name
+		    // and the ordinal.  These are set by the ISN_CONSTRUCT
+		    // instruction.  So don't generate instructions to set
+		    // these variables.
+		    continue;
+
 		if (m->ocm_init != NULL)
 		{
 		    char_u *expr = m->ocm_init;
@@ -3481,8 +3515,7 @@ compile_def_function(
 
     // Compiling a function in an interface is done to get the function type.
     // No code is actually compiled.
-    if (ufunc->uf_class != NULL
-			   && (ufunc->uf_class->class_flags & CLASS_INTERFACE))
+    if (ufunc->uf_class != NULL && IS_INTERFACE(ufunc->uf_class))
     {
 	ufunc->uf_def_status = UF_NOT_COMPILED;
 	ret = OK;
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -3258,6 +3258,12 @@ exec_instructions(ectx_T *ectx)
 		++tv->vval.v_object->obj_class->class_refcount;
 		tv->vval.v_object->obj_refcount = 1;
 		object_created(tv->vval.v_object);
+
+		// When creating an enum value object, initialize the name and
+		// ordinal object variables.
+		class_T *en = tv->vval.v_object->obj_class;
+		if (IS_ENUM(en))
+		    enum_set_internal_obj_vars(en, tv->vval.v_object);
 		break;
 
 	    // execute Ex command line
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -446,7 +446,7 @@ compile_class_object_index(cctx_T *cctx,
 	if (m_idx >= 0)
 	{
 	    ufunc_T *fp = cl->class_obj_methods[m_idx];
-	    // Private methods are not accessible outside the class
+	    // Private object methods are not accessible outside the class
 	    if (*name == '_' && !inside_class(cctx, cl))
 	    {
 		semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
@@ -488,7 +488,7 @@ compile_class_object_index(cctx_T *cctx,
 	if (m_idx >= 0)
 	{
 	    ufunc_T *fp = cl->class_class_functions[m_idx];
-	    // Private methods are not accessible outside the class
+	    // Private class methods are not accessible outside the class
 	    if (*name == '_' && !inside_class(cctx, cl))
 	    {
 		semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
@@ -2462,7 +2462,8 @@ compile_subscript(
 		return FAIL;
 	    ppconst->pp_is_const = FALSE;
 
-	    if ((type = get_type_on_stack(cctx, 0)) != &t_unknown
+	    type = get_type_on_stack(cctx, 0);
+	    if (type != &t_unknown
 		    && (type->tt_type == VAR_CLASS
 					       || type->tt_type == VAR_OBJECT))
 	    {
--- a/src/vim9type.c
+++ b/src/vim9type.c
@@ -719,7 +719,7 @@ check_typval_type(type_T *expected, typv
 
     if (expected == NULL)
 	return OK;  // didn't expect anything.
-		    //
+
     ga_init2(&type_list, sizeof(type_T *), 10);
 
     // A null_function and null_partial are special cases, they can be used to
@@ -1739,8 +1739,15 @@ type_name(type_T *type, char **tofree)
 
     if (type->tt_type == VAR_OBJECT || type->tt_type == VAR_CLASS)
     {
-	char_u *class_name = type->tt_class == NULL ? (char_u *)"Unknown"
-				    : type->tt_class->class_name;
+	char_u *class_name;
+	if (type->tt_class != NULL)
+	{
+	    class_name = type->tt_class->class_name;
+	    if (IS_ENUM(type->tt_class))
+		name = "enum";
+	}
+	else
+	    class_name = (char_u *)"Unknown";
 	size_t len = STRLEN(name) + STRLEN(class_name) + 3;
 	*tofree = alloc(len);
 	if (*tofree != NULL)
@@ -1869,18 +1876,26 @@ check_typval_is_value(typval_T *tv)
 {
     if (tv == NULL)
 	return OK;
-    if (tv->v_type == VAR_CLASS)
+
+    switch (tv->v_type)
     {
-	if (tv->vval.v_class != NULL)
-	    semsg(_(e_using_class_as_value_str), tv->vval.v_class->class_name);
-	else
-	    emsg(e_using_class_as_var_val);
-	return FAIL;
-    }
-    else if (tv->v_type == VAR_TYPEALIAS)
-    {
-        semsg(_(e_using_typealias_as_value_str), tv->vval.v_typealias->ta_name);
-	return FAIL;
+	case VAR_CLASS:
+	    {
+		class_T *cl = tv->vval.v_class;
+		if (IS_ENUM(cl))
+		    semsg(_(e_using_enum_as_value_str), cl->class_name);
+		else
+		    semsg(_(e_using_class_as_value_str), cl->class_name);
+	    }
+	    return FAIL;
+
+	case VAR_TYPEALIAS:
+	    semsg(_(e_using_typealias_as_value_str),
+		    tv->vval.v_typealias->ta_name);
+	    return FAIL;
+
+	default:
+	    break;
     }
     return OK;
 }
@@ -1893,17 +1908,25 @@ check_type_is_value(type_T *type)
 {
     if (type == NULL)
 	return OK;
-    if (type->tt_type == VAR_CLASS)
+    switch (type->tt_type)
     {
-        semsg(_(e_using_class_as_value_str), type->tt_class->class_name);
-	return FAIL;
-    }
-    else if (type->tt_type == VAR_TYPEALIAS)
-    {
-	// TODO: Not sure what could be done here to get a name.
-	//       Maybe an optional argument?
-        emsg(_(e_using_typealias_as_var_val));
-	return FAIL;
+	case VAR_CLASS:
+	    if (IS_ENUM(type->tt_class))
+		semsg(_(e_using_enum_as_value_str),
+			type->tt_class->class_name);
+	    else
+		semsg(_(e_using_class_as_value_str),
+			type->tt_class->class_name);
+	    return FAIL;
+
+	case VAR_TYPEALIAS:
+	    // TODO: Not sure what could be done here to get a name.
+	    //       Maybe an optional argument?
+	    emsg(_(e_using_typealias_as_var_val));
+	    return FAIL;
+
+	default:
+	    break;
     }
     return OK;
 }