patch 9.1.0523: Vim9: cannot downcast an object

Problem:  Vim9: cannot downcast an object (Ernie Rael)
Solution: Fix class downcasting issue (LemonBoy).

When casting an object from one class to another the target type may be
a subclass (downcast) or superclass (upcast) of the source one.
Upcasts require a runtime type check to be emitted.

Add a disassembly test.

fixes: #13244
closes: #15079

Signed-off-by: LemonBoy <thatlemon@gmail.com>
Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index 8e328c2..13e8ea6 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -10871,4 +10871,21 @@
   v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got number', 6)
 enddef
 
+def Test_class_cast()
+  var lines =<< trim END
+    vim9script
+    class A
+    endclass
+    class B extends A
+      var mylen: number
+    endclass
+    def F(o: A): number
+      return (<B>o).mylen
+    enddef
+
+    defcompile F
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index 89dccee..7746b23 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -1761,6 +1761,74 @@
         instr)
 enddef
 
+def Test_disassemble_object_cast()
+  # Downcasting.
+  var lines =<< trim END
+      vim9script
+      class A
+      endclass
+      class B extends A
+        var mylen: number
+      endclass
+      def F(o: A): number
+        return (<B>o).mylen
+      enddef
+
+      g:instr = execute('disassemble F')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('\<SNR>\d*_F\_s*' ..
+        'return (<B>o).mylen\_s*' ..
+        '0 LOAD arg\[-1\]\_s*' ..
+        '1 CHECKTYPE object<B> stack\[-1\]\_s*' ..
+        '2 OBJ_MEMBER 0\_s*' ..
+        '3 RETURN\_s*',
+        g:instr)
+
+  # Upcasting.
+  lines =<< trim END
+      vim9script
+      class A
+        var mylen: number
+      endclass
+      class B extends A
+      endclass
+      def F(o: B): number
+        return (<A>o).mylen
+      enddef
+
+      g:instr = execute('disassemble F')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('\<SNR>\d*_F\_s*' ..
+        'return (<A>o).mylen\_s*' ..
+        '0 LOAD arg\[-1\]\_s*' ..
+        '1 OBJ_MEMBER 0\_s*' ..
+        '2 RETURN\_s*',
+        g:instr)
+
+  # Casting, type is not statically known.
+  lines =<< trim END
+      vim9script
+      class A
+      endclass
+      class B extends A
+      endclass
+      def F(o: any): any
+        return <A>o
+      enddef
+
+      g:instr = execute('disassemble F')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('\<SNR>\d*_F\_s*' ..
+        'return <A>o\_s*' ..
+        '0 LOAD arg\[-1\]\_s*' ..
+        '1 CHECKTYPE object<A> stack\[-1\]\_s*' ..
+        '2 RETURN\_s*',
+        g:instr)
+enddef
+
 def s:Computing()
   var nr = 3
   var nrres = nr + 7