patch 9.0.2076: Vim9: No support for type aliases
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>
This commit is contained in:
committed by
Christian Brabandt
parent
4bca4897a1
commit
ec3cebbd2b
@ -743,12 +743,13 @@ constructor methods.
|
||||
|
||||
7. Type definition *Vim9-type* *:type*
|
||||
|
||||
{not implemented yet}
|
||||
A type definition is giving a name to a type specification. This also known
|
||||
type alias. For Example: >
|
||||
|
||||
A type definition is giving a name to a type specification. For Example: >
|
||||
|
||||
:type ListOfStrings list<string>
|
||||
: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.
|
||||
|
||||
==============================================================================
|
||||
|
||||
|
||||
28
src/errors.h
28
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\""));
|
||||
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
|
||||
// E1393 - E1499 unused (reserved for Vim9 class support)
|
||||
// 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[]
|
||||
|
||||
20
src/eval.c
20
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:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
30
src/typval.c
30
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:
|
||||
|
||||
@ -704,6 +704,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
2076,
|
||||
/**/
|
||||
2075,
|
||||
/**/
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
111
src/vim9class.c
111
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);
|
||||
}
|
||||
|
||||
void
|
||||
typealias_unref(typealias_T *ta)
|
||||
{
|
||||
if (ta != NULL && --ta->ta_refcount <= 0)
|
||||
typealias_free(ta);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle ":type". Create an alias for a type specification.
|
||||
*/
|
||||
void
|
||||
ex_type(exarg_T *eap UNUSED)
|
||||
{
|
||||
// TODO
|
||||
char_u *arg = eap->arg;
|
||||
|
||||
if (!current_script_is_vim9()
|
||||
|| (cmdmod.cmod_flags & CMOD_LEGACY)
|
||||
|| !getline_equal(eap->getline, eap->cookie, getsourceline))
|
||||
{
|
||||
emsg(_(e_type_can_only_be_defined_in_vim9_script));
|
||||
return;
|
||||
}
|
||||
|
||||
if (*arg == NUL)
|
||||
{
|
||||
emsg(_(e_missing_typealias_name));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ASCII_ISUPPER(*arg))
|
||||
{
|
||||
semsg(_(e_type_name_must_start_with_uppercase_letter_str), arg);
|
||||
return;
|
||||
}
|
||||
|
||||
char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
|
||||
if (!IS_WHITE_OR_NUL(*name_end))
|
||||
{
|
||||
semsg(_(e_white_space_required_after_name_str), arg);
|
||||
return;
|
||||
}
|
||||
char_u *name_start = arg;
|
||||
|
||||
arg = skipwhite(name_end);
|
||||
if (*arg != '=')
|
||||
{
|
||||
semsg(_(e_missing_equal_str), arg);
|
||||
return;
|
||||
}
|
||||
if (!IS_WHITE_OR_NUL(*(arg + 1)))
|
||||
{
|
||||
semsg(_(e_white_space_required_after_str_str), "=", arg);
|
||||
return;
|
||||
}
|
||||
arg++;
|
||||
arg = skipwhite(arg);
|
||||
|
||||
if (*arg == NUL)
|
||||
{
|
||||
emsg(_(e_missing_typealias_type));
|
||||
return;
|
||||
}
|
||||
|
||||
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
|
||||
type_T *type = parse_type(&arg, &si->sn_type_list, TRUE);
|
||||
if (type == NULL)
|
||||
return;
|
||||
|
||||
if (*arg != NUL)
|
||||
{
|
||||
// some text after the type
|
||||
semsg(_(e_trailing_characters_str), arg);
|
||||
return;
|
||||
}
|
||||
|
||||
int cc = *name_end;
|
||||
*name_end = NUL;
|
||||
|
||||
typval_T tv;
|
||||
tv.v_type = VAR_UNKNOWN;
|
||||
if (eval_variable_import(name_start, &tv) == OK)
|
||||
{
|
||||
if (tv.v_type == VAR_TYPEALIAS)
|
||||
semsg(_(e_typealias_already_exists_for_str), name_start);
|
||||
else
|
||||
semsg(_(e_redefining_script_item_str), name_start);
|
||||
clear_tv(&tv);
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Add the user-defined type to the script-local variables.
|
||||
tv.v_type = VAR_TYPEALIAS;
|
||||
tv.v_lock = 0;
|
||||
tv.vval.v_typealias = ALLOC_CLEAR_ONE(typealias_T);
|
||||
++tv.vval.v_typealias->ta_refcount;
|
||||
tv.vval.v_typealias->ta_name = vim_strsave(name_start);
|
||||
tv.vval.v_typealias->ta_type = type;
|
||||
set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
|
||||
ASSIGN_CONST | ASSIGN_FINAL, 0);
|
||||
|
||||
done:
|
||||
*name_end = cc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -1327,11 +1327,12 @@ assignment_len(char_u *p, int *heredoc)
|
||||
/*
|
||||
* Generate the load instruction for "name".
|
||||
*/
|
||||
static void
|
||||
static int
|
||||
generate_loadvar(cctx_T *cctx, lhs_T *lhs)
|
||||
{
|
||||
char_u *name = lhs->lhs_name;
|
||||
type_T *type = lhs->lhs_type;
|
||||
int res = OK;
|
||||
|
||||
switch (lhs->lhs_dest)
|
||||
{
|
||||
@ -1360,7 +1361,7 @@ generate_loadvar(cctx_T *cctx, lhs_T *lhs)
|
||||
generate_LOAD(cctx, ISN_LOADT, 0, name + 2, type);
|
||||
break;
|
||||
case dest_script:
|
||||
compile_load_scriptvar(cctx,
|
||||
res = compile_load_scriptvar(cctx,
|
||||
name + (name[1] == ':' ? 2 : 0), NULL, NULL);
|
||||
break;
|
||||
case dest_env:
|
||||
@ -1392,6 +1393,8 @@ generate_loadvar(cctx_T *cctx, lhs_T *lhs)
|
||||
// list or dict value should already be on the stack.
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2240,12 +2243,13 @@ compile_load_lhs(
|
||||
&& need_type(rhs_type, member_type, FALSE,
|
||||
-3, 0, cctx, FALSE, FALSE) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
else
|
||||
generate_loadvar(cctx, lhs);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
return generate_loadvar(cctx, lhs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Produce code for loading "lhs" and also take care of an index.
|
||||
* Return OK/FAIL.
|
||||
@ -2301,7 +2305,8 @@ compile_load_lhs_with_index(lhs_T *lhs, char_u *var_start, cctx_T *cctx)
|
||||
return generate_CLASSMEMBER(cctx, TRUE, cl, lhs->lhs_member_idx);
|
||||
}
|
||||
|
||||
compile_load_lhs(lhs, var_start, NULL, cctx);
|
||||
if (compile_load_lhs(lhs, var_start, NULL, cctx) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (lhs->lhs_has_index)
|
||||
{
|
||||
@ -2510,6 +2515,7 @@ push_default_value(
|
||||
case VAR_VOID:
|
||||
case VAR_INSTR:
|
||||
case VAR_CLASS:
|
||||
case VAR_TYPEALIAS:
|
||||
case VAR_SPECIAL: // cannot happen
|
||||
// This is skipped for local variables, they are always
|
||||
// initialized to zero. But in a "for" or "while" loop
|
||||
@ -3963,6 +3969,11 @@ compile_def_function(
|
||||
line = (char_u *)"";
|
||||
break;
|
||||
|
||||
case CMD_type:
|
||||
emsg(_(e_type_can_only_be_used_in_script));
|
||||
goto erret;
|
||||
break;
|
||||
|
||||
case CMD_global:
|
||||
if (check_global_and_subst(ea.cmd, p) == FAIL)
|
||||
goto erret;
|
||||
|
||||
@ -3807,6 +3807,13 @@ exec_instructions(ectx_T *ectx)
|
||||
case ISN_STORE:
|
||||
--ectx->ec_stack.ga_len;
|
||||
tv = STACK_TV_VAR(iptr->isn_arg.number);
|
||||
if (STACK_TV_BOT(0)->v_type == VAR_TYPEALIAS)
|
||||
{
|
||||
semsg(_(e_using_typealias_as_variable),
|
||||
STACK_TV_BOT(0)->vval.v_typealias->ta_name);
|
||||
clear_tv(STACK_TV_BOT(0));
|
||||
goto on_error;
|
||||
}
|
||||
clear_tv(tv);
|
||||
*tv = *STACK_TV_BOT(0);
|
||||
break;
|
||||
@ -7517,6 +7524,7 @@ tv2bool(typval_T *tv)
|
||||
case VAR_INSTR:
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
case VAR_TYPEALIAS:
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
@ -238,6 +238,7 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
|
||||
case VAR_INSTR:
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
case VAR_TYPEALIAS:
|
||||
case VAR_UNKNOWN:
|
||||
case VAR_ANY:
|
||||
case VAR_VOID:
|
||||
@ -529,6 +530,13 @@ compile_load_scriptvar(
|
||||
{
|
||||
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
|
||||
|
||||
if (sv->sv_tv->v_type == VAR_TYPEALIAS)
|
||||
{
|
||||
semsg(_(e_using_typealias_as_variable),
|
||||
sv->sv_tv->vval.v_typealias->ta_name);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
generate_VIM9SCRIPT(cctx, ISN_LOADSCRIPT,
|
||||
current_sctx.sc_sid, idx, sv->sv_type);
|
||||
return OK;
|
||||
|
||||
@ -240,6 +240,7 @@ may_generate_2STRING(int offset, int tolerant, cctx_T *cctx)
|
||||
case VAR_INSTR:
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
case VAR_TYPEALIAS:
|
||||
to_string_error(type->tt_type);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
@ -1354,7 +1354,9 @@ parse_type(char_u **arg, garray_T *type_gap, int give_error)
|
||||
}
|
||||
|
||||
// It can be a class or interface name, possibly imported.
|
||||
int did_emsg_before = did_emsg;
|
||||
typval_T tv;
|
||||
|
||||
tv.v_type = VAR_UNKNOWN;
|
||||
if (eval_variable_import(*arg, &tv) == OK)
|
||||
{
|
||||
@ -1377,11 +1379,22 @@ parse_type(char_u **arg, garray_T *type_gap, int give_error)
|
||||
return type;
|
||||
}
|
||||
}
|
||||
else if (tv.v_type == VAR_TYPEALIAS)
|
||||
{
|
||||
// user defined type
|
||||
type_T *type = copy_type(tv.vval.v_typealias->ta_type, type_gap);
|
||||
*arg += len;
|
||||
clear_tv(&tv);
|
||||
// Skip over ".TypeName".
|
||||
while (ASCII_ISALNUM(**arg) || **arg == '_' || **arg == '.')
|
||||
++*arg;
|
||||
return type;
|
||||
}
|
||||
|
||||
clear_tv(&tv);
|
||||
}
|
||||
|
||||
if (give_error)
|
||||
if (give_error && (did_emsg == did_emsg_before))
|
||||
semsg(_(e_type_not_recognized_str), *arg);
|
||||
return NULL;
|
||||
}
|
||||
@ -1416,6 +1429,7 @@ equal_type(type_T *type1, type_T *type2, int flags)
|
||||
case VAR_INSTR:
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
case VAR_TYPEALIAS:
|
||||
break; // not composite is always OK
|
||||
case VAR_LIST:
|
||||
case VAR_DICT:
|
||||
@ -1666,6 +1680,7 @@ vartype_name(vartype_T type)
|
||||
case VAR_INSTR: return "instr";
|
||||
case VAR_CLASS: return "class";
|
||||
case VAR_OBJECT: return "object";
|
||||
case VAR_TYPEALIAS: return "typealias";
|
||||
|
||||
case VAR_FUNC:
|
||||
case VAR_PARTIAL: return "func";
|
||||
@ -1795,12 +1810,25 @@ f_typename(typval_T *argvars, typval_T *rettv)
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
ga_init2(&type_list, sizeof(type_T *), 10);
|
||||
if (argvars[0].v_type == VAR_TYPEALIAS)
|
||||
type = argvars[0].vval.v_typealias->ta_type;
|
||||
else
|
||||
type = typval2type(argvars, get_copyID(), &type_list, TVTT_DO_MEMBER);
|
||||
name = type_name(type, &tofree);
|
||||
if (argvars[0].v_type == VAR_TYPEALIAS)
|
||||
{
|
||||
vim_snprintf((char *)IObuff, IOSIZE, "typealias<%s>", name);
|
||||
rettv->vval.v_string = vim_strsave((char_u *)IObuff);
|
||||
if (tofree != NULL)
|
||||
vim_free(tofree);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tofree != NULL)
|
||||
rettv->vval.v_string = (char_u *)tofree;
|
||||
else
|
||||
rettv->vval.v_string = vim_strsave((char_u *)name);
|
||||
}
|
||||
clear_type_list(&type_list);
|
||||
}
|
||||
|
||||
|
||||
@ -1374,6 +1374,7 @@ write_viminfo_varlist(FILE *fp)
|
||||
case VAR_INSTR:
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
case VAR_TYPEALIAS:
|
||||
continue;
|
||||
}
|
||||
fprintf(fp, "!%s\t%s\t", this_var->di_key, s);
|
||||
|
||||
Reference in New Issue
Block a user