mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 9.0.1780: Vim9 type not defined during object creation
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>
This commit is contained in:
committed by
Christian Brabandt
parent
8dabccd295
commit
618e47d1cd
@@ -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.
|
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 ~
|
Extending a class ~
|
||||||
*extends*
|
*extends*
|
||||||
A class can extend one other class. *E1352* *E1353* *E1354*
|
A class can extend one other class. *E1352* *E1353* *E1354*
|
||||||
|
@@ -210,6 +210,17 @@ def Test_class_basic()
|
|||||||
var v = a.Foo(,)
|
var v = a.Foo(,)
|
||||||
END
|
END
|
||||||
v9.CheckScriptFailure(lines, 'E15:')
|
v9.CheckScriptFailure(lines, 'E15:')
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
this.y = {
|
||||||
|
X: 1
|
||||||
|
}
|
||||||
|
endclass
|
||||||
|
var a = A.new()
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_class_defined_twice()
|
def Test_class_defined_twice()
|
||||||
@@ -668,14 +679,28 @@ def Test_class_object_member_inits()
|
|||||||
END
|
END
|
||||||
v9.CheckScriptFailure(lines, 'E1022:')
|
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
|
lines =<< trim END
|
||||||
vim9script
|
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
|
this.col = 1
|
||||||
endclass
|
endclass
|
||||||
|
|
||||||
|
assert_equal(init_count, 0)
|
||||||
|
var a = A.new()
|
||||||
|
assert_equal(init_count, 2)
|
||||||
END
|
END
|
||||||
v9.CheckScriptFailure(lines, 'E1330:')
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
# Test for initializing an object member with an unknown variable/type
|
# Test for initializing an object member with an unknown variable/type
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
@@ -683,8 +708,9 @@ def Test_class_object_member_inits()
|
|||||||
class A
|
class A
|
||||||
this.value = init_val
|
this.value = init_val
|
||||||
endclass
|
endclass
|
||||||
|
var a = A.new()
|
||||||
END
|
END
|
||||||
v9.CheckScriptFailureList(lines, ['E121:', 'E1329:'])
|
v9.CheckScriptFailure(lines, 'E1001:')
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_class_object_member_access()
|
def Test_class_object_member_access()
|
||||||
@@ -2625,4 +2651,67 @@ def Test_new_return_type()
|
|||||||
v9.CheckScriptFailure(lines, 'E1365:')
|
v9.CheckScriptFailure(lines, 'E1365:')
|
||||||
enddef
|
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
|
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
@@ -699,6 +699,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1780,
|
||||||
/**/
|
/**/
|
||||||
1779,
|
1779,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -67,66 +67,48 @@ parse_member(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char_u *expr_start = skipwhite(type_arg);
|
char_u *init_arg = skipwhite(type_arg);
|
||||||
char_u *expr_end = expr_start;
|
if (type == NULL && *init_arg != '=')
|
||||||
if (type == NULL && *expr_start != '=')
|
|
||||||
{
|
{
|
||||||
emsg(_(e_type_or_initialization_required));
|
emsg(_(e_type_or_initialization_required));
|
||||||
return FAIL;
|
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),
|
semsg(_(e_white_space_required_before_and_after_str_at_str),
|
||||||
"=", type_arg);
|
"=", type_arg);
|
||||||
return FAIL;
|
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);
|
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)
|
if (type == NULL)
|
||||||
{
|
type = &t_any;
|
||||||
// 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);
|
|
||||||
|
|
||||||
if (res == OK)
|
*init_expr = vim_strnsave(expr_start, expr_end - expr_start);
|
||||||
{
|
// Free the memory pointed by expr_start.
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clear_evalarg(&evalarg, NULL);
|
clear_evalarg(&evalarg, NULL);
|
||||||
}
|
}
|
||||||
if (!valid_declaration_type(type))
|
else if (!valid_declaration_type(type))
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
*type_ret = type;
|
*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;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1740,10 +1722,14 @@ inside_class(cctx_T *cctx_arg, class_T *cl)
|
|||||||
void
|
void
|
||||||
copy_object(typval_T *from, typval_T *to)
|
copy_object(typval_T *from, typval_T *to)
|
||||||
{
|
{
|
||||||
*to = *from;
|
if (from->vval.v_object == NULL)
|
||||||
if (to->vval.v_object != NULL)
|
to->vval.v_object = NULL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
to->vval.v_object = from->vval.v_object;
|
||||||
++to->vval.v_object->obj_refcount;
|
++to->vval.v_object->obj_refcount;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Free an object.
|
* Free an object.
|
||||||
@@ -1787,10 +1773,14 @@ object_unref(object_T *obj)
|
|||||||
void
|
void
|
||||||
copy_class(typval_T *from, typval_T *to)
|
copy_class(typval_T *from, typval_T *to)
|
||||||
{
|
{
|
||||||
*to = *from;
|
if (from->vval.v_class == NULL)
|
||||||
if (to->vval.v_class != NULL)
|
to->vval.v_class = NULL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
to->vval.v_class = from->vval.v_class;
|
||||||
++to->vval.v_class->class_refcount;
|
++to->vval.v_class->class_refcount;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unreference a class. Free it when the reference count goes down to zero.
|
* Unreference a class. Free it when the reference count goes down to zero.
|
||||||
|
@@ -3150,6 +3150,19 @@ compile_def_function(
|
|||||||
semsg(_(e_trailing_characters_str), expr);
|
semsg(_(e_trailing_characters_str), expr);
|
||||||
goto erret;
|
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
|
else
|
||||||
push_default_value(&cctx, m->ocm_type->tt_type,
|
push_default_value(&cctx, m->ocm_type->tt_type,
|
||||||
|
Reference in New Issue
Block a user