]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.2029: tests: the test_vim9_class.vim testfile is too long v9.1.2029
authorYegappan Lakshmanan <yegappan@yahoo.com>
Sun, 28 Dec 2025 14:08:21 +0000 (14:08 +0000)
committerChristian Brabandt <cb@256bit.org>
Sun, 28 Dec 2025 14:08:21 +0000 (14:08 +0000)
Problem:  tests: the test_vim9_class.vim testfile is too long
Solution: Split out the interface related test cases into a new
          test file test_vim9_interface.vim (Yegappan Lakshmanan)

closes: #19032

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/testdir/Make_all.mak
src/testdir/test_vim9_class.vim
src/testdir/test_vim9_interface.vim [new file with mode: 0644]
src/version.c

index 9c0d11585a789e5f4a34f81512f910abda2a8e0c..31f66d7baf1cd4a9f09ef3aef2d3600b758ddea8 100644 (file)
@@ -50,6 +50,7 @@ TEST_VIM9 = \
        test_vim9_func \
        test_vim9_generics \
        test_vim9_import \
+       test_vim9_interface \
        test_vim9_python3 \
        test_vim9_script \
        test_vim9_typealias
@@ -66,6 +67,7 @@ TEST_VIM9_RES = \
        test_vim9_func.res \
        test_vim9_generics.res \
        test_vim9_import.res \
+       test_vim9_interface.res \
        test_vim9_python3.res \
        test_vim9_script.res \
        test_vim9_typealias.res
index 69fad33813c8fd44d4529a38a30a38f4943a1a69..7f0326e4a9347c7b4e36f298d36b573464b2e8a7 100644 (file)
@@ -568,24 +568,6 @@ def Test_using_null_class()
   v9.CheckSourceSuccess(lines)
 enddef
 
-def Test_class_interface_wrong_end()
-  var lines =<< trim END
-    vim9script
-    abstract class SomeName
-      var member = 'text'
-    endinterface
-  END
-  v9.CheckSourceFailure(lines, 'E476: Invalid command: endinterface, expected endclass', 4)
-
-  lines =<< trim END
-    vim9script
-    export interface AnotherName
-      var member: string
-    endclass
-  END
-  v9.CheckSourceFailure(lines, 'E476: Invalid command: endclass, expected endinterface', 4)
-enddef
-
 def Test_object_not_set()
   # Use an uninitialized object in script context
   var lines =<< trim END
@@ -2103,51 +2085,6 @@ func Test_class_garbagecollect()
   call v9.CheckSourceSuccess(lines)
 endfunc
 
-" Test interface garbage collection
-func Test_interface_garbagecollect()
-  let lines =<< trim END
-    vim9script
-
-    interface I
-      var ro_obj_var: number
-
-      def ObjFoo(): number
-    endinterface
-
-    class A implements I
-      static var ro_class_var: number = 10
-      public static var rw_class_var: number = 20
-      static var _priv_class_var: number = 30
-      var ro_obj_var: number = 40
-      var _priv_obj_var: number = 60
-
-      static def _ClassBar(): number
-        return _priv_class_var
-      enddef
-
-      static def ClassFoo(): number
-        return ro_class_var + rw_class_var + A._ClassBar()
-      enddef
-
-      def _ObjBar(): number
-        return this._priv_obj_var
-      enddef
-
-      def ObjFoo(): number
-        return this.ro_obj_var + this._ObjBar()
-      enddef
-    endclass
-
-    assert_equal(60, A.ClassFoo())
-    var o = A.new()
-    assert_equal(100, o.ObjFoo())
-    test_garbagecollect_now()
-    assert_equal(60, A.ClassFoo())
-    assert_equal(100, o.ObjFoo())
-  END
-  call v9.CheckSourceSuccess(lines)
-endfunc
-
 def Test_class_method()
   var lines =<< trim END
     vim9script
@@ -2340,5327 +2277,3951 @@ def Test_class_object_to_string()
   v9.CheckSourceSuccess(lines)
 enddef
 
-def Test_interface_basics()
+def Test_class_used_as_type()
   var lines =<< trim END
     vim9script
-    interface Something
-      var ro_var: list<number>
-      def GetCount(): number
-    endinterface
-  END
-  v9.CheckSourceSuccess(lines)
-
-  lines =<< trim END
-    interface SomethingWrong
-      static var count = 7
-    endinterface
-  END
-  v9.CheckSourceFailure(lines, 'E1342: Interface can only be defined in Vim9 script', 1)
 
-  lines =<< trim END
-    vim9script
+    class Point
+      var x = 0
+      var y = 0
+    endclass
 
-    interface Some
-      var value: number
-      def Method(value: number)
-    endinterface
+    var p: Point
+    p = Point.new(2, 33)
+    assert_equal(2, p.x)
+    assert_equal(33, p.y)
   END
-  # The argument name and the object member name are the same, but this is not a
-  # problem because object members are always accessed with the "this." prefix.
   v9.CheckSourceSuccess(lines)
 
   lines =<< trim END
     vim9script
-    interface somethingWrong
-      static var count = 7
-    endinterface
-  END
-  v9.CheckSourceFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong', 2)
-
-  lines =<< trim END
-    vim9script
-    interface SomethingWrong
-      var value: string
-      var count = 7
-      def GetCount(): number
-    endinterface
-  END
-  v9.CheckSourceFailure(lines, 'E1344: Cannot initialize a variable in an interface', 4)
 
-  lines =<< trim END
-    vim9script
-    interface SomethingWrong
-      var value: string
-      var count: number
-      def GetCount(): number
-        return 5
-      enddef
+    interface HasX
+      var x: number
     endinterface
-  END
-  v9.CheckSourceFailure(lines, 'E1345: Not a valid command in an interface: return 5', 6)
 
-  # Test for "interface" cannot be abbreviated
-  lines =<< trim END
-    vim9script
-    inte Something
-    endinterface
-  END
-  v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: inte Something', 2)
+    class Point implements HasX
+      var x = 0
+      var y = 0
+    endclass
 
-  # Test for "endinterface" cannot be abbreviated
-  lines =<< trim END
-    vim9script
-    interface Something
-    endin
+    var p: Point
+    p = Point.new(2, 33)
+    var hx = p
+    assert_equal(2, hx.x)
   END
-  v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: endin', 3)
+  v9.CheckSourceSuccess(lines)
 
-  # Additional commands after "interface name"
   lines =<< trim END
     vim9script
-    interface Something | var x = 10 | var y = 20
-    endinterface
-  END
-  v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = 10", 2)
 
-  lines =<< trim END
-    vim9script
-    export interface EnterExit
-      def Enter(): void
-      def Exit(): void
-    endinterface
-  END
-  writefile(lines, 'XdefIntf.vim', 'D')
+    class Point
+      var x = 0
+      var y = 0
+    endclass
 
-  lines =<< trim END
-    vim9script
-    import './XdefIntf.vim' as defIntf
-    export def With(ee: defIntf.EnterExit, F: func)
-      ee.Enter()
-      try
-        F()
-      finally
-        ee.Exit()
-      endtry
-    enddef
+    var p: Point
+    p = 'text'
   END
-  v9.CheckScriptSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<Point> but got string', 9)
+enddef
 
-  var imported =<< trim END
+def Test_class_extends()
+  var lines =<< trim END
     vim9script
-    export abstract class EnterExit
-      def Enter(): void
+    class Base
+      var one = 1
+      def GetOne(): number
+        return this.one
       enddef
-      def Exit(): void
+    endclass
+    class Child extends Base
+      var two = 2
+      def GetTotal(): number
+        return this.one + this.two
       enddef
     endclass
-  END
-  writefile(imported, 'XdefIntf2.vim', 'D')
-
-  lines[1] = " import './XdefIntf2.vim' as defIntf"
-  v9.CheckScriptSuccess(lines)
-enddef
-
-" Test for using string() with an interface
-def Test_interface_to_string()
-  var lines =<< trim END
-    vim9script
-    interface Intf
-      def Method(nr: number)
-    endinterface
-    assert_equal("interface Intf", string(Intf))
+    var o = Child.new()
+    assert_equal(1, o.one)
+    assert_equal(2, o.two)
+    assert_equal(1, o.GetOne())
+    assert_equal(3, o.GetTotal())
   END
   v9.CheckSourceSuccess(lines)
-enddef
 
-def Test_class_implements_interface()
-  var lines =<< trim END
+  lines =<< trim END
     vim9script
-
-    interface Some
-      var count: number
-      def Method(nr: number)
-    endinterface
-
-    class SomeImpl implements Some
-      var count: number
-      def Method(nr: number)
-        echo nr
-      enddef
+    class Base
+      var one = 1
     endclass
-
-    interface Another
-      var member: string
-    endinterface
-
-    class AnotherImpl implements Some, Another
-      var member = 'abc'
-      var count = 20
-      def Method(nr: number)
-        echo nr
-      enddef
+    class Child extends Base
+      var two = 2
     endclass
+    var o = Child.new(3, 44)
+    assert_equal(3, o.one)
+    assert_equal(44, o.two)
   END
   v9.CheckSourceSuccess(lines)
 
   lines =<< trim END
     vim9script
-
-    interface Some
-      var count: number
-    endinterface
-
-    class SomeImpl implements Some implements Some
-      var count: number
+    class Base
+      var one = 1
+    endclass
+    class Child extends Base extends Base
+      var two = 2
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1350: Duplicate "implements"', 7)
+  v9.CheckSourceFailure(lines, 'E1352: Duplicate "extends"', 5)
 
   lines =<< trim END
     vim9script
-
-    interface Some
-      var count: number
-    endinterface
-
-    class SomeImpl implements Some, Some
-      var count: number
+    class Child extends BaseClass
+      var two = 2
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1351: Duplicate interface after "implements": Some', 7)
+  v9.CheckSourceFailure(lines, 'E1353: Class name not found: BaseClass', 4)
 
   lines =<< trim END
     vim9script
-
-    interface Some
-      var counter: number
-      def Method(nr: number)
-    endinterface
-
-    class SomeImpl implements Some
-      var count: number
-      def Method(nr: number)
-        echo nr
-      enddef
+    var SomeVar = 99
+    class Child extends SomeVar
+      var two = 2
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1348: Variable "counter" of interface "Some" is not implemented', 13)
+  v9.CheckSourceFailure(lines, 'E1354: Cannot extend SomeVar', 5)
 
   lines =<< trim END
     vim9script
-
-    interface Some
-      var count: number
-      def Methods(nr: number)
-    endinterface
-
-    class SomeImpl implements Some
-      var count: number
-      def Method(nr: number)
-        echo nr
+    class Child
+      var age: number
+      def ToString(): number
+        return this.age
+      enddef
+      def ToString(): string
+        return this.age
       enddef
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1349: Method "Methods" of interface "Some" is not implemented', 13)
+  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: ToString', 9)
 
-  # Check different order of members in class and interface works.
   lines =<< trim END
     vim9script
+    class Base
+      var name: string
+      static def ToString(): string
+        return 'Base class'
+      enddef
+    endclass
 
-      interface Result
-        var label: string
-        var errpos: number
-      endinterface
-
-      # order of members is opposite of interface
-      class Failure implements Result
-        public var lnum: number = 5
-        var errpos: number = 42
-        var label: string = 'label'
-      endclass
-
-    def Test()
-      var result: Result = Failure.new()
-
-        assert_equal('label', result.label)
-        assert_equal(42, result.errpos)
+    class Child extends Base
+      var age: number
+      def ToString(): string
+        return Base.ToString() .. ': ' .. this.age
       enddef
+    endclass
 
-    Test()
+    var o = Child.new('John', 42)
+    assert_equal('Base class: 42', o.ToString())
   END
   v9.CheckSourceSuccess(lines)
 
-  # Interface name after "extends" doesn't end in a space or NUL character
   lines =<< trim END
     vim9script
-    interface A
-    endinterface
-    class B extends A"
+    class Base
+      var value = 1
+      def new(init: number)
+        this.value = number + 1
+      enddef
+    endclass
+    class Child extends Base
+      def new()
+        this.new(3)
+      enddef
     endclass
+    var c = Child.new()
   END
-  v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4)
+  v9.CheckSourceFailure(lines, 'E1385: Class method "new" accessible only using class "Child"', 1)
 
-  # Trailing characters after a class name
+  # base class with more than one object member
   lines =<< trim END
     vim9script
-    class A bbb
+
+    class Result
+      var success: bool
+      var value: any = null
     endclass
-  END
-  v9.CheckSourceFailure(lines, 'E488: Trailing characters: bbb', 2)
 
-  # using "implements" with a non-existing class
-  lines =<< trim END
-    vim9script
-    class A implements B
+    class Success extends Result
+      def new(this.value = v:none)
+        this.success = true
+      enddef
     endclass
+
+    var v = Success.new('asdf')
+    assert_equal("object of Success {success: true, value: 'asdf'}", string(v))
   END
-  v9.CheckSourceFailure(lines, 'E1346: Interface name not found: B', 3)
+  v9.CheckSourceSuccess(lines)
 
-  # using "implements" with a regular class
+  # class name after "extends" doesn't end in a space or NUL character
   lines =<< trim END
     vim9script
     class A
     endclass
-    class B implements A
+    class B extends A"
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: A', 5)
+  v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4)
+enddef
 
-  # using "implements" with a variable
-  lines =<< trim END
+def Test_using_base_class()
+  var lines =<< trim END
     vim9script
-    var T: number = 10
-    class A implements T
-    endclass
-  END
-  v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: T', 4)
 
-  # implements should be followed by a white space
-  lines =<< trim END
-    vim9script
-    interface A
-    endinterface
-    class B implements A;
-    endclass
-  END
-  v9.CheckSourceFailure(lines, 'E1315: White space required after name: A;', 4)
+    class BaseEE
+      def Enter(): any
+        return null
+      enddef
+      def Exit(resource: any): void
+      enddef
+    endclass
 
-  lines =<< trim END
-    vim9script
+    class ChildEE extends BaseEE
+      def Enter(): any
+        return 42
+      enddef
 
-    interface One
-      def IsEven(nr: number): bool
-    endinterface
-    class Two implements One
-      def IsEven(nr: number): string
+      def Exit(resource: number): void
+        g:result ..= '/exit'
       enddef
     endclass
+
+    def With(ee: BaseEE)
+      var r = ee.Enter()
+      try
+        g:result ..= r
+      finally
+        g:result ..= '/finally'
+        ee.Exit(r)
+      endtry
+    enddef
+
+    g:result = ''
+    With(ChildEE.new())
+    assert_equal('42/finally/exit', g:result)
   END
-  v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number): string', 9)
+  v9.CheckSourceSuccess(lines)
+  unlet g:result
+enddef
 
-  lines =<< trim END
+" Test for using a method from the super class
+def Test_super_dispatch()
+  # See #15448 and #15463
+  var lines =<< trim END
     vim9script
 
-    interface One
-      def IsEven(nr: number): bool
-    endinterface
-    class Two implements One
-      def IsEven(nr: bool): bool
+    class A
+      def String(): string
+        return 'A'
       enddef
     endclass
-  END
-  v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(bool): bool', 9)
 
-  lines =<< trim END
-    vim9script
-
-    interface One
-      def IsEven(nr: number): bool
-    endinterface
-    class Two implements One
-      def IsEven(nr: number, ...extra: list<number>): bool
+    class B extends A
+      def String(): string
+        return super.String()
       enddef
     endclass
+
+    class C extends B
+    endclass
+
+    assert_equal('A', C.new().String())
   END
-  v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number, ...list<number>): bool', 9)
+  v9.CheckSourceSuccess(lines)
 
-  # access superclass interface members from subclass, mix variable order
   lines =<< trim END
     vim9script
 
-    interface I1
-      var mvar1: number
-      var mvar2: number
-    endinterface
-
-    # NOTE: the order is swapped
-    class A implements I1
-      var mvar2: number
-      var mvar1: number
-      public static var svar2: number
-      public static var svar1: number
-      def new()
-        svar1 = 11
-        svar2 = 12
-        this.mvar1 = 111
-        this.mvar2 = 112
+    class A
+      def F(): string
+        return 'AA'
       enddef
     endclass
 
     class B extends A
-      def new()
-        this.mvar1 = 121
-        this.mvar2 = 122
+      def F(): string
+        return 'BB'
+      enddef
+      def S(): string
+        return super.F()
+      enddef
+      def S0(): string
+        return this.S()
       enddef
     endclass
 
     class C extends B
-      def new()
-        this.mvar1 = 131
-        this.mvar2 = 132
+      def F(): string
+        return 'CC'
+      enddef
+      def ToB(): string
+        return super.F()
       enddef
     endclass
 
-    def F2(i: I1): list<number>
-      return [ i.mvar1, i.mvar2 ]
-    enddef
+    assert_equal('AA', B.new().S())
+    assert_equal('AA', C.new().S())
+    assert_equal('AA', B.new().S0())
+    assert_equal('AA', C.new().S0())
 
-    var oa = A.new()
-    var ob = B.new()
-    var oc = C.new()
+    assert_equal('BB', C.new().ToB())
 
-    assert_equal([111, 112], F2(oa))
-    assert_equal([121, 122], F2(ob))
-    assert_equal([131, 132], F2(oc))
+    assert_equal('CC', C.new().F())
+    assert_equal('BB', B.new().F())
+    assert_equal('AA', A.new().F())
   END
   v9.CheckSourceSuccess(lines)
 
-  # Access superclass interface members from subclass, mix variable order.
-  # Two interfaces, one on A, one on B; each has both kinds of variables
   lines =<< trim END
     vim9script
 
-    interface I1
-      var mvar1: number
-      var mvar2: number
-    endinterface
+    var call_chain: list<string>
 
-    interface I2
-      var mvar3: number
-      var mvar4: number
-    endinterface
+    abstract class A
+      abstract def _G(): string
 
-    class A implements I1
-      public static var svar1: number
-      public static var svar2: number
-      var mvar1: number
-      var mvar2: number
-      def new()
-        svar1 = 11
-        svar2 = 12
-        this.mvar1 = 111
-        this.mvar2 = 112
+      def F(): string
+        call_chain->add('A.F()')
+        return this._G()
+      enddef
+      def _H(): string
+        call_chain->add('A._H()')
+        return this.F()
       enddef
     endclass
 
-    class B extends A implements I2
-      static var svar3: number
-      static var svar4: number
-      var mvar3: number
-      var mvar4: number
-      def new()
-        svar3 = 23
-        svar4 = 24
-        this.mvar1 = 121
-        this.mvar2 = 122
-        this.mvar3 = 123
-        this.mvar4 = 124
+    class B extends A
+      def _G(): string
+        call_chain->add('B.G()')
+        return 'BBB'
+      enddef
+      def SF(): string
+        call_chain->add('B.SF()')
+        return super._H()
       enddef
     endclass
 
     class C extends B
-      public static var svar5: number
-      def new()
-        svar5 = 1001
-        this.mvar1 = 131
-        this.mvar2 = 132
-        this.mvar3 = 133
-        this.mvar4 = 134
+    endclass
+
+    class D extends C
+      def SF(): string
+        call_chain->add('D.SF()')
+        return super.SF()
       enddef
     endclass
 
-    def F2(i: I1): list<number>
-      return [ i.mvar1, i.mvar2 ]
-    enddef
+    class E extends D
+      def SF(): string
+        call_chain->add('E.SF()')
+        return super.SF()
+      enddef
+    endclass
 
-    def F4(i: I2): list<number>
-      return [ i.mvar3, i.mvar4 ]
-    enddef
+    class F extends E
+      def _G(): string
+        call_chain->add('F._G()')
+        return 'FFF'
+      enddef
+    endclass
 
-    var oa = A.new()
-    var ob = B.new()
-    var oc = C.new()
+    # E.new() -> A.F() -> B._G()
+    call_chain = []
+    var o1 = E.new()
+    assert_equal('BBB', o1.F())
+    assert_equal(['A.F()', 'B.G()'], call_chain)
 
-    assert_equal([[111, 112]], [F2(oa)])
-    assert_equal([[121, 122], [123, 124]], [F2(ob), F4(ob)])
-    assert_equal([[131, 132], [133, 134]], [F2(oc), F4(oc)])
+    # F.new() -> E.SF() -> D.SF() -> B.SF() -> A._H() -> A.F() -> F._G()
+    call_chain = []
+    var o2 = F.new()
+    assert_equal('FFF', o2.SF())
+    assert_equal(['E.SF()', 'D.SF()', 'B.SF()', 'A._H()', 'A.F()', 'F._G()'], call_chain)
   END
   v9.CheckSourceSuccess(lines)
 
-  # Using two interface names without a space after the ","
+  # problems with method dispatch: super -> abstract
+  # https://github.com/vim/vim/issues/15514
   lines =<< trim END
     vim9script
-    interface A
-    endinterface
-    interface B
-    endinterface
-    class C implements A,B
+    abstract class B
+      abstract def ToString(): string
     endclass
-  END
-  v9.CheckSourceFailure(lines, 'E1315: White space required after name: A,B', 6)
 
-  # No interface name after a comma
-  lines =<< trim END
-    vim9script
-    interface A
-    endinterface
-    class B implements A,
+    class C extends B
+      def ToString(): string
+        return super.ToString()
+      enddef
     endclass
+
+    try
+      defcompile C.ToString
+      call assert_false(1, 'command should have failed')
+    catch
+      call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly')
+    endtry
   END
-  v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 4)
+  v9.CheckSourceSuccess(lines)
 
-  # No interface name after implements
+  # problems with method dispatch: super -> abstract -> concrete
   lines =<< trim END
     vim9script
-    class A implements
+
+    class A
+      def ToString()
+        echo 'A'
+      enddef
     endclass
-  END
-  v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 2)
-enddef
 
-def Test_call_interface_method()
-  var lines =<< trim END
-    vim9script
-    interface Base
-      def Enter(): void
-    endinterface
+    abstract class B extends A
+      abstract def ToString()
+    endclass
 
-    class Child implements Base
-      def Enter(): void
-        g:result ..= 'child'
+    class C extends B
+      def ToString()
+        super.ToString()
       enddef
     endclass
 
-    def F(obj: Base)
-      obj.Enter()
-    enddef
-
-    g:result = ''
-    F(Child.new())
-    assert_equal('child', g:result)
-    unlet g:result
+    try
+      defcompile C.ToString
+      call assert_false(1, 'command should have failed')
+    catch
+      call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly')
+    endtry
   END
   v9.CheckSourceSuccess(lines)
 
+  # Invoking a super method and an interface method which have the same name.
   lines =<< trim END
     vim9script
-    class Base
-      def Enter(): void
-        g:result ..= 'base'
+
+    interface I
+      def ToString(): string
+    endinterface
+
+    # Note that A does not implement I.
+    class A
+      def ToString(): string
+        return 'A'
       enddef
     endclass
 
-    class Child extends Base
-      def Enter(): void
-        g:result ..= 'child'
+    class B extends A implements I
+      def ToString(): string
+        return super.ToString()
       enddef
     endclass
 
-    def F(obj: Base)
-      obj.Enter()
-    enddef
+     def TestI(i: I): string
+       return i.ToString()
+     enddef
 
-    g:result = ''
-    F(Child.new())
-    assert_equal('child', g:result)
-    unlet g:result
+     assert_equal('A', B.new().ToString())
+     assert_equal('A', TestI(B.new()))
   END
   v9.CheckSourceSuccess(lines)
 
-  # method of interface returns a value
+  # super and an abstract class with no abstract methods
   lines =<< trim END
     vim9script
-    interface Base
-      def Enter(): string
-    endinterface
 
-    class Child implements Base
-      def Enter(): string
-        g:result ..= 'child'
-        return "/resource"
+    class A
+      def ToString(): string
+        return 'A'
       enddef
     endclass
 
-    def F(obj: Base)
-      var r = obj.Enter()
-      g:result ..= r
-    enddef
-
-    g:result = ''
-    F(Child.new())
-    assert_equal('child/resource', g:result)
-    unlet g:result
-  END
-  v9.CheckSourceSuccess(lines)
-
-  lines =<< trim END
-    vim9script
-    class Base
-      def Enter(): string
-        return null_string
-      enddef
+    # An abstract class with no abstract methods.
+    abstract class B extends A
     endclass
 
-    class Child extends Base
-      def Enter(): string
-        g:result ..= 'child'
-        return "/resource"
+    class C extends B
+      def ToString(): string
+        return super.ToString()
       enddef
     endclass
 
-    def F(obj: Base)
-      var r = obj.Enter()
-      g:result ..= r
+    def TestA(a: A): string
+      return a.ToString()
     enddef
 
-    g:result = ''
-    F(Child.new())
-    assert_equal('child/resource', g:result)
-    unlet g:result
-  END
-  v9.CheckSourceSuccess(lines)
+    def TestB(b: B): string
+      return b.ToString()
+    enddef
 
-  # No class that implements the interface.
-  lines =<< trim END
+    assert_equal('A', C.new().ToString())
+    assert_equal('A', TestA(A.new()))
+    assert_equal('A', TestA(C.new()))
+    assert_equal('A', TestB(C.new()))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # super and an abstract class with no abstract methods and the initial
+  # implements clause
+  lines =<< trim END
     vim9script
 
-    interface IWithEE
-      def Enter(): any
-      def Exit(): void
+    interface I
+      def ToString(): string
     endinterface
 
-    def With1(ee: IWithEE, F: func)
-      var r = ee.Enter()
+    # Note that A does not implement I.
+    class A
+      def ToString(): string
+        return 'A'
+      enddef
+    endclass
+
+    # An abstract class with no abstract methods.
+    abstract class B extends A implements I
+    endclass
+
+    class C extends B implements I
+      def ToString(): string
+        return super.ToString()
+      enddef
+    endclass
+
+    # Note that A.ToString() is different from I.ToString().
+    def TestA(a: A): string
+      return a.ToString()
     enddef
 
-    defcompile
+    assert_equal('A', C.new().ToString())
+    assert_equal('A', TestA(A.new()))
+    assert_equal('A', TestA(C.new()))
   END
   v9.CheckSourceSuccess(lines)
-enddef
 
-def Test_class_used_as_type()
-  var lines =<< trim END
+  # Invoking a class method in the parent class using "super" should fail
+  lines =<< trim END
     vim9script
 
-    class Point
-      var x = 0
-      var y = 0
+    class A
+      static def Fn(): string
+        return 'A'
+      enddef
     endclass
 
-    var p: Point
-    p = Point.new(2, 33)
-    assert_equal(2, p.x)
-    assert_equal(33, p.y)
+    class B extends A
+      static def Fn(): string
+        return super.Fn()
+      enddef
+    endclass
+    defcompile
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1325: Method "Fn" not found in class "B"')
 
+  # Missing name after "super" keyword
   lines =<< trim END
     vim9script
-
-    interface HasX
-      var x: number
-    endinterface
-
-    class Point implements HasX
-      var x = 0
-      var y = 0
+    class A
     endclass
+    class B extends A
+      def Fn()
+        var x = super.()
+      enddef
+    endclass
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1127: Missing name after dot', 1)
+enddef
 
-    var p: Point
-    p = Point.new(2, 33)
-    var hx = p
-    assert_equal(2, hx.x)
+def Test_class_import()
+  var lines =<< trim END
+    vim9script
+    export class Animal
+      var kind: string
+      var name: string
+    endclass
   END
-  v9.CheckSourceSuccess(lines)
+  writefile(lines, 'Xanimal.vim', 'D')
 
   lines =<< trim END
     vim9script
+    import './Xanimal.vim' as animal
 
