patch 9.1.0988: Vim9: no error when using uninitialized var in new()
Problem: Vim9: no error when using uninitialized var in new()
(lifepillar, Aliaksei Budavei)
Solution: Give an error if an uninitialized object variable is referenced
in new() (Yegappan Lakshmanan)
fixes: #14411
fixes: #16344
closes: #16374
Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/errors.h b/src/errors.h
index 1e59597..ad36e33 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3616,8 +3616,10 @@
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 4a7962a..c7a0fbe 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -11723,4 +11723,120 @@
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 1dbbca5..987aa9b 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 988,
+/**/
987,
/**/
986,
diff --git a/src/vim9execute.c b/src/vim9execute.c
index de12d8e..dde95b5 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -4844,6 +4844,20 @@
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 @@
// 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