diff --git a/src/errors.h b/src/errors.h index 1e59597d96..ad36e33a63 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3616,8 +3616,10 @@ EXTERN char e_duplicate_enum_str[] INIT(= N_("E1428: Duplicate enum value: %s")); EXTERN char e_class_can_only_be_used_in_script[] INIT(= N_("E1429: Class can only be used in a script")); +EXTERN char e_uninitialized_object_var_reference[] + INIT(= N_("E1430: Uninitialized object variable '%s' referenced")); #endif -// E1429 - E1499 unused (reserved for Vim9 class support) +// E1431 - E1499 unused (reserved for Vim9 class support) EXTERN char e_cannot_mix_positional_and_non_positional_str[] INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s")); EXTERN char e_fmt_arg_nr_unused_str[] diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 4a7962a6cb..c7a0fbefa7 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -11723,4 +11723,120 @@ def Test_use_object_method_in_a_method_call() v9.CheckSourceFailure(lines, 'E1326: Variable "NewCost" not found in object "Foo"') enddef +" Test for referencing an object variable which is not yet initialized +def Test_uninitialized_object_var() + var lines =<< trim END + vim9script + class Foo + const two: number = Foo.Two(this) + const one: number = 1 + + static def Two(that: Foo): number + return that.one + 2 + enddef + endclass + + echo Foo.Two(Foo.new()) + END + v9.CheckSourceFailure(lines, "E1430: Uninitialized object variable 'one' referenced") + + lines =<< trim END + vim9script + class Foo + const one: number = Foo.One(this) + + static def One(that: Foo): number + return 1 + enddef + endclass + + assert_equal(1, Foo.One(Foo.new())) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Foo + const one: number = 1 + const two: number = Foo.Two(this) + + static def Two(that: Foo): number + return that.one + 1 + enddef + endclass + + assert_equal(2, Foo.Two(Foo.new())) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Foo + const Id: func(any): any = ((_) => (v) => v)(this) + + static def Id(that: Foo): func(any): any + return that.Id + enddef + endclass + + assert_equal(5, Foo.Id(Foo.new())(5)) + assert_equal(7, Foo.new().Id(7)) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Foo + const Id: func(any): any = ((that) => (_) => that)(this) + + static def Id(that: Foo): func(any): any + return that.Id + enddef + endclass + + const Id0: func(any): any = Foo.Id(Foo.new()) + const Id1: func(any): any = Foo.new().Id + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Foo + const Id: any = Foo.Id(this) + + static def Id(that: Foo): any + return that.Id + enddef + endclass + + const Id2: any = Foo.Id(Foo.new()) + const Id3: any = Foo.new().Id + END + v9.CheckSourceFailure(lines, "E1430: Uninitialized object variable 'Id' referenced") + + lines =<< trim END + vim9script + + class Foo + var x: string = '' + var Y: func(): string = () => this.x + endclass + + var foo = Foo.new('ok') + assert_equal('ok', foo.Y()) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class Foo + var x: string = this.x + endclass + + var foo = Foo.new('ok') + END + v9.CheckSourceFailure(lines, "E1430: Uninitialized object variable 'x' referenced") +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 1dbbca5631..987aa9b3f2 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 988, /**/ 987, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index de12d8e6a6..dde95b5115 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -4844,6 +4844,20 @@ exec_instructions(ectx_T *ectx) int arg_set = tv->v_type != VAR_UNKNOWN && !(tv->v_type == VAR_SPECIAL && tv->vval.v_number == VVAL_NONE); + + if (iptr->isn_type == ISN_JUMP_IF_ARG_NOT_SET && !arg_set) + { + dfunc_T *df = ((dfunc_T *)def_functions.ga_data) + + ectx->ec_dfunc_idx; + ufunc_T *ufunc = df->df_ufunc; + // jump_arg_off is negative for arguments + size_t argidx = ufunc->uf_def_args.ga_len + + iptr->isn_arg.jumparg.jump_arg_off + + STACK_FRAME_SIZE; + type_T *t = ufunc->uf_arg_types[argidx]; + tv->v_type = t->tt_type; + } + if (iptr->isn_type == ISN_JUMP_IF_ARG_SET ? arg_set : !arg_set) ectx->ec_iidx = iptr->isn_arg.jumparg.jump_where; break; @@ -5718,6 +5732,17 @@ exec_instructions(ectx_T *ectx) // The members are located right after the object struct. typval_T *mtv = ((typval_T *)(obj + 1)) + idx; + if (mtv->v_type == VAR_UNKNOWN) + { + // Referencing an object variable (without a type) + // which is not yet initialized. So the type is not + // yet known. + ocmember_T *m = &obj->obj_class->class_obj_members[idx]; + SOURCING_LNUM = iptr->isn_lnum; + semsg(_(e_uninitialized_object_var_reference), + m->ocm_name); + goto on_error; + } copy_tv(mtv, tv); // Unreference the object after getting the member, it may