summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2023-08-22 21:29:28 +0200
committerChristian Brabandt <cb@256bit.org>2023-08-22 21:29:28 +0200
commit618e47d1cd93954bad26d47e5353b4f1432daa5e (patch)
tree56be8ec254e3039862771c0a046c7fbb4ac3ffc1
parent8dabccd295271104ad5af0abc48e283d644cff59 (diff)
patch 9.0.1780: Vim9 type not defined during object creationv9.0.1780
Problem: Vim9 type not defined during object creation Solution: Define type during object creation and not during class definition, parse mulit-line member initializers, fix lock initialization If type is not specified for a member, set it during object creation instead of during class definition. Add a runtime type check for the object member initialization expression Also, while at it, when copying an object or class, make sure the lock is correctly initialized. And finally, parse multi-line member initializers correctly. closes: #11957 closes: #12868 closes: #12869 closes: #12881 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com> Co-authored-by: LemonBoy <thatlemon@gmail.com>
-rw-r--r--runtime/doc/vim9class.txt6
-rw-r--r--src/testdir/test_vim9_class.vim97
-rw-r--r--src/version.c2
-rw-r--r--src/vim9class.c76
-rw-r--r--src/vim9compile.c13
5 files changed, 147 insertions, 47 deletions
diff --git a/runtime/doc/vim9class.txt b/runtime/doc/vim9class.txt
index a68b97c87b..c68288a0c5 100644
--- a/runtime/doc/vim9class.txt
+++ b/runtime/doc/vim9class.txt
@@ -423,6 +423,12 @@ Each member and function name can be used only once. It is not possible to
define a function with the same name and different type of arguments.
+Member Initialization ~
+If the type of a member is not explicitly specified in a class, then it is set
+to "any" during class definition. When an object is instantiated from the
+class, then the type of the member is set.
+
+
Extending a class ~
*extends*
A class can extend one other class. *E1352* *E1353* *E1354*
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index dbd4c3bcbe..7e33c6d448 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -210,6 +210,17 @@ def Test_class_basic()
var v = a.Foo(,)
END
v9.CheckScriptFailure(lines, 'E15:')
+
+ lines =<< trim END
+ vim9script
+ class A
+ this.y = {
+ X: 1
+ }
+ endclass
+ var a = A.new()
+ END
+ v9.CheckScriptSuccess(lines)
enddef
def Test_class_defined_twice()
@@ -668,14 +679,28 @@ def Test_class_object_member_inits()
END
v9.CheckScriptFailure(lines, 'E1022:')
+ # If the type is not specified for a member, then it should be set during
+ # object creation and not when defining the class.
lines =<< trim END
vim9script
- class TextPosition
- this.lnum = v:none
+
+ var init_count = 0
+ def Init(): string
+ init_count += 1
+ return 'foo'
+ enddef
+
+ class A
+ this.str1 = Init()
+ this.str2: string = Init()
this.col = 1
endclass
+
+ assert_equal(init_count, 0)
+ var a = A.new()
+ assert_equal(init_count, 2)
END
- v9.CheckScriptFailure(lines, 'E1330:')
+ v9.CheckScriptSuccess(lines)
# Test for initializing an object member with an unknown variable/type
lines =<< trim END
@@ -683,8 +708,9 @@ def Test_class_object_member_inits()
class A
this.value = init_val
endclass
+ var a = A.new()
END
- v9.CheckScriptFailureList(lines, ['E121:', 'E1329:'])
+ v9.CheckScriptFailure(lines, 'E1001:')
enddef
def Test_class_object_member_access()
@@ -2625,4 +2651,67 @@ def Test_new_return_type()
v9.CheckScriptFailure(lines, 'E1365:')
enddef
+" Test for checking a member initialization type at run time.
+def Test_runtime_type_check_for_member_init()
+ var lines =<< trim END
+ vim9script
+
+ var retnum: bool = false
+
+ def F(): any
+ retnum = !retnum
+ if retnum
+ return 1
+ else
+ return "hello"
+ endif
+ enddef
+
+ class C
+ this._foo: bool = F()
+ endclass
+
+ var c1 = C.new()
+ var c2 = C.new()
+ END
+ v9.CheckScriptFailure(lines, 'E1012:')
+enddef
+
+" Test for locking a variable referring to an object and reassigning to another
+" object.
+def Test_object_lockvar()
+ var lines =<< trim END
+ vim9script
+
+ class C
+ this.val: number
+ def new(this.val)
+ enddef
+ endclass
+
+ var some_dict: dict<C> = { a: C.new(1), b: C.new(2), c: C.new(3), }
+ lockvar 2 some_dict
+
+ var current: C
+ current = some_dict['c']
+ assert_equal(3, current.val)
+ current = some_dict['b']
+ assert_equal(2, current.val)
+
+ def F()
+ current = some_dict['c']
+ enddef
+
+ def G()
+ current = some_dict['b']
+ enddef
+
+ F()
+ assert_equal(3, current.val)
+ G()
+ assert_equal(2, current.val)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/version.c b/src/version.c
index 3ca3167cb9..7b0615208d 100644
--- a/src/version.c
+++ b/src/version.c
@@ -700,6 +700,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1780,
+/**/
1779,
/**/
1778,
diff --git a/src/vim9class.c b/src/vim9class.c
index e4130459b2..00b1f7d98f 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -67,66 +67,48 @@ parse_member(
return FAIL;
}
- char_u *expr_start = skipwhite(type_arg);
- char_u *expr_end = expr_start;
- if (type == NULL && *expr_start != '=')
+ char_u *init_arg = skipwhite(type_arg);
+ if (type == NULL && *init_arg != '=')
{
emsg(_(e_type_or_initialization_required));
return FAIL;
}
- if (*expr_start == '=')
+ if (init_expr == NULL && *init_arg == '=')
{
- if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1]))
+ emsg(_(e_cannot_initialize_member_in_interface));
+ return FAIL;
+ }
+
+ if (*init_arg == '=')
+ {
+ evalarg_T evalarg;
+ char_u *expr_start, *expr_end;
+
+ if (!VIM_ISWHITE(init_arg[-1]) || !VIM_ISWHITE(init_arg[1]))
{
semsg(_(e_white_space_required_before_and_after_str_at_str),
"=", type_arg);
return FAIL;
}
- expr_start = skipwhite(expr_start + 1);
+ init_arg = skipwhite(init_arg + 1);
- expr_end = expr_start;
- evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, FALSE);
- skip_expr(&expr_end, NULL);
+ (void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, &evalarg);
+ // No type specified for the member. Set it to "any" and the correct type will be
+ // set when the object is instantiated.
if (type == NULL)
- {
- // No type specified, use the type of the initializer.
- typval_T tv;
- tv.v_type = VAR_UNKNOWN;
- char_u *expr = expr_start;
- int res = eval0(expr, &tv, eap, &evalarg);
+ type = &t_any;
- if (res == OK)
- {
- type = typval2type(&tv, get_copyID(), type_list,
- TVTT_DO_MEMBER);
- clear_tv(&tv);
- }
- if (type == NULL)
- {
- semsg(_(e_cannot_get_object_member_type_from_initializer_str),
- expr_start);
- clear_evalarg(&evalarg, NULL);
- return FAIL;
- }
- }
+ *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
+ // Free the memory pointed by expr_start.
clear_evalarg(&evalarg, NULL);
}
- if (!valid_declaration_type(type))
+ else if (!valid_declaration_type(type))
return FAIL;
*type_ret = type;
- if (expr_end > expr_start)
- {
- if (init_expr == NULL)
- {
- emsg(_(e_cannot_initialize_member_in_interface));
- return FAIL;
- }
- *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
- }
return OK;
}
@@ -1740,9 +1722,13 @@ inside_class(cctx_T *cctx_arg, class_T *cl)
void
copy_object(typval_T *from, typval_T *to)
{
- *to = *from;
- if (to->vval.v_object != NULL)
+ if (from->vval.v_object == NULL)
+ to->vval.v_object = NULL;
+ else
+ {
+ to->vval.v_object = from->vval.v_object;
++to->vval.v_object->obj_refcount;
+ }
}
/*
@@ -1787,9 +1773,13 @@ object_unref(object_T *obj)
void
copy_class(typval_T *from, typval_T *to)
{
- *to = *from;
- if (to->vval.v_class != NULL)
+ if (from->vval.v_class == NULL)
+ to->vval.v_class = NULL;
+ else
+ {
+ to->vval.v_class = from->vval.v_class;
++to->vval.v_class->class_refcount;
+ }
}
/*
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 028b0ca152..09b4bf143b 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -3150,6 +3150,19 @@ compile_def_function(
semsg(_(e_trailing_characters_str), expr);
goto erret;
}
+
+ type_T *type = get_type_on_stack(&cctx, 0);
+ if (m->ocm_type->tt_type != type->tt_type)
+ {
+ // The type of the member initialization expression is
+ // determined at run time. Add a runtime type check.
+ where_T where = WHERE_INIT;
+ where.wt_kind = WT_MEMBER;
+ where.wt_func_name = (char *)m->ocm_name;
+ if (need_type_where(type, m->ocm_type, FALSE, -1,
+ where, &cctx, FALSE, FALSE) == FAIL)
+ goto erret;
+ }
}
else
push_default_value(&cctx, m->ocm_type->tt_type,