Skip to content

Commit

Permalink
patch 9.0.2076: Vim9: No support for type aliases
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
yegappan authored and chrisbra committed Oct 27, 2023
1 parent 4bca489 commit ec3cebb
Show file tree
Hide file tree
Showing 22 changed files with 710 additions and 36 deletions.
9 changes: 5 additions & 4 deletions runtime/doc/vim9class.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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. For Example: >
A type definition is giving a name to a type specification. This also known
type alias. 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.

==============================================================================

Expand Down
30 changes: 22 additions & 8 deletions src/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
Expand Down Expand Up @@ -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[]
Expand Down
20 changes: 20 additions & 0 deletions src/eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down
8 changes: 8 additions & 0 deletions src/evalfunc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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:
Expand Down
39 changes: 38 additions & 1 deletion src/evalvars.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -3162,6 +3190,7 @@ eval_variable(
}
}

done:
if (len > 0)
name[len] = cc;

Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/if_py_both.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions src/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions src/proto/vim9class.pro
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions src/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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.
Expand Down Expand Up @@ -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.
*/
Expand All @@ -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;
};

Expand Down
12 changes: 6 additions & 6 deletions src/testdir/test_vim9_class.vim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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.
Expand Down

0 comments on commit ec3cebb

Please sign in to comment.