-    class Point
-      var x = 0
-      var y = 0
-    endclass
+    var a: animal.Animal
+    a = animal.Animal.new('fish', 'Eric')
+    assert_equal('fish', a.kind)
+    assert_equal('Eric', a.name)
 
-    var p: Point
-    p = 'text'
+    var b: animal.Animal = animal.Animal.new('cat', 'Garfield')
+    assert_equal('cat', b.kind)
+    assert_equal('Garfield', b.name)
   END
-  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<Point> but got string', 9)
+  v9.CheckScriptSuccess(lines)
 enddef
 
-def Test_class_extends()
+" Test for importing a class into a legacy script and calling the class method
+def Test_class_method_from_legacy_script()
   var lines =<< trim END
     vim9script
-    class Base
-      var one = 1
-      def GetOne(): number
-        return this.one
+    export class A
+      static var name: string = 'a'
+      static def SetName(n: string)
+        name = n
       enddef
     endclass
-    class Child extends Base
-      var two = 2
-      def GetTotal(): number
-        return this.one + this.two
+  END
+  writefile(lines, 'Xvim9export.vim', 'D')
+
+  lines =<< trim END
+    import './Xvim9export.vim' as vim9
+
+    call s:vim9.A.SetName('b')
+    call assert_equal('b', s:vim9.A.name)
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for extending an imported class
+def Test_extend_imported_class()
+  var lines =<< trim END
+    vim9script
+    export class Imp_C1
+      def Fn1(): number
+        return 5
       enddef
     endclass
-    var o = Child.new()
-    assert_equal(1, o.one)
-    assert_equal(2, o.two)
-    assert_equal(1, o.GetOne())
-    assert_equal(3, o.GetTotal())
   END
-  v9.CheckSourceSuccess(lines)
+  writefile(lines, 'Xextendimportclass.vim', 'D')
 
   lines =<< trim END
     vim9script
-    class Base
-      var one = 1
+    import './Xextendimportclass.vim' as XClass
+
+    class A extends XClass.Imp_C1
     endclass
-    class Child extends Base
-      var two = 2
+    var a = A.new()
+    assert_equal(5, a.Fn1())
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
+def Test_abstract_class()
+  var lines =<< trim END
+    vim9script
+    abstract class Base
+      var name: string
     endclass
-    var o = Child.new(3, 44)
-    assert_equal(3, o.one)
-    assert_equal(44, o.two)
+    class Person extends Base
+      var age: number
+    endclass
+    var p: Base = Person.new('Peter', 42)
+    assert_equal('Peter', p.name)
+    assert_equal(42, p.age)
   END
   v9.CheckSourceSuccess(lines)
 
   lines =<< trim END
     vim9script
-    class Base
-      var one = 1
+    abstract class Base
+      var name: string
     endclass
-    class Child extends Base extends Base
-      var two = 2
+    class Person extends Base
+      var age: number
     endclass
+    var p = Base.new('Peter')
   END
-  v9.CheckSourceFailure(lines, 'E1352: Duplicate "extends"', 5)
+  v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "Base"', 8)
 
   lines =<< trim END
-    vim9script
-    class Child extends BaseClass
-      var two = 2
+    abstract class Base
+      var name: string
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1353: Class name not found: BaseClass', 4)
+  v9.CheckSourceFailure(lines, 'E1316: Class can only be defined in Vim9 script', 1)
 
+  # Test for "abstract" cannot be abbreviated
   lines =<< trim END
     vim9script
-    var SomeVar = 99
-    class Child extends SomeVar
-      var two = 2
+    abs class A
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1354: Cannot extend SomeVar', 5)
+  v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs class A', 2)
 
+  # Additional commands after "abstract class"
   lines =<< trim END
     vim9script
-    class Child
-      var age: number
-      def ToString(): number
-        return this.age
-      enddef
-      def ToString(): string
-        return this.age
-      enddef
+    abstract class Something | var x = []
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: ToString', 9)
+  v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = []", 2)
 
+  # Abstract class cannot have a "new" function
   lines =<< trim END
     vim9script
-    class Base
-      var name: string
-      static def ToString(): string
-        return 'Base class'
-      enddef
-    endclass
-
-    class Child extends Base
-      var age: number
-      def ToString(): string
-        return Base.ToString() .. ': ' .. this.age
+    abstract class Base
+      def new()
       enddef
     endclass
-
-    var o = Child.new('John', 42)
-    assert_equal('Base class: 42', o.ToString())
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1359: Cannot define a "new" method in an abstract class', 4)
 
+  # extending an abstract class with class methods and variables
   lines =<< trim END
     vim9script
-    class Base
-      var value = 1
-      def new(init: number)
-        this.value = number + 1
+    abstract class A
+      static var s: string = 'vim'
+      static def Fn(): list<number>
+        return [10]
       enddef
     endclass
-    class Child extends Base
-      def new()
-        this.new(3)
-      enddef
+    class B extends A
     endclass
-    var c = Child.new()
+    var b = B.new()
+    assert_equal('vim', A.s)
+    assert_equal([10], A.Fn())
   END
-  v9.CheckSourceFailure(lines, 'E1385: Class method "new" accessible only using class "Child"', 1)
+  v9.CheckScriptSuccess(lines)
+enddef
 
-  # base class with more than one object member
-  lines =<< trim END
+def Test_closure_in_class()
+  var lines =<< trim END
     vim9script
 
-    class Result
-      var success: bool
-      var value: any = null
-    endclass
+    class Foo
+      var y: list<string> = ['B']
 
-    class Success extends Result
-      def new(this.value = v:none)
-        this.success = true
+      def new()
+        g:result = filter(['A', 'B'], (_, v) => index(this.y, v) == -1)
       enddef
     endclass
 
-    var v = Success.new('asdf')
-    assert_equal("object of Success {success: true, value: 'asdf'}", string(v))
+    Foo.new()
+    assert_equal(['A'], g:result)
   END
   v9.CheckSourceSuccess(lines)
-
-  # class name after "extends" doesn't end in a space or NUL character
-  lines =<< trim END
-    vim9script
-    class A
-    endclass
-    class B extends A"
-    endclass
-  END
-  v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4)
 enddef
 
-def Test_using_base_class()
+def Test_construct_object_from_legacy()
+  # Cannot directly invoke constructor from legacy
   var lines =<< trim END
     vim9script
 
-    class BaseEE
-      def Enter(): any
-        return null
-      enddef
-      def Exit(resource: any): void
-      enddef
-    endclass
-
-    class ChildEE extends BaseEE
-      def Enter(): any
-        return 42
-      enddef
+    var newCalled = false
 
-      def Exit(resource: number): void
-        g:result ..= '/exit'
+    class A
+      def new(arg: string)
+        newCalled = true
       enddef
     endclass
 
-    def With(ee: BaseEE)
-      var r = ee.Enter()
-      try
-        g:result ..= r
-      finally
-        g:result ..= '/finally'
-        ee.Exit(r)
-      endtry
+    export def CreateA(...args: list<any>): A
+      return call(A.new, args)
     enddef
 
-    g:result = ''
-    With(ChildEE.new())
-    assert_equal('42/finally/exit', g:result)
+    g:P = CreateA
+    legacy call g:P('some_arg')
+    assert_equal(true, newCalled)
+    unlet g:P
   END
   v9.CheckSourceSuccess(lines)
-  unlet g:result
-enddef
 
-" Test for using a method from the super class
-def Test_super_dispatch()
-  # See #15448 and #15463
-  var lines =<< trim END
+  lines =<< trim END
     vim9script
 
+    var newCalled = false
+
     class A
-      def String(): string
-        return 'A'
+      static def CreateA(options = {}): any
+        return A.new()
       enddef
-    endclass
-
-    class B extends A
-      def String(): string
-        return super.String()
+      def new()
+        newCalled = true
       enddef
     endclass
 
-    class C extends B
-    endclass
-
-    assert_equal('A', C.new().String())
+    g:P = A.CreateA
+    legacy call g:P()
+    assert_equal(true, newCalled)
+    unlet g:P
   END
   v9.CheckSourceSuccess(lines)
 
+  # This also tests invoking "new()" with "call"
   lines =<< trim END
     vim9script
 
+    var createdObject: any
+
     class A
-      def F(): string
-        return 'AA'
+      var val1: number
+      var val2: number
+      static def CreateA(...args: list<any>): any
+        createdObject = call(A.new, args)
+        return createdObject
       enddef
     endclass
 
-    class B extends A
-      def F(): string
-        return 'BB'
-      enddef
-      def S(): string
-        return super.F()
-      enddef
-      def S0(): string
-        return this.S()
-      enddef
-    endclass
+    g:P = A.CreateA
+    legacy call g:P(3, 5)
+    assert_equal(3, createdObject.val1)
+    assert_equal(5, createdObject.val2)
+    legacy call g:P()
+    assert_equal(0, createdObject.val1)
+    assert_equal(0, createdObject.val2)
+    legacy call g:P(7)
+    assert_equal(7, createdObject.val1)
+    assert_equal(0, createdObject.val2)
+    unlet g:P
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
 
-    class C extends B
-      def F(): string
-        return 'CC'
+def Test_defer_with_object()
+  var lines =<< trim END
+    vim9script
+
+    class CWithEE
+      def Enter()
+        g:result ..= "entered/"
       enddef
-      def ToB(): string
-        return super.F()
+      def Exit()
+        g:result ..= "exited"
       enddef
     endclass
 
-    assert_equal('AA', B.new().S())
-    assert_equal('AA', C.new().S())
-    assert_equal('AA', B.new().S0())
-    assert_equal('AA', C.new().S0())
-
-    assert_equal('BB', C.new().ToB())
+    def With(ee: CWithEE, F: func)
+      ee.Enter()
+      defer ee.Exit()
+      F()
+    enddef
 
-    assert_equal('CC', C.new().F())
-    assert_equal('BB', B.new().F())
-    assert_equal('AA', A.new().F())
+    g:result = ''
+    var obj = CWithEE.new()
+    obj->With(() => {
+      g:result ..= "called/"
+    })
+    assert_equal('entered/called/exited', g:result)
   END
   v9.CheckSourceSuccess(lines)
+  unlet g:result
 
   lines =<< trim END
     vim9script
 
-    var call_chain: list<string>
-
-    abstract class A
-      abstract def _G(): string
-
-      def F(): string
-        call_chain->add('A.F()')
-        return this._G()
+    class BaseWithEE
+      def Enter()
+        g:result ..= "entered-base/"
       enddef
-      def _H(): string
-        call_chain->add('A._H()')
-        return this.F()
+      def Exit()
+        g:result ..= "exited-base"
       enddef
     endclass
 
-    class B extends A
-      def _G(): string
-        call_chain->add('B.G()')
-        return 'BBB'
+    class CWithEE extends BaseWithEE
+      def Enter()
+        g:result ..= "entered-child/"
       enddef
-      def SF(): string
-        call_chain->add('B.SF()')
-        return super._H()
+      def Exit()
+        g:result ..= "exited-child"
       enddef
     endclass
 
-    class C extends B
-    endclass
+    def With(ee: BaseWithEE, F: func)
+      ee.Enter()
+      defer ee.Exit()
+      F()
+    enddef
 
-    class D extends C
-      def SF(): string
-        call_chain->add('D.SF()')
-        return super.SF()
-      enddef
+    g:result = ''
+    var obj = CWithEE.new()
+    obj->With(() => {
+      g:result ..= "called/"
+    })
+    assert_equal('entered-child/called/exited-child', g:result)
+  END
+  v9.CheckSourceSuccess(lines)
+  unlet g:result
+enddef
+
+" The following test used to crash Vim (Github issue #12676)
+def Test_extends_method_crashes_vim()
+  var lines =<< trim END
+    vim9script
+
+    class Observer
     endclass
 
-    class E extends D
-      def SF(): string
-        call_chain->add('E.SF()')
-        return super.SF()
+    class Property
+      var value: any
+
+      def Set(v: any)
+        if v != this.value
+          this.value = v
+        endif
       enddef
-    endclass
 
-    class F extends E
-      def _G(): string
-        call_chain->add('F._G()')
-        return 'FFF'
+      def Register(observer: Observer)
       enddef
     endclass
 
-    # E.new() -> A.F() -> B._G()
-    call_chain = []
-    var o1 = E.new()
-    assert_equal('BBB', o1.F())
-    assert_equal(['A.F()', 'B.G()'], call_chain)
+    class Bool extends Property
+      var value2: bool
+    endclass
 
-    # F.new() -> E.SF() -> D.SF() -> B.SF() -> A._H() -> A.F() -> F._G()
-    call_chain = []
-    var o2 = F.new()
-    assert_equal('FFF', o2.SF())
-    assert_equal(['E.SF()', 'D.SF()', 'B.SF()', 'A._H()', 'A.F()', 'F._G()'], call_chain)
-  END
-  v9.CheckSourceSuccess(lines)
+    def Observe(obj: Property, who: Observer)
+      obj.Register(who)
+    enddef
 
-  # problems with method dispatch: super -> abstract
-  # https://github.com/vim/vim/issues/15514
-  lines =<< trim END
-    vim9script
-    abstract class B
-      abstract def ToString(): string
-    endclass
+    var p = Bool.new(false)
+    var myObserver = Observer.new()
 
-    class C extends B
-      def ToString(): string
-        return super.ToString()
-      enddef
-    endclass
+    Observe(p, myObserver)
 
-    try
-      defcompile C.ToString
-      call assert_false(1, 'command should have failed')
-    catch
-      call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly')
-    endtry
+    p.Set(true)
   END
   v9.CheckSourceSuccess(lines)
+enddef
 
-  # problems with method dispatch: super -> abstract -> concrete
-  lines =<< trim END
+" Test for calling a method in a class that is extended
+def Test_call_method_in_extended_class()
+  var lines =<< trim END
     vim9script
 
-    class A
-      def ToString()
-        echo 'A'
+    var prop_init_called = false
+    var prop_register_called = false
+
+    class Property
+      def Init()
+        prop_init_called = true
       enddef
-    endclass
 
-    abstract class B extends A
-      abstract def ToString()
+      def Register()
+        prop_register_called = true
+      enddef
     endclass
 
-    class C extends B
-      def ToString()
-        super.ToString()
-      enddef
+    class Bool extends Property
     endclass
 
-    try
-      defcompile C.ToString
-      call assert_false(1, 'command should have failed')
-    catch
-      call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly')
-    endtry
+    def Observe(obj: Property)
+      obj.Register()
+    enddef
+
+    var p = Property.new()
+    Observe(p)
+
+    p.Init()
+    assert_true(prop_init_called)
+    assert_true(prop_register_called)
   END
   v9.CheckSourceSuccess(lines)
+enddef
 
-  # Invoking a super method and an interface method which have the same name.
-  lines =<< trim END
+def Test_instanceof()
+  var lines =<< trim END
     vim9script
 
-    interface I
-      def ToString(): string
+    class Base1
+    endclass
+
+    class Base2 extends Base1
+    endclass
+
+    interface Intf1
     endinterface
 
-    # Note that A does not implement I.
-    class A
-      def ToString(): string
-        return 'A'
-      enddef
+    class Mix1 implements Intf1
     endclass
 
-    class B extends A implements I
-      def ToString(): string
-        return super.ToString()
-      enddef
+    class Base3 extends Mix1
     endclass
 
-     def TestI(i: I): string
-       return i.ToString()
-     enddef
+    type AliasBase1 = Base1
+    type AliasBase2 = Base2
+    type AliasIntf1 = Intf1
+    type AliasMix1 = Mix1
 
-     assert_equal('A', B.new().ToString())
-     assert_equal('A', TestI(B.new()))
-  END
-  v9.CheckSourceSuccess(lines)
+    var b1 = Base1.new()
+    var b2 = Base2.new()
+    var b3 = Base3.new()
 
-  # super and an abstract class with no abstract methods
-  lines =<< trim END
-    vim9script
+    assert_true(instanceof(b1, Base1))
+    assert_true(instanceof(b2, Base1))
+    assert_false(instanceof(b1, Base2))
+    assert_true(instanceof(b3, Mix1))
+    assert_true(instanceof(b3, Base1, Base2, Intf1))
 
-    class A
-      def ToString(): string
-        return 'A'
-      enddef
-    endclass
+    assert_true(instanceof(b1, AliasBase1))
+    assert_true(instanceof(b2, AliasBase1))
+    assert_false(instanceof(b1, AliasBase2))
+    assert_true(instanceof(b3, AliasMix1))
+    assert_true(instanceof(b3, AliasBase1, AliasBase2, AliasIntf1))
 
-    # An abstract class with no abstract methods.
-    abstract class B extends A
-    endclass
+    def Foo()
+      var a1 = Base1.new()
+      var a2 = Base2.new()
+      var a3 = Base3.new()
 
-    class C extends B
-      def ToString(): string
-        return super.ToString()
-      enddef
-    endclass
+      assert_true(instanceof(a1, Base1))
+      assert_true(instanceof(a2, Base1))
+      assert_false(instanceof(a1, Base2))
+      assert_true(instanceof(a3, Mix1))
+      assert_true(instanceof(a3, Base1, Base2, Intf1))
 
-    def TestA(a: A): string
-      return a.ToString()
+      assert_true(instanceof(a1, AliasBase1))
+      assert_true(instanceof(a2, AliasBase1))
+      assert_false(instanceof(a1, AliasBase2))
+      assert_true(instanceof(a3, AliasMix1))
+      assert_true(instanceof(a3, AliasBase1, AliasBase2, AliasIntf1))
     enddef
+    Foo()
 
-    def TestB(b: B): string
-      return b.ToString()
-    enddef
+    var o_null: Base1
+    assert_false(instanceof(o_null, Base1))
 
-    assert_equal('A', C.new().ToString())
-    assert_equal('A', TestA(A.new()))
-    assert_equal('A', TestA(C.new()))
-    assert_equal('A', TestB(C.new()))
   END
   v9.CheckSourceSuccess(lines)
 
-  # super and an abstract class with no abstract methods and the initial
-  # implements clause
   lines =<< trim END
     vim9script
 
-    interface I
-      def ToString(): string
-    endinterface
-
-    # Note that A does not implement I.
-    class A
-      def ToString(): string
-        return 'A'
-      enddef
+    class Base1
     endclass
+    instanceof(Base1.new())
+  END
+  v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof')
 
-    # An abstract class with no abstract methods.
-    abstract class B extends A implements I
-    endclass
+  lines =<< trim END
+    vim9script
 
-    class C extends B implements I
-      def ToString(): string
-        return super.ToString()
-      enddef
+    class Base1
     endclass
-
-    # Note that A.ToString() is different from I.ToString().
-    def TestA(a: A): string
-      return a.ToString()
+    def F()
+      instanceof(Base1.new())
     enddef
-
-    assert_equal('A', C.new().ToString())
-    assert_equal('A', TestA(A.new()))
-    assert_equal('A', TestA(C.new()))
+    F()
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof')
 
-  # Invoking a class method in the parent class using "super" should fail
   lines =<< trim END
     vim9script
 
-    class A
-      static def Fn(): string
-        return 'A'
-      enddef
+    class Base1
     endclass
 
-    class B extends A
-      static def Fn(): string
-        return super.Fn()
-      enddef
+    class Base2
     endclass
-    defcompile
+
+    var o = Base2.new()
+    instanceof(o, Base1, Base2, 3)
   END
-  v9.CheckSourceFailure(lines, 'E1325: Method "Fn" not found in class "B"')
+  v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4', 10)
 
-  # Missing name after "super" keyword
   lines =<< trim END
     vim9script
-    class A
+
+    class Base1
     endclass
-    class B extends A
-      def Fn()
-        var x = super.()
-      enddef
+
+    class Base2
     endclass
-    defcompile
+
+    def F()
+      var o = Base2.new()
+      instanceof(o, Base1, Base2, 3)
+    enddef
+    F()
   END
-  v9.CheckSourceFailure(lines, 'E1127: Missing name after dot', 1)
+  v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4')
 enddef
 
-def Test_class_import()
+" Test for calling a method in the parent class that is extended partially.
+" This used to fail with the 'E118: Too many arguments for function: Text' error
+" message (Github issue #12524).
+def Test_call_method_in_parent_class()
   var lines =<< trim END
     vim9script
-    export class Animal
-      var kind: string
-      var name: string
-    endclass
-  END
-  writefile(lines, 'Xanimal.vim', 'D')
 
-  lines =<< trim END
-    vim9script
-    import './Xanimal.vim' as animal
+    class Widget
+      var _lnum: number = 1
 
-    var a: animal.Animal
-    a = animal.Animal.new('fish', 'Eric')
-    assert_equal('fish', a.kind)
-    assert_equal('Eric', a.name)
+      def SetY(lnum: number)
+        this._lnum = lnum
+      enddef
 
-    var b: animal.Animal = animal.Animal.new('cat', 'Garfield')
-    assert_equal('cat', b.kind)
-    assert_equal('Garfield', b.name)
-  END
-  v9.CheckScriptSuccess(lines)
-enddef
+      def Text(): string
+        return ''
+      enddef
+    endclass
 
-" Test for importing a class into a legacy script and calling the class method
-def Test_class_method_from_legacy_script()
-  var lines =<< trim END
-    vim9script
-    export class A
-      static var name: string = 'a'
-      static def SetName(n: string)
-        name = n
+    class Foo extends Widget
+      def Text(): string
+        return '<Foo>'
       enddef
     endclass
-  END
-  writefile(lines, 'Xvim9export.vim', 'D')
 
-  lines =<< trim END
-    import './Xvim9export.vim' as vim9
+    def Stack(w1: Widget, w2: Widget): list<Widget>
+      w1.SetY(1)
+      w2.SetY(2)
+      return [w1, w2]
+    enddef
 
-    call s:vim9.A.SetName('b')
-    call assert_equal('b', s:vim9.A.name)
+    var foo1 = Foo.new()
+    var foo2 = Foo.new()
+    var l = Stack(foo1, foo2)
   END
-  v9.CheckScriptSuccess(lines)
+  v9.CheckSourceSuccess(lines)
 enddef
 
-" Test for implementing an imported interface
-def Test_implement_imported_interface()
+" Test for calling methods from three levels of classes
+def Test_multi_level_method_call()
   var lines =<< trim END
     vim9script
-    export interface Imp_Intf1
-      def Fn1(): number
-    endinterface
-    export interface Imp_Intf2
-      def Fn2(): number
-    endinterface
-  END
-  writefile(lines, 'Ximportinterface.vim', 'D')
 
-  lines =<< trim END
-    vim9script
-    import './Ximportinterface.vim' as Xintf
+    var A_func1: number = 0
+    var A_func2: number = 0
+    var A_func3: number = 0
+    var B_func2: number = 0
+    var B_func3: number = 0
+    var C_func3: number = 0
 
-    class A implements Xintf.Imp_Intf1, Xintf.Imp_Intf2
-      def Fn1(): number
-        return 10
+    class A
+      def Func1()
+        A_func1 += 1
       enddef
-      def Fn2(): number
-        return 20
+
+      def Func2()
+        A_func2 += 1
       enddef
-    endclass
-    var a = A.new()
-    assert_equal(10, a.Fn1())
-    assert_equal(20, a.Fn2())
-  END
-  v9.CheckScriptSuccess(lines)
-enddef
 
-" Test for extending an imported class
-def Test_extend_imported_class()
-  var lines =<< trim END
-    vim9script
-    export class Imp_C1
-      def Fn1(): number
-        return 5
+      def Func3()
+        A_func3 += 1
       enddef
     endclass
-  END
-  writefile(lines, 'Xextendimportclass.vim', 'D')
 
-  lines =<< trim END
-    vim9script
-    import './Xextendimportclass.vim' as XClass
+    class B extends A
+      def Func2()
+        B_func2 += 1
+      enddef
 
-    class A extends XClass.Imp_C1
+      def Func3()
+        B_func3 += 1
+      enddef
     endclass
-    var a = A.new()
-    assert_equal(5, a.Fn1())
-  END
-  v9.CheckScriptSuccess(lines)
-enddef
 
-def Test_abstract_class()
-  var lines =<< trim END
-    vim9script
-    abstract class Base
-      var name: string
-    endclass
-    class Person extends Base
-      var age: number
+    class C extends B
+      def Func3()
+        C_func3 += 1
+      enddef
     endclass
-    var p: Base = Person.new('Peter', 42)
-    assert_equal('Peter', p.name)
-    assert_equal(42, p.age)
-  END
-  v9.CheckSourceSuccess(lines)
 
-  lines =<< trim END
-    vim9script
-    abstract class Base
-      var name: string
-    endclass
-    class Person extends Base
-      var age: number
-    endclass
-    var p = Base.new('Peter')
-  END
-  v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "Base"', 8)
+    def A_CallFuncs(a: A)
+      a.Func1()
+      a.Func2()
+      a.Func3()
+    enddef
 
-  lines =<< trim END
-    abstract class Base
-      var name: string
-    endclass
-  END
-  v9.CheckSourceFailure(lines, 'E1316: Class can only be defined in Vim9 script', 1)
+    def B_CallFuncs(b: B)
+      b.Func1()
+      b.Func2()
+      b.Func3()
+    enddef
 
-  # Test for "abstract" cannot be abbreviated
-  lines =<< trim END
-    vim9script
-    abs class A
-    endclass
-  END
-  v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs class A', 2)
+    def C_CallFuncs(c: C)
+      c.Func1()
+      c.Func2()
+      c.Func3()
+    enddef
 
-  # Additional commands after "abstract class"
-  lines =<< trim END
-    vim9script
-    abstract class Something | var x = []
-    endclass
+    var cobj = C.new()
+    A_CallFuncs(cobj)
+    B_CallFuncs(cobj)
+    C_CallFuncs(cobj)
+    assert_equal(3, A_func1)
+    assert_equal(0, A_func2)
+    assert_equal(0, A_func3)
+    assert_equal(3, B_func2)
+    assert_equal(0, B_func3)
+    assert_equal(3, C_func3)
   END
-  v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = []", 2)
+  v9.CheckSourceSuccess(lines)
+enddef
 
-  # Abstract class cannot have a "new" function
-  lines =<< trim END
+" Test for using members from three levels of classes
+def Test_multi_level_member_access()
+  var lines =<< trim END
     vim9script
-    abstract class Base
-      def new()
-      enddef
-    endclass
-  END
-  v9.CheckSourceFailure(lines, 'E1359: Cannot define a "new" method in an abstract class', 4)
 
-  # extending an abstract class with class methods and variables
-  lines =<< trim END
-    vim9script
-    abstract class A
-      static var s: string = 'vim'
-      static def Fn(): list<number>
-        return [10]
-      enddef
+    class A
+      public var val1: number = 0
     endclass
+
     class B extends A
+      public var val2: number = 0
     endclass
-    var b = B.new()
-    assert_equal('vim', A.s)
-    assert_equal([10], A.Fn())
-  END
-  v9.CheckScriptSuccess(lines)
-enddef
 
-def Test_closure_in_class()
-  var lines =<< trim END
-    vim9script
+    class C extends B
+      public var val3: number = 0
+    endclass
 
-    class Foo
-      var y: list<string> = ['B']
+    def A_members(a: A)
+      a.val1 += 1
+    enddef
 
-      def new()
-        g:result = filter(['A', 'B'], (_, v) => index(this.y, v) == -1)
-      enddef
-    endclass
+    def B_members(b: B)
+      b.val1 += 1
+      b.val2 += 1
+    enddef
 
