patch 9.1.1116: Vim9: super not supported in lambda expressions Commit: https://github.com/vim/vim/commit/b5f463ce4f2bf17c91f1f5b04f9de9ee27753cf3 Author: Yegappan Lakshmanan <yegap...@yahoo.com> Date: Sun Feb 16 16:25:24 2025 +0100
patch 9.1.1116: Vim9: super not supported in lambda expressions Problem: Vim9: super not supported in lambda expressions (Aliaksei Budavei) Solution: Support using the super keyword in a closure in an instance method (Yegappan Lakshmanan) fixes: #16586 closes: #16647 Signed-off-by: Yegappan Lakshmanan <yegap...@yahoo.com> Signed-off-by: Christian Brabandt <c...@256bit.org> diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 7a269fc8f..0f692ed9c 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -3059,27 +3059,6 @@ def Test_class_extends() END v9.CheckSourceFailure(lines, 'E1354: Cannot extend SomeVar', 5) - lines =<< trim END - vim9script - class Base - var name: string - def ToString(): string - return this.name - enddef - endclass - - class Child extends Base - var age: number - def ToString(): string - return super.ToString() .. ': ' .. this.age - enddef - endclass - - var o = Child.new('John', 42) - assert_equal('John: 42', o.ToString()) - END - v9.CheckSourceSuccess(lines) - lines =<< trim END vim9script class Child @@ -3094,49 +3073,6 @@ def Test_class_extends() END v9.CheckSourceFailure(lines, 'E1355: Duplicate function: ToString', 9) - lines =<< trim END - vim9script - class Child - var age: number - def ToString(): string - return super .ToString() .. ': ' .. this.age - enddef - endclass - var o = Child.new(42) - echo o.ToString() - END - v9.CheckSourceFailure(lines, 'E1356: "super" must be followed by a dot', 1) - - lines =<< trim END - vim9script - class Base - var name: string - def ToString(): string - return this.name - enddef - endclass - - var age = 42 - def ToString(): string - return super.ToString() .. ': ' .. age - enddef - echo ToString() - END - v9.CheckSourceFailure(lines, 'E1357: Using "super" not in a class method', 1) - - lines =<< trim END - vim9script - class Child - var age: number - def ToString(): string - return super.ToString() .. ': ' .. this.age - enddef - endclass - var o = Child.new(42) - echo o.ToString() - END - v9.CheckSourceFailure(lines, 'E1358: Using "super" not in a child class', 1) - lines =<< trim END vim9script class Base @@ -3244,28 +3180,6 @@ def Test_using_base_class() END v9.CheckSourceSuccess(lines) unlet g:result - - # Using super, Child invokes Base method which has optional arg. #12471 - lines =<< trim END - vim9script - - class Base - var success: bool = false - def Method(arg = 0) - this.success = true - enddef - endclass - - class Child extends Base - def new() - super.Method() - enddef - endclass - - var obj = Child.new() - assert_equal(true, obj.success) - END - v9.CheckSourceSuccess(lines) enddef " Test for using a method from the super class @@ -12409,4 +12323,162 @@ def Test_protected_new_method() v9.CheckSourceSuccess(lines) enddef +" Test for using 'super' in a closure function inside an object method +def Test_super_in_closure() + var lines =<< trim END + vim9script + + class A + const _value: number + + def Fn(): func(any): number + return (_: any) => this._value + enddef + endclass + + class B extends A + def Fn(): func(any): number + return (_: any) => super._value + enddef + endclass + + assert_equal(100, A.new(100).Fn()(null)) + assert_equal(200, B.new(200).Fn()(null)) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using 'super' to access methods and variables +def Test_super_keyword() + var lines =<< trim END + vim9script + class Base + var name: string + def ToString(): string + return this.name + enddef + endclass + + class Child extends Base + var age: number + def ToString(): string + return super.ToString() .. ': ' .. this.age + enddef + endclass + + var o = Child.new('John', 42) + assert_equal('John: 42', o.ToString()) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Child + var age: number + def ToString(): string + return super .ToString() .. ': ' .. this.age + enddef + endclass + var o = Child.new(42) + echo o.ToString() + END + v9.CheckSourceFailure(lines, 'E1356: "super" must be followed by a dot', 1) + + lines =<< trim END + vim9script + class Base + var name: string + def ToString(): string + return this.name + enddef + endclass + + var age = 42 + def ToString(): string + return super.ToString() .. ': ' .. age + enddef + echo ToString() + END + v9.CheckSourceFailure(lines, 'E1357: Using "super" not in a class method', 1) + + lines =<< trim END + vim9script + class Child + var age: number + def ToString(): string + return super.ToString() .. ': ' .. this.age + enddef + endclass + var o = Child.new(42) + echo o.ToString() + END + v9.CheckSourceFailure(lines, 'E1358: Using "super" not in a child class', 1) + + # Using super, Child invokes Base method which has optional arg. #12471 + lines =<< trim END + vim9script + + class Base + var success: bool = false + def Method(arg = 0) + this.success = true + enddef + endclass + + class Child extends Base + def new() + super.Method() + enddef + endclass + + var obj = Child.new() + assert_equal(true, obj.success) + END + v9.CheckSourceSuccess(lines) + + # Using 'super' to access an object variable in the parent + lines =<< trim END + vim9script + + class A + var foo: string = 'xxx' + endclass + + class B extends A + def GetString(): string + return super.foo + enddef + endclass + + var b: B = B.new() + echo b.GetString() + END + v9.CheckSourceSuccess(lines) + + # Using super to access an overriden method in the parent class + lines =<< trim END + vim9script + + class A + def Foo(): string + return 'A.Foo' + enddef + endclass + + class B extends A + def Foo(): string + return 'B.Foo' + enddef + + def Bar(): string + return $'{super.Foo()} {this.Foo()}' + enddef + endclass + + var b = B.new() + assert_equal('A.Foo B.Foo', b.Bar()) + END + v9.CheckSourceSuccess(lines) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 0c85695b9..19968a66d 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 */ +/**/ + 1116, /**/ 1115, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 42a30b192..3d8921ad8 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -843,27 +843,21 @@ find_imported(char_u *name, size_t len, int load) imported_T * find_imported_from_extends(cctx_T *cctx, char_u *name, size_t len, int load) { - imported_T *ret = NULL; - class_T *cl_extends; - if (cctx == NULL || cctx->ctx_ufunc == NULL || cctx->ctx_ufunc->uf_class == NULL) return NULL; - cl_extends = cctx->ctx_ufunc->uf_class->class_extends; - - if (cl_extends == NULL || cl_extends->class_class_function_count_child <= 0) + class_T *cl_extends = cctx->ctx_ufunc->uf_class->class_extends; + if (cl_extends == NULL + || cl_extends->class_class_function_count_child <= 0) return NULL; - else - { - sctx_T current_sctx_save = current_sctx; - current_sctx = cl_extends->class_class_functions[0]->uf_script_ctx; - ret = find_imported(name, len, load); - current_sctx = current_sctx_save; + sctx_T current_sctx_save = current_sctx; + current_sctx = cl_extends->class_class_functions[0]->uf_script_ctx; + imported_T *ret = find_imported(name, len, load); + current_sctx = current_sctx_save; - return ret; - } + return ret; } /* diff --git a/src/vim9expr.c b/src/vim9expr.c index fe2be410b..67722d114 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -291,7 +291,7 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type) } class_T *cl = type->tt_class; - int is_super = type->tt_flags & TTFLAG_SUPER; + int is_super = ((type->tt_flags & TTFLAG_SUPER) == TTFLAG_SUPER); if (type == &t_super) { if (cctx->ctx_ufunc == NULL || cctx->ctx_ufunc->uf_class == NULL) @@ -693,6 +693,26 @@ generate_funcref(cctx_T *cctx, char_u *name, int has_g_prefix) return generate_PUSHFUNC(cctx, ufunc->uf_name, ufunc->uf_func_type, TRUE); } +/* + * Returns TRUE if compiling a class method. + */ + static int +compiling_a_class_method(cctx_T *cctx) +{ + // For an object method, the FC_OBJECT flag will be set. + // For a constructor method, the FC_NEW flag will be set. + // Excluding these methods, the others are class methods. + // When compiling a closure function inside an object method, + // cctx->ctx_outer->ctx_func will point to the object method. + return cctx->ctx_ufunc != NULL + && (cctx->ctx_ufunc->uf_flags & (FC_OBJECT|FC_NEW)) == 0 + && (cctx->ctx_outer == NULL + || cctx->ctx_outer->ctx_ufunc == NULL + || cctx->ctx_outer->ctx_ufunc->uf_class == NULL + || (cctx->ctx_outer->ctx_ufunc->uf_flags + & (FC_OBJECT|FC_NEW)) == 0); +} + /* * Compile a variable name into a load instruction. * "end" points to just after the name. @@ -807,9 +827,7 @@ compile_load( if (name == NULL) return FAIL; - if (STRCMP(name, "super") == 0 - && cctx->ctx_ufunc != NULL - && (cctx->ctx_ufunc->uf_flags & (FC_OBJECT|FC_NEW)) == 0) + if (STRCMP(name, "super") == 0 && compiling_a_class_method(cctx)) { // super.SomeFunc() in a class function: push &t_super type, this // is recognized in compile_subscript(). -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_dev+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1tjgpN-00CTWi-4T%40256bit.org.