mirror of
https://github.com/vim/vim
synced 2025-07-16 01:01:58 +00:00
patch 9.1.1267: Vim9: no support for type list/dict<object<any>>
Problem: Vim9: no support for type list/dict<object<any>> Solution: add proper support for t_object_any (Yegappan Lakshmanan) closes: #17025 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
7b6add0b4a
commit
de8f8f732a
@ -308,7 +308,7 @@ arg_object(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||
if (type->tt_type == VAR_OBJECT
|
||||
|| type_any_or_unknown(type))
|
||||
return OK;
|
||||
arg_type_mismatch(&t_object, type, context->arg_idx + 1);
|
||||
arg_type_mismatch(&t_object_any, type, context->arg_idx + 1);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
@ -540,8 +540,8 @@ EXTERN int garbage_collect_at_exit INIT(= FALSE);
|
||||
#define t_super (static_types[84])
|
||||
#define t_const_super (static_types[85])
|
||||
|
||||
#define t_object (static_types[86])
|
||||
#define t_const_object (static_types[87])
|
||||
#define t_object_any (static_types[86])
|
||||
#define t_const_object_any (static_types[87])
|
||||
|
||||
#define t_class (static_types[88])
|
||||
#define t_const_class (static_types[89])
|
||||
@ -731,7 +731,7 @@ EXTERN type_T static_types[96]
|
||||
{VAR_CLASS, 0, 0, TTFLAG_STATIC, &t_bool, NULL, NULL},
|
||||
{VAR_CLASS, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_bool, NULL, NULL},
|
||||
|
||||
// 86: t_object
|
||||
// 86: t_object_any
|
||||
{VAR_OBJECT, 0, 0, TTFLAG_STATIC, NULL, NULL, NULL},
|
||||
{VAR_OBJECT, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL, NULL},
|
||||
|
||||
|
@ -2430,7 +2430,7 @@ def Test_instanceof()
|
||||
enddef
|
||||
Bar()
|
||||
END
|
||||
v9.CheckSourceScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected object<Unknown> but got string')
|
||||
v9.CheckSourceScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected object<any> but got string')
|
||||
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
|
@ -564,7 +564,7 @@ def Test_using_null_class()
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
assert_equal(12, type(null_class))
|
||||
assert_equal('class<Unknown>', typename(null_class))
|
||||
assert_equal('class<any>', typename(null_class))
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
enddef
|
||||
@ -643,7 +643,6 @@ def Test_object_not_set()
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E1360: Using a null object', 1)
|
||||
|
||||
# TODO: this should not give an error but be handled at runtime
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
|
||||
@ -660,7 +659,7 @@ def Test_object_not_set()
|
||||
enddef
|
||||
Func()
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E1363: Incomplete type', 1)
|
||||
v9.CheckSourceFailure(lines, 'E1360: Using a null object', 1)
|
||||
|
||||
# Reference a object variable through a null class object which is stored in a
|
||||
# variable of type "any".
|
||||
@ -710,7 +709,7 @@ def Test_null_object_assign_compare()
|
||||
def F(): any
|
||||
return nullo
|
||||
enddef
|
||||
assert_equal('object<Unknown>', typename(F()))
|
||||
assert_equal('object<any>', typename(F()))
|
||||
|
||||
var o0 = F()
|
||||
assert_true(o0 == null_object)
|
||||
@ -12486,37 +12485,325 @@ def Test_method_call_from_list_of_objects()
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
|
||||
class C
|
||||
class A
|
||||
var n: list<number> = [100, 101]
|
||||
def F(): string
|
||||
return 'C.F'
|
||||
return 'A.F'
|
||||
enddef
|
||||
endclass
|
||||
|
||||
class D
|
||||
var x: string
|
||||
def new(this.x)
|
||||
class B
|
||||
var name: string
|
||||
var n: list<number> = [200, 201]
|
||||
def new(this.name)
|
||||
enddef
|
||||
def F(): string
|
||||
return 'B.F'
|
||||
enddef
|
||||
endclass
|
||||
|
||||
var obj1 = A.new()
|
||||
var obj2 = B.new('b1')
|
||||
|
||||
def CheckObjectList()
|
||||
var objlist = [obj1, obj2]
|
||||
assert_equal('list<object<any>>', typename(objlist))
|
||||
|
||||
# Use a member function
|
||||
assert_equal('A.F', objlist[0].F())
|
||||
assert_equal('B.F', objlist[1].F())
|
||||
|
||||
# Use a member variable on the RHS
|
||||
assert_equal([100, 101], objlist[0].n)
|
||||
assert_equal([200, 201], objlist[1].n)
|
||||
|
||||
# Use a member variable on the LHS
|
||||
objlist[0].n[1] = 110
|
||||
objlist[1].n[1] = 210
|
||||
assert_equal([100, 110], objlist[0].n)
|
||||
assert_equal([200, 210], objlist[1].n)
|
||||
|
||||
# Iterate using a for loop
|
||||
var s1 = []
|
||||
for o in objlist
|
||||
add(s1, o.F())
|
||||
endfor
|
||||
assert_equal(['A.F', 'B.F'], s1)
|
||||
|
||||
# Iterate using foreach()
|
||||
var s2 = []
|
||||
foreach(objlist, (k, v) => add(s2, v.F()))
|
||||
assert_equal(['A.F', 'B.F'], s2)
|
||||
|
||||
# Add a new list item
|
||||
objlist->add(B.new('b2'))
|
||||
assert_equal('b2', objlist[2].name)
|
||||
enddef
|
||||
def F(): string
|
||||
return 'D.F'
|
||||
|
||||
CheckObjectList()
|
||||
|
||||
var objlist = [A.new(), B.new('b2')]
|
||||
assert_equal('list<object<any>>', typename(objlist))
|
||||
|
||||
# Use a member function
|
||||
assert_equal('A.F', objlist[0].F())
|
||||
assert_equal('B.F', objlist[1].F())
|
||||
|
||||
# Use a member variable on the RHS
|
||||
assert_equal([100, 101], objlist[0].n)
|
||||
assert_equal([200, 201], objlist[1].n)
|
||||
|
||||
# Use a member variable on the LHS
|
||||
objlist[0].n[1] = 110
|
||||
objlist[1].n[1] = 210
|
||||
assert_equal([100, 110], objlist[0].n)
|
||||
assert_equal([200, 210], objlist[1].n)
|
||||
|
||||
# Iterate using a for loop
|
||||
var s1 = []
|
||||
for o in objlist
|
||||
add(s1, o.F())
|
||||
endfor
|
||||
assert_equal(['A.F', 'B.F'], s1)
|
||||
|
||||
# Iterate using foreach()
|
||||
var s2 = []
|
||||
foreach(objlist, (k, v) => add(s2, v.F()))
|
||||
assert_equal(['A.F', 'B.F'], s2)
|
||||
|
||||
# Add a new list item
|
||||
objlist->add(B.new('b2'))
|
||||
assert_equal('b2', objlist[2].name)
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
|
||||
class A
|
||||
endclass
|
||||
|
||||
class B
|
||||
endclass
|
||||
|
||||
var objlist = [A.new(), B.new()]
|
||||
def Fn()
|
||||
objlist->add(10)
|
||||
enddef
|
||||
endclass
|
||||
|
||||
var obj1 = C.new()
|
||||
var obj2 = D.new('a')
|
||||
try
|
||||
Fn()
|
||||
catch
|
||||
assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got number')
|
||||
endtry
|
||||
|
||||
def CheckObjectList()
|
||||
var items = [obj1, obj2]
|
||||
assert_equal('list<any>', typename(items))
|
||||
assert_equal('C.F', items[0].F())
|
||||
assert_equal('D.F', items[1].F())
|
||||
enddef
|
||||
try
|
||||
objlist->add(10)
|
||||
catch
|
||||
assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got number')
|
||||
endtry
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
CheckObjectList()
|
||||
# Adding an enum to a List of objects should fail
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
endclass
|
||||
class B
|
||||
endclass
|
||||
enum C
|
||||
Red,
|
||||
Green,
|
||||
endenum
|
||||
var items = [A.new(), B.new()]
|
||||
def Fn()
|
||||
items->add(C.Red)
|
||||
enddef
|
||||
|
||||
var items2 = [obj1, obj2]
|
||||
assert_equal('list<any>', typename(items2))
|
||||
assert_equal('C.F', items2[0].F())
|
||||
assert_equal('D.F', items2[1].F())
|
||||
try
|
||||
Fn()
|
||||
catch
|
||||
assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got enum<C>')
|
||||
endtry
|
||||
|
||||
try
|
||||
items->add(C.Green)
|
||||
catch
|
||||
assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got enum<C>')
|
||||
endtry
|
||||
|
||||
var items2 = [C.Red, C.Green]
|
||||
def Fn2()
|
||||
items2->add(A.new())
|
||||
enddef
|
||||
try
|
||||
Fn2()
|
||||
catch
|
||||
assert_exception('Vim(eval):E1012: Type mismatch; expected enum<C> but got object<A>')
|
||||
endtry
|
||||
|
||||
try
|
||||
items2->add(B.new())
|
||||
catch
|
||||
assert_exception('Vim(eval):E1012: Type mismatch; expected enum<C> but got object<B>')
|
||||
endtry
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
enddef
|
||||
|
||||
" Test for using a dict of objects
|
||||
def Test_dict_of_objects()
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
|
||||
class A
|
||||
var n: list<number> = [100, 101]
|
||||
def F(): string
|
||||
return 'A.F'
|
||||
enddef
|
||||
endclass
|
||||
|
||||
class B
|
||||
var name: string
|
||||
var n: list<number> = [200, 201]
|
||||
def new(this.name)
|
||||
enddef
|
||||
def F(): string
|
||||
return 'B.F'
|
||||
enddef
|
||||
endclass
|
||||
|
||||
var obj1 = A.new()
|
||||
var obj2 = B.new('b1')
|
||||
|
||||
def CheckObjectDict()
|
||||
var objdict = {o_a: obj1, o_b: obj2}
|
||||
assert_equal('dict<object<any>>', typename(objdict))
|
||||
|
||||
# Use a member function
|
||||
assert_equal('A.F', objdict.o_a.F())
|
||||
assert_equal('B.F', objdict.o_b.F())
|
||||
|
||||
# Use a member variable on the RHS
|
||||
assert_equal([100, 101], objdict.o_a.n)
|
||||
assert_equal([200, 201], objdict.o_b.n)
|
||||
|
||||
# Use a member variable on the LHS
|
||||
objdict.o_a.n[1] = 110
|
||||
objdict.o_b.n[1] = 210
|
||||
assert_equal([100, 110], objdict.o_a.n)
|
||||
assert_equal([200, 210], objdict.o_b.n)
|
||||
|
||||
# Iterate using a for loop
|
||||
var l = []
|
||||
for v in values(objdict)
|
||||
add(l, v.F())
|
||||
endfor
|
||||
assert_equal(['A.F', 'B.F'], l)
|
||||
|
||||
# Iterate using foreach()
|
||||
l = []
|
||||
foreach(objdict, (k, v) => add(l, v.F()))
|
||||
assert_equal(['A.F', 'B.F'], l)
|
||||
|
||||
# Add a new dict item
|
||||
objdict['o_b2'] = B.new('b2')
|
||||
assert_equal('b2', objdict.o_b2.name)
|
||||
enddef
|
||||
|
||||
CheckObjectDict()
|
||||
|
||||
var objdict = {o_a: A.new(), o_b: B.new('b2')}
|
||||
assert_equal('dict<object<any>>', typename(objdict))
|
||||
assert_equal('A.F', objdict.o_a.F())
|
||||
assert_equal('B.F', objdict.o_b.F())
|
||||
assert_equal([100, 101], objdict.o_a.n)
|
||||
assert_equal([200, 201], objdict.o_b.n)
|
||||
|
||||
var l = []
|
||||
for v in values(objdict)
|
||||
add(l, v.F())
|
||||
endfor
|
||||
assert_equal(['A.F', 'B.F'], l)
|
||||
|
||||
# Add a new dict item
|
||||
objdict['o_b2'] = B.new('b2')
|
||||
assert_equal('b2', objdict.o_b2.name)
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
|
||||
class A
|
||||
endclass
|
||||
class B
|
||||
endclass
|
||||
class C
|
||||
endclass
|
||||
var objdict = {a: A.new(), b: B.new()}
|
||||
def Fn()
|
||||
objdict['c'] = C.new()
|
||||
enddef
|
||||
|
||||
try
|
||||
Fn()
|
||||
catch
|
||||
assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got number')
|
||||
endtry
|
||||
|
||||
try
|
||||
objdict['c'] = C.new()
|
||||
catch
|
||||
assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got number')
|
||||
endtry
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Adding an enum to a Dict of objects should fail
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
endclass
|
||||
class B
|
||||
endclass
|
||||
enum C
|
||||
Red,
|
||||
Green,
|
||||
endenum
|
||||
var items = {o_a: A.new(), o_b: B.new()}
|
||||
def Fn()
|
||||
items['o_c'] = C.Red
|
||||
enddef
|
||||
|
||||
try
|
||||
Fn()
|
||||
catch
|
||||
assert_exception('Vim(eval):E1012: Type mismatch; expected object<any> but got enum<C>')
|
||||
endtry
|
||||
|
||||
try
|
||||
items['o_c'] = C.Green
|
||||
catch
|
||||
assert_exception('Vim(var):E1012: Type mismatch; expected object<any> but got enum<C>')
|
||||
endtry
|
||||
|
||||
var items2 = {red: C.Red, green: C.Green}
|
||||
def Fn2()
|
||||
items2['o_a'] = A.new()
|
||||
enddef
|
||||
try
|
||||
Fn2()
|
||||
catch
|
||||
assert_exception('Vim(eval):E1012: Type mismatch; expected enum<C> but got object<A>')
|
||||
endtry
|
||||
|
||||
try
|
||||
items2['o_a'] = B.new()
|
||||
catch
|
||||
assert_exception('Vim(var):E1012: Type mismatch; expected enum<C> but got object<B>')
|
||||
endtry
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
enddef
|
||||
|
@ -5180,7 +5180,7 @@ def Test_null_values()
|
||||
[null_dict, 1, '{}', 4, 'dict<any>'],
|
||||
[null_function, 1, "function('')", 2, 'func(...): unknown'],
|
||||
[null_list, 1, '[]', 3, 'list<any>'],
|
||||
[null_object, 1, 'object of [unknown]', 13, 'object<Unknown>'],
|
||||
[null_object, 1, 'object of [unknown]', 13, 'object<any>'],
|
||||
[null_partial, 1, "function('')", 2, 'func(...): unknown'],
|
||||
[null_string, 1, "''", 1, 'string']
|
||||
]
|
||||
|
@ -704,6 +704,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1267,
|
||||
/**/
|
||||
1266,
|
||||
/**/
|
||||
|
@ -2498,7 +2498,8 @@ compile_load_lhs(
|
||||
: get_type_on_stack(cctx, 0);
|
||||
|
||||
if (lhs->lhs_type->tt_type == VAR_CLASS
|
||||
|| lhs->lhs_type->tt_type == VAR_OBJECT)
|
||||
|| (lhs->lhs_type->tt_type == VAR_OBJECT
|
||||
&& lhs->lhs_type != &t_object_any))
|
||||
{
|
||||
// Check whether the class or object variable is modifiable
|
||||
if (!lhs_class_member_modifiable(lhs, var_start, cctx))
|
||||
@ -2522,7 +2523,7 @@ compile_load_lhs(
|
||||
return OK;
|
||||
}
|
||||
|
||||
return generate_loadvar(cctx, lhs);
|
||||
return generate_loadvar(cctx, lhs);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2684,7 +2684,8 @@ compile_subscript(
|
||||
type = get_type_on_stack(cctx, 0);
|
||||
if (type != &t_unknown
|
||||
&& (type->tt_type == VAR_CLASS
|
||||
|| type->tt_type == VAR_OBJECT))
|
||||
|| (type->tt_type == VAR_OBJECT
|
||||
&& type != &t_object_any)))
|
||||
{
|
||||
// class member: SomeClass.varname
|
||||
// class method: SomeClass.SomeMethod()
|
||||
|
@ -756,7 +756,7 @@ generate_SETTYPE(
|
||||
generate_PUSHOBJ(cctx_T *cctx)
|
||||
{
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
if (generate_instr_type(cctx, ISN_PUSHOBJ, &t_object) == NULL)
|
||||
if (generate_instr_type(cctx, ISN_PUSHOBJ, &t_object_any) == NULL)
|
||||
return FAIL;
|
||||
return OK;
|
||||
}
|
||||
@ -2142,7 +2142,8 @@ generate_PCALL(
|
||||
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
|
||||
if (type->tt_type == VAR_ANY || type->tt_type == VAR_UNKNOWN)
|
||||
if (type->tt_type == VAR_ANY || type->tt_type == VAR_UNKNOWN
|
||||
|| type == &t_object_any)
|
||||
ret_type = &t_any;
|
||||
else if (type->tt_type == VAR_FUNC || type->tt_type == VAR_PARTIAL)
|
||||
{
|
||||
@ -2213,7 +2214,9 @@ generate_STRINGMEMBER(cctx_T *cctx, char_u *name, size_t len)
|
||||
// check for dict type
|
||||
type = get_type_on_stack(cctx, 0);
|
||||
if (type->tt_type != VAR_DICT
|
||||
&& type->tt_type != VAR_ANY && type->tt_type != VAR_UNKNOWN)
|
||||
&& type->tt_type != VAR_OBJECT
|
||||
&& type->tt_type != VAR_ANY
|
||||
&& type->tt_type != VAR_UNKNOWN)
|
||||
{
|
||||
char *tofree;
|
||||
|
||||
|
@ -764,7 +764,7 @@ oc_typval2type(typval_T *tv)
|
||||
if (tv->vval.v_object != NULL)
|
||||
return &tv->vval.v_object->obj_class->class_object_type;
|
||||
|
||||
return &t_object;
|
||||
return &t_object_any;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1307,8 +1307,11 @@ check_type_maybe(
|
||||
return MAYBE; // use runtime type check
|
||||
if (actual->tt_type != VAR_OBJECT)
|
||||
return FAIL; // don't use tt_class
|
||||
if (actual->tt_class == NULL)
|
||||
return OK; // A null object matches
|
||||
if (actual->tt_class == NULL) // null object
|
||||
return OK;
|
||||
// t_object_any matches any object except for an enum item
|
||||
if (expected == &t_object_any && !IS_ENUM(actual->tt_class))
|
||||
return OK;
|
||||
|
||||
// For object method arguments, do a invariant type check in
|
||||
// an extended class. For all others, do a covariance type check.
|
||||
@ -2122,6 +2125,11 @@ common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap)
|
||||
common_type_var_func(type1, type2, dest, type_gap);
|
||||
return;
|
||||
}
|
||||
else if (type1->tt_type == VAR_OBJECT)
|
||||
{
|
||||
*dest = &t_object_any;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*dest = &t_any;
|
||||
@ -2428,7 +2436,7 @@ type_name_class_or_obj(char *name, type_T *type, char **tofree)
|
||||
name = "enum";
|
||||
}
|
||||
else
|
||||
class_name = (char_u *)"Unknown";
|
||||
class_name = (char_u *)"any";
|
||||
|
||||
size_t len = STRLEN(name) + STRLEN(class_name) + 3;
|
||||
*tofree = alloc(len);
|
||||
|
Reference in New Issue
Block a user