mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 9.0.1977: Vim9: object members can change type
Problem: Vim9: object members can change type Solution: Check type during assignment to object/class var closes: #13127 closes: #13262 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
parent
b9a974df9e
commit
fe7b20a1a3
@ -2,6 +2,7 @@
|
|||||||
int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl);
|
int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl);
|
||||||
void ex_class(exarg_T *eap);
|
void ex_class(exarg_T *eap);
|
||||||
type_T *class_member_type(class_T *cl, int is_object, char_u *name, char_u *name_end, int *member_idx);
|
type_T *class_member_type(class_T *cl, int is_object, char_u *name, char_u *name_end, int *member_idx);
|
||||||
|
type_T *class_member_type_by_idx(class_T *cl, int is_object, int member_idx);
|
||||||
void ex_enum(exarg_T *eap);
|
void ex_enum(exarg_T *eap);
|
||||||
void ex_type(exarg_T *eap);
|
void ex_type(exarg_T *eap);
|
||||||
int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
|
int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
|
||||||
|
@ -1509,10 +1509,11 @@ typedef enum {
|
|||||||
* Entry for an object or class member variable.
|
* Entry for an object or class member variable.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char_u *ocm_name; // allocated
|
char_u *ocm_name; // allocated
|
||||||
omacc_T ocm_access;
|
omacc_T ocm_access;
|
||||||
|
int ocm_has_type; // type specified explicitly
|
||||||
type_T *ocm_type;
|
type_T *ocm_type;
|
||||||
char_u *ocm_init; // allocated
|
char_u *ocm_init; // allocated
|
||||||
} ocmember_T;
|
} ocmember_T;
|
||||||
|
|
||||||
// used for the lookup table of a class member index and object method index
|
// used for the lookup table of a class member index and object method index
|
||||||
|
@ -6418,4 +6418,390 @@ def Test_extended_obj_method_type_check()
|
|||||||
v9.CheckSourceFailure(lines, 'E1383: Method "Doit": type mismatch, expected func(object<B>): object<B> but got func(object<B>): object<A>', 20)
|
v9.CheckSourceFailure(lines, 'E1383: Method "Doit": type mismatch, expected func(object<B>): object<B> but got func(object<B>): object<A>', 20)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
" Test type checking for class variable in assignments
|
||||||
|
func Test_class_variable_complex_type_check()
|
||||||
|
" class variable with a specific type. Try assigning a different type at
|
||||||
|
" script level.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public static Fn: func(list<dict<number>>): dict<list<number>> = Foo
|
||||||
|
endclass
|
||||||
|
test_garbagecollect_now()
|
||||||
|
A.Fn = "abc"
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<number>>): dict<list<number>> but got string', 9)
|
||||||
|
|
||||||
|
" class variable with a specific type. Try assigning a different type at
|
||||||
|
" class def method level.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public static Fn: func(list<dict<number>>): dict<list<number>> = Foo
|
||||||
|
def Bar()
|
||||||
|
Fn = "abc"
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
var a = A.new()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
a.Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<number>>): dict<list<number>> but got string', 1)
|
||||||
|
|
||||||
|
" class variable with a specific type. Try assigning a different type at
|
||||||
|
" script def method level.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public static Fn: func(list<dict<number>>): dict<list<number>> = Foo
|
||||||
|
endclass
|
||||||
|
def Bar()
|
||||||
|
A.Fn = "abc"
|
||||||
|
enddef
|
||||||
|
test_garbagecollect_now()
|
||||||
|
Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<number>>): dict<list<number>> but got string', 1)
|
||||||
|
|
||||||
|
" class variable without any type. Should be set to the initialization
|
||||||
|
" expression type. Try assigning a different type from script level.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public static Fn = Foo
|
||||||
|
endclass
|
||||||
|
test_garbagecollect_now()
|
||||||
|
A.Fn = "abc"
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<number>>): dict<list<number>> but got string', 9)
|
||||||
|
|
||||||
|
" class variable without any type. Should be set to the initialization
|
||||||
|
" expression type. Try assigning a different type at class def level.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public static Fn = Foo
|
||||||
|
def Bar()
|
||||||
|
Fn = "abc"
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
var a = A.new()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
a.Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<number>>): dict<list<number>> but got string', 1)
|
||||||
|
|
||||||
|
" class variable without any type. Should be set to the initialization
|
||||||
|
" expression type. Try assigning a different type at script def level.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public static Fn = Foo
|
||||||
|
endclass
|
||||||
|
def Bar()
|
||||||
|
A.Fn = "abc"
|
||||||
|
enddef
|
||||||
|
test_garbagecollect_now()
|
||||||
|
Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<number>>): dict<list<number>> but got string', 1)
|
||||||
|
|
||||||
|
" class variable with 'any" type. Can be assigned different types.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public static Fn: any = Foo
|
||||||
|
public static Fn2: any
|
||||||
|
endclass
|
||||||
|
test_garbagecollect_now()
|
||||||
|
assert_equal('func(list<dict<number>>): dict<list<number>>', typename(A.Fn))
|
||||||
|
A.Fn = "abc"
|
||||||
|
test_garbagecollect_now()
|
||||||
|
assert_equal('string', typename(A.Fn))
|
||||||
|
A.Fn2 = Foo
|
||||||
|
test_garbagecollect_now()
|
||||||
|
assert_equal('func(list<dict<number>>): dict<list<number>>', typename(A.Fn2))
|
||||||
|
A.Fn2 = "xyz"
|
||||||
|
test_garbagecollect_now()
|
||||||
|
assert_equal('string', typename(A.Fn2))
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" class variable with 'any" type. Can be assigned different types.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public static Fn: any = Foo
|
||||||
|
public static Fn2: any
|
||||||
|
|
||||||
|
def Bar()
|
||||||
|
assert_equal('func(list<dict<number>>): dict<list<number>>', typename(Fn))
|
||||||
|
Fn = "abc"
|
||||||
|
assert_equal('string', typename(Fn))
|
||||||
|
Fn2 = Foo
|
||||||
|
assert_equal('func(list<dict<number>>): dict<list<number>>', typename(Fn2))
|
||||||
|
Fn2 = "xyz"
|
||||||
|
assert_equal('string', typename(Fn2))
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
var a = A.new()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
a.Bar()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
A.Fn = Foo
|
||||||
|
a.Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" class variable with 'any" type. Can be assigned different types.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public static Fn: any = Foo
|
||||||
|
public static Fn2: any
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def Bar()
|
||||||
|
assert_equal('func(list<dict<number>>): dict<list<number>>', typename(A.Fn))
|
||||||
|
A.Fn = "abc"
|
||||||
|
assert_equal('string', typename(A.Fn))
|
||||||
|
A.Fn2 = Foo
|
||||||
|
assert_equal('func(list<dict<number>>): dict<list<number>>', typename(A.Fn2))
|
||||||
|
A.Fn2 = "xyz"
|
||||||
|
assert_equal('string', typename(A.Fn2))
|
||||||
|
enddef
|
||||||
|
Bar()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
A.Fn = Foo
|
||||||
|
Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" Test type checking for object variable in assignments
|
||||||
|
func Test_object_variable_complex_type_check()
|
||||||
|
" object variable with a specific type. Try assigning a different type at
|
||||||
|
" script level.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public this.Fn: func(list<dict<number>>): dict<list<number>> = Foo
|
||||||
|
endclass
|
||||||
|
var a = A.new()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
a.Fn = "abc"
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<number>>): dict<list<number>> but got string', 10)
|
||||||
|
|
||||||
|
" object variable with a specific type. Try assigning a different type at
|
||||||
|
" object def method level.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public this.Fn: func(list<dict<number>>): dict<list<number>> = Foo
|
||||||
|
def Bar()
|
||||||
|
this.Fn = "abc"
|
||||||
|
this.Fn = Foo
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
var a = A.new()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
a.Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<number>>): dict<list<number>> but got string', 1)
|
||||||
|
|
||||||
|
" object variable with a specific type. Try assigning a different type at
|
||||||
|
" script def method level.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public this.Fn: func(list<dict<number>>): dict<list<number>> = Foo
|
||||||
|
endclass
|
||||||
|
def Bar()
|
||||||
|
var a = A.new()
|
||||||
|
a.Fn = "abc"
|
||||||
|
a.Fn = Foo
|
||||||
|
enddef
|
||||||
|
test_garbagecollect_now()
|
||||||
|
Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<number>>): dict<list<number>> but got string', 2)
|
||||||
|
|
||||||
|
" object variable without any type. Should be set to the initialization
|
||||||
|
" expression type. Try assigning a different type from script level.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public this.Fn = Foo
|
||||||
|
endclass
|
||||||
|
var a = A.new()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
a.Fn = "abc"
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<number>>): dict<list<number>> but got string', 10)
|
||||||
|
|
||||||
|
" object variable without any type. Should be set to the initialization
|
||||||
|
" expression type. Try assigning a different type at object def level.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public this.Fn = Foo
|
||||||
|
def Bar()
|
||||||
|
this.Fn = "abc"
|
||||||
|
this.Fn = Foo
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
var a = A.new()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
a.Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<number>>): dict<list<number>> but got string', 1)
|
||||||
|
|
||||||
|
" object variable without any type. Should be set to the initialization
|
||||||
|
" expression type. Try assigning a different type at script def level.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public this.Fn = Foo
|
||||||
|
endclass
|
||||||
|
def Bar()
|
||||||
|
var a = A.new()
|
||||||
|
a.Fn = "abc"
|
||||||
|
a.Fn = Foo
|
||||||
|
enddef
|
||||||
|
test_garbagecollect_now()
|
||||||
|
Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<number>>): dict<list<number>> but got string', 2)
|
||||||
|
|
||||||
|
" object variable with 'any" type. Can be assigned different types.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public this.Fn: any = Foo
|
||||||
|
public this.Fn2: any
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
assert_equal('func(list<dict<number>>): dict<list<number>>', typename(a.Fn))
|
||||||
|
a.Fn = "abc"
|
||||||
|
test_garbagecollect_now()
|
||||||
|
assert_equal('string', typename(a.Fn))
|
||||||
|
a.Fn2 = Foo
|
||||||
|
test_garbagecollect_now()
|
||||||
|
assert_equal('func(list<dict<number>>): dict<list<number>>', typename(a.Fn2))
|
||||||
|
a.Fn2 = "xyz"
|
||||||
|
test_garbagecollect_now()
|
||||||
|
assert_equal('string', typename(a.Fn2))
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" object variable with 'any" type. Can be assigned different types.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public this.Fn: any = Foo
|
||||||
|
public this.Fn2: any
|
||||||
|
|
||||||
|
def Bar()
|
||||||
|
assert_equal('func(list<dict<number>>): dict<list<number>>', typename(this.Fn))
|
||||||
|
this.Fn = "abc"
|
||||||
|
assert_equal('string', typename(this.Fn))
|
||||||
|
this.Fn2 = Foo
|
||||||
|
assert_equal('func(list<dict<number>>): dict<list<number>>', typename(this.Fn2))
|
||||||
|
this.Fn2 = "xyz"
|
||||||
|
assert_equal('string', typename(this.Fn2))
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
a.Bar()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
a.Fn = Foo
|
||||||
|
a.Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
" object variable with 'any" type. Can be assigned different types.
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Foo(l: list<dict<number>>): dict<list<number>>
|
||||||
|
return {}
|
||||||
|
enddef
|
||||||
|
class A
|
||||||
|
public this.Fn: any = Foo
|
||||||
|
public this.Fn2: any
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def Bar()
|
||||||
|
var a = A.new()
|
||||||
|
assert_equal('func(list<dict<number>>): dict<list<number>>', typename(a.Fn))
|
||||||
|
a.Fn = "abc"
|
||||||
|
assert_equal('string', typename(a.Fn))
|
||||||
|
a.Fn2 = Foo
|
||||||
|
assert_equal('func(list<dict<number>>): dict<list<number>>', typename(a.Fn2))
|
||||||
|
a.Fn2 = "xyz"
|
||||||
|
assert_equal('string', typename(a.Fn2))
|
||||||
|
enddef
|
||||||
|
test_garbagecollect_now()
|
||||||
|
Bar()
|
||||||
|
test_garbagecollect_now()
|
||||||
|
Bar()
|
||||||
|
END
|
||||||
|
call v9.CheckSourceSuccess(lines)
|
||||||
|
endfunc
|
||||||
|
|
||||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
@ -704,6 +704,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 */
|
||||||
|
/**/
|
||||||
|
1977,
|
||||||
/**/
|
/**/
|
||||||
1976,
|
1976,
|
||||||
/**/
|
/**/
|
||||||
|
@ -72,6 +72,7 @@ parse_member(
|
|||||||
char_u *varname,
|
char_u *varname,
|
||||||
int has_public, // TRUE if "public" seen before "varname"
|
int has_public, // TRUE if "public" seen before "varname"
|
||||||
char_u **varname_end,
|
char_u **varname_end,
|
||||||
|
int *has_type,
|
||||||
garray_T *type_list,
|
garray_T *type_list,
|
||||||
type_T **type_ret,
|
type_T **type_ret,
|
||||||
char_u **init_expr)
|
char_u **init_expr)
|
||||||
@ -86,6 +87,7 @@ parse_member(
|
|||||||
char_u *colon = skipwhite(*varname_end);
|
char_u *colon = skipwhite(*varname_end);
|
||||||
char_u *type_arg = colon;
|
char_u *type_arg = colon;
|
||||||
type_T *type = NULL;
|
type_T *type = NULL;
|
||||||
|
*has_type = FALSE;
|
||||||
if (*colon == ':')
|
if (*colon == ':')
|
||||||
{
|
{
|
||||||
if (VIM_ISWHITE(**varname_end))
|
if (VIM_ISWHITE(**varname_end))
|
||||||
@ -102,6 +104,7 @@ parse_member(
|
|||||||
type = parse_type(&type_arg, type_list, TRUE);
|
type = parse_type(&type_arg, type_list, TRUE);
|
||||||
if (type == NULL)
|
if (type == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
*has_type = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
char_u *init_arg = skipwhite(type_arg);
|
char_u *init_arg = skipwhite(type_arg);
|
||||||
@ -160,6 +163,7 @@ add_member(
|
|||||||
char_u *varname,
|
char_u *varname,
|
||||||
char_u *varname_end,
|
char_u *varname_end,
|
||||||
int has_public,
|
int has_public,
|
||||||
|
int has_type,
|
||||||
type_T *type,
|
type_T *type,
|
||||||
char_u *init_expr)
|
char_u *init_expr)
|
||||||
{
|
{
|
||||||
@ -169,6 +173,7 @@ add_member(
|
|||||||
m->ocm_name = vim_strnsave(varname, varname_end - varname);
|
m->ocm_name = vim_strnsave(varname, varname_end - varname);
|
||||||
m->ocm_access = has_public ? VIM_ACCESS_ALL
|
m->ocm_access = has_public ? VIM_ACCESS_ALL
|
||||||
: *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
|
: *varname == '_' ? VIM_ACCESS_PRIVATE : VIM_ACCESS_READ;
|
||||||
|
m->ocm_has_type = has_type;
|
||||||
m->ocm_type = type;
|
m->ocm_type = type;
|
||||||
if (init_expr != NULL)
|
if (init_expr != NULL)
|
||||||
m->ocm_init = init_expr;
|
m->ocm_init = init_expr;
|
||||||
@ -1149,6 +1154,10 @@ add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
|
|||||||
static void
|
static void
|
||||||
add_class_members(class_T *cl, exarg_T *eap)
|
add_class_members(class_T *cl, exarg_T *eap)
|
||||||
{
|
{
|
||||||
|
garray_T type_list;
|
||||||
|
|
||||||
|
ga_init2(&type_list, sizeof(type_T *), 10);
|
||||||
|
|
||||||
// Allocate a typval for each class member and initialize it.
|
// Allocate a typval for each class member and initialize it.
|
||||||
cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
|
cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
|
||||||
cl->class_class_member_count);
|
cl->class_class_member_count);
|
||||||
@ -1164,6 +1173,13 @@ add_class_members(class_T *cl, exarg_T *eap)
|
|||||||
typval_T *etv = eval_expr(m->ocm_init, eap);
|
typval_T *etv = eval_expr(m->ocm_init, eap);
|
||||||
if (etv != NULL)
|
if (etv != NULL)
|
||||||
{
|
{
|
||||||
|
if (m->ocm_type->tt_type == VAR_ANY
|
||||||
|
&& !m->ocm_has_type
|
||||||
|
&& etv->v_type != VAR_SPECIAL)
|
||||||
|
// If the member variable type is not yet set, then use
|
||||||
|
// the initialization expression type.
|
||||||
|
m->ocm_type = typval2type(etv, get_copyID(), &type_list,
|
||||||
|
TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
|
||||||
*tv = *etv;
|
*tv = *etv;
|
||||||
vim_free(etv);
|
vim_free(etv);
|
||||||
}
|
}
|
||||||
@ -1175,6 +1191,8 @@ add_class_members(class_T *cl, exarg_T *eap)
|
|||||||
tv->vval.v_string = NULL;
|
tv->vval.v_string = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear_type_list(&type_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1643,6 +1661,7 @@ early_ret:
|
|||||||
char_u *varname_end = NULL;
|
char_u *varname_end = NULL;
|
||||||
type_T *type = NULL;
|
type_T *type = NULL;
|
||||||
char_u *init_expr = NULL;
|
char_u *init_expr = NULL;
|
||||||
|
int has_type = FALSE;
|
||||||
|
|
||||||
if (!is_class && *varname == '_')
|
if (!is_class && *varname == '_')
|
||||||
{
|
{
|
||||||
@ -1653,7 +1672,7 @@ early_ret:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parse_member(eap, line, varname, has_public,
|
if (parse_member(eap, line, varname, has_public,
|
||||||
&varname_end, &type_list, &type,
|
&varname_end, &has_type, &type_list, &type,
|
||||||
is_class ? &init_expr: NULL) == FAIL)
|
is_class ? &init_expr: NULL) == FAIL)
|
||||||
break;
|
break;
|
||||||
if (is_reserved_varname(varname, varname_end))
|
if (is_reserved_varname(varname, varname_end))
|
||||||
@ -1668,7 +1687,7 @@ early_ret:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (add_member(&objmembers, varname, varname_end,
|
if (add_member(&objmembers, varname, varname_end,
|
||||||
has_public, type, init_expr) == FAIL)
|
has_public, has_type, type, init_expr) == FAIL)
|
||||||
{
|
{
|
||||||
vim_free(init_expr);
|
vim_free(init_expr);
|
||||||
break;
|
break;
|
||||||
@ -1776,12 +1795,14 @@ early_ret:
|
|||||||
// "static _varname"
|
// "static _varname"
|
||||||
// "static varname"
|
// "static varname"
|
||||||
// "public static varname"
|
// "public static varname"
|
||||||
char_u *varname = p;
|
char_u *varname = p;
|
||||||
char_u *varname_end = NULL;
|
char_u *varname_end = NULL;
|
||||||
type_T *type = NULL;
|
int has_type = FALSE;
|
||||||
char_u *init_expr = NULL;
|
type_T *type = NULL;
|
||||||
|
char_u *init_expr = NULL;
|
||||||
|
|
||||||
if (parse_member(eap, line, varname, has_public,
|
if (parse_member(eap, line, varname, has_public,
|
||||||
&varname_end, &type_list, &type,
|
&varname_end, &has_type, &type_list, &type,
|
||||||
is_class ? &init_expr : NULL) == FAIL)
|
is_class ? &init_expr : NULL) == FAIL)
|
||||||
break;
|
break;
|
||||||
if (is_reserved_varname(varname, varname_end))
|
if (is_reserved_varname(varname, varname_end))
|
||||||
@ -1796,7 +1817,7 @@ early_ret:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (add_member(&classmembers, varname, varname_end,
|
if (add_member(&classmembers, varname, varname_end,
|
||||||
has_public, type, init_expr) == FAIL)
|
has_public, has_type, type, init_expr) == FAIL)
|
||||||
{
|
{
|
||||||
vim_free(init_expr);
|
vim_free(init_expr);
|
||||||
break;
|
break;
|
||||||
@ -2080,6 +2101,35 @@ class_member_type(
|
|||||||
return m->ocm_type;
|
return m->ocm_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a class or object variable index, return the variable type
|
||||||
|
*/
|
||||||
|
type_T *
|
||||||
|
class_member_type_by_idx(
|
||||||
|
class_T *cl,
|
||||||
|
int is_object,
|
||||||
|
int member_idx)
|
||||||
|
{
|
||||||
|
ocmember_T *m;
|
||||||
|
int member_count;
|
||||||
|
|
||||||
|
if (is_object)
|
||||||
|
{
|
||||||
|
m = cl->class_obj_members;
|
||||||
|
member_count = cl->class_obj_member_count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m = cl->class_class_members;
|
||||||
|
member_count = cl->class_class_member_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (member_idx >= member_count)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return m[member_idx].ocm_type;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle ":enum" up to ":endenum".
|
* Handle ":enum" up to ":endenum".
|
||||||
*/
|
*/
|
||||||
|
@ -1766,6 +1766,9 @@ compile_lhs(
|
|||||||
}
|
}
|
||||||
lhs->lhs_dest = dest_class_member;
|
lhs->lhs_dest = dest_class_member;
|
||||||
lhs->lhs_class = cctx->ctx_ufunc->uf_class;
|
lhs->lhs_class = cctx->ctx_ufunc->uf_class;
|
||||||
|
lhs->lhs_type =
|
||||||
|
class_member_type_by_idx(cctx->ctx_ufunc->uf_class,
|
||||||
|
FALSE, lhs->lhs_classmember_idx);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -3308,7 +3311,15 @@ compile_def_function(
|
|||||||
}
|
}
|
||||||
|
|
||||||
type_T *type = get_type_on_stack(&cctx, 0);
|
type_T *type = get_type_on_stack(&cctx, 0);
|
||||||
if (m->ocm_type->tt_type != type->tt_type)
|
if (m->ocm_type->tt_type == VAR_ANY
|
||||||
|
&& !m->ocm_has_type
|
||||||
|
&& type->tt_type != VAR_SPECIAL)
|
||||||
|
{
|
||||||
|
// If the member variable type is not yet set, then use
|
||||||
|
// the initialization expression type.
|
||||||
|
m->ocm_type = type;
|
||||||
|
}
|
||||||
|
else if (m->ocm_type->tt_type != type->tt_type)
|
||||||
{
|
{
|
||||||
// The type of the member initialization expression is
|
// The type of the member initialization expression is
|
||||||
// determined at run time. Add a runtime type check.
|
// determined at run time. Add a runtime type check.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user