-    Foo.new()
-    assert_equal(['A'], g:result)
+    def C_members(c: C)
+      c.val1 += 1
+      c.val2 += 1
+      c.val3 += 1
+    enddef
+
+    var cobj = C.new()
+    A_members(cobj)
+    B_members(cobj)
+    C_members(cobj)
+    assert_equal(3, cobj.val1)
+    assert_equal(2, cobj.val2)
+    assert_equal(1, cobj.val3)
   END
   v9.CheckSourceSuccess(lines)
 enddef
 
-def Test_construct_object_from_legacy()
-  # Cannot directly invoke constructor from legacy
+" Test expansion of <stack> with class methods.
+def Test_stack_expansion_with_methods()
   var lines =<< trim END
     vim9script
 
-    var newCalled = false
-
-    class A
-      def new(arg: string)
-        newCalled = true
+    class C
+      def M1()
+        F0()
       enddef
     endclass
 
-    export def CreateA(...args: list<any>): A
-      return call(A.new, args)
+    def F0()
+      assert_match('<SNR>\d\+_F\[1\]\.\.<SNR>\d\+_C\.M1\[1\]\.\.<SNR>\d\+_F0\[1\]$', expand('<stack>'))
     enddef
 
-    g:P = CreateA
-    legacy call g:P('some_arg')
-    assert_equal(true, newCalled)
-    unlet g:P
+    def F()
+      C.new().M1()
+    enddef
+
+    F()
   END
   v9.CheckSourceSuccess(lines)
+enddef
 
-  lines =<< trim END
+" Test the return type of the new() constructor
+def Test_new_return_type()
+  # new() uses the default return type and there is no return statement
+  var lines =<< trim END
     vim9script
 
-    var newCalled = false
+    class C
+      var _bufnr: number
 
-    class A
-      static def CreateA(options = {}): any
-        return A.new()
-      enddef
-      def new()
-        newCalled = true
+      def new(this._bufnr)
+        if !bufexists(this._bufnr)
+          this._bufnr = -1
+        endif
       enddef
     endclass
 
-    g:P = A.CreateA
-    legacy call g:P()
-    assert_equal(true, newCalled)
-    unlet g:P
+    var c = C.new(12345)
+    assert_equal('object<C>', typename(c))
+
+    var v1: C
+    v1 = C.new(12345)
+    assert_equal('object<C>', typename(v1))
+
+    def F()
+      var v2: C
+      v2 = C.new(12345)
+      assert_equal('object<C>', typename(v2))
+    enddef
+    F()
   END
   v9.CheckSourceSuccess(lines)
 
-  # This also tests invoking "new()" with "call"
+  # new() uses the default return type and an empty 'return' statement
   lines =<< trim END
     vim9script
 
-    var createdObject: any
+    class C
+      var _bufnr: number
 
-    class A
-      var val1: number
-      var val2: number
-      static def CreateA(...args: list<any>): any
-        createdObject = call(A.new, args)
-        return createdObject
+      def new(this._bufnr)
+        if !bufexists(this._bufnr)
+          this._bufnr = -1
+          return
+        endif
       enddef
     endclass
 
-    g:P = A.CreateA
-    legacy call g:P(3, 5)
-    assert_equal(3, createdObject.val1)
-    assert_equal(5, createdObject.val2)
-    legacy call g:P()
-    assert_equal(0, createdObject.val1)
-    assert_equal(0, createdObject.val2)
-    legacy call g:P(7)
-    assert_equal(7, createdObject.val1)
-    assert_equal(0, createdObject.val2)
-    unlet g:P
+    var c = C.new(12345)
+    assert_equal('object<C>', typename(c))
+
+    var v1: C
+    v1 = C.new(12345)
+    assert_equal('object<C>', typename(v1))
+
+    def F()
+      var v2: C
+      v2 = C.new(12345)
+      assert_equal('object<C>', typename(v2))
+    enddef
+    F()
   END
   v9.CheckSourceSuccess(lines)
-enddef
 
-def Test_defer_with_object()
-  var lines =<< trim END
+  # new() uses "any" return type and returns "this"
+  lines =<< trim END
     vim9script
 
-    class CWithEE
-      def Enter()
-        g:result ..= "entered/"
-      enddef
-      def Exit()
-        g:result ..= "exited"
+    class C
+      var _bufnr: number
+
+      def new(this._bufnr): any
+        if !bufexists(this._bufnr)
+          this._bufnr = -1
+          return this
+        endif
       enddef
     endclass
-
-    def With(ee: CWithEE, F: func)
-      ee.Enter()
-      defer ee.Exit()
-      F()
-    enddef
-
-    g:result = ''
-    var obj = CWithEE.new()
-    obj->With(() => {
-      g:result ..= "called/"
-    })
-    assert_equal('entered/called/exited', g:result)
   END
-  v9.CheckSourceSuccess(lines)
-  unlet g:result
+  v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 11)
 
+  # new() uses 'Dict' return type and returns a Dict
   lines =<< trim END
     vim9script
 
-    class BaseWithEE
-      def Enter()
-        g:result ..= "entered-base/"
-      enddef
-      def Exit()
-        g:result ..= "exited-base"
-      enddef
-    endclass
+    class C
+      var _state: dict<any>
 
-    class CWithEE extends BaseWithEE
-      def Enter()
-        g:result ..= "entered-child/"
-      enddef
-      def Exit()
-        g:result ..= "exited-child"
+      def new(): dict<any>
+        this._state = {}
+        return this._state
       enddef
     endclass
 
-    def With(ee: BaseWithEE, F: func)
-      ee.Enter()
-      defer ee.Exit()
-      F()
-    enddef
-
-    g:result = ''
-    var obj = CWithEE.new()
-    obj->With(() => {
-      g:result ..= "called/"
-    })
-    assert_equal('entered-child/called/exited-child', g:result)
+    var c = C.new()
+    assert_equal('object<C>', typename(c))
   END
-  v9.CheckSourceSuccess(lines)
-  unlet g:result
+  v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 9)
 enddef
 
-" The following test used to crash Vim (Github issue #12676)
-def Test_extends_method_crashes_vim()
+" Test for checking a member initialization type at run time.
+def Test_runtime_type_check_for_member_init()
   var lines =<< trim END
     vim9script
 
-    class Observer
-    endclass
-
-    class Property
-      var value: any
-
-      def Set(v: any)
-        if v != this.value
-          this.value = v
-        endif
-      enddef
-
-      def Register(observer: Observer)
-      enddef
-    endclass
-
-    class Bool extends Property
-      var value2: bool
-    endclass
+    var retnum: bool = false
 
-    def Observe(obj: Property, who: Observer)
-      obj.Register(who)
+    def F(): any
+      retnum = !retnum
+      if retnum
+        return 1
+      else
+        return "hello"
+      endif
     enddef
 
-    var p = Bool.new(false)
-    var myObserver = Observer.new()
-
-    Observe(p, myObserver)
+    class C
+      var _foo: bool = F()
+    endclass
 
-    p.Set(true)
+    var c1 = C.new()
+    var c2 = C.new()
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected bool but got string', 0)
 enddef
 
-" Test for calling a method in a class that is extended
-def Test_call_method_in_extended_class()
+" Test for locking a variable referring to an object and reassigning to another
+" object.
+def Test_lockvar_object()
   var lines =<< trim END
     vim9script
 
-    var prop_init_called = false
-    var prop_register_called = false
-
-    class Property
-      def Init()
-        prop_init_called = true
-      enddef
-
-      def Register()
-        prop_register_called = true
+    class C
+      var val: number
+      def new(this.val)
       enddef
     endclass
 
-    class Bool extends Property
-    endclass
+    var some_dict: dict<C> = { a: C.new(1), b: C.new(2), c: C.new(3), }
+    lockvar 2 some_dict
 
-    def Observe(obj: Property)
-      obj.Register()
+    var current: C
+    current = some_dict['c']
+    assert_equal(3, current.val)
+    current = some_dict['b']
+    assert_equal(2, current.val)
+
+    def F()
+      current = some_dict['c']
     enddef
 
-    var p = Property.new()
-    Observe(p)
+    def G()
+      current = some_dict['b']
+    enddef
 
-    p.Init()
-    assert_true(prop_init_called)
-    assert_true(prop_register_called)
+    F()
+    assert_equal(3, current.val)
+    G()
+    assert_equal(2, current.val)
   END
   v9.CheckSourceSuccess(lines)
 enddef
 
-def Test_instanceof()
+" Test trying to lock an object variable from various places
+def Test_lockvar_object_variable()
+  # An object variable lockvar has several cases:
+  # object method, scriptlevel, scriplevel from :def, :def arg
+  # method arg, static method arg.
+  # Also different depths
+
+  #
+  # lockvar of read-only object variable
+  #
+
+  # read-only lockvar from object method
   var lines =<< trim END
     vim9script
 
-    class Base1
+    class C
+      var val1: number
+      def Lock()
+        lockvar this.val1
+      enddef
     endclass
+    var o = C.new(3)
+    o.Lock()
+  END
+  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"')
 
-    class Base2 extends Base1
-    endclass
-
-    interface Intf1
-    endinterface
-
-    class Mix1 implements Intf1
-    endclass
+  # read-only lockvar from scriptlevel
+  lines =<< trim END
+    vim9script
 
-    class Base3 extends Mix1
+    class C
+      var val2: number
     endclass
+    var o = C.new(3)
+    lockvar o.val2
+  END
+  v9.CheckSourceFailure(lines, 'E1335: Variable "val2" in class "C" is not writable')
 
-    type AliasBase1 = Base1
-    type AliasBase2 = Base2
-    type AliasIntf1 = Intf1
-    type AliasMix1 = Mix1
-
-    var b1 = Base1.new()
-    var b2 = Base2.new()
-    var b3 = Base3.new()
-
-    assert_true(instanceof(b1, Base1))
-    assert_true(instanceof(b2, Base1))
-    assert_false(instanceof(b1, Base2))
-    assert_true(instanceof(b3, Mix1))
-    assert_true(instanceof(b3, Base1, Base2, Intf1))
-
-    assert_true(instanceof(b1, AliasBase1))
-    assert_true(instanceof(b2, AliasBase1))
-    assert_false(instanceof(b1, AliasBase2))
-    assert_true(instanceof(b3, AliasMix1))
-    assert_true(instanceof(b3, AliasBase1, AliasBase2, AliasIntf1))
-
-    def Foo()
-      var a1 = Base1.new()
-      var a2 = Base2.new()
-      var a3 = Base3.new()
-
-      assert_true(instanceof(a1, Base1))
-      assert_true(instanceof(a2, Base1))
-      assert_false(instanceof(a1, Base2))
-      assert_true(instanceof(a3, Mix1))
-      assert_true(instanceof(a3, Base1, Base2, Intf1))
+  # read-only lockvar of scriptlevel variable from def
+  lines =<< trim END
+    vim9script
 
-      assert_true(instanceof(a1, AliasBase1))
-      assert_true(instanceof(a2, AliasBase1))
-      assert_false(instanceof(a1, AliasBase2))
-      assert_true(instanceof(a3, AliasMix1))
-      assert_true(instanceof(a3, AliasBase1, AliasBase2, AliasIntf1))
+    class C
+      var val3: number
+    endclass
+    var o = C.new(3)
+    def Lock()
+      lockvar o.val3
     enddef
-    Foo()
-
-    var o_null: Base1
-    assert_false(instanceof(o_null, Base1))
-
+    Lock()
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1335: Variable "val3" in class "C" is not writable')
 
+  # read-only lockvar of def argument variable
   lines =<< trim END
     vim9script
 
-    class Base1
+    class C
+      var val4: number
     endclass
-    instanceof(Base1.new())
+    def Lock(o: C)
+      lockvar o.val4
+    enddef
+    Lock(C.new(3))
   END
-  v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof')
+  v9.CheckSourceFailure(lines, 'E1335: Variable "val4" in class "C" is not writable')
 
+  # read-only lockvar from object method arg
   lines =<< trim END
     vim9script
 
-    class Base1
+    class C
+      var val5: number
+      def Lock(c: C)
+        lockvar c.val5
+      enddef
     endclass
-    def F()
-      instanceof(Base1.new())
-    enddef
-    F()
+    var o = C.new(3)
+    o.Lock(C.new(5))
   END
-  v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof')
+  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"')
 
+  # read-only lockvar from class method arg
   lines =<< trim END
     vim9script
 
-    class Base1
+    class C
+      var val6: number
+      static def Lock(c: C)
+        lockvar c.val6
+      enddef
     endclass
+    var o = C.new(3)
+    C.Lock(o)
+  END
+  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"')
 
-    class Base2
-    endclass
+  #
+  # lockvar of public object variable
+  #
 
-    var o = Base2.new()
-    instanceof(o, Base1, Base2, 3)
+  # lockvar from object method
+  lines =<< trim END
+    vim9script
+
+    class C
+      public var val1: number
+      def Lock()
+        lockvar this.val1
+      enddef
+    endclass
+    var o = C.new(3)
+    o.Lock()
   END
-  v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4', 10)
+  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"', 1)
 
+  # lockvar from scriptlevel
   lines =<< trim END
     vim9script
 
-    class Base1
+    class C
+      public var val2: number
     endclass
+    var o = C.new(3)
+    lockvar o.val2
+  END
+  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val2" in class "C"', 7)
 
-    class Base2
-    endclass
+  # lockvar of scriptlevel variable from def
+  lines =<< trim END
+    vim9script
 
-    def F()
-      var o = Base2.new()
-      instanceof(o, Base1, Base2, 3)
+    class C
+      public var val3: number
+    endclass
+    var o = C.new(3)
+    def Lock()
+      lockvar o.val3
     enddef
-    F()
+    Lock()
   END
-  v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4')
-enddef
+  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val3" in class "C"', 1)
 
-" Test for calling a method in the parent class that is extended partially.
-" This used to fail with the 'E118: Too many arguments for function: Text' error
-" message (Github issue #12524).
-def Test_call_method_in_parent_class()
-  var lines =<< trim END
+  # lockvar of def argument variable
+  lines =<< trim END
     vim9script
 
-    class Widget
-      var _lnum: number = 1
+    class C
+      public var val4: number
+    endclass
+    def Lock(o: C)
+      lockvar o.val4
+    enddef
+    Lock(C.new(3))
+  END
+  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val4" in class "C"', 1)
 
-      def SetY(lnum: number)
-        this._lnum = lnum
-      enddef
+  # lockvar from object method arg
+  lines =<< trim END
+    vim9script
 
-      def Text(): string
-        return ''
+    class C
+      public var val5: number
+      def Lock(c: C)
+        lockvar c.val5
       enddef
     endclass
+    var o = C.new(3)
+    o.Lock(C.new(5))
+  END
+  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"', 1)
 
-    class Foo extends Widget
-      def Text(): string
-        return '<Foo>'
+  # lockvar from class method arg
+  lines =<< trim END
+    vim9script
+
+    class C
+      public var val6: number
+      static def Lock(c: C)
+        lockvar c.val6
       enddef
     endclass
-
-    def Stack(w1: Widget, w2: Widget): list<Widget>
-      w1.SetY(1)
-      w2.SetY(2)
-      return [w1, w2]
-    enddef
-
-    var foo1 = Foo.new()
-    var foo2 = Foo.new()
-    var l = Stack(foo1, foo2)
+    var o = C.new(3)
+    C.Lock(o)
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"', 1)
 enddef
 
-" Test for calling methods from three levels of classes
-def Test_multi_level_method_call()
+" Test trying to lock a class variable from various places
+def Test_lockvar_class_variable()
+
+  # lockvar bare static from object method
   var lines =<< trim END
     vim9script
 
-    var A_func1: number = 0
-    var A_func2: number = 0
-    var A_func3: number = 0
-    var B_func2: number = 0
-    var B_func3: number = 0
-    var C_func3: number = 0
-
-    class A
-      def Func1()
-        A_func1 += 1
+    class C
+      public static var sval1: number
+      def Lock()
+        lockvar sval1
       enddef
+    endclass
+    var o = C.new()
+    o.Lock()
+  END
+  v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval1" in class "C"', 1)
 
-      def Func2()
-        A_func2 += 1
-      enddef
+  # lockvar C.static from object method
+  lines =<< trim END
+    vim9script
 
-      def Func3()
-        A_func3 += 1
+    class C
+      public static var sval2: number
+      def Lock()
+        lockvar C.sval2
       enddef
     endclass
+    var o = C.new()
+    o.Lock()
+  END
+  v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval2" in class "C"', 1)
 
-    class B extends A
-      def Func2()
-        B_func2 += 1
-      enddef
+  # lockvar bare static from class method
+  lines =<< trim END
+    vim9script
 
-      def Func3()
-        B_func3 += 1
+    class C
+      public static var sval3: number
+      static def Lock()
+        lockvar sval3
       enddef
     endclass
+    C.Lock()
+  END
+  v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval3" in class "C"', 1)
 
-    class C extends B
-      def Func3()
-        C_func3 += 1
+  # lockvar C.static from class method
+  lines =<< trim END
+    vim9script
+
+    class C
+      public static var sval4: number
+      static def Lock()
+        lockvar C.sval4
       enddef
     endclass
+    C.Lock()
+  END
+  v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval4" in class "C"', 1)
 
-    def A_CallFuncs(a: A)
-      a.Func1()
-      a.Func2()
-      a.Func3()
-    enddef
+  # lockvar C.static from script level
+  lines =<< trim END
+    vim9script
 
-    def B_CallFuncs(b: B)
-      b.Func1()
-      b.Func2()
-      b.Func3()
-    enddef
+    class C
+      public static var sval5: number
+    endclass
+    lockvar C.sval5
+  END
+  v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval5" in class "C"', 6)
 
-    def C_CallFuncs(c: C)
-      c.Func1()
-      c.Func2()
-      c.Func3()
-    enddef
+  # lockvar o.static from script level
+  lines =<< trim END
+    vim9script
 
-    var cobj = C.new()
-    A_CallFuncs(cobj)
-    B_CallFuncs(cobj)
-    C_CallFuncs(cobj)
-    assert_equal(3, A_func1)
-    assert_equal(0, A_func2)
-    assert_equal(0, A_func3)
-    assert_equal(3, B_func2)
-    assert_equal(0, B_func3)
-    assert_equal(3, C_func3)
+    class C
+      public static var sval6: number
+    endclass
+    var o = C.new()
+    lockvar o.sval6
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1375: Class variable "sval6" accessible only using class "C"', 7)
 enddef
 
-" Test for using members from three levels of classes
-def Test_multi_level_member_access()
+" Test locking an argument to :def
+def Test_lockvar_argument()
+  # Lockvar a function arg
   var lines =<< trim END
     vim9script
 
-    class A
-      public var val1: number = 0
-    endclass
+    def Lock(val: any)
+        lockvar val
+    enddef
 
-    class B extends A
-      public var val2: number = 0
-    endclass
+    var d = {a: 1, b: 2}
+    Lock(d)
 
-    class C extends B
-      public var val3: number = 0
-    endclass
+    d->extend({c: 3})
+  END
+  v9.CheckSourceFailure(lines, 'E741: Value is locked: extend() argument')
 
-    def A_members(a: A)
-      a.val1 += 1
-    enddef
+  # Lockvar a function arg. Verify "sval" is interpreted as argument and not a
+  # class member in "C". This tests lval_root_is_arg.
+  lines =<< trim END
+    vim9script
 
-    def B_members(b: B)
-      b.val1 += 1
-      b.val2 += 1
-    enddef
+    class C
+      public static var sval: list<number>
+    endclass
 
-    def C_members(c: C)
-      c.val1 += 1
-      c.val2 += 1
-      c.val3 += 1
+    def Lock2(sval: any)
+      lockvar sval
     enddef
 
-    var cobj = C.new()
-    A_members(cobj)
-    B_members(cobj)
-    C_members(cobj)
-    assert_equal(3, cobj.val1)
-    assert_equal(2, cobj.val2)
-    assert_equal(1, cobj.val3)
+    var o = C.new()
+    Lock2(o)
   END
   v9.CheckSourceSuccess(lines)
-enddef
 
-" Test expansion of <stack> with class methods.
-def Test_stack_expansion_with_methods()
-  var lines =<< trim END
+  # Lock a class.
+  lines =<< trim END
     vim9script
 
     class C
-      def M1()
-        F0()
-      enddef
+      public static var sval: list<number>
     endclass
 
-    def F0()
-      assert_match('<SNR>\d\+_F\[1\]\.\.<SNR>\d\+_C\.M1\[1\]\.\.<SNR>\d\+_F0\[1\]$', expand('<stack>'))
-    enddef
-
-    def F()
-      C.new().M1()
+    def Lock2(sval: any)
+      lockvar sval
     enddef
 
-    F()
+    Lock2(C)
   END
-  v9.CheckSourceSuccess(lines)
-enddef
+  v9.CheckSourceFailure(lines, 'E1405: Class "C" cannot be used as a value')
 
-" Test the return type of the new() constructor
-def Test_new_return_type()
-  # new() uses the default return type and there is no return statement
-  var lines =<< trim END
+  # Lock an object.
+  lines =<< trim END
     vim9script
 
     class C
-      var _bufnr: number
-
-      def new(this._bufnr)
-        if !bufexists(this._bufnr)
-          this._bufnr = -1
-        endif
-      enddef
+      public static var sval: list<number>
     endclass
 
-    var c = C.new(12345)
-    assert_equal('object<C>', typename(c))
-
-    var v1: C
-    v1 = C.new(12345)
-    assert_equal('object<C>', typename(v1))
-
-    def F()
-      var v2: C
-      v2 = C.new(12345)
-      assert_equal('object<C>', typename(v2))
+    def Lock2(sval: any)
+      lockvar sval
     enddef
-    F()
+
+    Lock2(C.new())
   END
   v9.CheckSourceSuccess(lines)
 
-  # new() uses the default return type and an empty 'return' statement
+  # In this case (unlike previous) "lockvar sval" is a class member.
   lines =<< trim END
     vim9script
 
     class C
-      var _bufnr: number
-
-      def new(this._bufnr)
-        if !bufexists(this._bufnr)
-          this._bufnr = -1
-          return
-        endif
+      public static var sval: list<number>
+      def Lock2()
+        lockvar sval
       enddef
     endclass
 
-    var c = C.new(12345)
-    assert_equal('object<C>', typename(c))
 
-    var v1: C
-    v1 = C.new(12345)
-    assert_equal('object<C>', typename(v1))
+    var o = C.new()
+    o.Lock2()
+  END
+  v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval" in class "C"', 1)
+enddef
 
-    def F()
-      var v2: C
-      v2 = C.new(12345)
-      assert_equal('object<C>', typename(v2))
-    enddef
-    F()
+" Test that this can be locked without error
+def Test_lockvar_this()
+  # lockvar this
+  var lines =<< trim END
+    vim9script
+    class C
+      def TLock()
+        lockvar this
+      enddef
+    endclass
+    var o = C.new()
+    o.TLock()
   END
   v9.CheckSourceSuccess(lines)
 
-  # new() uses "any" return type and returns "this"
+  # lockvar four   (four letter word, but not this)
   lines =<< trim END
     vim9script
-
     class C
-      var _bufnr: number
-
-      def new(this._bufnr): any
-        if !bufexists(this._bufnr)
-          this._bufnr = -1
-          return this
-        endif
+      def TLock4()
+        var four: number
+        lockvar four
       enddef
     endclass
+    var o = C.new()
+    o.TLock4()
   END
-  v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 11)
+  v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable')
 
-  # new() uses 'Dict' return type and returns a Dict
+  # lockvar this5; "this" + one char, 5 letter word, starting with "this"
   lines =<< trim END
     vim9script
-
     class C
-      var _state: dict<any>
-
-      def new(): dict<any>
-        this._state = {}
-        return this._state
+      def TLock5()
+        var this5: number
+        lockvar this5
       enddef
     endclass
-
-    var c = C.new()
-    assert_equal('object<C>', typename(c))
+    var o = C.new()
+    o.TLock5()
   END
-  v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 9)
+  v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable')
 enddef
 
-" Test for checking a member initialization type at run time.
-def Test_runtime_type_check_for_member_init()
+" Test some general lockvar cases
+def Test_lockvar_general()
+  # lockvar an object and a class. It does nothing
   var lines =<< trim END
     vim9script
-
-    var retnum: bool = false
-
-    def F(): any
-      retnum = !retnum
-      if retnum
-        return 1
-      else
-        return "hello"
-      endif
-    enddef
-
     class C
-      var _foo: bool = F()
     endclass
-
-    var c1 = C.new()
-    var c2 = C.new()
+    var o = C.new()
+    lockvar o
+    lockvar C
   END
-  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected bool but got string', 0)
-enddef
+  v9.CheckSourceSuccess(lines)
 
-" Test for locking a variable referring to an object and reassigning to another
-" object.
-def Test_lockvar_object()
-  var lines =<< trim END
+  # Lock a list element that's nested in an object variable from a :def
+  lines =<< trim END
     vim9script
 
     class C
-      var val: number
-      def new(this.val)
-      enddef
+      public var val: list<list<number>> = [ [1], [2], [3] ]
     endclass
-
-    var some_dict: dict<C> = { a: C.new(1), b: C.new(2), c: C.new(3), }
-    lockvar 2 some_dict
-
-    var current: C
-    current = some_dict['c']
-    assert_equal(3, current.val)
-    current = some_dict['b']
-    assert_equal(2, current.val)
-
-    def F()
-      current = some_dict['c']
-    enddef
-
-    def G()
-      current = some_dict['b']
+    def Lock2(obj: any)
+      lockvar obj.val[1]
     enddef
 
-    F()
-    assert_equal(3, current.val)
-    G()
-    assert_equal(2, current.val)
+    var o = C.new()
+    Lock2(o)
+    o.val[0] = [9]
+    assert_equal([ [9], [2], [3] ], o.val)
+    try
+      o.val[1] = [999]
+      call assert_false(true, 'assign should have failed')
+    catch
+      assert_exception('E741:')
+    endtry
+    o.val[2] = [8]
+    assert_equal([ [9], [2], [8] ], o.val)
   END
   v9.CheckSourceSuccess(lines)
-enddef
-
-" Test trying to lock an object variable from various places
-def Test_lockvar_object_variable()
-  # An object variable lockvar has several cases:
-  # object method, scriptlevel, scriplevel from :def, :def arg
-  # method arg, static method arg.
-  # Also different depths
-
-  #
-  # lockvar of read-only object variable
-  #
-
-  # read-only lockvar from object method
-  var lines =<< trim END
-    vim9script
-
-    class C
-      var val1: number
-      def Lock()
-        lockvar this.val1
-      enddef
-    endclass
-    var o = C.new(3)
-    o.Lock()
-  END
-  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"')
 
-  # read-only lockvar from scriptlevel
+  # Lock a list element that's nested in an object variable from scriptlevel
   lines =<< trim END
     vim9script
 
     class C
-      var val2: number
+      public var val: list<list<number>> = [ [1], [2], [3] ]
     endclass
-    var o = C.new(3)
-    lockvar o.val2
-  END
-  v9.CheckSourceFailure(lines, 'E1335: Variable "val2" in class "C" is not writable')
-
-  # read-only lockvar of scriptlevel variable from def
-  lines =<< trim END
-    vim9script
 
-    class C
-      var val3: number
-    endclass
-    var o = C.new(3)
-    def Lock()
-      lockvar o.val3
-    enddef
-    Lock()
+    var o = C.new()
+    lockvar o.val[1]
+    o.val[0] = [9]
+    assert_equal([ [9], [2], [3] ], o.val)
+    try
+      o.val[1] = [999]
+      call assert_false(true, 'assign should have failed')
+    catch
+      assert_exception('E741:')
+    endtry
+    o.val[2] = [8]
+    assert_equal([ [9], [2], [8] ], o.val)
   END
