summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2023-10-27 19:35:26 +0200
committerChristian Brabandt <cb@256bit.org>2023-10-27 19:35:26 +0200
commitec3cebbd2b6b7583d2f683f5e66345163ec122aa (patch)
tree66f0c9b69e88fa4a0d35ae738f687b5679bc56e0 /src
parent4bca4897a12dfb91b3b27e3083fd5f370bd857d1 (diff)
patch 9.0.2076: Vim9: No support for type aliasesv9.0.2076
Problem: Vim9: No support for type aliases Solution: Implement :type command A type definition is giving a name to a type specification. This also known type alias. :type ListOfStrings = list<string> The type alias can be used wherever a built-in type can be used. The type alias name must start with an upper case character. closes: #13407 Signed-off-by: Christian Brabandt <cb@256bit.org> Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Diffstat (limited to 'src')
-rw-r--r--src/errors.h30
-rw-r--r--src/eval.c20
-rw-r--r--src/evalfunc.c8
-rw-r--r--src/evalvars.c39
-rw-r--r--src/if_py_both.h1
-rw-r--r--src/json.c1
-rw-r--r--src/proto/vim9class.pro2
-rw-r--r--src/structs.h10
-rw-r--r--src/testdir/test_vim9_class.vim12
-rw-r--r--src/testdir/test_vim9_script.vim380
-rw-r--r--src/testing.c4
-rw-r--r--src/typval.c30
-rw-r--r--src/version.c2
-rw-r--r--src/vim.h4
-rw-r--r--src/vim9class.c111
-rw-r--r--src/vim9compile.c23
-rw-r--r--src/vim9execute.c8
-rw-r--r--src/vim9expr.c8
-rw-r--r--src/vim9instr.c1
-rw-r--r--src/vim9type.c40
-rw-r--r--src/viminfo.c3
21 files changed, 705 insertions, 32 deletions
diff --git a/src/errors.h b/src/errors.h
index 72957d8b93..4c6b63bd75 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3384,17 +3384,17 @@ EXTERN char e_invalid_object_variable_declaration_str[]
EXTERN char e_not_valid_command_in_class_str[]
INIT(= N_("E1318: Not a valid command in a class: %s"));
EXTERN char e_using_class_as_number[]
- INIT(= N_("E1319: Using a class as a Number"));
+ INIT(= N_("E1319: Using a Class as a Number"));
EXTERN char e_using_object_as_number[]
- INIT(= N_("E1320: Using an object as a Number"));
+ INIT(= N_("E1320: Using an Object as a Number"));
EXTERN char e_using_class_as_float[]
- INIT(= N_("E1321: Using a class as a Float"));
+ INIT(= N_("E1321: Using a Class as a Float"));
EXTERN char e_using_object_as_float[]
- INIT(= N_("E1322: Using an object as a Float"));
+ INIT(= N_("E1322: Using an Object as a Float"));
EXTERN char e_using_class_as_string[]
- INIT(= N_("E1323: Using a class as a String"));
+ INIT(= N_("E1323: Using a Class as a String"));
EXTERN char e_using_object_as_string[]
- INIT(= N_("E1324: Using an object as a String"));
+ INIT(= N_("E1324: Using an Object as a String"));
EXTERN char e_method_not_found_on_class_str_str[]
INIT(= N_("E1325: Method \"%s\" not found in class \"%s\""));
EXTERN char e_variable_not_found_on_object_str_str[]
@@ -3538,8 +3538,22 @@ EXTERN char e_cannot_lock_object_variable_str[]
INIT(= N_("E1391: Cannot (un)lock variable \"%s\" in class \"%s\""));
EXTERN char e_cannot_lock_class_variable_str[]
INIT(= N_("E1392: Cannot (un)lock class variable \"%s\" in class \"%s\""));
-#endif
-// E1393 - E1499 unused (reserved for Vim9 class support)
+EXTERN char e_type_can_only_be_defined_in_vim9_script[]
+ INIT(= N_("E1393: Type can only be defined in Vim9 script"));
+EXTERN char e_type_name_must_start_with_uppercase_letter_str[]
+ INIT(= N_("E1394: Type name must start with an uppercase letter: %s"));
+EXTERN char e_using_typealias_as_variable[]
+ INIT(= N_("E1395: Type alias \"%s\" cannot be used as a variable"));
+EXTERN char e_typealias_already_exists_for_str[]
+ INIT(= N_("E1396: Type alias \"%s\" already exists"));
+EXTERN char e_missing_typealias_name[]
+ INIT(= N_("E1397: Missing type alias name"));
+EXTERN char e_missing_typealias_type[]
+ INIT(= N_("E1398: Missing type alias type"));
+EXTERN char e_type_can_only_be_used_in_script[]
+ INIT(= N_("E1399: Type can only be used in a script"));
+#endif
+// E1400 - 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[]
diff --git a/src/eval.c b/src/eval.c
index e888fecc8a..ce981223fd 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1883,6 +1883,14 @@ set_var_lval(
if (eval_variable(lp->ll_name, (int)STRLEN(lp->ll_name),
lp->ll_sid, &tv, &di, EVAL_VAR_VERBOSE) == OK)
{
+ if (di != NULL && di->di_tv.v_type == VAR_TYPEALIAS)
+ {
+ semsg(_(e_using_typealias_as_variable),
+ di->di_tv.vval.v_typealias->ta_name);
+ clear_tv(&tv);
+ return;
+ }
+
if ((di == NULL
|| (!var_check_ro(di->di_flags, lp->ll_name, FALSE)
&& !tv_check_lock(&di->di_tv, lp->ll_name, FALSE)))
@@ -2013,6 +2021,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
case VAR_INSTR:
case VAR_CLASS:
case VAR_OBJECT:
+ case VAR_TYPEALIAS:
break;
case VAR_BLOB:
@@ -5004,6 +5013,7 @@ check_can_index(typval_T *rettv, int evaluate, int verbose)
case VAR_INSTR:
case VAR_CLASS:
case VAR_OBJECT:
+ case VAR_TYPEALIAS:
if (verbose)
emsg(_(e_cannot_index_special_variable));
return FAIL;
@@ -5109,6 +5119,7 @@ eval_index_inner(
case VAR_INSTR:
case VAR_CLASS:
case VAR_OBJECT:
+ case VAR_TYPEALIAS:
break; // not evaluating, skipping over subscript
case VAR_NUMBER:
@@ -6046,6 +6057,7 @@ set_ref_in_item(
case VAR_FLOAT:
case VAR_STRING:
case VAR_BLOB:
+ case VAR_TYPEALIAS:
case VAR_INSTR:
// Types that do not contain any other item
break;
@@ -6329,6 +6341,13 @@ echo_string_core(
*tofree = NULL;
r = (char_u *)get_var_special_name(tv->vval.v_number);
break;
+
+ case VAR_TYPEALIAS:
+ *tofree = vim_strsave(tv->vval.v_typealias->ta_name);
+ r = *tofree;
+ if (r == NULL)
+ r = (char_u *)"";
+ break;
}
if (--recurse == 0)
@@ -7201,6 +7220,7 @@ item_copy(
case VAR_INSTR:
case VAR_CLASS:
case VAR_OBJECT:
+ case VAR_TYPEALIAS:
copy_tv(from, to);
break;
case VAR_LIST:
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 9e4e26ba0c..7f7914eca7 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -3890,6 +3890,12 @@ f_empty(typval_T *argvars, typval_T *rettv)
|| !channel_is_open(argvars[0].vval.v_channel);
break;
#endif
+ case VAR_TYPEALIAS:
+ n = argvars[0].vval.v_typealias == NULL
+ || argvars[0].vval.v_typealias->ta_name == NULL
+ || *argvars[0].vval.v_typealias->ta_name == NUL;
+ break;
+
case VAR_UNKNOWN:
case VAR_ANY:
case VAR_VOID:
@@ -7539,6 +7545,7 @@ f_len(typval_T *argvars, typval_T *rettv)
case VAR_INSTR:
case VAR_CLASS:
case VAR_OBJECT:
+ case VAR_TYPEALIAS:
emsg(_(e_invalid_type_for_len));
break;
}
@@ -10885,6 +10892,7 @@ f_type(typval_T *argvars, typval_T *rettv)
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_UNKNOWN:
case VAR_ANY:
case VAR_VOID:
diff --git a/src/evalvars.c b/src/evalvars.c
index d7a1a96a54..5c7bc12d2f 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -158,6 +158,7 @@ static struct vimvar
{VV_NAME("sizeofpointer", VAR_NUMBER), NULL, VV_RO},
{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},
};
// shorthand
@@ -260,6 +261,7 @@ evalvars_init(void)
set_vim_var_nr(VV_TYPE_BLOB, VAR_TYPE_BLOB);
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_ECHOSPACE, sc_col - 1);
@@ -1834,6 +1836,12 @@ ex_let_one(
return NULL;
}
+ if (tv->v_type == VAR_TYPEALIAS)
+ {
+ semsg(_(e_using_typealias_as_variable), tv->vval.v_typealias->ta_name);
+ return NULL;
+ }
+
if (*arg == '$')
{
// ":let $VAR = expr": Set environment variable.
@@ -2331,6 +2339,7 @@ item_lock(typval_T *tv, int deep, int lock, int check_refcount)
case VAR_INSTR:
case VAR_CLASS:
case VAR_OBJECT:
+ case VAR_TYPEALIAS:
break;
case VAR_BLOB:
@@ -2998,7 +3007,7 @@ eval_variable(
}
// Check for local variable when debugging.
- if ((tv = lookup_debug_var(name)) == NULL)
+ if ((sid == 0) && (tv = lookup_debug_var(name)) == NULL)
{
// Check for user-defined variables.
dictitem_T *v = find_var(name, &ht, flags & EVAL_VAR_NOAUTOLOAD);
@@ -3114,6 +3123,25 @@ eval_variable(
}
}
+ if ((tv->v_type == VAR_TYPEALIAS || tv->v_type == VAR_CLASS)
+ && sid != 0)
+ {
+ // type alias or class imported from another script. Check
+ // whether it is exported from the other script.
+ sv = find_typval_in_script(tv, sid, TRUE);
+ if (sv == NULL)
+ {
+ ret = FAIL;
+ goto done;
+ }
+ if ((sv->sv_flags & SVFLAG_EXPORTED) == 0)
+ {
+ semsg(_(e_item_not_exported_in_script_str), name);
+ ret = FAIL;
+ goto done;
+ }
+ }
+
// If a list or dict variable wasn't initialized and has meaningful
// type, do it now. Not for global variables, they are not
// declared.
@@ -3162,6 +3190,7 @@ eval_variable(
}
}
+done:
if (len > 0)
name[len] = cc;
@@ -3948,6 +3977,14 @@ set_var_const(
goto failed;
}
+ if (di->di_tv.v_type == VAR_TYPEALIAS)
+ {
+ semsg(_(e_using_typealias_as_variable),
+ di->di_tv.vval.v_typealias->ta_name);
+ clear_tv(&di->di_tv);
+ goto failed;
+ }
+
if (var_in_vim9script && (flags & ASSIGN_FOR_LOOP) == 0)
{
where_T where = WHERE_INIT;
diff --git a/src/if_py_both.h b/src/if_py_both.h
index 06201711f6..f8438639e6 100644
--- a/src/if_py_both.h
+++ b/src/if_py_both.h
@@ -6772,6 +6772,7 @@ ConvertToPyObject(typval_T *tv)
case VAR_INSTR:
case VAR_CLASS:
case VAR_OBJECT:
+ case VAR_TYPEALIAS:
Py_INCREF(Py_None);
return Py_None;
case VAR_BOOL:
diff --git a/src/json.c b/src/json.c
index e2a011309c..66b8bf9d14 100644
--- a/src/json.c
+++ b/src/json.c
@@ -310,6 +310,7 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options)
case VAR_INSTR:
case VAR_CLASS:
case VAR_OBJECT:
+ case VAR_TYPEALIAS:
semsg(_(e_cannot_json_encode_str), vartype_name(val->v_type));
return FAIL;
diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro
index 62d1b7d0f3..3000f57a38 100644
--- a/src/proto/vim9class.pro
+++ b/src/proto/vim9class.pro
@@ -4,6 +4,8 @@ void ex_class(exarg_T *eap);
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);
+void typealias_free(typealias_T *ta);
+void typealias_unref(typealias_T *ta);
void ex_type(exarg_T *eap);
int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
ufunc_T *find_class_func(char_u **arg);
diff --git a/src/structs.h b/src/structs.h
index 209067b2e0..36968741e3 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1468,6 +1468,7 @@ typedef struct ectx_S ectx_T;
typedef struct instr_S instr_T;
typedef struct class_S class_T;
typedef struct object_S object_T;
+typedef struct typealias_S typealias_T;
typedef enum
{
@@ -1489,6 +1490,7 @@ typedef enum
VAR_INSTR, // "v_instr" is used
VAR_CLASS, // "v_class" is used (also used for interface)
VAR_OBJECT, // "v_object" is used
+ VAR_TYPEALIAS // "v_typealias" is used
} vartype_T;
// A type specification.
@@ -1602,6 +1604,13 @@ struct object_S
int obj_copyID; // used by garbage collection
};
+struct typealias_S
+{
+ int ta_refcount;
+ type_T *ta_type;
+ char_u *ta_name;
+};
+
/*
* Structure to hold an internal variable without a name.
*/
@@ -1625,6 +1634,7 @@ struct typval_S
instr_T *v_instr; // instructions to execute
class_T *v_class; // class value (can be NULL)
object_T *v_object; // object value (can be NULL)
+ typealias_T *v_typealias; // user-defined type name
} vval;
};
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index 1c309e4f0c..a8d0fd6697 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -170,7 +170,7 @@ def Test_class_basic()
if A
endif
END
- v9.CheckSourceFailure(lines, 'E1319: Using a class as a Number', 4)
+ v9.CheckSourceFailure(lines, 'E1319: Using a Class as a Number', 4)
# Test for using object as a bool
lines =<< trim END
@@ -181,7 +181,7 @@ def Test_class_basic()
if a
endif
END
- v9.CheckSourceFailure(lines, 'E1320: Using an object as a Number', 5)
+ v9.CheckSourceFailure(lines, 'E1320: Using an Object as a Number', 5)
# Test for using class as a float
lines =<< trim END
@@ -190,7 +190,7 @@ def Test_class_basic()
endclass
sort([1.1, A], 'f')
END
- v9.CheckSourceFailure(lines, 'E1321: Using a class as a Float', 4)
+ v9.CheckSourceFailure(lines, 'E1321: Using a Class as a Float', 4)
# Test for using object as a float
lines =<< trim END
@@ -200,7 +200,7 @@ def Test_class_basic()
var a = A.new()
sort([1.1, a], 'f')
END
- v9.CheckSourceFailure(lines, 'E1322: Using an object as a Float', 5)
+ v9.CheckSourceFailure(lines, 'E1322: Using an Object as a Float', 5)
# Test for using class as a string
lines =<< trim END
@@ -209,7 +209,7 @@ def Test_class_basic()
endclass
:exe 'call ' .. A
END
- v9.CheckSourceFailure(lines, 'E1323: Using a class as a String', 4)
+ v9.CheckSourceFailure(lines, 'E1323: Using a Class as a String', 4)
# Test for using object as a string
lines =<< trim END
@@ -219,7 +219,7 @@ def Test_class_basic()
var a = A.new()
:exe 'call ' .. a
END
- v9.CheckSourceFailure(lines, 'E1324: Using an object as a String', 5)
+ v9.CheckSourceFailure(lines, 'E1324: Using an Object as a String', 5)
# Test creating a class with member variables and methods, calling a object
# method. Check for using type() and typename() with a class and an object.
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index cac8484977..2bc9b14aa3 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -4722,7 +4722,7 @@ def Test_defer_after_exception()
assert_equal([2, 3, 1, 4, 5, 6, 7], callTrace)
END
- v9.CheckScriptSuccess(lines)
+ v9.CheckSourceSuccess(lines)
enddef
" Test for multiple deferred function which throw exceptions.
@@ -4780,6 +4780,384 @@ def Test_multidefer_with_exception()
assert_equal('E605: Exception not caught: InnerException', v:errmsg)
assert_equal([11, 9, 10, 7, 8, 5, 1, 3, 4, 12, 15, 16], callTrace)
END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for :type command to create type aliases
+def Test_typealias()
+ var lines =<< trim END
+ vim9script
+ type ListOfStrings = list<string>
+ var a: ListOfStrings = ['a', 'b']
+ assert_equal(['a', 'b'], a)
+ def Foo(b: ListOfStrings): ListOfStrings
+ var c: ListOfStrings = ['c', 'd']
+ assert_equal(['c', 'd'], c)
+ return b
+ enddef
+ assert_equal(['e', 'f'], Foo(['e', 'f']))
+ assert_equal('typealias<list<string>>', typename(ListOfStrings))
+ assert_equal(v:t_typealias, type(ListOfStrings))
+ assert_equal('ListOfStrings', string(ListOfStrings))
+ assert_equal(false, null == ListOfStrings)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Use :type outside a Vim9 script
+ lines =<< trim END
+ type Index = number
+ END
+ v9.CheckSourceFailure(lines, 'E1393: Type can only be defined in Vim9 script', 1)
+
+ # Use :type without any arguments
+ lines =<< trim END
+ vim9script
+ type
+ END
+ v9.CheckSourceFailure(lines, 'E1397: Missing type alias name', 2)
+
+ # Use :type with a name but no type
+ lines =<< trim END
+ vim9script
+ type MyType
+ END
+ v9.CheckSourceFailure(lines, "E398: Missing '=': ", 2)
+
+ # Use :type with a name but no type following "="
+ lines =<< trim END
+ vim9script
+ type MyType =
+ END
+ v9.CheckSourceFailure(lines, 'E1398: Missing type alias type', 2)
+
+ # No space before or after "="
+ lines =<< trim END
+ vim9script
+ type MyType=number
+ END
+ v9.CheckSourceFailure(lines, 'E1315: White space required after name: MyType=number', 2)
+
+ # No space after "="
+ lines =<< trim END
+ vim9script
+ type MyType =number
+ END
+ v9.CheckSourceFailure(lines, "E1069: White space required after '=': =number", 2)
+
+ # type alias without "="
+ lines =<< trim END
+ vim9script
+ type Index number
+ END
+ v9.CheckSourceFailure(lines, "E398: Missing '=': number", 2)
+
+ # type alias for a non-existing type
+ lines =<< trim END
+ vim9script
+ type Index = integer
+ END
+ v9.CheckSourceFailure(lines, 'E1010: Type not recognized: integer', 2)
+
+ # type alias starting with lower-case letter
+ lines =<< trim END
+ vim9script
+ type index number
+ END
+ v9.CheckSourceFailure(lines, 'E1394: Type name must start with an uppercase letter: index number', 2)
+
+ # No white space following the alias name
+ lines =<< trim END
+ vim9script
+ type Index:number
+ END
+ v9.CheckSourceFailure(lines, 'E1315: White space required after name: Index:number', 2)
+
+ # something following the type alias
+ lines =<< trim END
+ vim9script
+ type ListOfNums = list<number> string
+ END
+ v9.CheckSourceFailure(lines, 'E488: Trailing characters: string', 2)
+
+ # type alias name collides with a variable name
+ lines =<< trim END
+ vim9script
+ var ListOfNums: number = 10
+ type ListOfNums = list<number>
+ END
+ v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "ListOfNums"', 3)
+
+ # duplicate type alias name
+ lines =<< trim END
+ vim9script
+ type MyList = list<number>
+ type MyList = list<string>
+ END
+ v9.CheckSourceFailure(lines, 'E1396: Type alias "MyList" already exists', 3)
+
+ # Sourcing a script twice (which will free script local variables)
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ type AC = C
+ assert_equal('typealias<object<C>>', typename(AC))
+ END
+ new
+ setline(1, lines)
+ :source
+ :source
+ bw!
+
+ # Assigning to a type alias (script level)
+ lines =<< trim END
+ vim9script
+ type MyType = list<number>
+ MyType = [1, 2, 3]
+ END
+ v9.CheckSourceFailure(lines, 'E1395: Type alias "MyType" cannot be used as a variable', 3)
+
+ # Assigning a type alias (def function level)
+ lines =<< trim END
+ vim9script
+ type A = list<string>
+ def Foo()
+ var x = A
+ enddef
+ Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1395: Type alias "A" cannot be used as a variable', 1)
+
+ # Using type alias in an expression (script level)
+ lines =<< trim END
+ vim9script
+ type MyType = list<number>
+ assert_fails('var m = MyType', 'E1395: Type alias "MyType" cannot be used as a variable')
+ assert_fails('var i = MyType + 1', 'E1395: Type alias "MyType" cannot be used as a variable')
+ assert_fails('var f = 1.0 + MyType', 'E1395: Type alias "MyType" cannot be used as a variable')
+ assert_fails('MyType += 10', 'E1395: Type alias "MyType" cannot be used as a variable')
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Using type alias in an expression (def function level)
+ lines =<< trim END
+ vim9script
+ type MyType = list<number>
+ def Foo()
+ var x = MyType + 1
+ enddef
+ Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1395: Type alias "MyType" cannot be used as a variable', 1)
+
+ # Using type alias in an expression (def function level)
+ lines =<< trim END
+ vim9script
+ type MyType = list<number>
+ def Foo()
+ MyType = list<string>
+ enddef
+ Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E46: Cannot change read-only variable "MyType"', 1)
+
+ # Using type alias in an expression (def function level)
+ lines =<< trim END
+ vim9script
+ type MyType = list<number>
+ def Foo()
+ MyType += 10
+ enddef
+ Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E46: Cannot change read-only variable "MyType"', 1)
+
+ # Creating a typealias in a def function
+ lines =<< trim END
+ vim9script
+ def Foo()
+ var n: number = 10
+ type A = list<string>
+ enddef
+ defcompile
+ END
+ v9.CheckSourceFailure(lines, 'E1399: Type can only be used in a script', 2)
+
+ # json_encode should fail with a type alias
+ lines =<< trim END
+ vim9script
+ type A = list<string>
+ var x = json_encode(A)
+ END
+ v9.CheckSourceFailure(lines, 'E1161: Cannot json encode a typealias', 3)
+
+ # Comparing type alias with a number (script level)
+ lines =<< trim END
+ vim9script
+ type A = list<string>
+ var n: number
+ var x = A == n
+ END
+ v9.CheckSourceFailure(lines, 'E1072: Cannot compare typealias with number', 4)
+
+ # Comparing type alias with a number (def function level)
+ lines =<< trim END
+ vim9script
+ type A = list<string>
+ def Foo()
+ var n: number
+ var x = A == n
+ enddef
+ Foo()
+ END
+ v9.CheckSourceFailure(lines, 'E1395: Type alias "A" cannot be used as a variable', 2)
+enddef
+
+" Test for exporting and importing type aliases
+def Test_import_typealias()
+ var lines =<< trim END
+ vim9script
+ export type MyType = list<number>
+ END
+ writefile(lines, 'Xtypeexport.vim', 'D')
+
+ lines =<< trim END
+ vim9script
+ import './Xtypeexport.vim' as A
+
+ var myList: A.MyType = [1, 2, 3]
+ def Foo(l: A.MyType)
+ assert_equal([1, 2, 3], l)
+ enddef
+ Foo(myList)
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Use a non existing type alias
+ lines =<< trim END
+ vim9script
+ import './Xtypeexport.vim' as A
+
+ var myNum: A.SomeType = 10
+ END
+ v9.CheckScriptFailure(lines, 'E1010: Type not recognized: A.SomeType = 10', 4)
+
+ # Use a type alias that is not exported
+ lines =<< trim END
+ vim9script
+ type NewType = dict<string>
+ END
+ writefile(lines, 'Xtypeexport2.vim', 'D')
+ lines =<< trim END
+ vim9script
+ import './Xtypeexport2.vim' as A
+
+ var myDict: A.NewType = {}
+ END
+ v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NewType', 4)
+
+ # Using the same name as an imported type alias
+ lines =<< trim END
+ vim9script
+ export type MyType2 = list<number>
+ END
+ writefile(lines, 'Xtypeexport3.vim', 'D')
+ lines =<< trim END
+ vim9script
+ import './Xtypeexport3.vim' as A
+
+ type MyType2 = A.MyType2
+ var myList1: A.MyType2 = [1, 2, 3]
+ var myList2: MyType2 = [4, 5, 6]
+ assert_equal([1, 2, 3], myList1)
+ assert_equal([4, 5, 6], myList2)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for using typealias as a def function argument and return type
+def Test_typealias_func_argument()
+ var lines =<< trim END
+ vim9script
+ type A = list<number>
+ def Foo(l: A): A
+ assert_equal([1, 2], l)
+ return l
+ enddef
+ var x: A = [1, 2]
+ assert_equal([1, 2], Foo(x))
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # passing a type alias variable to a function expecting a specific type
+ lines =<< trim END
+ vim9script
+ type A = list<number>
+ def Foo(l: list<number>)
+ assert_equal([1, 2], l)
+ enddef
+ var x: A = [1, 2]
+ Foo(x)
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # passing a type alias variable to a function expecting any
+ lines =<< trim END
+ vim9script
+ type A = list<number>
+ def Foo(l: any)
+ assert_equal([1, 2], l)
+ enddef
+ var x: A = [1, 2]
+ Foo(x)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Using a type alias with a builtin function
+def Test_typealias_with_builtin_functions()
+ var lines =<< trim END
+ vim9script
+ type A = list<func>
+ assert_equal(0, empty(A))
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Using a type alias with len()
+ lines =<< trim END
+ vim9script
+ type A = list<func>
+ var x = len(A)
+ END
+ v9.CheckScriptFailure(lines, 'E701: Invalid type for len()', 3)
+
+ # Using a type alias with eval()
+ lines =<< trim END
+ vim9script
+ type A = number
+ def Foo()
+ var x = eval("A")
+ enddef
+ Foo()
+ END
+ v9.CheckScriptFailure(lines, 'E1395: Type alias "A" cannot be used as a variable', 1)
+enddef
+
+" Test for type alias refcount
+def Test_typealias_refcount()
+ var lines =<< trim END
+ vim9script
+ type A = list<func>
+ assert_equal(1, test_refcount(A))
+ END
+ v9.CheckScriptSuccess(lines)
+
+ lines =<< trim END
+ vim9script
+ type B = list<number>
+ var x: B = []
+ assert_equal(1, test_refcount(B))
+ END
v9.CheckScriptSuccess(lines)
enddef
diff --git a/src/testing.c b/src/testing.c
index fd55927dfb..1835643689 100644
--- a/src/testing.c
+++ b/src/testing.c
@@ -1132,6 +1132,10 @@ f_test_refcount(typval_T *argvars, typval_T *rettv)
if (argvars[0].vval.v_dict != NULL)
retval = argvars[0].vval.v_dict->dv_refcount - 1;
break;
+ case VAR_TYPEALIAS:
+ if (argvars[0].vval.v_typealias != NULL)
+ retval = argvars[0].vval.v_typealias->ta_refcount - 1;
+ break;
}
rettv->v_type = VAR_NUMBER;
diff --git a/src/typval.c b/src/typval.c
index 08dd2313f2..da5d7affb7 100644
--- a/src/typval.c
+++ b/src/typval.c
@@ -92,6 +92,10 @@ free_tv(typval_T *varp)
object_unref(varp->vval.v_object);
break;
+ case VAR_TYPEALIAS:
+ typealias_unref(varp->vval.v_typealias);
+ break;
+
case VAR_NUMBER:
case VAR_FLOAT:
case VAR_ANY:
@@ -169,6 +173,10 @@ clear_tv(typval_T *varp)
object_unref(varp->vval.v_object);
varp->vval.v_object = NULL;
break;
+ case VAR_TYPEALIAS:
+ typealias_unref(varp->vval.v_typealias);
+ varp->vval.v_typealias = NULL;
+ break;
case VAR_UNKNOWN:
case VAR_ANY:
case VAR_VOID:
@@ -262,6 +270,10 @@ tv_get_bool_or_number_chk(
case VAR_VOID:
emsg(_(e_cannot_use_void_value));
break;
+ case VAR_TYPEALIAS:
+ semsg(_(e_using_typealias_as_variable),
+ varp->vval.v_typealias->ta_name);
+ break;
case VAR_UNKNOWN:
case VAR_ANY:
case VAR_INSTR:
@@ -379,6 +391,10 @@ tv_get_float_chk(typval_T *varp, int *error)
case VAR_VOID:
emsg(_(e_cannot_use_void_value));
break;
+ case VAR_TYPEALIAS:
+ semsg(_(e_using_typealias_as_variable),
+ varp->vval.v_typealias->ta_name);
+ break;
case VAR_UNKNOWN:
case VAR_ANY:
case VAR_INSTR:
@@ -1129,6 +1145,7 @@ tv_get_string_buf_chk_strict(typval_T *varp, char_u *buf, int strict)
case VAR_VOID:
emsg(_(e_cannot_use_void_value));
break;
+ case VAR_TYPEALIAS:
case VAR_UNKNOWN:
case VAR_ANY:
case VAR_INSTR:
@@ -1290,6 +1307,15 @@ copy_tv(typval_T *from, typval_T *to)
++to->vval.v_dict->dv_refcount;
}
break;
+ case VAR_TYPEALIAS:
+ if (from->vval.v_typealias == NULL)
+ to->vval.v_typealias = NULL;
+ else
+ {
+ to->vval.v_typealias = from->vval.v_typealias;
+ ++to->vval.v_typealias->ta_refcount;
+ }
+ break;
case VAR_VOID:
emsg(_(e_cannot_use_void_value));
break;
@@ -1596,6 +1622,7 @@ typval_compare_null(typval_T *tv1, typval_T *tv2)
case VAR_FLOAT: if (!in_vim9script())
return tv->vval.v_float == 0.0;
break;
+ case VAR_TYPEALIAS: return tv->vval.v_typealias == NULL;
default: break;
}
}
@@ -2069,6 +2096,9 @@ tv_equal(
case VAR_FUNC:
return tv1->vval.v_string == tv2->vval.v_string;
+ case VAR_TYPEALIAS:
+ return tv1->vval.v_typealias == tv2->vval.v_typealias;
+
case VAR_UNKNOWN:
case VAR_ANY:
case VAR_VOID:
diff --git a/src/version.c b/src/version.c
index 8c90857204..d233fa6ab9 100644
--- 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 */
/**/
+ 2076,
+/**/
2075,
/**/
2074,
diff --git a/src/vim.h b/src/vim.h
index 2b6c787b99..6fb8978224 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -2142,7 +2142,8 @@ typedef int sock_T;
#define VV_SIZEOFPOINTER 104
#define VV_MAXCOL 105
#define VV_PYTHON3_VERSION 106
-#define VV_LEN 107 // number of v: vars
+#define VV_TYPE_TYPEALIAS 107
+#define VV_LEN 108 // number of v: vars
// used for v_number in VAR_BOOL and VAR_SPECIAL
#define VVAL_FALSE 0L // VAR_BOOL
@@ -2165,6 +2166,7 @@ typedef int sock_T;
#define VAR_TYPE_INSTR 11
#define VAR_TYPE_CLASS 12
#define VAR_TYPE_OBJECT 13
+#define VAR_TYPE_TYPEALIAS 15
#define DICT_MAXNEST 100 // maximum nesting of lists and dicts
diff --git a/src/vim9class.c b/src/vim9class.c
index a05dcce3da..0e8dc2df77 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -2095,12 +2095,119 @@ ex_enum(exarg_T *eap UNUSED)
}
/*
- * Handle ":type".
+ * Type aliases (:type)
+ */
+
+ void
+typealias_free(typealias_T *ta)
+{
+ // ta->ta_type is freed in clear_type_list()
+ vim_free(ta->ta_name);
+ vim_free(ta);
+}
+