# HG changeset patch # User Christian Brabandt # Date 1702313106 -3600 # Node ID a259471e74fe9e1bb25747581bc92e4a7e86a586 # Parent bc834a974df8e63afd7d6af6d231f334edaf6fc2 patch 9.0.2156: Vim9: can use typealias in assignment Commit: https://github.com/vim/vim/commit/9ed53752df1020a6881ac68d1bde2852c9a680aa Author: Ernie Rael Date: Mon Dec 11 17:40:46 2023 +0100 patch 9.0.2156: Vim9: can use typealias in assignment Problem: Vim9: can use typealias in an assignment Solution: Generate errors when class/typealias involved in the rhs of an assignment closes: #13637 Signed-off-by: Ernie Rael Signed-off-by: Christian Brabandt Generate errors when class/typealias involved in assignment. diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -3557,10 +3557,16 @@ EXTERN char e_using_typealias_as_float[] INIT(= N_("E1401: Using type alias \"%s\" as a Float")); EXTERN char e_using_typealias_as_string[] INIT(= N_("E1402: Using type alias \"%s\" as a String")); -EXTERN char e_using_typealias_as_value[] +EXTERN char e_using_typealias_as_value_str[] INIT(= N_("E1403: Type alias \"%s\" cannot be used as a value")); EXTERN char e_abstract_cannot_be_used_in_interface[] INIT(= N_("E1404: Abstract cannot be used in an interface")); +EXTERN char e_using_class_as_value_str[] + INIT(= N_("E1403: Class \"%s\" cannot be used as a value")); +EXTERN char e_using_class_as_var_val[] + INIT(= N_("E1405: Cannot use a Class as a variable or value")); +EXTERN char e_using_typealias_as_var_val[] + INIT(= N_("E1406: Cannot use a Typealias as a variable or value")); #endif // E1405 - E1499 unused (reserved for Vim9 class support) EXTERN char e_cannot_mix_positional_and_non_positional_str[] diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -1836,11 +1836,8 @@ ex_let_one( return NULL; } - if (tv->v_type == VAR_TYPEALIAS) - { - semsg(_(e_using_typealias_as_value), tv->vval.v_typealias->ta_name); + if (check_typval_is_value(tv) == FAIL) return NULL; - } if (*arg == '$') { diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro --- a/src/proto/vim9type.pro +++ b/src/proto/vim9type.pro @@ -35,4 +35,7 @@ type_T *get_member_type_from_stack(int c char *vartype_name(vartype_T type); char *type_name(type_T *type, char **tofree); void f_typename(typval_T *argvars, typval_T *rettv); +int check_vartype_is_value(vartype_T typ); +int check_typval_is_value(typval_T *tv); +int check_type_is_value(type_T *type); /* vim: set ft=c : */ diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -3057,6 +3057,18 @@ def Test_dict_item_assign() v9.CheckSourceSuccess(lines) enddef +def Test_class_assign() + var lines =<< trim END + vim9script + class C + endclass + class D + endclass + assert_fails('C = D', 'E1403: Class "D" cannot be used as a value') + END + v9.CheckSourceSuccess(lines) +enddef + " Test for using various types (dict, list, blob, funcref, class) as variable " in assignments with a different type def Test_type_check() @@ -3093,7 +3105,7 @@ def Test_type_check() assert_fails('N = l', 'E1012: Type mismatch; expected number but got list') assert_fails('N = b', 'E1012: Type mismatch; expected number but got blob') assert_fails('N = Fn', 'E1012: Type mismatch; expected number but got func([unknown]): number') - assert_fails('N = A', 'E1012: Type mismatch; expected number but got class') + assert_fails('N = A', 'E1403: Class "A" cannot be used as a value') assert_fails('N = o', 'E1012: Type mismatch; expected number but got object') # Use a compound operator with different RHS types @@ -3101,7 +3113,7 @@ def Test_type_check() assert_fails('N += l', 'E734: Wrong variable type for +=') assert_fails('N += b', 'E974: Using a Blob as a Number') assert_fails('N += Fn', 'E734: Wrong variable type for +=') - assert_fails('N += A', 'E1319: Using a Class as a Number') + assert_fails('N += A', 'E1403: Class "A" cannot be used as a value') assert_fails('N += o', 'E1320: Using an Object as a Number') # Initialize multiple variables using [] @@ -3109,7 +3121,7 @@ def Test_type_check() assert_fails('var [X2: number, Y: number] = [1, l]', 'E1012: Type mismatch; expected number but got list') assert_fails('var [X3: number, Y: number] = [1, b]', 'E1012: Type mismatch; expected number but got blob') assert_fails('var [X4: number, Y: number] = [1, Fn]', 'E1012: Type mismatch; expected number but got func([unknown]): number') - assert_fails('var [X7: number, Y: number] = [1, A]', 'E1012: Type mismatch; expected number but got class') + assert_fails('var [X7: number, Y: number] = [1, A]', 'E1403: Class "A" cannot be used as a value') assert_fails('var [X8: number, Y: number] = [1, o]', 'E1012: Type mismatch; expected number but got object') # String concatenation with various LHS types @@ -3117,7 +3129,7 @@ def Test_type_check() assert_fails('S ..= l', 'E734: Wrong variable type for .=') assert_fails('S ..= b', 'E976: Using a Blob as a String') assert_fails('S ..= Fn', 'E734: Wrong variable type for .=') - assert_fails('S ..= A', 'E1323: Using a Class as a String') + assert_fails('S ..= A', 'E1403: Class "A" cannot be used as a value') assert_fails('S ..= o', 'E1324: Using an Object as a String') # String concatenation with various RHS types diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -3093,25 +3093,77 @@ def Test_closure_in_class() v9.CheckSourceSuccess(lines) enddef -def Test_call_constructor_from_legacy() +def Test_construct_object_from_legacy() + # Cannot directly invoke constructor from legacy var lines =<< trim END vim9script - var newCalled = 'false' - - class A + var newCalled = false + + class A + def new(arg: string) + newCalled = true + enddef + endclass + + export def CreateA(...args: list): A + return call(A.new, args) + enddef + + g:P = CreateA + legacy call g:P('some_arg') + assert_equal(true, newCalled) + unlet g:P + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + var newCalled = false + + class A + static def CreateA(options = {}): any + return A.new() + enddef def new() - newCalled = 'true' - enddef - endclass - - export def F(options = {}): any - return A - enddef - - g:p = F() - legacy call p.new() - assert_equal('true', newCalled) + newCalled = true + enddef + endclass + + g:P = A.CreateA + legacy call g:P() + assert_equal(true, newCalled) + unlet g:P + END + v9.CheckSourceSuccess(lines) + + # This also tests invoking "new()" with "call" + lines =<< trim END + vim9script + + var createdObject: any + + class A + this.val1: number + this.val2: number + static def CreateA(...args: list): any + createdObject = call(A.new, args) + return createdObject + enddef + endclass + + g:P = A.CreateA + legacy call g:P(3, 5) + assert_equal(3, createdObject.val1) + assert_equal(5, createdObject.val2) + legacy call g:P() + assert_equal(0, createdObject.val1) + assert_equal(0, createdObject.val2) + legacy call g:P(7) + assert_equal(7, createdObject.val1) + assert_equal(0, createdObject.val2) + unlet g:P END v9.CheckSourceSuccess(lines) enddef diff --git a/src/testdir/test_vim9_typealias.vim b/src/testdir/test_vim9_typealias.vim --- a/src/testdir/test_vim9_typealias.vim +++ b/src/testdir/test_vim9_typealias.vim @@ -212,7 +212,7 @@ def Test_typealias() enddef Foo() END - v9.CheckSourceFailure(lines, 'E1403: Type alias "A" cannot be used as a value', 1) + v9.CheckSourceFailure(lines, 'E1406: Cannot use a Typealias as a variable or value', 1) # Using type alias in an expression (script level) lines =<< trim END diff --git a/src/version.c b/src/version.c --- 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 */ /**/ + 2156, +/**/ 2155, /**/ 2154, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2797,6 +2797,8 @@ compile_assignment( rhs_type = cctx->ctx_type_stack.ga_len == 0 ? &t_void : get_type_on_stack(cctx, 0); + if (check_type_is_value(rhs_type) == FAIL) + goto theend; if (lhs.lhs_lvar != NULL && (is_decl || !lhs.lhs_has_type)) { if ((rhs_type->tt_type == VAR_FUNC diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -3813,10 +3813,8 @@ 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) + if (check_typval_is_value(STACK_TV_BOT(0)) == FAIL) { - semsg(_(e_using_typealias_as_value), - STACK_TV_BOT(0)->vval.v_typealias->ta_name); clear_tv(STACK_TV_BOT(0)); goto on_error; } diff --git a/src/vim9type.c b/src/vim9type.c --- a/src/vim9type.c +++ b/src/vim9type.c @@ -1846,4 +1846,66 @@ f_typename(typval_T *argvars, typval_T * clear_type_list(&type_list); } +/* + * Check if the typval_T is a value type; report an error if it is not. + * Note: a type, user defined or typealias, is not a value type. + * + * Return OK if it's a value type, else FAIL + */ + int +check_typval_is_value(typval_T *tv) +{ + if (tv->v_type == VAR_CLASS) + { + semsg(_(e_using_class_as_value_str), tv->vval.v_class->class_name); + return FAIL; + } + else if (tv->v_type == VAR_TYPEALIAS) + { + semsg(_(e_using_typealias_as_value_str), tv->vval.v_typealias->ta_name); + return FAIL; + } + return OK; +} + +/* + * Same as above, except check type_T. + */ + int +check_type_is_value(type_T *type) +{ + if (type->tt_type == VAR_CLASS) + { + semsg(_(e_using_class_as_value_str), type->tt_class->class_name); + return FAIL; + } + else if (type->tt_type == VAR_TYPEALIAS) + { + // Not sure what could be done here to get a name + // TODO: MAYBE AN OPTIONAL ARGUMENT + emsg(_(e_using_typealias_as_var_val)); + return FAIL; + } + return OK; +} + +/* + * Same as above, except check vartype_T. + */ + int +check_vartype_is_value(vartype_T typ) +{ + if (typ == VAR_CLASS) + { + emsg(_(e_using_class_as_var_val)); + return FAIL; + } + else if (typ == VAR_TYPEALIAS) + { + emsg(_(e_using_typealias_as_var_val)); + return FAIL; + } + return OK; +} + #endif // FEAT_EVAL