-  v9.CheckSourceFailure(lines, 'E1335: Variable "val3" in class "C" is not writable')
+  v9.CheckSourceSuccess(lines)
 
-  # read-only lockvar of def argument variable
+  # lock a script level variable from an object method
   lines =<< trim END
     vim9script
 
     class C
-      var val4: number
+      def Lock()
+        lockvar l
+      enddef
     endclass
-    def Lock(o: C)
-      lockvar o.val4
-    enddef
-    Lock(C.new(3))
+
+    var l = [1]
+    C.new().Lock()
+    l[0] = 11
   END
-  v9.CheckSourceFailure(lines, 'E1335: Variable "val4" in class "C" is not writable')
+  v9.CheckSourceFailure(lines, 'E741: Value is locked: l[0] = 11', 11)
 
-  # read-only lockvar from object method arg
+  # lock a list element referenced by a protected object variable
+  # in an object fetched via a script level list
   lines =<< trim END
     vim9script
 
     class C
-      var val5: number
-      def Lock(c: C)
-        lockvar c.val5
+      var _v1: list<list<number>>
+      def Lock()
+        lockvar lc[0]._v1[1]
       enddef
     endclass
-    var o = C.new(3)
-    o.Lock(C.new(5))
-  END
-  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"')
 
-  # read-only lockvar from class method arg
-  lines =<< trim END
-    vim9script
+    var l = [[1], [2], [3]]
+    var o = C.new(l)
+    var lc: list<C> = [ o ]
 
-    class C
-      var val6: number
-      static def Lock(c: C)
-        lockvar c.val6
-      enddef
-    endclass
-    var o = C.new(3)
-    C.Lock(o)
+    o.Lock()
+    l[0] = [22]
+    l[1] = [33]
   END
-  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"')
-
-  #
-  # lockvar of public object variable
-  #
+  v9.CheckSourceFailure(lines, 'E741: Value is locked: l[1] = [33]', 16)
 
-  # lockvar from object method
+  # similar to the previous test, except the locking code is executing
+  # in a class that does not own the protected variable.
+  # Note that the locking code is in a class has a protected variable of
+  # the same name.
   lines =<< trim END
     vim9script
 
-    class C
-      public var val1: number
-      def Lock()
-        lockvar this.val1
+    class C2
+      var _v1: list<list<number>>
+      def Lock(obj: any)
+        lockvar lc[0]._v1[1]
       enddef
     endclass
-    var o = C.new(3)
-    o.Lock()
-  END
-  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"', 1)
-
-  # lockvar from scriptlevel
-  lines =<< trim END
-    vim9script
 
     class C
-      public var val2: number
+      var _v1: list<list<number>>
     endclass
-    var o = C.new(3)
-    lockvar o.val2
-  END
-  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val2" in class "C"', 7)
 
-  # lockvar of scriptlevel variable from def
-  lines =<< trim END
-    vim9script
+    var l = [[1], [2], [3]]
+    var o = C.new(l)
+    var lc: list<C> = [ o ]
 
-    class C
-      public var val3: number
-    endclass
-    var o = C.new(3)
-    def Lock()
-      lockvar o.val3
-    enddef
-    Lock()
+    var o2 = C2.new()
+    o2.Lock(o)
   END
-  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val3" in class "C"', 1)
+  v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "C"')
+enddef
 
-  # lockvar of def argument variable
-  lines =<< trim END
+" Test builtin islocked()
+def Test_lockvar_islocked()
+  # Can't lock class/object variable
+  # Lock class/object variable's value
+  # Lock item of variable's value (a list item)
+  # variable is at index 1 within class/object
+  var lines =<< trim END
     vim9script
 
     class C
-      public var val4: number
+      var o0: list<list<number>> = [ [0],  [1],  [2]]
+      var o1: list<list<number>> = [[10], [11], [12]]
+      static var c0: list<list<number>> = [[20], [21], [22]]
+      static var c1: list<list<number>> = [[30], [31], [32]]
     endclass
-    def Lock(o: C)
-      lockvar o.val4
+
+    def LockIt(arg: any)
+      lockvar arg
     enddef
-    Lock(C.new(3))
-  END
-  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val4" in class "C"', 1)
 
-  # lockvar from object method arg
-  lines =<< trim END
-    vim9script
+    def UnlockIt(arg: any)
+      unlockvar arg
+    enddef
 
-    class C
-      public var val5: number
-      def Lock(c: C)
-        lockvar c.val5
-      enddef
-    endclass
-    var o = C.new(3)
-    o.Lock(C.new(5))
-  END
-  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"', 1)
-
-  # lockvar from class method arg
-  lines =<< trim END
-    vim9script
-
-    class C
-      public var val6: number
-      static def Lock(c: C)
-        lockvar c.val6
-      enddef
-    endclass
-    var o = C.new(3)
-    C.Lock(o)
-  END
-  v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"', 1)
-enddef
-
-" Test trying to lock a class variable from various places
-def Test_lockvar_class_variable()
-
-  # lockvar bare static from object method
-  var lines =<< trim END
-    vim9script
-
-    class C
-      public static var sval1: number
-      def Lock()
-        lockvar sval1
-      enddef
-    endclass
-    var o = C.new()
-    o.Lock()
-  END
-  v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval1" in class "C"', 1)
-
-  # lockvar C.static from object method
-  lines =<< trim END
-    vim9script
-
-    class C
-      public static var sval2: number
-      def Lock()
-        lockvar C.sval2
-      enddef
-    endclass
-    var o = C.new()
-    o.Lock()
-  END
-  v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval2" in class "C"', 1)
-
-  # lockvar bare static from class method
-  lines =<< trim END
-    vim9script
-
-    class C
-      public static var sval3: number
-      static def Lock()
-        lockvar sval3
-      enddef
-    endclass
-    C.Lock()
-  END
-  v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval3" in class "C"', 1)
-
-  # lockvar C.static from class method
-  lines =<< trim END
-    vim9script
-
-    class C
-      public static var sval4: number
-      static def Lock()
-        lockvar C.sval4
-      enddef
-    endclass
-    C.Lock()
-  END
-  v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval4" in class "C"', 1)
-
-  # lockvar C.static from script level
-  lines =<< trim END
-    vim9script
-
-    class C
-      public static var sval5: number
-    endclass
-    lockvar C.sval5
-  END
-  v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval5" in class "C"', 6)
-
-  # lockvar o.static from script level
-  lines =<< trim END
-    vim9script
-
-    class C
-      public static var sval6: number
-    endclass
-    var o = C.new()
-    lockvar o.sval6
-  END
-  v9.CheckSourceFailure(lines, 'E1375: Class variable "sval6" accessible only using class "C"', 7)
-enddef
-
-" Test locking an argument to :def
-def Test_lockvar_argument()
-  # Lockvar a function arg
-  var lines =<< trim END
-    vim9script
-
-    def Lock(val: any)
-        lockvar val
-    enddef
-
-    var d = {a: 1, b: 2}
-    Lock(d)
-
-    d->extend({c: 3})
-  END
-  v9.CheckSourceFailure(lines, 'E741: Value is locked: extend() argument')
-
-  # Lockvar a function arg. Verify "sval" is interpreted as argument and not a
-  # class member in "C". This tests lval_root_is_arg.
-  lines =<< trim END
-    vim9script
-
-    class C
-      public static var sval: list<number>
-    endclass
-
-    def Lock2(sval: any)
-      lockvar sval
-    enddef
-
-    var o = C.new()
-    Lock2(o)
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # Lock a class.
-  lines =<< trim END
-    vim9script
-
-    class C
-      public static var sval: list<number>
-    endclass
-
-    def Lock2(sval: any)
-      lockvar sval
-    enddef
-
-    Lock2(C)
-  END
-  v9.CheckSourceFailure(lines, 'E1405: Class "C" cannot be used as a value')
-
-  # Lock an object.
-  lines =<< trim END
-    vim9script
-
-    class C
-      public static var sval: list<number>
-    endclass
-
-    def Lock2(sval: any)
-      lockvar sval
-    enddef
-
-    Lock2(C.new())
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # In this case (unlike previous) "lockvar sval" is a class member.
-  lines =<< trim END
-    vim9script
-
-    class C
-      public static var sval: list<number>
-      def Lock2()
-        lockvar sval
-      enddef
-    endclass
-
-
-    var o = C.new()
-    o.Lock2()
-  END
-  v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval" in class "C"', 1)
-enddef
-
-" Test that this can be locked without error
-def Test_lockvar_this()
-  # lockvar this
-  var lines =<< trim END
-    vim9script
-    class C
-      def TLock()
-        lockvar this
-      enddef
-    endclass
-    var o = C.new()
-    o.TLock()
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # lockvar four   (four letter word, but not this)
-  lines =<< trim END
-    vim9script
-    class C
-      def TLock4()
-        var four: number
-        lockvar four
-      enddef
-    endclass
-    var o = C.new()
-    o.TLock4()
-  END
-  v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable')
-
-  # lockvar this5; "this" + one char, 5 letter word, starting with "this"
-  lines =<< trim END
-    vim9script
-    class C
-      def TLock5()
-        var this5: number
-        lockvar this5
-      enddef
-    endclass
-    var o = C.new()
-    o.TLock5()
-  END
-  v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable')
-enddef
-
-" Test some general lockvar cases
-def Test_lockvar_general()
-  # lockvar an object and a class. It does nothing
-  var lines =<< trim END
-    vim9script
-    class C
-    endclass
-    var o = C.new()
-    lockvar o
-    lockvar C
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # Lock a list element that's nested in an object variable from a :def
-  lines =<< trim END
-    vim9script
-
-    class C
-      public var val: list<list<number>> = [ [1], [2], [3] ]
-    endclass
-    def Lock2(obj: any)
-      lockvar obj.val[1]
-    enddef
-
-    var o = C.new()
-    Lock2(o)
-    o.val[0] = [9]
-    assert_equal([ [9], [2], [3] ], o.val)
-    try
-      o.val[1] = [999]
-      call assert_false(true, 'assign should have failed')
-    catch
-      assert_exception('E741:')
-    endtry
-    o.val[2] = [8]
-    assert_equal([ [9], [2], [8] ], o.val)
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # Lock a list element that's nested in an object variable from scriptlevel
-  lines =<< trim END
-    vim9script
-
-    class C
-      public var val: list<list<number>> = [ [1], [2], [3] ]
-    endclass
-
-    var o = C.new()
-    lockvar o.val[1]
-    o.val[0] = [9]
-    assert_equal([ [9], [2], [3] ], o.val)
-    try
-      o.val[1] = [999]
-      call assert_false(true, 'assign should have failed')
-    catch
-      assert_exception('E741:')
-    endtry
-    o.val[2] = [8]
-    assert_equal([ [9], [2], [8] ], o.val)
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # lock a script level variable from an object method
-  lines =<< trim END
-    vim9script
-
-    class C
-      def Lock()
-        lockvar l
-      enddef
-    endclass
-
-    var l = [1]
-    C.new().Lock()
-    l[0] = 11
-  END
-  v9.CheckSourceFailure(lines, 'E741: Value is locked: l[0] = 11', 11)
-
-  # lock a list element referenced by a protected object variable
-  # in an object fetched via a script level list
-  lines =<< trim END
-    vim9script
-
-    class C
-      var _v1: list<list<number>>
-      def Lock()
-        lockvar lc[0]._v1[1]
-      enddef
-    endclass
-
-    var l = [[1], [2], [3]]
-    var o = C.new(l)
-    var lc: list<C> = [ o ]
-
-    o.Lock()
-    l[0] = [22]
-    l[1] = [33]
-  END
-  v9.CheckSourceFailure(lines, 'E741: Value is locked: l[1] = [33]', 16)
-
-  # similar to the previous test, except the locking code is executing
-  # in a class that does not own the protected variable.
-  # Note that the locking code is in a class has a protected variable of
-  # the same name.
-  lines =<< trim END
-    vim9script
-
-    class C2
-      var _v1: list<list<number>>
-      def Lock(obj: any)
-        lockvar lc[0]._v1[1]
-      enddef
-    endclass
-
-    class C
-      var _v1: list<list<number>>
-    endclass
-
-    var l = [[1], [2], [3]]
-    var o = C.new(l)
-    var lc: list<C> = [ o ]
-
-    var o2 = C2.new()
-    o2.Lock(o)
-  END
-  v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "C"')
-enddef
-
-" Test builtin islocked()
-def Test_lockvar_islocked()
-  # Can't lock class/object variable
-  # Lock class/object variable's value
-  # Lock item of variable's value (a list item)
-  # variable is at index 1 within class/object
-  var lines =<< trim END
-    vim9script
-
-    class C
-      var o0: list<list<number>> = [ [0],  [1],  [2]]
-      var o1: list<list<number>> = [[10], [11], [12]]
-      static var c0: list<list<number>> = [[20], [21], [22]]
-      static var c1: list<list<number>> = [[30], [31], [32]]
-    endclass
-
-    def LockIt(arg: any)
-      lockvar arg
-    enddef
-
-    def UnlockIt(arg: any)
-      unlockvar arg
-    enddef
-
-    var obj = C.new()
-    #lockvar obj.o1         # can't lock something you can't write to
-
-    try
-      lockvar obj.o1         # can't lock something you can't write to
-      call assert_false(1, '"lockvar obj.o1" should have failed')
-    catch
-      call assert_exception('E1335:')
-    endtry
-
-    LockIt(obj.o1)         # but can lock it's value
-    assert_equal(1, islocked("obj.o1"))
-    assert_equal(1, islocked("obj.o1[0]"))
-    assert_equal(1, islocked("obj.o1[1]"))
-    UnlockIt(obj.o1)
-    assert_equal(0, islocked("obj.o1"))
-    assert_equal(0, islocked("obj.o1[0]"))
-
-    lockvar obj.o1[0]
-    assert_equal(0, islocked("obj.o1"))
-    assert_equal(1, islocked("obj.o1[0]"))
-    assert_equal(0, islocked("obj.o1[1]"))
-    unlockvar obj.o1[0]
-    assert_equal(0, islocked("obj.o1"))
-    assert_equal(0, islocked("obj.o1[0]"))
-
-    # Same thing, but with a static
-
-    try
-      lockvar C.c1         # can't lock something you can't write to
-      call assert_false(1, '"lockvar C.c1" should have failed')
-    catch
-      call assert_exception('E1335:')
-    endtry
-
-    LockIt(C.c1)         # but can lock it's value
-    assert_equal(1, islocked("C.c1"))
-    assert_equal(1, islocked("C.c1[0]"))
-    assert_equal(1, islocked("C.c1[1]"))
-    UnlockIt(C.c1)
-    assert_equal(0, islocked("C.c1"))
-    assert_equal(0, islocked("C.c1[0]"))
-
-    lockvar C.c1[0]
-    assert_equal(0, islocked("C.c1"))
-    assert_equal(1, islocked("C.c1[0]"))
-    assert_equal(0, islocked("C.c1[1]"))
-    unlockvar C.c1[0]
-    assert_equal(0, islocked("C.c1"))
-    assert_equal(0, islocked("C.c1[0]"))
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # Do islocked() from an object method
-  # and then from a class method
-  lines =<< trim END
-    vim9script
-
-    var l0o0 = [  [0],   [1],   [2]]
-    var l0o1 = [ [10],  [11],  [12]]
-    var l0c0 = [[120], [121], [122]]
-    var l0c1 = [[130], [131], [132]]
-
-    class C0
-      var o0: list<list<number>> =   l0o0
-      var o1: list<list<number>> =   l0o1
-      static var c0: list<list<number>> = l0c0
-      static var c1: list<list<number>> = l0c1
-      def Islocked(arg: string): number
-          return islocked(arg)
-      enddef
-      static def SIslocked(arg: string): number
-        return islocked(arg)
-      enddef
-    endclass
-
-    var l2o0 = [[20000], [20001], [20002]]
-    var l2o1 = [[20010], [20011], [20012]]
-    var l2c0 = [[20120], [20121], [20122]]
-    var l2c1 = [[20130], [20131], [20132]]
-
-    class C2
-      var o0: list<list<number>> =   l2o0
-      var o1: list<list<number>> =   l2o1
-      static var c0: list<list<number>> = l2c0
-      static var c1: list<list<number>> = l2c1
-      def Islocked(arg: string): number
-          return islocked(arg)
-      enddef
-      static def SIslocked(arg: string): number
-        return islocked(arg)
-      enddef
-    endclass
-
-    var obj0 = C0.new()
-    var obj2 = C2.new()
-
-    var l = [ obj0, null_object, obj2 ]
-
-    # lock list, object func access through script var expr
-    assert_equal(0, obj0.Islocked("l[0].o0"))
-    assert_equal(0, obj0.Islocked("l[0].o0[2]"))
-    lockvar l0o0
-    assert_equal(1, obj0.Islocked("l[0].o0"))
-    assert_equal(1, obj0.Islocked("l[0].o0[2]"))
-
-    #echo "check-b" obj2.Islocked("l[1].o1")    # NULL OBJECT
-
-    # lock list element, object func access through script var expr
-    lockvar l0o1[1]
-    assert_equal(0, obj0.Islocked("this.o1[0]"))
-    assert_equal(1, obj0.Islocked("this.o1[1]"))
-
-    assert_equal(0, obj0.Islocked("this.o1"))
-    lockvar l0o1
-    assert_equal(1, obj0.Islocked("this.o1"))
-    unlockvar l0o1
-
-    lockvar l0c1[1]
-
-    # static by class name member expr from same class
-    assert_equal(0, obj0.Islocked("C0.c1[0]"))
-    assert_equal(1, obj0.Islocked("C0.c1[1]"))
-    # static by bare name member expr from same class
-    assert_equal(0, obj0.Islocked("c1[0]"))
-    assert_equal(1, obj0.Islocked("c1[1]"))
-
-    # static by class name member expr from other class
-    assert_equal(0, obj2.Islocked("C0.c1[0]"))
-    assert_equal(1, obj2.Islocked("C0.c1[1]"))
-    # static by bare name member expr from other class
-    assert_equal(0, obj2.Islocked("c1[0]"))
-    assert_equal(0, obj2.Islocked("c1[1]"))
-
-
-    # static by bare name in same class
-    assert_equal(0, obj0.Islocked("c0"))
-    lockvar l0c0
-    assert_equal(1, obj0.Islocked("c0"))
-
-    #
-    # similar stuff, but use static method
-    #
-
-    unlockvar l0o0
-
-    # lock list, object func access through script var expr
-    assert_equal(0, C0.SIslocked("l[0].o0"))
-    assert_equal(0, C0.SIslocked("l[0].o0[2]"))
-    lockvar l0o0
-    assert_equal(1, C0.SIslocked("l[0].o0"))
-    assert_equal(1, C0.SIslocked("l[0].o0[2]"))
-
-    unlockvar l0o1
-
-    # can't access "this" from class method
-    try
-      C0.SIslocked("this.o1[0]")
-      call assert_0(1, '"C0.SIslocked("this.o1[0]")" should have failed')
-    catch
-      call assert_exception('E121: Undefined variable: this')
-    endtry
-
-    lockvar l0c1[1]
-
-    # static by class name member expr from same class
-    assert_equal(0, C0.SIslocked("C0.c1[0]"))
-    assert_equal(1, C0.SIslocked("C0.c1[1]"))
-    # static by bare name member expr from same class
-    assert_equal(0, C0.SIslocked("c1[0]"))
-    assert_equal(1, C0.SIslocked("c1[1]"))
-
-    # static by class name member expr from other class
-    assert_equal(0, C2.SIslocked("C0.c1[0]"))
-    assert_equal(1, C2.SIslocked("C0.c1[1]"))
-    # static by bare name member expr from other class
-    assert_equal(0, C2.SIslocked("c1[0]"))
-    assert_equal(0, C2.SIslocked("c1[1]"))
-
-
-    # static by bare name in same class
-    unlockvar l0c0
-    assert_equal(0, C0.SIslocked("c0"))
-    lockvar l0c0
-    assert_equal(1, C0.SIslocked("c0"))
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # Check islocked class/object from various places.
-  lines =<< trim END
-    vim9script
-
-    class C
-      def Islocked(arg: string): number
-        return islocked(arg)
-      enddef
-      static def SIslocked(arg: string): number
-        return islocked(arg)
-      enddef
-    endclass
-    var obj = C.new()
-
-    # object method
-    assert_equal(0, obj.Islocked("this"))
-    assert_equal(0, obj.Islocked("C"))
-
-    # class method
-    ### assert_equal(0, C.SIslocked("this"))
-    assert_equal(0, C.SIslocked("C"))
-
-    #script level
-    var v: number
-    v = islocked("C")
-    assert_equal(0, v)
-    v = islocked("obj")
-    assert_equal(0, v)
-  END
-  v9.CheckSourceSuccess(lines)
-enddef
-
-def Test_lockvar_islocked_notfound()
-  # Try non-existent things
-  var lines =<< trim END
-    vim9script
-
-    class C
-      def Islocked(arg: string): number
-          return islocked(arg)
-      enddef
-      static def SIslocked(arg: string): number
-        return islocked(arg)
-      enddef
-    endclass
-    var obj = C.new()
-    assert_equal(-1, obj.Islocked("anywhere"))
-    assert_equal(-1, C.SIslocked("notanywhere"))
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # Something not found of the form "name1.name2" is an error
-  lines =<< trim END
-    vim9script
-
-    islocked("one.two")
-  END
-  v9.CheckSourceFailure(lines, 'E121: Undefined variable: one')
-
-  lines =<< trim END
-    vim9script
-
-    class C
-      var val = { key: "value" }
-      def Islocked(arg: string): number
-          return islocked(arg)
-      enddef
-    endclass
-    var obj = C.new()
-    obj.Islocked("this.val.not_there"))
-  END
-  v9.CheckSourceFailure(lines, 'E716: Key not present in Dictionary: "not_there"')
-
-  lines =<< trim END
-    vim9script
-
-    class C
-      def Islocked(arg: string): number
-          return islocked(arg)
-      enddef
-    endclass
-    var obj = C.new()
-    obj.Islocked("this.notobjmember")
-  END
-  v9.CheckSourceFailure(lines, 'E1326: Variable "notobjmember" not found in object "C"')
-
-  # access a script variable through methods
-  lines =<< trim END
-    vim9script
-
-    var l = [1]
-    class C
-      def Islocked(arg: string): number
-          return islocked(arg)
-      enddef
-      static def SIslocked(arg: string): number
-        return islocked(arg)
-      enddef
-    endclass
-    var obj = C.new()
-    assert_equal(0, obj.Islocked("l"))
-    assert_equal(0, C.SIslocked("l"))
-    lockvar l
-    assert_equal(1, obj.Islocked("l"))
-    assert_equal(1, C.SIslocked("l"))
-  END
-  v9.CheckSourceSuccess(lines)
-enddef
-
-" Test for a protected object method
-def Test_protected_object_method()
-  # Try calling a protected method using an object (at the script level)
-  var lines =<< trim END
-    vim9script
-
-    class A
-      def _Foo(): number
-        return 1234
-      enddef
-    endclass
-    var a = A.new()
-    a._Foo()
-  END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9)
-
-  # Try calling a protected method using an object (from a def function)
-  lines =<< trim END
-    vim9script
-
-    class A
-      def _Foo(): number
-        return 1234
-      enddef
-    endclass
-    def T()
-      var a = A.new()
-      a._Foo()
-    enddef
-    T()
-  END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2)
-
-  # Use a protected method from another object method (in script context)
-  lines =<< trim END
-    vim9script
-
-    class A
-      def _Foo(): number
-        return 1234
-      enddef
-      def Bar(): number
-        return this._Foo()
-      enddef
-    endclass
-    var a = A.new()
-    assert_equal(1234, a.Bar())
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # Use a protected method from another object method (def function context)
-  lines =<< trim END
-    vim9script
-
-    class A
-      def _Foo(): number
-        return 1234
-      enddef
-      def Bar(): number
-        return this._Foo()
-      enddef
-    endclass
-    def T()
-      var a = A.new()
-      assert_equal(1234, a.Bar())
-    enddef
-    T()
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # Try calling a protected method without the "this" prefix
-  lines =<< trim END
-    vim9script
-
-    class A
-      def _Foo(): number
-        return 1234
-      enddef
-      def Bar(): number
-        return _Foo()
-      enddef
-    endclass
-    var a = A.new()
-    a.Bar()
-  END
-  v9.CheckSourceFailure(lines, 'E117: Unknown function: _Foo', 1)
-
-  # Try calling a protected method using the class name
-  lines =<< trim END
-    vim9script
-
-    class A
-      def _Foo(): number
-        return 1234
-      enddef
-    endclass
-    A._Foo()
-  END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8)
-
-  # Define two protected methods with the same name
-  lines =<< trim END
-    vim9script
-
-    class A
-      def _Foo()
-      enddef
-      def _Foo()
-      enddef
-    endclass
-    var a = A.new()
-  END
-  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7)
-
-  # Define a protected method and a object method with the same name
-  lines =<< trim END
-    vim9script
-
-    class A
-      def _Foo()
-      enddef
-      def Foo()
-      enddef
-    endclass
-    var a = A.new()
-  END
-  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7)
-
-  # Define an object method and a protected method with the same name
-  lines =<< trim END
-    vim9script
-
-    class A
-      def Foo()
-      enddef
-      def _Foo()
-      enddef
-    endclass
-    var a = A.new()
-  END
-  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7)
-
-  # Call a public method and a protected method from a protected method
-  lines =<< trim END
-    vim9script
-
-    class A
-      def Foo(): number
-        return 100
-      enddef
-      def _Bar(): number
-        return 200
-      enddef
-      def _Baz()
-        assert_equal(100, this.Foo())
-        assert_equal(200, this._Bar())
-      enddef
-      def T()
-        this._Baz()
-      enddef
-    endclass
-    var a = A.new()
-    a.T()
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # Try calling a protected method from another class
-  lines =<< trim END
-    vim9script
-
-    class A
-      def _Foo(): number
-        return 100
-      enddef
-    endclass
-    class B
-      def Foo(): number
-        var a = A.new()
-        a._Foo()
-      enddef
-    endclass
-    var b = B.new()
-    b.Foo()
-  END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2)
-
-  # Call a protected object method from a child class object method
-  lines =<< trim END
-    vim9script
-    class A
-      def _Foo(): number
-        return 1234
-      enddef
-    endclass
-    class B extends A
-      def Bar()
-      enddef
-    endclass
-    class C extends B
-      def Baz(): number
-        return this._Foo()
-      enddef
-    endclass
-    var c = C.new()
-    assert_equal(1234, c.Baz())
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # Call a protected object method from a child class object
-  lines =<< trim END
-    vim9script
-    class A
-      def _Foo(): number
-        return 1234
-      enddef
-    endclass
-    class B extends A
-      def Bar()
-      enddef
-    endclass
-    class C extends B
-      def Baz(): number
-      enddef
-    endclass
-    var c = C.new()
-    assert_equal(1234, c._Foo())
-  END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 16)
-
-  # Using "_" prefix in a method name should fail outside of a class
-  lines =<< trim END
-    vim9script
-    def _Foo(): number
-      return 1234
-    enddef
-    var a = _Foo()
-  END
-  v9.CheckSourceFailure(lines, 'E1267: Function name must start with a capital: _Foo(): number', 2)
-
-  # Test for initializing a protected funcref instance variable to a protected
-  # class method from another class
-  lines =<< trim END
-    vim9script
-
-    class A
-      static def _Internal(): string
-      enddef
-    endclass
-
-    class B
-      var Fn: func = A._Internal
-    endclass
-    var b = B.new()
-  END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 1)
-enddef
-
-" Test for an protected class method
-def Test_protected_class_method()
-  # Try calling a class protected method (at the script level)
-  var lines =<< trim END
-    vim9script
-
-    class A
-      static def _Foo(): number
-        return 1234
-      enddef
-    endclass
-    A._Foo()
-  END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8)
-
-  # Try calling a class protected method (from a def function)
-  lines =<< trim END
-    vim9script
-
-    class A
-      static def _Foo(): number
-        return 1234
-      enddef
-    endclass
-    def T()
-      A._Foo()
-    enddef
-    T()
-  END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
-
-  # Try calling a class protected method using an object (at the script level)
-  lines =<< trim END
-    vim9script
-
-    class A
-      static def _Foo(): number
-        return 1234
-      enddef
-    endclass
-    var a = A.new()
-    a._Foo()
-  END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9)
-
-  # Try calling a class protected method using an object (from a def function)
-  lines =<< trim END
-    vim9script
-
-    class A
-      static def _Foo(): number
-        return 1234
-      enddef
-    endclass
-    def T()
-      var a = A.new()
-      a._Foo()
-    enddef
-    T()
-  END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 2)
-
-  # Use a class protected method from an object method
-  lines =<< trim END
-    vim9script
-
-    class A
-      static def _Foo(): number
-        return 1234
-      enddef
-      def Bar()
-        assert_equal(1234, _Foo())
-      enddef
-    endclass
-    var a = A.new()
-    a.Bar()
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # Use a class protected method from another class protected method without the
-  # class name prefix.
-  lines =<< trim END
-    vim9script
+    var obj = C.new()
+    #lockvar obj.o1         # can't lock something you can't write to
 
