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 <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index 7a269fc..0f692ed 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -3061,27 +3061,6 @@
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(): number
@@ -3096,49 +3075,6 @@
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
var name: string
static def ToString(): string
@@ -3244,28 +3180,6 @@
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 @@
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 0c85695..19968a6 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1116,
+/**/
1115,
/**/
1114,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 42a30b1..3d8921a 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -843,27 +843,21 @@
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 fe2be41..67722d1 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -291,7 +291,7 @@
}
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)
@@ -694,6 +694,26 @@
}
/*
+ * 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.
* "is_expr" is TRUE when evaluating an expression, might be a funcref.
@@ -807,9 +827,7 @@
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().