-    class A
-      static def _Foo1(): number
-        return 1234
-      enddef
-      static def _Foo2()
-        assert_equal(1234, _Foo1())
-      enddef
-      def Bar()
-        _Foo2()
-      enddef
-    endclass
-    var a = A.new()
-    a.Bar()
-  END
-  v9.CheckSourceSuccess(lines)
+    try
+      lockvar obj.o1         # can't lock something you can't write to
+      call assert_false(1, '"lockvar obj.o1" should have failed')
+    catch
+      call assert_exception('E1335:')
+    endtry
 
-  # Declare a class method and a class protected method with the same name
-  lines =<< trim END
-    vim9script
+    LockIt(obj.o1)         # but can lock it's value
+    assert_equal(1, islocked("obj.o1"))
+    assert_equal(1, islocked("obj.o1[0]"))
+    assert_equal(1, islocked("obj.o1[1]"))
+    UnlockIt(obj.o1)
+    assert_equal(0, islocked("obj.o1"))
+    assert_equal(0, islocked("obj.o1[0]"))
 
-    class A
-      static def _Foo()
-      enddef
-      static def Foo()
-      enddef
-    endclass
-    var a = A.new()
-  END
-  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7)
+    lockvar obj.o1[0]
+    assert_equal(0, islocked("obj.o1"))
+    assert_equal(1, islocked("obj.o1[0]"))
+    assert_equal(0, islocked("obj.o1[1]"))
+    unlockvar obj.o1[0]
+    assert_equal(0, islocked("obj.o1"))
+    assert_equal(0, islocked("obj.o1[0]"))
 
-  # Try calling a class protected method from another class
-  lines =<< trim END
-    vim9script
+    # Same thing, but with a static
 
-    class A
-      static def _Foo(): number
-        return 1234
-      enddef
-    endclass
-    class B
-      def Foo(): number
-        return A._Foo()
-      enddef
-    endclass
-    var b = B.new()
-    assert_equal(1234, b.Foo())
-  END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
+    try
+      lockvar C.c1         # can't lock something you can't write to
+      call assert_false(1, '"lockvar C.c1" should have failed')
+    catch
+      call assert_exception('E1335:')
+    endtry
 
-  # Call a protected class method from a child class object method
-  lines =<< trim END
-    vim9script
-    class A
-      static def _Foo(): number
-        return 1234
-      enddef
-    endclass
-    class B extends A
-      def Bar()
-      enddef
-    endclass
-    class C extends B
-      def Baz(): number
-        return A._Foo()
-      enddef
-    endclass
-    var c = C.new()
-    assert_equal(1234, c.Baz())
+    LockIt(C.c1)         # but can lock it's value
+    assert_equal(1, islocked("C.c1"))
+    assert_equal(1, islocked("C.c1[0]"))
+    assert_equal(1, islocked("C.c1[1]"))
+    UnlockIt(C.c1)
+    assert_equal(0, islocked("C.c1"))
+    assert_equal(0, islocked("C.c1[0]"))
+
+    lockvar C.c1[0]
+    assert_equal(0, islocked("C.c1"))
+    assert_equal(1, islocked("C.c1[0]"))
+    assert_equal(0, islocked("C.c1[1]"))
+    unlockvar C.c1[0]
+    assert_equal(0, islocked("C.c1"))
+    assert_equal(0, islocked("C.c1[0]"))
   END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
+  v9.CheckSourceSuccess(lines)
 
-  # Call a protected class method from a child class protected class method
+  # Do islocked() from an object method
+  # and then from a class method
   lines =<< trim END
     vim9script
-    class A
-      static def _Foo(): number
-        return 1234
-      enddef
-    endclass
-    class B extends A
-      def Bar()
+
+    var l0o0 = [  [0],   [1],   [2]]
+    var l0o1 = [ [10],  [11],  [12]]
+    var l0c0 = [[120], [121], [122]]
+    var l0c1 = [[130], [131], [132]]
+
+    class C0
+      var o0: list<list<number>> =   l0o0
+      var o1: list<list<number>> =   l0o1
+      static var c0: list<list<number>> = l0c0
+      static var c1: list<list<number>> = l0c1
+      def Islocked(arg: string): number
+          return islocked(arg)
       enddef
-    endclass
-    class C extends B
-      static def Baz(): number
-        return A._Foo()
+      static def SIslocked(arg: string): number
+        return islocked(arg)
       enddef
     endclass
-    assert_equal(1234, C.Baz())
-  END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
 
-  # Call a protected class method from a child class object
-  lines =<< trim END
-    vim9script
-    class A
-      static def _Foo(): number
-        return 1234
-      enddef
-    endclass
-    class B extends A
-      def Bar()
+    var l2o0 = [[20000], [20001], [20002]]
+    var l2o1 = [[20010], [20011], [20012]]
+    var l2c0 = [[20120], [20121], [20122]]
+    var l2c1 = [[20130], [20131], [20132]]
+
+    class C2
+      var o0: list<list<number>> =   l2o0
+      var o1: list<list<number>> =   l2o1
+      static var c0: list<list<number>> = l2c0
+      static var c1: list<list<number>> = l2c1
+      def Islocked(arg: string): number
+          return islocked(arg)
       enddef
-    endclass
-    class C extends B
-      def Baz(): number
+      static def SIslocked(arg: string): number
+        return islocked(arg)
       enddef
     endclass
-    var c = C.new()
-    assert_equal(1234, C._Foo())
-  END
-  v9.CheckSourceFailure(lines, 'E1325: Method "_Foo" not found in class "C"', 16)
 
-  # Test for initializing a protected funcref class variable to a protected
-  # class method from another class
-  lines =<< trim END
-    vim9script
+    var obj0 = C0.new()
+    var obj2 = C2.new()
 
-    class A
-      static def _Internal(): string
-      enddef
-    endclass
+    var l = [ obj0, null_object, obj2 ]
 
-    class B
-      static var Fn: func = A._Internal
-    endclass
-  END
-  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 10)
-enddef
+    # lock list, object func access through script var expr
+    assert_equal(0, obj0.Islocked("l[0].o0"))
+    assert_equal(0, obj0.Islocked("l[0].o0[2]"))
+    lockvar l0o0
+    assert_equal(1, obj0.Islocked("l[0].o0"))
+    assert_equal(1, obj0.Islocked("l[0].o0[2]"))
 
-" Test for using the return value of a class/object method as a function
-" argument.
-def Test_objmethod_funcarg()
-  var lines =<< trim END
-    vim9script
+    #echo "check-b" obj2.Islocked("l[1].o1")    # NULL OBJECT
 
-    class C
-      def Foo(): string
-        return 'foo'
-      enddef
-    endclass
+    # lock list element, object func access through script var expr
+    lockvar l0o1[1]
+    assert_equal(0, obj0.Islocked("this.o1[0]"))
+    assert_equal(1, obj0.Islocked("this.o1[1]"))
 
-    def Bar(a: number, s: string): string
-      return s
-    enddef
+    assert_equal(0, obj0.Islocked("this.o1"))
+    lockvar l0o1
+    assert_equal(1, obj0.Islocked("this.o1"))
+    unlockvar l0o1
 
-    def Baz(c: C)
-      assert_equal('foo', Bar(10, c.Foo()))
-    enddef
+    lockvar l0c1[1]
 
-    var t = C.new()
-    Baz(t)
+    # static by class name member expr from same class
+    assert_equal(0, obj0.Islocked("C0.c1[0]"))
+    assert_equal(1, obj0.Islocked("C0.c1[1]"))
+    # static by bare name member expr from same class
+    assert_equal(0, obj0.Islocked("c1[0]"))
+    assert_equal(1, obj0.Islocked("c1[1]"))
+
+    # static by class name member expr from other class
+    assert_equal(0, obj2.Islocked("C0.c1[0]"))
+    assert_equal(1, obj2.Islocked("C0.c1[1]"))
+    # static by bare name member expr from other class
+    assert_equal(0, obj2.Islocked("c1[0]"))
+    assert_equal(0, obj2.Islocked("c1[1]"))
+
+
+    # static by bare name in same class
+    assert_equal(0, obj0.Islocked("c0"))
+    lockvar l0c0
+    assert_equal(1, obj0.Islocked("c0"))
+
+    #
+    # similar stuff, but use static method
+    #
+
+    unlockvar l0o0
+
+    # lock list, object func access through script var expr
+    assert_equal(0, C0.SIslocked("l[0].o0"))
+    assert_equal(0, C0.SIslocked("l[0].o0[2]"))
+    lockvar l0o0
+    assert_equal(1, C0.SIslocked("l[0].o0"))
+    assert_equal(1, C0.SIslocked("l[0].o0[2]"))
+
+    unlockvar l0o1
+
+    # can't access "this" from class method
+    try
+      C0.SIslocked("this.o1[0]")
+      call assert_0(1, '"C0.SIslocked("this.o1[0]")" should have failed')
+    catch
+      call assert_exception('E121: Undefined variable: this')
+    endtry
+
+    lockvar l0c1[1]
+
+    # static by class name member expr from same class
+    assert_equal(0, C0.SIslocked("C0.c1[0]"))
+    assert_equal(1, C0.SIslocked("C0.c1[1]"))
+    # static by bare name member expr from same class
+    assert_equal(0, C0.SIslocked("c1[0]"))
+    assert_equal(1, C0.SIslocked("c1[1]"))
+
+    # static by class name member expr from other class
+    assert_equal(0, C2.SIslocked("C0.c1[0]"))
+    assert_equal(1, C2.SIslocked("C0.c1[1]"))
+    # static by bare name member expr from other class
+    assert_equal(0, C2.SIslocked("c1[0]"))
+    assert_equal(0, C2.SIslocked("c1[1]"))
+
+
+    # static by bare name in same class
+    unlockvar l0c0
+    assert_equal(0, C0.SIslocked("c0"))
+    lockvar l0c0
+    assert_equal(1, C0.SIslocked("c0"))
   END
   v9.CheckSourceSuccess(lines)
 
+  # Check islocked class/object from various places.
   lines =<< trim END
     vim9script
 
     class C
-      static def Foo(): string
-        return 'foo'
+      def Islocked(arg: string): number
+        return islocked(arg)
+      enddef
+      static def SIslocked(arg: string): number
+        return islocked(arg)
       enddef
     endclass
+    var obj = C.new()
 
-    def Bar(a: number, s: string): string
-      return s
-    enddef
+    # object method
+    assert_equal(0, obj.Islocked("this"))
+    assert_equal(0, obj.Islocked("C"))
 
-    def Baz()
-      assert_equal('foo', Bar(10, C.Foo()))
-    enddef
+    # class method
+    ### assert_equal(0, C.SIslocked("this"))
+    assert_equal(0, C.SIslocked("C"))
 
-    Baz()
+    #script level
+    var v: number
+    v = islocked("C")
+    assert_equal(0, v)
+    v = islocked("obj")
+    assert_equal(0, v)
   END
   v9.CheckSourceSuccess(lines)
 enddef
 
-def Test_static_inheritence()
-  # subclasses get their own static copy
+def Test_lockvar_islocked_notfound()
+  # Try non-existent things
   var lines =<< trim END
     vim9script
 
-    class A
-      static var _svar: number
-      var _mvar: number
-      def new()
-        _svar = 1
-        this._mvar = 101
-      enddef
-      def AccessObject(): number
-        return this._mvar
+    class C
+      def Islocked(arg: string): number
+          return islocked(arg)
       enddef
-      def AccessStaticThroughObject(): number
-        return _svar
+      static def SIslocked(arg: string): number
+        return islocked(arg)
       enddef
     endclass
+    var obj = C.new()
+    assert_equal(-1, obj.Islocked("anywhere"))
+    assert_equal(-1, C.SIslocked("notanywhere"))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Something not found of the form "name1.name2" is an error
+  lines =<< trim END
+    vim9script
+
+    islocked("one.two")
+  END
+  v9.CheckSourceFailure(lines, 'E121: Undefined variable: one')
 
-    class B extends A
-      def new()
-        this._mvar = 102
+  lines =<< trim END
+    vim9script
+
+    class C
+      var val = { key: "value" }
+      def Islocked(arg: string): number
+          return islocked(arg)
       enddef
     endclass
+    var obj = C.new()
+    obj.Islocked("this.val.not_there"))
+  END
+  v9.CheckSourceFailure(lines, 'E716: Key not present in Dictionary: "not_there"')
 
-    class C extends B
-      def new()
-        this._mvar = 103
-      enddef
+  lines =<< trim END
+    vim9script
 
-      def AccessPrivateStaticThroughClassName(): number
-        assert_equal(1, A._svar)
-        return 444
+    class C
+      def Islocked(arg: string): number
+          return islocked(arg)
       enddef
     endclass
+    var obj = C.new()
+    obj.Islocked("this.notobjmember")
+  END
+  v9.CheckSourceFailure(lines, 'E1326: Variable "notobjmember" not found in object "C"')
 
-    var oa = A.new()
-    var ob = B.new()
-    var oc = C.new()
-    assert_equal(101, oa.AccessObject())
-    assert_equal(102, ob.AccessObject())
-    assert_equal(103, oc.AccessObject())
-
-    assert_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access protected variable "_svar" in class "A"')
+  # access a script variable through methods
+  lines =<< trim END
+    vim9script
 
-    # verify object properly resolves to correct static
-    assert_equal(1, oa.AccessStaticThroughObject())
-    assert_equal(1, ob.AccessStaticThroughObject())
-    assert_equal(1, oc.AccessStaticThroughObject())
+    var l = [1]
+    class C
+      def Islocked(arg: string): number
+          return islocked(arg)
+      enddef
+      static def SIslocked(arg: string): number
+        return islocked(arg)
+      enddef
+    endclass
+    var obj = C.new()
+    assert_equal(0, obj.Islocked("l"))
+    assert_equal(0, C.SIslocked("l"))
+    lockvar l
+    assert_equal(1, obj.Islocked("l"))
+    assert_equal(1, C.SIslocked("l"))
   END
   v9.CheckSourceSuccess(lines)
 enddef
 
-" Test for declaring duplicate object and class members
-def Test_dup_member_variable()
-  # Duplicate member variable
+" Test for a protected object method
+def Test_protected_object_method()
+  # Try calling a protected method using an object (at the script level)
   var lines =<< trim END
     vim9script
-    class C
-      var val = 10
-      var val = 20
+
+    class A
+      def _Foo(): number
+        return 1234
+      enddef
     endclass
+    var a = A.new()
+    a._Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9)
 
-  # Duplicate protected member variable
+  # Try calling a protected method using an object (from a def function)
   lines =<< trim END
     vim9script
-    class C
-      var _val = 10
-      var _val = 20
+
+    class A
+      def _Foo(): number
+        return 1234
+      enddef
     endclass
+    def T()
+      var a = A.new()
+      a._Foo()
+    enddef
+    T()
   END
-  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2)
 
-  # Duplicate public member variable
+  # Use a protected method from another object method (in script context)
   lines =<< trim END
     vim9script
-    class C
-      public var val = 10
-      public var val = 20
+
+    class A
+      def _Foo(): number
+        return 1234
+      enddef
+      def Bar(): number
+        return this._Foo()
+      enddef
     endclass
+    var a = A.new()
+    assert_equal(1234, a.Bar())
   END
-  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
+  v9.CheckSourceSuccess(lines)
 
-  # Duplicate protected member variable
+  # Use a protected method from another object method (def function context)
   lines =<< trim END
     vim9script
-    class C
-      var val = 10
-      var _val = 20
+
+    class A
+      def _Foo(): number
+        return 1234
+      enddef
+      def Bar(): number
+        return this._Foo()
+      enddef
     endclass
+    def T()
+      var a = A.new()
+      assert_equal(1234, a.Bar())
+    enddef
+    T()
   END
-  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4)
+  v9.CheckSourceSuccess(lines)
 
-  # Duplicate public and protected member variable
+  # Try calling a protected method without the "this" prefix
   lines =<< trim END
     vim9script
-    class C
-      var _val = 20
-      public var val = 10
+
+    class A
+      def _Foo(): number
+        return 1234
+      enddef
+      def Bar(): number
+        return _Foo()
+      enddef
     endclass
+    var a = A.new()
+    a.Bar()
   END
-  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
+  v9.CheckSourceFailure(lines, 'E117: Unknown function: _Foo', 1)
 
-  # Duplicate class member variable
+  # Try calling a protected method using the class name
   lines =<< trim END
     vim9script
-    class C
-      static var s: string = "abc"
-      static var _s: string = "def"
+
+    class A
+      def _Foo(): number
+        return 1234
+      enddef
     endclass
+    A._Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8)
 
-  # Duplicate public and protected class member variable
+  # Define two protected methods with the same name
   lines =<< trim END
     vim9script
-    class C
-      public static var s: string = "abc"
-      static var _s: string = "def"
+
+    class A
+      def _Foo()
+      enddef
+      def _Foo()
+      enddef
     endclass
+    var a = A.new()
   END
-  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4)
+  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7)
 
-  # Duplicate class and object member variable
+  # Define a protected method and a object method with the same name
   lines =<< trim END
     vim9script
-    class C
-      static var val = 10
-      var val = 20
-      def new()
+
+    class A
+      def _Foo()
+      enddef
+      def Foo()
       enddef
     endclass
-    var c = C.new()
-    assert_equal(10, C.val)
-    assert_equal(20, c.val)
+    var a = A.new()
   END
-  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
+  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7)
 
-  # Duplicate object member variable in a derived class
+  # Define an object method and a protected method with the same name
   lines =<< trim END
     vim9script
+
     class A
-      var val = 10
-    endclass
-    class B extends A
-    endclass
-    class C extends B
-      var val = 20
+      def Foo()
+      enddef
+      def _Foo()
+      enddef
     endclass
+    var a = A.new()
   END
-  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9)
+  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7)
 
-  # Duplicate object protected member variable in a derived class
+  # Call a public method and a protected method from a protected method
   lines =<< trim END
     vim9script
+
     class A
-      var _val = 10
+      def Foo(): number
+        return 100
+      enddef
+      def _Bar(): number
+        return 200
+      enddef
+      def _Baz()
+        assert_equal(100, this.Foo())
+        assert_equal(200, this._Bar())
+      enddef
+      def T()
+        this._Baz()
+      enddef
     endclass
-    class B extends A
+    var a = A.new()
+    a.T()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Try calling a protected method from another class
+  lines =<< trim END
+    vim9script
+
+    class A
+      def _Foo(): number
+        return 100
+      enddef
     endclass
-    class C extends B
-      var _val = 20
+    class B
+      def Foo(): number
+        var a = A.new()
+        a._Foo()
+      enddef
     endclass
+    var b = B.new()
+    b.Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2)
 
-  # Duplicate object protected member variable in a derived class
+  # Call a protected object method from a child class object method
   lines =<< trim END
     vim9script
     class A
-      var val = 10
+      def _Foo(): number
+        return 1234
+      enddef
     endclass
     class B extends A
+      def Bar()
+      enddef
     endclass
     class C extends B
-      var _val = 20
+      def Baz(): number
+        return this._Foo()
+      enddef
     endclass
+    var c = C.new()
+    assert_equal(1234, c.Baz())
   END
-  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9)
+  v9.CheckSourceSuccess(lines)
 
-  # Duplicate object member variable in a derived class
+  # Call a protected object method from a child class object
   lines =<< trim END
     vim9script
     class A
-      var _val = 10
+      def _Foo(): number
+        return 1234
+      enddef
     endclass
     class B extends A
+      def Bar()
+      enddef
     endclass
     class C extends B
-      var val = 20
+      def Baz(): number
+      enddef
     endclass
+    var c = C.new()
+    assert_equal(1234, c._Foo())
   END
-  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 16)
 
-  # Two member variables with a common prefix
+  # Using "_" prefix in a method name should fail outside of a class
   lines =<< trim END
     vim9script
-    class A
-      public static var svar2: number
-      public static var svar: number
-    endclass
-  END
-  v9.CheckSourceSuccess(lines)
-enddef
-
-" Test for accessing a protected member outside a class in a def function
-def Test_protected_member_access_outside_class()
-  # try to modify a protected instance variable from a def function
-  var lines =<< trim END
-    vim9script
-    class A
-      var _val = 10
-    endclass
-    def T()
-      var a = A.new()
-      a._val = 20
+    def _Foo(): number
+      return 1234
     enddef
-    T()
+    var a = _Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 2)
+  v9.CheckSourceFailure(lines, 'E1267: Function name must start with a capital: _Foo(): number', 2)
 
-  # access a non-existing protected instance variable from a def function
+  # Test for initializing a protected funcref instance variable to a protected
+  # class method from another class
   lines =<< trim END
     vim9script
+
     class A
-      var _val = 10
+      static def _Internal(): string
+      enddef
     endclass
-    def T()
-      var a = A.new()
-      a._a = 1
-    enddef
-    T()
+
+    class B
+      var Fn: func = A._Internal
+    endclass
+    var b = B.new()
   END
-  v9.CheckSourceFailure(lines, 'E1326: Variable "_a" not found in object "A"', 2)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 1)
+enddef
 
-  # try to read a protected class variable from a def function using an instance
-  lines =<< trim END
+" Test for an protected class method
+def Test_protected_class_method()
+  # Try calling a class protected method (at the script level)
+  var lines =<< trim END
     vim9script
+
     class A
-      static var _val = 10
+      static def _Foo(): number
+        return 1234
+      enddef
     endclass
-    def T()
-      var a = A.new()
-      var x = a._val
-    enddef
-    T()
+    A._Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8)
 
-  # try to modify a protected class variable from a def function using an
-  # instance
+  # Try calling a class protected method (from a def function)
   lines =<< trim END
     vim9script
+
     class A
-      static var _val = 10
+      static def _Foo(): number
+        return 1234
+      enddef
     endclass
     def T()
-      var a = A.new()
-      a._val = 3
+      A._Foo()
     enddef
     T()
   END
-  v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
 
-  # try to read a protected class variable from a def function using the class
+  # Try calling a class protected method using an object (at the script level)
   lines =<< trim END
     vim9script
+
     class A
-      static var _val = 10
+      static def _Foo(): number
+        return 1234
+      enddef
     endclass
-    def T()
-      var x = A._val
-    enddef
-    T()
+    var a = A.new()
+    a._Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9)
 
-  # try to modify a protected class variable from a def function using the class
+  # Try calling a class protected method using an object (from a def function)
   lines =<< trim END
     vim9script
+
     class A
-      static var _val = 10
+      static def _Foo(): number
+        return 1234
+      enddef
     endclass
     def T()
-      A._val = 3
+      var a = A.new()
+      a._Foo()
     enddef
     T()
   END
-  v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 2)
 
-  # initialize a protected class variable using a protected class variable
-  # from another class
+  # Use a class protected method from an object method
   lines =<< trim END
     vim9script
+
     class A
-      static var _aval = 10
-    endclass
-    class B
-      static var _bval = A._aval
+      static def _Foo(): number
+        return 1234
+      enddef
+      def Bar()
+        assert_equal(1234, _Foo())
+      enddef
     endclass
+    var a = A.new()
+    a.Bar()
   END
-  v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 7)
+  v9.CheckSourceSuccess(lines)
 
-  # initialize a protected instance variable using a protected class variable
-  # from another class
+  # Use a class protected method from another class protected method without the
+  # class name prefix.
   lines =<< trim END
     vim9script
-    class A
-      static var _aval = 10
-    endclass
-    class B
-      var _bval = A._aval
-    endclass
-    var b = B.new()
-  END
-  v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 1)
-enddef
 
-" Test for changing the member access of an interface in a implementation class
-def Test_change_interface_member_access()
-  var lines =<< trim END
-    vim9script
-    interface A
-      var val: number
-    endinterface
-    class B implements A
-      public var val = 10
+    class A
+      static def _Foo1(): number
+        return 1234
+      enddef
+      static def _Foo2()
+        assert_equal(1234, _Foo1())
+      enddef
+      def Bar()
+        _Foo2()
+      enddef
     endclass
+    var a = A.new()
+    a.Bar()
   END
-  v9.CheckSourceFailure(lines, 'E1367: Access level of variable "val" of interface "A" is different', 7)
+  v9.CheckSourceSuccess(lines)
 
+  # Declare a class method and a class protected method with the same name
   lines =<< trim END
     vim9script
-    interface A
-      var val: number
-    endinterface
-    class B implements A
-      public var val = 10
-    endclass
-  END
-  v9.CheckSourceFailure(lines, 'E1367: Access level of variable "val" of interface "A" is different', 7)
-enddef
 
-" Test for trying to change a readonly member from a def function
-def Test_readonly_member_change_in_def_func()
-  var lines =<< trim END
-    vim9script
     class A
-      var val: number
+      static def _Foo()
+      enddef
+      static def Foo()
+      enddef
     endclass
-    def T()
-      var a = A.new()
-      a.val = 20
-    enddef
-    T()
+    var a = A.new()
   END
-  v9.CheckSourceFailure(lines, 'E1335: Variable "val" in class "A" is not writable', 2)
-enddef
+  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7)
 
-" Test for reading and writing a class member from a def function
-def Test_modify_class_member_from_def_function()
-  var lines =<< trim END
+  # Try calling a class protected method from another class
+  lines =<< trim END
     vim9script
-    class A
-      var var1: number = 10
-      public static var var2: list<number> = [1, 2]
-      public static var var3: dict<number> = {a: 1, b: 2}
-      static var _priv_var4: number = 40
-    endclass
-    def T()
-      assert_equal([1, 2], A.var2)
-      assert_equal({a: 1, b: 2}, A.var3)
-      A.var2 = [3, 4]
-      A.var3 = {c: 3, d: 4}
-      assert_equal([3, 4], A.var2)
-      assert_equal({c: 3, d: 4}, A.var3)
-      assert_fails('echo A._priv_var4', 'E1333: Cannot access protected variable "_priv_var4" in class "A"')
-    enddef
-    T()
-  END
-  v9.CheckSourceSuccess(lines)
-enddef
 
-" Test for accessing a class member variable using an object
-def Test_class_variable_access_using_object()
-  var lines =<< trim END
-    vim9script
     class A
-      public static var svar1: list<number> = [1]
-      public static var svar2: list<number> = [2]
-    endclass
-
-    A.svar1->add(3)
-    A.svar2->add(4)
-    assert_equal([1, 3], A.svar1)
-    assert_equal([2, 4], A.svar2)
-
-    def Foo()
-      A.svar1->add(7)
-      A.svar2->add(8)
-      assert_equal([1, 3, 7], A.svar1)
-      assert_equal([2, 4, 8], A.svar2)
-    enddef
-    Foo()
+      static def _Foo(): number
+        return 1234
+      enddef
+    endclass
+    class B
+      def Foo(): number
+        return A._Foo()
+      enddef
+    endclass
+    var b = B.new()
+    assert_equal(1234, b.Foo())
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
 
-  # Cannot read from a class variable using an object in script context
+  # Call a protected class method from a child class object method
   lines =<< trim END
     vim9script
     class A
-      public var var1: number
-      public static var svar2: list<number> = [1]
+      static def _Foo(): number
+        return 1234
+      enddef
     endclass
-
-    var a = A.new()
-    echo a.svar2
+    class B extends A
+      def Bar()
+      enddef
+    endclass
+    class C extends B
+      def Baz(): number
+        return A._Foo()
+      enddef
+    endclass
+    var c = C.new()
+    assert_equal(1234, c.Baz())
   END
-  v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
 
-  # Cannot write to a class variable using an object in script context
+  # Call a protected class method from a child class protected class method
   lines =<< trim END
     vim9script
     class A
-      public var var1: number
-      public static var svar2: list<number> = [1]
+      static def _Foo(): number
+        return 1234
+      enddef
     endclass
-
-    var a = A.new()
-    a.svar2 = [2]
+    class B extends A
+      def Bar()
+      enddef
+    endclass
+    class C extends B
+      static def Baz(): number
+        return A._Foo()
+      enddef
+    endclass
+    assert_equal(1234, C.Baz())
   END
-  v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
 
-  # Cannot read from a class variable using an object in def method context
+  # Call a protected class method from a child class object
   lines =<< trim END
     vim9script
     class A
-      public var var1: number
-      public static var svar2: list<number> = [1]
+      static def _Foo(): number
+        return 1234
+      enddef
     endclass
-
-    def T()
-      var a = A.new()
-      echo a.svar2
-    enddef
-    T()
+    class B extends A
+      def Bar()
+      enddef
+    endclass
+    class C extends B
+      def Baz(): number
+      enddef
+    endclass
+    var c = C.new()
+    assert_equal(1234, C._Foo())
   END
-  v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2)
+  v9.CheckSourceFailure(lines, 'E1325: Method "_Foo" not found in class "C"', 16)
 
-  # Cannot write to a class variable using an object in def method context
+  # Test for initializing a protected funcref class variable to a protected
+  # class method from another class
   lines =<< trim END
     vim9script
+
     class A
-      public var var1: number
-      public static var svar2: list<number> = [1]
+      static def _Internal(): string
+      enddef
     endclass
 
-    def T()
-      var a = A.new()
-      a.svar2 = [2]
-    enddef
-    T()
+    class B
+      static var Fn: func = A._Internal
+    endclass
   END
-  v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2)
+  v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 10)
 enddef
 
-" Test for using a interface method using a child object
-def Test_interface_method_from_child()
+" Test for using the return value of a class/object method as a function
+" argument.
+def Test_objmethod_funcarg()
   var lines =<< trim END
     vim9script
 
-    interface A
-      def Foo(): string
-    endinterface
-
-    class B implements A
+    class C
       def Foo(): string
         return 'foo'
       enddef
     endclass
 
-    class C extends B
-      def Bar(): string
-        return 'bar'
-      enddef
-    endclass
-
-    def T1(a: A)
-      assert_equal('foo', a.Foo())
+    def Bar(a: number, s: string): string
+      return s
     enddef
 
-    def T2(b: B)
-      assert_equal('foo', b.Foo())
+    def Baz(c: C)
+      assert_equal('foo', Bar(10, c.Foo()))
     enddef
 
-    var c = C.new()
-    T1(c)
-    T2(c)
+    var t = C.new()
+    Baz(t)
   END
   v9.CheckSourceSuccess(lines)
-enddef
 
-" Test for using an interface method using a child object when it is overridden
-" by the child class.
-def Test_interface_overridden_method_from_child()
-  var lines =<< trim END
+  lines =<< trim END
     vim9script
 
-    interface A
-      def Foo(): string
-    endinterface
-
-    class B implements A
-      def Foo(): string
-        return 'b-foo'
-      enddef
-    endclass
-
-    class C extends B
-      def Bar(): string
-        return 'bar'
-      enddef
-      def Foo(): string
-        return 'c-foo'
+    class C
+      static def Foo(): string
+        return 'foo'
       enddef
     endclass
 
-    def T1(a: A)
-      assert_equal('c-foo', a.Foo())
+    def Bar(a: number, s: string): string
+      return s
     enddef
 
-    def T2(b: B)
-      assert_equal('c-foo', b.Foo())
+    def Baz()
+      assert_equal('foo', Bar(10, C.Foo()))
     enddef
 
-    var c = C.new()
-    T1(c)
-    T2(c)
+    Baz()
   END
   v9.CheckSourceSuccess(lines)
 enddef
 
-" Test for interface inheritance
-def Test_interface_inheritance()
+def Test_static_inheritence()
+  # subclasses get their own static copy
   var lines =<< trim END
     vim9script
 
-    interface A
-      def A_Fn(): string
-    endinterface
-
-    interface B
-      def B_Fn(): string
-    endinterface
-
-    interface C
-      def C_Fn(): string
-    endinterface
-
-    class C1 implements A
-      def A_Fn(): string
-        return 'c1-a'
+    class A
+      static var _svar: number
+      var _mvar: number
+      def new()
+        _svar = 1
+        this._mvar = 101
       enddef
-    endclass
-
-    class C2 extends C1 implements B
-      def B_Fn(): string
-        return 'c2-b'
+      def AccessObject(): number
+        return this._mvar
       enddef
-      def A_Fn(): string
-        return 'c2-a'
+      def AccessStaticThroughObject(): number
+        return _svar
       enddef
     endclass
 
-    class C3 extends C2 implements C
-      def C_Fn(): string
-        return 'c3-c'
-      enddef
-      def A_Fn(): string
-        return 'c3-a'
-      enddef
-      def B_Fn(): string
-        return 'c3-b'
+    class B extends A
+      def new()
+        this._mvar = 102
       enddef
     endclass
 
-    def T1(a: A, s: string)
-      assert_equal(s, a.A_Fn())
-    enddef
-
-    def T2(b: B, s: string)
-      assert_equal(s, b.B_Fn())
-    enddef
-
-    def T3(c: C, s: string)
-      assert_equal(s, c.C_Fn())
-    enddef
-
-    def T4(c1: C1)
-      T1(c1, 'c3-a')
-    enddef
-
-    def T5(c2: C2)
-      T1(c2, 'c3-a')
-      T2(c2, 'c3-b')
-    enddef
-
-    def T6(c3: C3)
-      T1(c3, 'c3-a')
-      T2(c3, 'c3-b')
-      T3(c3, 'c3-c')
-    enddef
-
-    var o3 = C3.new()
-    T4(o3)
-    T5(o3)
-    T6(o3)
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # Both the parent and child classes implement the same interface
-  lines =<< trim END
-    vim9script
-
-    interface I
-      def Foo(): string
-    endinterface
-
-    class A implements I
-      def Foo(): string
-        return 'A-foo'
+    class C extends B
+      def new()
+        this._mvar = 103
       enddef
-    endclass
 
-    class B implements I
-      def Foo(): string
-        return 'B-foo'
+      def AccessPrivateStaticThroughClassName(): number
+        assert_equal(1, A._svar)
+        return 444
       enddef
     endclass
 
-    def Bar(i1: I): string
-      return i1.Foo()
-    enddef
+    var oa = A.new()
+    var ob = B.new()
+    var oc = C.new()
+    assert_equal(101, oa.AccessObject())
+    assert_equal(102, ob.AccessObject())
+    assert_equal(103, oc.AccessObject())
 
-    var b = B.new()
-    assert_equal('B-foo', Bar(b))
-  END
-  v9.CheckSourceSuccess(lines)
-enddef
+    assert_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access protected variable "_svar" in class "A"')
 
-" Test for abstract methods
-def Test_abstract_method()
-  # Use two abstract methods
-  var lines =<< trim END
-    vim9script
-    abstract class A
-      def M1(): number
-        return 10
-      enddef
-      abstract def M2(): number
-      abstract def M3(): number
-    endclass
-    class B extends A
-      def M2(): number
-        return 20
-      enddef
-      def M3(): number
-        return 30
-      enddef
-    endclass
-    var b = B.new()
-    assert_equal([10, 20, 30], [b.M1(), b.M2(), b.M3()])
+    # verify object properly resolves to correct static
+    assert_equal(1, oa.AccessStaticThroughObject())
+    assert_equal(1, ob.AccessStaticThroughObject())
+    assert_equal(1, oc.AccessStaticThroughObject())
   END
   v9.CheckSourceSuccess(lines)
+enddef
 
-  # Don't define an abstract method
-  lines =<< trim END
-    vim9script
-    abstract class A
-      abstract def Foo()
-    endclass
-    class B extends A
+" Test for declaring duplicate object and class members
+def Test_dup_member_variable()
+  # Duplicate member variable
+  var lines =<< trim END
+    vim9script
+    class C
+      var val = 10
+      var val = 20
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented', 6)
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
 
-  # Use abstract method in a concrete class
+  # Duplicate protected member variable
   lines =<< trim END
     vim9script
-    class A
-      abstract def Foo()
-    endclass
-    class B extends A
+    class C
+      var _val = 10
+      var _val = 20
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class', 3)
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4)
 
-  # Use abstract method in an interface
+  # Duplicate public member variable
   lines =<< trim END
     vim9script
-    interface A
-      abstract def Foo()
-    endinterface
-    class B implements A
-      def Foo()
-      enddef
+    class C
+      public var val = 10
+      public var val = 20
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
 
-  # Use abstract static method in an interface
+  # Duplicate protected member variable
   lines =<< trim END
     vim9script
-    interface A
-      abstract static def Foo()
-      enddef
-    endinterface
+    class C
+      var val = 10
+      var _val = 20
+    endclass
   END
-  v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4)
 
-  # Use abstract static variable in an interface
+  # Duplicate public and protected member variable
   lines =<< trim END
     vim9script
-    interface A
-      abstract static foo: number = 10
-    endinterface
+    class C
+      var _val = 20
+      public var val = 10
+    endclass
   END
-  v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
 
-  # Abbreviate the "abstract" keyword
+  # Duplicate class member variable
   lines =<< trim END
     vim9script
-    class A
-      abs def Foo()
+    class C
+      static var s: string = "abc"
+      static var _s: string = "def"
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs def Foo()', 3)
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4)
 
-  # Use "abstract" with a member variable
+  # Duplicate public and protected class member variable
   lines =<< trim END
     vim9script
-    abstract class A
-      abstract this.val = 10
+    class C
+      public static var s: string = "abc"
+      static var _s: string = "def"
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3)
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4)
 
-  # Use a static abstract method
+  # Duplicate class and object member variable
   lines =<< trim END
     vim9script
-    abstract class A
-      abstract static def Foo(): number
+    class C
+      static var val = 10
+      var val = 20
+      def new()
+      enddef
     endclass
+    var c = C.new()
+    assert_equal(10, C.val)
+    assert_equal(20, c.val)
   END
-  v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3)
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
 
-  # Type mismatch between abstract method and concrete method
+  # Duplicate object member variable in a derived class
   lines =<< trim END
     vim9script
-    abstract class A
-      abstract def Foo(a: string, b: number): list<number>
+    class A
+      var val = 10
     endclass
     class B extends A
-      def Foo(a: number, b: string): list<string>
-        return []
-      enddef
+    endclass
+    class C extends B
+      var val = 20
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1383: Method "Foo": type mismatch, expected func(string, number): list<number> but got func(number, string): list<string>', 9)
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9)
 
-  # Invoke an abstract method from a def function
+  # Duplicate object protected member variable in a derived class
   lines =<< trim END
     vim9script
-    abstract class A
-      abstract def Foo(): list<number>
+    class A
+      var _val = 10
     endclass
     class B extends A
-      def Foo(): list<number>
-        return [3, 5]
-      enddef
     endclass
-    def Bar(c: B)
-      assert_equal([3, 5], c.Foo())
-    enddef
-    var b = B.new()
-    Bar(b)
+    class C extends B
+      var _val = 20
+    endclass
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9)
 
-  # Use a static method in an abstract class
+  # Duplicate object protected member variable in a derived class
   lines =<< trim END
     vim9script
-    abstract class A
-      static def Foo(): string
-        return 'foo'
-      enddef
+    class A
+      var val = 10
+    endclass
+    class B extends A
+    endclass
+    class C extends B
+      var _val = 20
     endclass
-    assert_equal('foo', A.Foo())
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9)
 
-    # Invoke method returning a value through the abstract class. See #15432.
+  # Duplicate object member variable in a derived class
   lines =<< trim END
     vim9script
-
-    abstract class A
-        abstract def String(): string
+    class A
+      var _val = 10
     endclass
-
     class B extends A
-        def String(): string
-            return 'B'
-        enddef
     endclass
-
-    def F(o: A)
-        assert_equal('B', o.String())
-    enddef
-    F(B.new())
+    class C extends B
+      var val = 20
+    endclass
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9)
 
-  # Invoke abstract method returning a value does not compile
+  # Two member variables with a common prefix
   lines =<< trim END
     vim9script
-
-    abstract class A
-      abstract def String(): string
-        return 'X'
-      enddef
+    class A
+      public static var svar2: number
+      public static var svar: number
     endclass
   END
-  v9.CheckScriptFailure(lines, "E1318: Not a valid command in a class: return 'X'")
+  v9.CheckSourceSuccess(lines)
 enddef
 
-" Test for calling a class method from a subclass
-def Test_class_method_call_from_subclass()
-  # class method call from a subclass
+" Test for accessing a protected member outside a class in a def function
+def Test_protected_member_access_outside_class()
+  # try to modify a protected instance variable from a def function
   var lines =<< trim END
     vim9script
+    class A
+      var _val = 10
+    endclass
+    def T()
+      var a = A.new()
+      a._val = 20
+    enddef
+    T()
+  END
+  v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 2)
 
+  # access a non-existing protected instance variable from a def function
+  lines =<< trim END
+    vim9script
     class A
-      static def Foo()
-        echo "foo"
-      enddef
+      var _val = 10
     endclass
+    def T()
+      var a = A.new()
+      a._a = 1
+    enddef
+    T()
+  END
+  v9.CheckSourceFailure(lines, 'E1326: Variable "_a" not found in object "A"', 2)
 
-    class B extends A
-      def Bar()
-        Foo()
-      enddef
+  # try to read a protected class variable from a def function using an instance
+  lines =<< trim END
+    vim9script
+    class A
+      static var _val = 10
     endclass
+    def T()
+      var a = A.new()
+      var x = a._val
+    enddef
+    T()
+  END
+  v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2)
 
-    var b = B.new()
-    b.Bar()
+  # try to modify a protected class variable from a def function using an
+  # instance
+  lines =<< trim END
+    vim9script
+    class A
+      static var _val = 10
+    endclass
+    def T()
+      var a = A.new()
+      a._val = 3
+    enddef
+    T()
   END
-  v9.CheckSourceFailure(lines, 'E1384: Class method "Foo" accessible only inside class "A"', 1)
-enddef
+  v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2)
 
-" Test for calling a class method using an object in a def function context and
-" script context.
-def Test_class_method_call_using_object()
-  # script context
-  var lines =<< trim END
+  # try to read a protected class variable from a def function using the class
+  lines =<< trim END
     vim9script
     class A
-      static def Foo(): list<string>
-        return ['a', 'b']
-      enddef
-      def Bar()
-        assert_equal(['a', 'b'], A.Foo())
-        assert_equal(['a', 'b'], Foo())
-      enddef
+      static var _val = 10
+    endclass
+    def T()
+      var x = A._val
+    enddef
+    T()
+  END
+  v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1)
+
+  # try to modify a protected class variable from a def function using the class
+  lines =<< trim END
+    vim9script
+    class A
+      static var _val = 10
     endclass
-
     def T()
-      assert_equal(['a', 'b'], A.Foo())
-      var t_a = A.new()
-      t_a.Bar()
+      A._val = 3
     enddef
-
-    assert_equal(['a', 'b'], A.Foo())
-    var a = A.new()
-    a.Bar()
     T()
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1)
 
-  # script context
+  # initialize a protected class variable using a protected class variable
+  # from another class
   lines =<< trim END
     vim9script
     class A
-      static def Foo(): string
-        return 'foo'
-      enddef
+      static var _aval = 10
+    endclass
+    class B
+      static var _bval = A._aval
     endclass
-
-    var a = A.new()
-    assert_equal('foo', a.Foo())
   END
-  v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 9)
+  v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 7)
 
-  # def function context
+  # initialize a protected instance variable using a protected class variable
+  # from another class
   lines =<< trim END
     vim9script
     class A
-      static def Foo(): string
-        return 'foo'
-      enddef
+      static var _aval = 10
     endclass
+    class B
+      var _bval = A._aval
+    endclass
+    var b = B.new()
+  END
+  v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 1)
+enddef
 
+" Test for trying to change a readonly member from a def function
+def Test_readonly_member_change_in_def_func()
+  var lines =<< trim END
+    vim9script
+    class A
+      var val: number
+    endclass
     def T()
       var a = A.new()
-      assert_equal('foo', a.Foo())
+      a.val = 20
     enddef
     T()
   END
-  v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 2)
+  v9.CheckSourceFailure(lines, 'E1335: Variable "val" in class "A" is not writable', 2)
 enddef
 
-def Test_class_variable()
+" Test for reading and writing a class member from a def function
+def Test_modify_class_member_from_def_function()
   var lines =<< trim END
     vim9script
-
     class A
-      public static var val: number = 10
-      static def ClassFunc()
-        assert_equal(10, val)
-      enddef
-      def ObjFunc()
-        assert_equal(10, val)
-      enddef
+      var var1: number = 10
+      public static var var2: list<number> = [1, 2]
+      public static var var3: dict<number> = {a: 1, b: 2}
+      static var _priv_var4: number = 40
     endclass
+    def T()
+      assert_equal([1, 2], A.var2)
+      assert_equal({a: 1, b: 2}, A.var3)
+      A.var2 = [3, 4]
+      A.var3 = {c: 3, d: 4}
+      assert_equal([3, 4], A.var2)
+      assert_equal({c: 3, d: 4}, A.var3)
+      assert_fails('echo A._priv_var4', 'E1333: Cannot access protected variable "_priv_var4" in class "A"')
+    enddef
+    T()
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
 
-    class B extends A
+" Test for accessing a class member variable using an object
+def Test_class_variable_access_using_object()
+  var lines =<< trim END
+    vim9script
+    class A
+      public static var svar1: list<number> = [1]
+      public static var svar2: list<number> = [2]
     endclass
 
-    assert_equal(10, A.val)
-    A.ClassFunc()
-    var a = A.new()
-    a.ObjFunc()
-    var b = B.new()
-    b.ObjFunc()
+    A.svar1->add(3)
+    A.svar2->add(4)
+    assert_equal([1, 3], A.svar1)
+    assert_equal([2, 4], A.svar2)
 
-    def T1(a1: A)
-      a1.ObjFunc()
-      A.ClassFunc()
+    def Foo()
+      A.svar1->add(7)
+      A.svar2->add(8)
+      assert_equal([1, 3, 7], A.svar1)
+      assert_equal([2, 4, 8], A.svar2)
     enddef
-    T1(b)
-
-    A.val = 20
-    assert_equal(20, A.val)
+    Foo()
   END
   v9.CheckSourceSuccess(lines)
 
-  # Modifying a parent class variable from a child class method
+  # Cannot read from a class variable using an object in script context
   lines =<< trim END
     vim9script
-
     class A
-      static var val: number = 10
+      public var var1: number
+      public static var svar2: list<number> = [1]
     endclass
 
-    class B extends A
-      static def ClassFunc()
-        val = 20
-      enddef
-    endclass
-    B.ClassFunc()
+    var a = A.new()
+    echo a.svar2
   END
-  v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
+  v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8)
 
-  # Reading a parent class variable from a child class method
+  # Cannot write to a class variable using an object in script context
   lines =<< trim END
     vim9script
-
     class A
-      static var val: number = 10
+      public var var1: number
+      public static var svar2: list<number> = [1]
     endclass
 
-    class B extends A
-      static def ClassFunc()
-        var i = val
-      enddef
-    endclass
-    B.ClassFunc()
+    var a = A.new()
+    a.svar2 = [2]
   END
-  v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
+  v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8)
 
-  # Modifying a parent class variable from a child object method
+  # Cannot read from a class variable using an object in def method context
   lines =<< trim END
     vim9script
-
     class A
-      static var val: number = 10
+      public var var1: number
+      public static var svar2: list<number> = [1]
     endclass
 
-    class B extends A
-      def ObjFunc()
-        val = 20
-      enddef
-    endclass
-    var b = B.new()
-    b.ObjFunc()
+    def T()
+      var a = A.new()
+      echo a.svar2
+    enddef
+    T()
   END
-  v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
+  v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2)
 
-  # Reading a parent class variable from a child object method
+  # Cannot write to a class variable using an object in def method context
   lines =<< trim END
     vim9script
-
     class A
-      static var val: number = 10
+      public var var1: number
+      public static var svar2: list<number> = [1]
     endclass
 
+    def T()
+      var a = A.new()
+      a.svar2 = [2]
+    enddef
+    T()
+  END
+  v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2)
+enddef
+
+" Test for abstract methods
+def Test_abstract_method()
+  # Use two abstract methods
+  var lines =<< trim END
+    vim9script
+    abstract class A
+      def M1(): number
+        return 10
+      enddef
+      abstract def M2(): number
+      abstract def M3(): number
+    endclass
     class B extends A
-      def ObjFunc()
-        var i = val
+      def M2(): number
+        return 20
+      enddef
+      def M3(): number
+        return 30
       enddef
     endclass
     var b = B.new()
-    b.ObjFunc()
+    assert_equal([10, 20, 30], [b.M1(), b.M2(), b.M3()])
   END
-  v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
+  v9.CheckSourceSuccess(lines)
 
-  # Modifying a class variable using an object at script level
+  # Don't define an abstract method
   lines =<< trim END
     vim9script
-
-    class A
-      static var val: number = 10
+    abstract class A
+      abstract def Foo()
     endclass
-    var a = A.new()
-    a.val = 20
-  END
-  v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7)
-
-  # Reading a class variable using an object at script level
-  lines =<< trim END
-    vim9script
-
-    class A
-      static var val: number = 10
+    class B extends A
     endclass
-    var a = A.new()
-    var i = a.val
   END
-  v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7)
+  v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented', 6)
 
-  # Modifying a class variable using an object at function level
+  # Use abstract method in a concrete class
   lines =<< trim END
     vim9script
-
     class A
-      static var val: number = 10
+      abstract def Foo()
+    endclass
+    class B extends A
     endclass
-
-    def T()
-      var a = A.new()
-      a.val = 20
-    enddef
-    T()
   END
-  v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2)
+  v9.CheckSourceFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class', 3)
 
-  # Reading a class variable using an object at function level
+  # Use abstract method in an interface
   lines =<< trim END
     vim9script
-
-    class A
-      static var val: number = 10
-    endclass
-    def T()
-      var a = A.new()
-      var i = a.val
-    enddef
-    T()
+    interface A
+      abstract def Foo()
+    endinterface
+    class B implements A
+      def Foo()
+      enddef
+    endclass
   END
-  v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2)
+  v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)
 
-  # Use old implicit var declaration syntax (without initialization)
+  # Use abstract static method in an interface
   lines =<< trim END
     vim9script
-
-    class A
-      static val: number
-    endclass
+    interface A
+      abstract static def Foo()
+      enddef
+    endinterface
   END
-  v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)
+  v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)
 
-  # Use old implicit var declaration syntax (with initialization)
+  # Use abstract static variable in an interface
   lines =<< trim END
     vim9script
-
-    class A
-      static val: number = 10
-    endclass
+    interface A
+      abstract static foo: number = 10
+    endinterface
   END
-  v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)
+  v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)
 
-  # Use old implicit var declaration syntax (type inferred)
+  # Abbreviate the "abstract" keyword
   lines =<< trim END
     vim9script
-
     class A
-      static val = 10
+      abs def Foo()
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)
+  v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs def Foo()', 3)
 
-  # Missing ":var" in "var" class variable declaration (without initialization)
+  # Use "abstract" with a member variable
   lines =<< trim END
     vim9script
-
-    class A
-      static var: number
+    abstract class A
+      abstract this.val = 10
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number', 4)
+  v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3)
 
-  # Missing ":var" in "var" class variable declaration (with initialization)
+  # Use a static abstract method
   lines =<< trim END
     vim9script
-
-    class A
-      static var: number = 10
+    abstract class A
+      abstract static def Foo(): number
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number = 10', 4)
+  v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3)
 
-  # Missing ":var" in "var" class variable declaration (type inferred)
+  # Type mismatch between abstract method and concrete method
   lines =<< trim END
     vim9script
-
-    class A
-      static var = 10
+    abstract class A
+      abstract def Foo(a: string, b: number): list<number>
+    endclass
+    class B extends A
+      def Foo(a: number, b: string): list<string>
+        return []
+      enddef
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var = 10', 4)
-
-enddef
+  v9.CheckSourceFailure(lines, 'E1383: Method "Foo": type mismatch, expected func(string, number): list<number> but got func(number, string): list<string>', 9)
 
-" Test for using a duplicate class method and class variable in a child class
-def Test_dup_class_member()
-  # duplicate class variable, class method and overridden object method
-  var lines =<< trim END
+  # Invoke an abstract method from a def function
+  lines =<< trim END
     vim9script
-    class A
-      static var sval = 100
-      static def Check()
-        assert_equal(100, sval)
-      enddef
-      def GetVal(): number
-        return sval
-      enddef
+    abstract class A
+      abstract def Foo(): list<number>
     endclass
-
     class B extends A
-      static var sval = 200
-      static def Check()
-        assert_equal(200, sval)
-      enddef
-      def GetVal(): number
-        return sval
+      def Foo(): list<number>
+        return [3, 5]
       enddef
     endclass
-
-    def T1(aa: A): number
-      return aa.GetVal()
-    enddef
-
-    def T2(bb: B): number
-      return bb.GetVal()
+    def Bar(c: B)
+      assert_equal([3, 5], c.Foo())
     enddef
-
-    assert_equal(100, A.sval)
-    assert_equal(200, B.sval)
-    var a = A.new()
-    assert_equal(100, a.GetVal())
     var b = B.new()
-    assert_equal(200, b.GetVal())
-    assert_equal(200, T1(b))
-    assert_equal(200, T2(b))
+    Bar(b)
   END
   v9.CheckSourceSuccess(lines)
 
-  # duplicate class variable and class method
+  # Use a static method in an abstract class
   lines =<< trim END
     vim9script
-    class A
-      static var sval = 100
-      static def Check()
-        assert_equal(100, sval)
-      enddef
-      def GetVal(): number
-        return sval
+    abstract class A
+      static def Foo(): string
+        return 'foo'
       enddef
     endclass
+    assert_equal('foo', A.Foo())
+  END
+  v9.CheckSourceSuccess(lines)
 
-    class B extends A
-      static var sval = 200
-      static def Check()
-        assert_equal(200, sval)
-      enddef
+    # Invoke method returning a value through the abstract class. See #15432.
+  lines =<< trim END
+    vim9script
+
+    abstract class A
+        abstract def String(): string
     endclass
 
-    def T1(aa: A): number
-      return aa.GetVal()
-    enddef
+    class B extends A
+        def String(): string
+            return 'B'
+        enddef
+    endclass
 
-    def T2(bb: B): number
-      return bb.GetVal()
+    def F(o: A)
+        assert_equal('B', o.String())
     enddef
-
-    assert_equal(100, A.sval)
-    assert_equal(200, B.sval)
-    var a = A.new()
-    assert_equal(100, a.GetVal())
-    var b = B.new()
-    assert_equal(100, b.GetVal())
-    assert_equal(100, T1(b))
-    assert_equal(100, T2(b))
+    F(B.new())
   END
   v9.CheckSourceSuccess(lines)
-enddef
-
-" Test for calling an instance method using the class
-def Test_instance_method_call_using_class()
-  # Invoke an object method using a class in script context
-  var lines =<< trim END
-    vim9script
-    class A
-      def Foo()
-        echo "foo"
-      enddef
-    endclass
-    A.Foo()
-  END
-  v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 7)
 
-  # Invoke an object method using a class in def function context
+  # Invoke abstract method returning a value does not compile
   lines =<< trim END
     vim9script
-    class A
-      def Foo()
-        echo "foo"
+
+    abstract class A
+      abstract def String(): string
+        return 'X'
       enddef
     endclass
-    def T()
-      A.Foo()
-    enddef
-    T()
   END
-  v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 1)
+  v9.CheckScriptFailure(lines, "E1318: Not a valid command in a class: return 'X'")
 enddef
 
-" Test for duplicate class method and instance method
-def Test_dup_classmethod_objmethod()
-  # Duplicate instance method
+" Test for calling a class method from a subclass
+def Test_class_method_call_from_subclass()
+  # class method call from a subclass
   var lines =<< trim END
     vim9script
+
     class A
       static def Foo()
-      enddef
-      def Foo()
+        echo "foo"
       enddef
     endclass
-  END
-  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6)
 
-  # Duplicate protected instance method
-  lines =<< trim END
-    vim9script
-    class A
-      static def Foo()
-      enddef
-      def _Foo()
+    class B extends A
+      def Bar()
+        Foo()
       enddef
     endclass
+
+    var b = B.new()
+    b.Bar()
   END
-  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)
+  v9.CheckSourceFailure(lines, 'E1384: Class method "Foo" accessible only inside class "A"', 1)
+enddef
 
-  # Duplicate class method
-  lines =<< trim END
+" Test for calling a class method using an object in a def function context and
+" script context.
+def Test_class_method_call_using_object()
+  # script context
+  var lines =<< trim END
     vim9script
     class A
-      def Foo()
+      static def Foo(): list<string>
+        return ['a', 'b']
       enddef
-      static def Foo()
+      def Bar()
+        assert_equal(['a', 'b'], A.Foo())
+        assert_equal(['a', 'b'], Foo())
       enddef
     endclass
+
+    def T()
+      assert_equal(['a', 'b'], A.Foo())
+      var t_a = A.new()
+      t_a.Bar()
+    enddef
+
+    assert_equal(['a', 'b'], A.Foo())
+    var a = A.new()
+    a.Bar()
+    T()
   END
-  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6)
+  v9.CheckSourceSuccess(lines)
 
-  # Duplicate protected class method
+  # script context
   lines =<< trim END
     vim9script
     class A
-      def Foo()
-      enddef
-      static def _Foo()
+      static def Foo(): string
+        return 'foo'
       enddef
     endclass
+
+    var a = A.new()
+    assert_equal('foo', a.Foo())
   END
-  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)
+  v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 9)
 
-  # Duplicate protected class and object method
+  # def function context
   lines =<< trim END
     vim9script
     class A
-      def _Foo()
-      enddef
-      static def _Foo()
+      static def Foo(): string
+        return 'foo'
       enddef
     endclass
+
+    def T()
+      var a = A.new()
+      assert_equal('foo', a.Foo())
+    enddef
+    T()
   END
-  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)
+  v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 2)
 enddef
 
-" Test for an instance method access level comparison with parent instance
-" methods.
-def Test_instance_method_access_level()
-  # protected method in subclass
+def Test_class_variable()
   var lines =<< trim END
     vim9script
+
     class A
-      def Foo()
+      public static var val: number = 10
+      static def ClassFunc()
+        assert_equal(10, val)
+      enddef
+      def ObjFunc()
+        assert_equal(10, val)
       enddef
     endclass
+
     class B extends A
     endclass
-    class C extends B
-      def _Foo()
-      enddef
-    endclass
+
+    assert_equal(10, A.val)
+    A.ClassFunc()
+    var a = A.new()
+    a.ObjFunc()
+    var b = B.new()
+    b.ObjFunc()
+
+    def T1(a1: A)
+      a1.ObjFunc()
+      A.ClassFunc()
+    enddef
+    T1(b)
+
+    A.val = 20
+    assert_equal(20, A.val)
   END
-  v9.CheckSourceFailure(lines, 'E1377: Access level of method "_Foo" is different in class "A"', 11)
+  v9.CheckSourceSuccess(lines)
 
-  # Public method in subclass
+  # Modifying a parent class variable from a child class method
   lines =<< trim END
     vim9script
+
     class A
-      def _Foo()
-      enddef
+      static var val: number = 10
     endclass
+
     class B extends A
-    endclass
-    class C extends B
-      def Foo()
+      static def ClassFunc()
+        val = 20
       enddef
     endclass
+    B.ClassFunc()
   END
-  v9.CheckSourceFailure(lines, 'E1377: Access level of method "Foo" is different in class "A"', 11)
-enddef
+  v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
 
-def Test_extend_empty_class()
-  var lines =<< trim END
+  # Reading a parent class variable from a child class method
+  lines =<< trim END
     vim9script
+
     class A
+      static var val: number = 10
     endclass
+
     class B extends A
-    endclass
-    class C extends B
-      public static var rw_class_var = 1
-      public var rw_obj_var = 2
-      static def ClassMethod(): number
-        return 3
-      enddef
-      def ObjMethod(): number
-        return 4
+      static def ClassFunc()
+        var i = val
       enddef
     endclass
-    assert_equal(1, C.rw_class_var)
-    assert_equal(3, C.ClassMethod())
-    var c = C.new()
-    assert_equal(2, c.rw_obj_var)
-    assert_equal(4, c.ObjMethod())
-  END
-  v9.CheckSourceSuccess(lines)
-enddef
-
-" A interface cannot have a static variable or a static method or a protected
-" variable or a protected method or a public variable
-def Test_interface_with_unsupported_members()
-  var lines =<< trim END
-    vim9script
-    interface A
-      static var num: number
-    endinterface
+    B.ClassFunc()
   END
-  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+  v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
 
+  # Modifying a parent class variable from a child object method
   lines =<< trim END
     vim9script
-    interface A
-      static var _num: number
-    endinterface
-  END
-  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
 
-  lines =<< trim END
-    vim9script
-    interface A
-      public static var num: number
-    endinterface
-  END
-  v9.CheckSourceFailure(lines, 'E1387: public variable not supported in an interface', 3)
+    class A
+      static var val: number = 10
+    endclass
 
-  lines =<< trim END
-    vim9script
-    interface A
-      public static var num: number
-    endinterface
+    class B extends A
+      def ObjFunc()
+        val = 20
+      enddef
+    endclass
+    var b = B.new()
+    b.ObjFunc()
   END
-  v9.CheckSourceFailure(lines, 'E1387: public variable not supported in an interface', 3)
+  v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
 
+  # Reading a parent class variable from a child object method
   lines =<< trim END
     vim9script
-    interface A
-      static var _num: number
-    endinterface
-  END
-  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
 
-  lines =<< trim END
-    vim9script
-    interface A
-      static def Foo(d: dict<any>): list<string>
-    endinterface
+    class A
+      static var val: number = 10
+    endclass
+
+    class B extends A
+      def ObjFunc()
+        var i = val
+      enddef
+    endclass
+    var b = B.new()
+    b.ObjFunc()
   END
-  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+  v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
 
+  # Modifying a class variable using an object at script level
   lines =<< trim END
     vim9script
-    interface A
-      static def _Foo(d: dict<any>): list<string>
-    endinterface
+
+    class A
+      static var val: number = 10
+    endclass
+    var a = A.new()
+    a.val = 20
   END
-  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+  v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7)
 
+  # Reading a class variable using an object at script level
   lines =<< trim END
     vim9script
-    interface A
-      var _Foo: list<string>
-    endinterface
+
+    class A
+      static var val: number = 10
+    endclass
+    var a = A.new()
+    var i = a.val
   END
-  v9.CheckSourceFailure(lines, 'E1379: Protected variable not supported in an interface', 3)
+  v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7)
 
+  # Modifying a class variable using an object at function level
   lines =<< trim END
     vim9script
-    interface A
-      def _Foo(d: dict<any>): list<string>
-    endinterface
-  END
-  v9.CheckSourceFailure(lines, 'E1380: Protected method not supported in an interface', 3)
-enddef
 
-" Test for extending an interface
-def Test_extend_interface()
-  var lines =<< trim END
-    vim9script
-    interface A
-      var var1: list<string>
-      def Foo()
-    endinterface
-    interface B extends A
-      var var2: dict<string>
-      def Bar()
-    endinterface
-    class C implements A, B
-      var var1 = [1, 2]
-      def Foo()
-      enddef
-      var var2 = {a: '1'}
-      def Bar()
-      enddef
+    class A
+      static var val: number = 10
     endclass
+
+    def T()
+      var a = A.new()
+      a.val = 20
+    enddef
+    T()
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2)
 
-  # extending empty interface
+  # Reading a class variable using an object at function level
   lines =<< trim END
     vim9script
-    interface A
-    endinterface
-    interface B extends A
-    endinterface
-    class C implements B
+
+    class A
+      static var val: number = 10
     endclass
+    def T()
+      var a = A.new()
+      var i = a.val
+    enddef
+    T()
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2)
 
+  # Use old implicit var declaration syntax (without initialization)
   lines =<< trim END
     vim9script
-    interface A
-      def Foo()
-    endinterface
-    interface B extends A
-      var var2: dict<string>
-    endinterface
-    class C implements A, B
-      var var2 = {a: '1'}
+
+    class A
+      static val: number
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1349: Method "Foo" of interface "A" is not implemented', 10)
+  v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)
 
+  # Use old implicit var declaration syntax (with initialization)
   lines =<< trim END
     vim9script
-    interface A
-      def Foo()
-    endinterface
-    interface B extends A
-      var var2: dict<string>
-    endinterface
-    class C implements A, B
-      def Foo()
-      enddef
+
+    class A
+      static val: number = 10
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1348: Variable "var2" of interface "B" is not implemented', 11)
+  v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)
 
-  # interface cannot extend a class
+  # Use old implicit var declaration syntax (type inferred)
   lines =<< trim END
     vim9script
+
     class A
+      static val = 10
     endclass
-    interface B extends A
-    endinterface
   END
-  v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5)
+  v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)
 
-  # class cannot extend an interface
+  # Missing ":var" in "var" class variable declaration (without initialization)
   lines =<< trim END
     vim9script
-    interface A
-    endinterface
-    class B extends A
+
+    class A
+      static var: number
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5)
+  v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number', 4)
 
-  # interface cannot implement another interface
+  # Missing ":var" in "var" class variable declaration (with initialization)
   lines =<< trim END
     vim9script
-    interface A
-    endinterface
-    interface B implements A
-    endinterface
+
+    class A
+      static var: number = 10
+    endclass
   END
-  v9.CheckSourceFailure(lines, 'E1381: Interface cannot use "implements"', 4)
+  v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number = 10', 4)
 
-  # interface cannot extend multiple interfaces
+  # Missing ":var" in "var" class variable declaration (type inferred)
   lines =<< trim END
     vim9script
-    interface A
-    endinterface
-    interface B
-    endinterface
-    interface C extends A, B
-    endinterface
+
+    class A
+      static var = 10
+    endclass
   END
-  v9.CheckSourceFailure(lines, 'E1315: White space required after name: A, B', 6)
+  v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var = 10', 4)
 
-  # Variable type in an extended interface is of different type
-  lines =<< trim END
-    vim9script
-    interface A
-      var val1: number
-    endinterface
-    interface B extends A
-      var val2: string
-    endinterface
-    interface C extends B
-      var val1: string
-      var val2: number
-    endinterface
-  END
-  v9.CheckSourceFailure(lines, 'E1382: Variable "val1": type mismatch, expected number but got string', 11)
 enddef
 
-" Test for a child class implementing an interface when some of the methods are
-" defined in the parent class.
-def Test_child_class_implements_interface()
+" Test for using a duplicate class method and class variable in a child class
+def Test_dup_class_member()
+  # duplicate class variable, class method and overridden object method
   var lines =<< trim END
     vim9script
-
-    interface Intf
-      def F1(): list<list<number>>
-      def F2(): list<list<number>>
-      def F3(): list<list<number>>
-      var var1: list<dict<number>>
-      var var2: list<dict<number>>
-      var var3: list<dict<number>>
-    endinterface
-
     class A
-      def A1()
+      static var sval = 100
+      static def Check()
+        assert_equal(100, sval)
       enddef
-      def F3(): list<list<number>>
-        return [[3]]
+      def GetVal(): number
+        return sval
       enddef
-      var v1: list<list<number>> = [[0]]
-      var var3 = [{c: 30}]
     endclass
 
     class B extends A
-      def B1()
+      static var sval = 200
+      static def Check()
+        assert_equal(200, sval)
       enddef
-      def F2(): list<list<number>>
-        return [[2]]
+      def GetVal(): number
+        return sval
       enddef
-      var v2: list<list<number>> = [[0]]
-      var var2 = [{b: 20}]
     endclass
 
-    class C extends B implements Intf
-      def C1()
-      enddef
-      def F1(): list<list<number>>
-        return [[1]]
-      enddef
-      var v3: list<list<number>> = [[0]]
-      var var1 = [{a: 10}]
-    endclass
+    def T1(aa: A): number
+      return aa.GetVal()
+    enddef
 
-    def T(if: Intf)
-      assert_equal([[1]], if.F1())
-      assert_equal([[2]], if.F2())
-      assert_equal([[3]], if.F3())
-      assert_equal([{a: 10}], if.var1)
-      assert_equal([{b: 20}], if.var2)
-      assert_equal([{c: 30}], if.var3)
+    def T2(bb: B): number
+      return bb.GetVal()
     enddef
 
-    var c = C.new()
-    T(c)
-    assert_equal([[1]], c.F1())
-    assert_equal([[2]], c.F2())
-    assert_equal([[3]], c.F3())
-    assert_equal([{a: 10}], c.var1)
-    assert_equal([{b: 20}], c.var2)
-    assert_equal([{c: 30}], c.var3)
+    assert_equal(100, A.sval)
+    assert_equal(200, B.sval)
+    var a = A.new()
+    assert_equal(100, a.GetVal())
+    var b = B.new()
+    assert_equal(200, b.GetVal())
+    assert_equal(200, T1(b))
+    assert_equal(200, T2(b))
   END
   v9.CheckSourceSuccess(lines)
 
-  # One of the interface methods is not found
+  # duplicate class variable and class method
   lines =<< trim END
     vim9script
-
-    interface Intf
-      def F1()
-      def F2()
-      def F3()
-    endinterface
-
     class A
-      def A1()
+      static var sval = 100
+      static def Check()
+        assert_equal(100, sval)
+      enddef
+      def GetVal(): number
+        return sval
       enddef
     endclass
 
     class B extends A
-      def B1()
-      enddef
-      def F2()
+      static var sval = 200
+      static def Check()
+        assert_equal(200, sval)
       enddef
     endclass
 
-    class C extends B implements Intf
-      def C1()
-      enddef
-      def F1()
+    def T1(aa: A): number
+      return aa.GetVal()
+    enddef
+
+    def T2(bb: B): number
+      return bb.GetVal()
+    enddef
+
+    assert_equal(100, A.sval)
+    assert_equal(200, B.sval)
+    var a = A.new()
+    assert_equal(100, a.GetVal())
+    var b = B.new()
+    assert_equal(100, b.GetVal())
+    assert_equal(100, T1(b))
+    assert_equal(100, T2(b))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for calling an instance method using the class
+def Test_instance_method_call_using_class()
+  # Invoke an object method using a class in script context
+  var lines =<< trim END
+    vim9script
+    class A
+      def Foo()
+        echo "foo"
       enddef
     endclass
+    A.Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1349: Method "F3" of interface "Intf" is not implemented', 26)
+  v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 7)
 
-  # One of the interface methods is of different type
+  # Invoke an object method using a class in def function context
   lines =<< trim END
     vim9script
-
-    interface Intf
-      def F1()
-      def F2()
-      def F3()
-    endinterface
-
     class A
-      def F3(): number
-        return 0
-      enddef
-      def A1()
+      def Foo()
+        echo "foo"
       enddef
     endclass
+    def T()
+      A.Foo()
+    enddef
+    T()
+  END
+  v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 1)
+enddef
 
-    class B extends A
-      def B1()
+" Test for duplicate class method and instance method
+def Test_dup_classmethod_objmethod()
+  # Duplicate instance method
+  var lines =<< trim END
+    vim9script
+    class A
+      static def Foo()
       enddef
-      def F2()
+      def Foo()
       enddef
     endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6)
 
-    class C extends B implements Intf
-      def C1()
+  # Duplicate protected instance method
+  lines =<< trim END
+    vim9script
+    class A
+      static def Foo()
       enddef
-      def F1()
+      def _Foo()
       enddef
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1383: Method "F3": type mismatch, expected func() but got func(): number', 29)
+  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)
 
-  # One of the interface variables is not present
+  # Duplicate class method
   lines =<< trim END
     vim9script
-
-    interface Intf
-      var var1: list<dict<number>>
-      var var2: list<dict<number>>
-      var var3: list<dict<number>>
-    endinterface
-
     class A
-      var v1: list<list<number>> = [[0]]
-    endclass
-
-    class B extends A
-      var v2: list<list<number>> = [[0]]
-      var var2 = [{b: 20}]
-    endclass
-
-    class C extends B implements Intf
-      var v3: list<list<number>> = [[0]]
-      var var1 = [{a: 10}]
+      def Foo()
+      enddef
+      static def Foo()
+      enddef
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1348: Variable "var3" of interface "Intf" is not implemented', 21)
+  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6)
 
-  # One of the interface variables is of different type
+  # Duplicate protected class method
   lines =<< trim END
     vim9script
-
-    interface Intf
-      var var1: list<dict<number>>
-      var var2: list<dict<number>>
-      var var3: list<dict<number>>
-    endinterface
-
     class A
-      var v1: list<list<number>> = [[0]]
-      var var3: list<dict<string>>
-    endclass
-
-    class B extends A
-      var v2: list<list<number>> = [[0]]
-      var var2 = [{b: 20}]
-    endclass
-
-    class C extends B implements Intf
-      var v3: list<list<number>> = [[0]]
-      var var1 = [{a: 10}]
+      def Foo()
+      enddef
+      static def _Foo()
+      enddef
     endclass
   END
-  v9.CheckSourceFailure(lines, 'E1382: Variable "var3": type mismatch, expected list<dict<number>> but got list<dict<string>>', 22)
-enddef
+  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)
 
-" Test for extending an interface with duplicate variables and methods
-def Test_interface_extends_with_dup_members()
-  var lines =<< trim END
+  # Duplicate protected class and object method
+  lines =<< trim END
     vim9script
-    interface A
-      var n1: number
-      def Foo1(): number
-    endinterface
-    interface B extends A
-      var n2: number
-      var n1: number
-      def Foo2(): number
-      def Foo1(): number
-    endinterface
-    class C implements B
-      var n1 = 10
-      var n2 = 20
-      def Foo1(): number
-        return 30
+    class A
+      def _Foo()
       enddef
-      def Foo2(): number
-        return 40
+      static def _Foo()
       enddef
     endclass
-    def T1(a: A)
-      assert_equal(10, a.n1)
-      assert_equal(30, a.Foo1())
-    enddef
-    def T2(b: B)
-      assert_equal(10, b.n1)
-      assert_equal(20, b.n2)
-      assert_equal(30, b.Foo1())
-      assert_equal(40, b.Foo2())
-    enddef
-    var c = C.new()
-    T1(c)
-    T2(c)
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)
 enddef
 
-" Test for implementing an interface with different ordering for the interface
-" member variables.
-def Test_implement_interface_with_different_variable_order()
+" Test for an instance method access level comparison with parent instance
+" methods.
+def Test_instance_method_access_level()
+  # protected method in subclass
   var lines =<< trim END
     vim9script
-
-    interface IX
-      var F: func(): string
-    endinterface
-
-    class X implements IX
-      var x: number
-      var F: func(): string = () => 'ok'
+    class A
+      def Foo()
+      enddef
     endclass
-
-    def Foo(ix: IX): string
-      return ix.F()
-    enddef
-
-    var x0 = X.new(0)
-    assert_equal('ok', Foo(x0))
-  END
-  v9.CheckSourceSuccess(lines)
-enddef
-
-" Test for inheriting interfaces from an imported super class
-def Test_interface_inheritance_with_imported_super()
-  var lines =<< trim END
-    vim9script
-
-    export interface I
-      def F(): string
-    endinterface
-
-    export class A implements I
-      def F(): string
-        return 'A'
+    class B extends A
+    endclass
+    class C extends B
+      def _Foo()
       enddef
     endclass
   END
-  writefile(lines, 'Xinheritintfimportclass.vim', 'D')
+  v9.CheckSourceFailure(lines, 'E1377: Access level of method "_Foo" is different in class "A"', 11)
 
+  # Public method in subclass
   lines =<< trim END
     vim9script
-
-    import './Xinheritintfimportclass.vim' as i_imp
-
-    # class C extends i_imp.A
-    class C extends i_imp.A implements i_imp.I
-      def F(): string
-        return 'C'
+    class A
+      def _Foo()
+      enddef
+    endclass
+    class B extends A
+    endclass
+    class C extends B
+      def Foo()
       enddef
     endclass
-
-    def TestI(i: i_imp.I): string
-        return i.F()
-    enddef
-
-    assert_equal('C', TestI(C.new()))
   END
-  v9.CheckSourceSuccess(lines)
+  v9.CheckSourceFailure(lines, 'E1377: Access level of method "Foo" is different in class "A"', 11)
 enddef
 
-" Test for using "any" type for a variable in a sub-class while it has a
-" concrete type in the interface
-def Test_implements_using_var_type_any()
+def Test_extend_empty_class()
   var lines =<< trim END
     vim9script
-    interface A
-      var val: list<dict<string>>
-    endinterface
-    class B implements A
-      var val = [{a: '1'}, {b: '2'}]
+    class A
     endclass
-    var b = B.new()
-    assert_equal([{a: '1'}, {b: '2'}], b.val)
-  END
-  v9.CheckSourceSuccess(lines)
-
-  # initialize instance variable using a different type
-  lines =<< trim END
-    vim9script
-    interface A
-      var val: list<dict<string>>
-    endinterface
-    class B implements A
-      var val = {a: 1, b: 2}
+    class B extends A
     endclass
-    var b = B.new()
+    class C extends B
+      public static var rw_class_var = 1
+      public var rw_obj_var = 2
+      static def ClassMethod(): number
+        return 3
+      enddef
+      def ObjMethod(): number
+        return 4
+      enddef
+    endclass
+    assert_equal(1, C.rw_class_var)
+    assert_equal(3, C.ClassMethod())
+    var c = C.new()
+    assert_equal(2, c.rw_obj_var)
+    assert_equal(4, c.ObjMethod())
   END
-  v9.CheckSourceFailure(lines, 'E1382: Variable "val": type mismatch, expected list<dict<string>> but got dict<number>', 1)
+  v9.CheckSourceSuccess(lines)
 enddef
 
 " Test for assigning to a member variable in a nested class
@@ -13271,18 +11832,4 @@ func Test_class_selfref_gc()
   call v9.CheckSourceSuccess(lines)
 endfunc
 
-" Test for defining an interface in a function
-def Test_interface_defined_in_function()
-  var lines =<< trim END
-    vim9script
-    def Fn()
-      var x = 1
-      interface Foo
-      endinterface
-    enddef
-    defcompile
-  END
-  v9.CheckScriptFailure(lines, 'E1436: Interface can only be used in a script', 2)
-enddef
-
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_interface.vim b/src/testdir/test_vim9_interface.vim
new file mode 100644 (file)
index 0000000..742b871
--- /dev/null
@@ -0,0 +1,1459 @@
+" Tests for Vim9 interface
+
+import './util/vim9.vim' as v9
+
+" Tests for basic interface declaration and errors
+def Test_interface_basics()
+  var lines =<< trim END
+    vim9script
+    interface Something
+      var ro_var: list<number>
+      def GetCount(): number
+    endinterface
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    interface SomethingWrong
+      static var count = 7
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1342: Interface can only be defined in Vim9 script', 1)
+
+  lines =<< trim END
+    vim9script
+
+    interface Some
+      var value: number
+      def Method(value: number)
+    endinterface
+  END
+  # The argument name and the object member name are the same, but this is not a
+  # problem because object members are always accessed with the "this." prefix.
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+    interface somethingWrong
+      static var count = 7
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong', 2)
+
+  lines =<< trim END
+    vim9script
+    interface SomethingWrong
+      var value: string
+      var count = 7
+      def GetCount(): number
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1344: Cannot initialize a variable in an interface', 4)
+
+  lines =<< trim END
+    vim9script
+    interface SomethingWrong
+      var value: string
+      var count: number
+      def GetCount(): number
+        return 5
+      enddef
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1345: Not a valid command in an interface: return 5', 6)
+
+  # Test for "interface" cannot be abbreviated
+  lines =<< trim END
+    vim9script
+    inte Something
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: inte Something', 2)
+
+  # Test for "endinterface" cannot be abbreviated
+  lines =<< trim END
+    vim9script
+    interface Something
+    endin
+  END
+  v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: endin', 3)
+
+  # Additional commands after "interface name"
+  lines =<< trim END
+    vim9script
+    interface Something | var x = 10 | var y = 20
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = 10", 2)
+
+  lines =<< trim END
+    vim9script
+    export interface EnterExit
+      def Enter(): void
+      def Exit(): void
+    endinterface
+  END
+  writefile(lines, 'XdefIntf.vim', 'D')
+
+  lines =<< trim END
+    vim9script
+    import './XdefIntf.vim' as defIntf
+    export def With(ee: defIntf.EnterExit, F: func)
+      ee.Enter()
+      try
+        F()
+      finally
+        ee.Exit()
+      endtry
+    enddef
+  END
+  v9.CheckScriptSuccess(lines)
+
+  var imported =<< trim END
+    vim9script
+    export abstract class EnterExit
+      def Enter(): void
+      enddef
+      def Exit(): void
+      enddef
+    endclass
+  END
+  writefile(imported, 'XdefIntf2.vim', 'D')
+
+  lines[1] = " import './XdefIntf2.vim' as defIntf"
+  v9.CheckScriptSuccess(lines)
+enddef
+
+def Test_class_interface_wrong_end()
+  var lines =<< trim END
+    vim9script
+    abstract class SomeName
+      var member = 'text'
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E476: Invalid command: endinterface, expected endclass', 4)
+
+  lines =<< trim END
+    vim9script
+    export interface AnotherName
+      var member: string
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E476: Invalid command: endclass, expected endinterface', 4)
+enddef
+
+" Test for using string() with an interface
+def Test_interface_to_string()
+  var lines =<< trim END
+    vim9script
+    interface Intf
+      def Method(nr: number)
+    endinterface
+    assert_equal("interface Intf", string(Intf))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_class_implements_interface()
+  var lines =<< trim END
+    vim9script
+
+    interface Some
+      var count: number
+      def Method(nr: number)
+    endinterface
+
+    class SomeImpl implements Some
+      var count: number
+      def Method(nr: number)
+        echo nr
+      enddef
+    endclass
+
+    interface Another
+      var member: string
+    endinterface
+
+    class AnotherImpl implements Some, Another
+      var member = 'abc'
+      var count = 20
+      def Method(nr: number)
+        echo nr
+      enddef
+    endclass
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+
+    interface Some
+      var count: number
+    endinterface
+
+    class SomeImpl implements Some implements Some
+      var count: number
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1350: Duplicate "implements"', 7)
+
+  lines =<< trim END
+    vim9script
+
+    interface Some
+      var count: number
+    endinterface
+
+    class SomeImpl implements Some, Some
+      var count: number
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1351: Duplicate interface after "implements": Some', 7)
+
+  lines =<< trim END
+    vim9script
+
+    interface Some
+      var counter: number
+      def Method(nr: number)
+    endinterface
+
+    class SomeImpl implements Some
+      var count: number
+      def Method(nr: number)
+        echo nr
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1348: Variable "counter" of interface "Some" is not implemented', 13)
+
+  lines =<< trim END
+    vim9script
+
+    interface Some
+      var count: number
+      def Methods(nr: number)
+    endinterface
+
+    class SomeImpl implements Some
+      var count: number
+      def Method(nr: number)
+        echo nr
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1349: Method "Methods" of interface "Some" is not implemented', 13)
+
+  # Check different order of members in class and interface works.
+  lines =<< trim END
+    vim9script
+
+      interface Result
+        var label: string
+        var errpos: number
+      endinterface
+
+      # order of members is opposite of interface
+      class Failure implements Result
+        public var lnum: number = 5
+        var errpos: number = 42
+        var label: string = 'label'
+      endclass
+
+    def Test()
+      var result: Result = Failure.new()
+
+        assert_equal('label', result.label)
+        assert_equal(42, result.errpos)
+      enddef
+
+    Test()
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Interface name after "extends" doesn't end in a space or NUL character
+  lines =<< trim END
+    vim9script
+    interface A
+    endinterface
+    class B extends A"
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4)
+
+  # Trailing characters after a class name
+  lines =<< trim END
+    vim9script
+    class A bbb
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E488: Trailing characters: bbb', 2)
+
+  # using "implements" with a non-existing class
+  lines =<< trim END
+    vim9script
+    class A implements B
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1346: Interface name not found: B', 3)
+
+  # using "implements" with a regular class
+  lines =<< trim END
+    vim9script
+    class A
+    endclass
+    class B implements A
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: A', 5)
+
+  # using "implements" with a variable
+  lines =<< trim END
+    vim9script
+    var T: number = 10
+    class A implements T
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: T', 4)
+
+  # implements should be followed by a white space
+  lines =<< trim END
+    vim9script
+    interface A
+    endinterface
+    class B implements A;
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1315: White space required after name: A;', 4)
+
+  lines =<< trim END
+    vim9script
+
+    interface One
+      def IsEven(nr: number): bool
+    endinterface
+    class Two implements One
+      def IsEven(nr: number): string
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number): string', 9)
+
+  lines =<< trim END
+    vim9script
+
+    interface One
+      def IsEven(nr: number): bool
+    endinterface
+    class Two implements One
+      def IsEven(nr: bool): bool
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(bool): bool', 9)
+
+  lines =<< trim END
+    vim9script
+
+    interface One
+      def IsEven(nr: number): bool
+    endinterface
+    class Two implements One
+      def IsEven(nr: number, ...extra: list<number>): bool
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number, ...list<number>): bool', 9)
+
+  # access superclass interface members from subclass, mix variable order
+  lines =<< trim END
+    vim9script
+
+    interface I1
+      var mvar1: number
+      var mvar2: number
+    endinterface
+
+    # NOTE: the order is swapped
+    class A implements I1
+      var mvar2: number
+      var mvar1: number
+      public static var svar2: number
+      public static var svar1: number
+      def new()
+        svar1 = 11
+        svar2 = 12
+        this.mvar1 = 111
+        this.mvar2 = 112
+      enddef
+    endclass
+
+    class B extends A
+      def new()
+        this.mvar1 = 121
+        this.mvar2 = 122
+      enddef
+    endclass
+
+    class C extends B
+      def new()
+        this.mvar1 = 131
+        this.mvar2 = 132
+      enddef
+    endclass
+
+    def F2(i: I1): list<number>
+      return [ i.mvar1, i.mvar2 ]
+    enddef
+
+    var oa = A.new()
+    var ob = B.new()
+    var oc = C.new()
+
+    assert_equal([111, 112], F2(oa))
+    assert_equal([121, 122], F2(ob))
+    assert_equal([131, 132], F2(oc))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Access superclass interface members from subclass, mix variable order.
+  # Two interfaces, one on A, one on B; each has both kinds of variables
+  lines =<< trim END
+    vim9script
+
+    interface I1
+      var mvar1: number
+      var mvar2: number
+    endinterface
+
+    interface I2
+      var mvar3: number
+      var mvar4: number
+    endinterface
+
+    class A implements I1
+      public static var svar1: number
+      public static var svar2: number
+      var mvar1: number
+      var mvar2: number
+      def new()
+        svar1 = 11
+        svar2 = 12
+        this.mvar1 = 111
+        this.mvar2 = 112
+      enddef
+    endclass
+
+    class B extends A implements I2
+      static var svar3: number
+      static var svar4: number
+      var mvar3: number
+      var mvar4: number
+      def new()
+        svar3 = 23
+        svar4 = 24
+        this.mvar1 = 121
+        this.mvar2 = 122
+        this.mvar3 = 123
+        this.mvar4 = 124
+      enddef
+    endclass
+
+    class C extends B
+      public static var svar5: number
+      def new()
+        svar5 = 1001
+        this.mvar1 = 131
+        this.mvar2 = 132
+        this.mvar3 = 133
+        this.mvar4 = 134
+      enddef
+    endclass
+
+    def F2(i: I1): list<number>
+      return [ i.mvar1, i.mvar2 ]
+    enddef
+
+    def F4(i: I2): list<number>
+      return [ i.mvar3, i.mvar4 ]
+    enddef
+
+    var oa = A.new()
+    var ob = B.new()
+    var oc = C.new()
+
+    assert_equal([[111, 112]], [F2(oa)])
+    assert_equal([[121, 122], [123, 124]], [F2(ob), F4(ob)])
+    assert_equal([[131, 132], [133, 134]], [F2(oc), F4(oc)])
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Using two interface names without a space after the ","
+  lines =<< trim END
+    vim9script
+    interface A
+    endinterface
+    interface B
+    endinterface
+    class C implements A,B
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1315: White space required after name: A,B', 6)
+
+  # No interface name after a comma
+  lines =<< trim END
+    vim9script
+    interface A
+    endinterface
+    class B implements A,
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 4)
+
+  # No interface name after implements
+  lines =<< trim END
+    vim9script
+    class A implements
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 2)
+enddef
+
+def Test_call_interface_method()
+  var lines =<< trim END
+    vim9script
+    interface Base
+      def Enter(): void
+    endinterface
+
+    class Child implements Base
+      def Enter(): void
+        g:result ..= 'child'
+      enddef
+    endclass
+
+    def F(obj: Base)
+      obj.Enter()
+    enddef
+
+    g:result = ''
+    F(Child.new())
+    assert_equal('child', g:result)
+    unlet g:result
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+    class Base
+      def Enter(): void
+        g:result ..= 'base'
+      enddef
+    endclass
+
+    class Child extends Base
+      def Enter(): void
+        g:result ..= 'child'
+      enddef
+    endclass
+
+    def F(obj: Base)
+      obj.Enter()
+    enddef
+
+    g:result = ''
+    F(Child.new())
+    assert_equal('child', g:result)
+    unlet g:result
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # method of interface returns a value
+  lines =<< trim END
+    vim9script
+    interface Base
+      def Enter(): string
+    endinterface
+
+    class Child implements Base
+      def Enter(): string
+        g:result ..= 'child'
+        return "/resource"
+      enddef
+    endclass
+
+    def F(obj: Base)
+      var r = obj.Enter()
+      g:result ..= r
+    enddef
+
+    g:result = ''
+    F(Child.new())
+    assert_equal('child/resource', g:result)
+    unlet g:result
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+    class Base
+      def Enter(): string
+        return null_string
+      enddef
+    endclass
+
+    class Child extends Base
+      def Enter(): string
+        g:result ..= 'child'
+        return "/resource"
+      enddef
+    endclass
+
+    def F(obj: Base)
+      var r = obj.Enter()
+      g:result ..= r
+    enddef
+
+    g:result = ''
+    F(Child.new())
+    assert_equal('child/resource', g:result)
+    unlet g:result
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # No class that implements the interface.
+  lines =<< trim END
+    vim9script
+
+    interface IWithEE
+      def Enter(): any
+      def Exit(): void
+    endinterface
+
+    def With1(ee: IWithEE, F: func)
+      var r = ee.Enter()
+    enddef
+
+    defcompile
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for implementing an imported interface
+def Test_implement_imported_interface()
+  var lines =<< trim END
+    vim9script
+    export interface Imp_Intf1
+      def Fn1(): number
+    endinterface
+    export interface Imp_Intf2
+      def Fn2(): number
+    endinterface
+  END
+  writefile(lines, 'Ximportinterface.vim', 'D')
+
+  lines =<< trim END
+    vim9script
+    import './Ximportinterface.vim' as Xintf
+
+    class A implements Xintf.Imp_Intf1, Xintf.Imp_Intf2
+      def Fn1(): number
+        return 10
+      enddef
+      def Fn2(): number
+        return 20
+      enddef
+    endclass
+    var a = A.new()
+    assert_equal(10, a.Fn1())
+    assert_equal(20, a.Fn2())
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for changing the member access of an interface in a implementation class
+def Test_change_interface_member_access()
+  var lines =<< trim END
+    vim9script
+    interface A
+      var val: number
+    endinterface
+    class B implements A
+      public var val = 10
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1367: Access level of variable "val" of interface "A" is different', 7)
+
+  lines =<< trim END
+    vim9script
+    interface A
+      var val: number
+    endinterface
+    class B implements A
+      public var val = 10
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1367: Access level of variable "val" of interface "A" is different', 7)
+enddef
+
+" Test for using a interface method using a child object
+def Test_interface_method_from_child()
+  var lines =<< trim END
+    vim9script
+
+    interface A
+      def Foo(): string
+    endinterface
+
+    class B implements A
+      def Foo(): string
+        return 'foo'
+      enddef
+    endclass
+
+    class C extends B
+      def Bar(): string
+        return 'bar'
+      enddef
+    endclass
+
+    def T1(a: A)
+      assert_equal('foo', a.Foo())
+    enddef
+
+    def T2(b: B)
+      assert_equal('foo', b.Foo())
+    enddef
+
+    var c = C.new()
+    T1(c)
+    T2(c)
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using an interface method using a child object when it is overridden
+" by the child class.
+def Test_interface_overridden_method_from_child()
+  var lines =<< trim END
+    vim9script
+
+    interface A
+      def Foo(): string
+    endinterface
+
+    class B implements A
+      def Foo(): string
+        return 'b-foo'
+      enddef
+    endclass
+
+    class C extends B
+      def Bar(): string
+        return 'bar'
+      enddef
+      def Foo(): string
+        return 'c-foo'
+      enddef
+    endclass
+
+    def T1(a: A)
+      assert_equal('c-foo', a.Foo())
+    enddef
+
+    def T2(b: B)
+      assert_equal('c-foo', b.Foo())
+    enddef
+
+    var c = C.new()
+    T1(c)
+    T2(c)
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for interface inheritance
+def Test_interface_inheritance()
+  var lines =<< trim END
+    vim9script
+
+    interface A
+      def A_Fn(): string
+    endinterface
+
+    interface B
+      def B_Fn(): string
+    endinterface
+
+    interface C
+      def C_Fn(): string
+    endinterface
+
+    class C1 implements A
+      def A_Fn(): string
+        return 'c1-a'
+      enddef
+    endclass
+
+    class C2 extends C1 implements B
+      def B_Fn(): string
+        return 'c2-b'
+      enddef
+      def A_Fn(): string
+        return 'c2-a'
+      enddef
+    endclass
+
+    class C3 extends C2 implements C
+      def C_Fn(): string
+        return 'c3-c'
+      enddef
+      def A_Fn(): string
+        return 'c3-a'
+      enddef
+      def B_Fn(): string
+        return 'c3-b'
+      enddef
+    endclass
+
+    def T1(a: A, s: string)
+      assert_equal(s, a.A_Fn())
+    enddef
+
+    def T2(b: B, s: string)
+      assert_equal(s, b.B_Fn())
+    enddef
+
+    def T3(c: C, s: string)
+      assert_equal(s, c.C_Fn())
+    enddef
+
+    def T4(c1: C1)
+      T1(c1, 'c3-a')
+    enddef
+
+    def T5(c2: C2)
+      T1(c2, 'c3-a')
+      T2(c2, 'c3-b')
+    enddef
+
+    def T6(c3: C3)
+      T1(c3, 'c3-a')
+      T2(c3, 'c3-b')
+      T3(c3, 'c3-c')
+    enddef
+
+    var o3 = C3.new()
+    T4(o3)
+    T5(o3)
+    T6(o3)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Both the parent and child classes implement the same interface
+  lines =<< trim END
+    vim9script
+
+    interface I
+      def Foo(): string
+    endinterface
+
+    class A implements I
+      def Foo(): string
+        return 'A-foo'
+      enddef
+    endclass
+
+    class B implements I
+      def Foo(): string
+        return 'B-foo'
+      enddef
+    endclass
+
+    def Bar(i1: I): string
+      return i1.Foo()
+    enddef
+
+    var b = B.new()
+    assert_equal('B-foo', Bar(b))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" A interface cannot have a static variable or a static method or a protected
+" variable or a protected method or a public variable
+def Test_interface_with_unsupported_members()
+  var lines =<< trim END
+    vim9script
+    interface A
+      static var num: number
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+
+  lines =<< trim END
+    vim9script
+    interface A
+      static var _num: number
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+
+  lines =<< trim END
+    vim9script
+    interface A
+      public static var num: number
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1387: public variable not supported in an interface', 3)
+
+  lines =<< trim END
+    vim9script
+    interface A
+      public static var num: number
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1387: public variable not supported in an interface', 3)
+
+  lines =<< trim END
+    vim9script
+    interface A
+      static var _num: number
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+
+  lines =<< trim END
+    vim9script
+    interface A
+      static def Foo(d: dict<any>): list<string>
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+
+  lines =<< trim END
+    vim9script
+    interface A
+      static def _Foo(d: dict<any>): list<string>
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+
+  lines =<< trim END
+    vim9script
+    interface A
+      var _Foo: list<string>
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1379: Protected variable not supported in an interface', 3)
+
+  lines =<< trim END
+    vim9script
+    interface A
+      def _Foo(d: dict<any>): list<string>
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1380: Protected method not supported in an interface', 3)
+enddef
+
+" Test for extending an interface
+def Test_extend_interface()
+  var lines =<< trim END
+    vim9script
+    interface A
+      var var1: list<string>
+      def Foo()
+    endinterface
+    interface B extends A
+      var var2: dict<string>
+      def Bar()
+    endinterface
+    class C implements A, B
+      var var1 = [1, 2]
+      def Foo()
+      enddef
+      var var2 = {a: '1'}
+      def Bar()
+      enddef
+    endclass
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # extending empty interface
+  lines =<< trim END
+    vim9script
+    interface A
+    endinterface
+    interface B extends A
+    endinterface
+    class C implements B
+    endclass
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+    interface A
+      def Foo()
+    endinterface
+    interface B extends A
+      var var2: dict<string>
+    endinterface
+    class C implements A, B
+      var var2 = {a: '1'}
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1349: Method "Foo" of interface "A" is not implemented', 10)
+
+  lines =<< trim END
+    vim9script
+    interface A
+      def Foo()
+    endinterface
+    interface B extends A
+      var var2: dict<string>
+    endinterface
+    class C implements A, B
+      def Foo()
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1348: Variable "var2" of interface "B" is not implemented', 11)
+
+  # interface cannot extend a class
+  lines =<< trim END
+    vim9script
+    class A
+    endclass
+    interface B extends A
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5)
+
+  # class cannot extend an interface
+  lines =<< trim END
+    vim9script
+    interface A
+    endinterface
+    class B extends A
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5)
+
+  # interface cannot implement another interface
+  lines =<< trim END
+    vim9script
+    interface A
+    endinterface
+    interface B implements A
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1381: Interface cannot use "implements"', 4)
+
+  # interface cannot extend multiple interfaces
+  lines =<< trim END
+    vim9script
+    interface A
+    endinterface
+    interface B
+    endinterface
+    interface C extends A, B
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1315: White space required after name: A, B', 6)
+
+  # Variable type in an extended interface is of different type
+  lines =<< trim END
+    vim9script
+    interface A
+      var val1: number
+    endinterface
+    interface B extends A
+      var val2: string
+    endinterface
+    interface C extends B
+      var val1: string
+      var val2: number
+    endinterface
+  END
+  v9.CheckSourceFailure(lines, 'E1382: Variable "val1": type mismatch, expected number but got string', 11)
+enddef
+
+" Test for a child class implementing an interface when some of the methods are
+" defined in the parent class.
+def Test_child_class_implements_interface()
+  var lines =<< trim END
+    vim9script
+
+    interface Intf
+      def F1(): list<list<number>>
+      def F2(): list<list<number>>
+      def F3(): list<list<number>>
+      var var1: list<dict<number>>
+      var var2: list<dict<number>>
+      var var3: list<dict<number>>
+    endinterface
+
+    class A
+      def A1()
+      enddef
+      def F3(): list<list<number>>
+        return [[3]]
+      enddef
+      var v1: list<list<number>> = [[0]]
+      var var3 = [{c: 30}]
+    endclass
+
+    class B extends A
+      def B1()
+      enddef
+      def F2(): list<list<number>>
+        return [[2]]
+      enddef
+      var v2: list<list<number>> = [[0]]
+      var var2 = [{b: 20}]
+    endclass
+
+    class C extends B implements Intf
+      def C1()
+      enddef
+      def F1(): list<list<number>>
+        return [[1]]
+      enddef
+      var v3: list<list<number>> = [[0]]
+      var var1 = [{a: 10}]
+    endclass
+
+    def T(if: Intf)
+      assert_equal([[1]], if.F1())
+      assert_equal([[2]], if.F2())
+      assert_equal([[3]], if.F3())
+      assert_equal([{a: 10}], if.var1)
+      assert_equal([{b: 20}], if.var2)
+      assert_equal([{c: 30}], if.var3)
+    enddef
+
+    var c = C.new()
+    T(c)
+    assert_equal([[1]], c.F1())
+    assert_equal([[2]], c.F2())
+    assert_equal([[3]], c.F3())
+    assert_equal([{a: 10}], c.var1)
+    assert_equal([{b: 20}], c.var2)
+    assert_equal([{c: 30}], c.var3)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # One of the interface methods is not found
+  lines =<< trim END
+    vim9script
+
+    interface Intf
+      def F1()
+      def F2()
+      def F3()
+    endinterface
+
+    class A
+      def A1()
+      enddef
+    endclass
+
+    class B extends A
+      def B1()
+      enddef
+      def F2()
+      enddef
+    endclass
+
+    class C extends B implements Intf
+      def C1()
+      enddef
+      def F1()
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1349: Method "F3" of interface "Intf" is not implemented', 26)
+
+  # One of the interface methods is of different type
+  lines =<< trim END
+    vim9script
+
+    interface Intf
+      def F1()
+      def F2()
+      def F3()
+    endinterface
+
+    class A
+      def F3(): number
+        return 0
+      enddef
+      def A1()
+      enddef
+    endclass
+
+    class B extends A
+      def B1()
+      enddef
+      def F2()
+      enddef
+    endclass
+
+    class C extends B implements Intf
+      def C1()
+      enddef
+      def F1()
+      enddef
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1383: Method "F3": type mismatch, expected func() but got func(): number', 29)
+
+  # One of the interface variables is not present
+  lines =<< trim END
+    vim9script
+
+    interface Intf
+      var var1: list<dict<number>>
+      var var2: list<dict<number>>
+      var var3: list<dict<number>>
+    endinterface
+
+    class A
+      var v1: list<list<number>> = [[0]]
+    endclass
+
+    class B extends A
+      var v2: list<list<number>> = [[0]]
+      var var2 = [{b: 20}]
+    endclass
+
+    class C extends B implements Intf
+      var v3: list<list<number>> = [[0]]
+      var var1 = [{a: 10}]
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1348: Variable "var3" of interface "Intf" is not implemented', 21)
+
+  # One of the interface variables is of different type
+  lines =<< trim END
+    vim9script
+
+    interface Intf
+      var var1: list<dict<number>>
+      var var2: list<dict<number>>
+      var var3: list<dict<number>>
+    endinterface
+
+    class A
+      var v1: list<list<number>> = [[0]]
+      var var3: list<dict<string>>
+    endclass
+
+    class B extends A
+      var v2: list<list<number>> = [[0]]
+      var var2 = [{b: 20}]
+    endclass
+
+    class C extends B implements Intf
+      var v3: list<list<number>> = [[0]]
+      var var1 = [{a: 10}]
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1382: Variable "var3": type mismatch, expected list<dict<number>> but got list<dict<string>>', 22)
+enddef
+
+" Test for extending an interface with duplicate variables and methods
+def Test_interface_extends_with_dup_members()
+  var lines =<< trim END
+    vim9script
+    interface A
+      var n1: number
+      def Foo1(): number
+    endinterface
+    interface B extends A
+      var n2: number
+      var n1: number
+      def Foo2(): number
+      def Foo1(): number
+    endinterface
+    class C implements B
+      var n1 = 10
+      var n2 = 20
+      def Foo1(): number
+        return 30
+      enddef
+      def Foo2(): number
+        return 40
+      enddef
+    endclass
+    def T1(a: A)
+      assert_equal(10, a.n1)
+      assert_equal(30, a.Foo1())
+    enddef
+    def T2(b: B)
+      assert_equal(10, b.n1)
+      assert_equal(20, b.n2)
+      assert_equal(30, b.Foo1())
+      assert_equal(40, b.Foo2())
+    enddef
+    var c = C.new()
+    T1(c)
+    T2(c)
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for implementing an interface with different ordering for the interface
+" member variables.
+def Test_implement_interface_with_different_variable_order()
+  var lines =<< trim END
+    vim9script
+
+    interface IX
+      var F: func(): string
+    endinterface
+
+    class X implements IX
+      var x: number
+      var F: func(): string = () => 'ok'
+    endclass
+
+    def Foo(ix: IX): string
+      return ix.F()
+    enddef
+
+    var x0 = X.new(0)
+    assert_equal('ok', Foo(x0))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for inheriting interfaces from an imported super class
+def Test_interface_inheritance_with_imported_super()
+  var lines =<< trim END
+    vim9script
+
+    export interface I
+      def F(): string
+    endinterface
+
+    export class A implements I
+      def F(): string
+        return 'A'
+      enddef
+    endclass
+  END
+  writefile(lines, 'Xinheritintfimportclass.vim', 'D')
+
+  lines =<< trim END
+    vim9script
+
+    import './Xinheritintfimportclass.vim' as i_imp
+
+    # class C extends i_imp.A
+    class C extends i_imp.A implements i_imp.I
+      def F(): string
+        return 'C'
+      enddef
+    endclass
+
+    def TestI(i: i_imp.I): string
+        return i.F()
+    enddef
+
+    assert_equal('C', TestI(C.new()))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for defining an interface in a function
+def Test_interface_defined_in_function()
+  var lines =<< trim END
+    vim9script
+    def Fn()
+      var x = 1
+      interface Foo
+      endinterface
+    enddef
+    defcompile
+  END
+  v9.CheckScriptFailure(lines, 'E1436: Interface can only be used in a script', 2)
+enddef
+
+" Test for using "any" type for a variable in a sub-class while it has a
+" concrete type in the interface
+def Test_implements_using_var_type_any()
+  var lines =<< trim END
+    vim9script
+    interface A
+      var val: list<dict<string>>
+    endinterface
+    class B implements A
+      var val = [{a: '1'}, {b: '2'}]
+    endclass
+    var b = B.new()
+    assert_equal([{a: '1'}, {b: '2'}], b.val)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # initialize instance variable using a different type
+  lines =<< trim END
+    vim9script
+    interface A
+      var val: list<dict<string>>
+    endinterface
+    class B implements A
+      var val = {a: 1, b: 2}
+    endclass
+    var b = B.new()
+  END
+  v9.CheckSourceFailure(lines, 'E1382: Variable "val": type mismatch, expected list<dict<string>> but got dict<number>', 1)
+enddef
+
+" Test interface garbage collection
+func Test_interface_garbagecollect()
+  let lines =<< trim END
+    vim9script
+
+    interface I
+      var ro_obj_var: number
+
+      def ObjFoo(): number
+    endinterface
+
+    class A implements I
+      static var ro_class_var: number = 10
+      public static var rw_class_var: number = 20
+      static var _priv_class_var: number = 30
+      var ro_obj_var: number = 40
+      var _priv_obj_var: number = 60
+
+      static def _ClassBar(): number
+        return _priv_class_var
+      enddef
+
+      static def ClassFoo(): number
+        return ro_class_var + rw_class_var + A._ClassBar()
+      enddef
+
+      def _ObjBar(): number
+        return this._priv_obj_var
+      enddef
+
+      def ObjFoo(): number
+        return this.ro_obj_var + this._ObjBar()
+      enddef
+    endclass
+
+    assert_equal(60, A.ClassFoo())
+    var o = A.new()
+    assert_equal(100, o.ObjFoo())
+    test_garbagecollect_now()
+    assert_equal(60, A.ClassFoo())
+    assert_equal(100, o.ObjFoo())
+  END
+  call v9.CheckSourceSuccess(lines)
+endfunc
+
+" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
index 0aa6ace8a3d5ef5cd4039b9e1e8d22f3caacb35c..b98a84e0d3b312180abb86388487f5a0fe889a73 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2029,
 /**/
     2028,
 /**/