changeset 33346:e8854b8d2486 bytecode-interpreter

maint: merge default to bytecode-interpreter
author John W. Eaton <jwe@octave.org>
date Sat, 06 Apr 2024 17:38:27 -0400
parents af9c0617f9f9 (current diff) 56d234504c01 (diff)
children 1941a21c23b9
files libinterp/corefcn/stack-frame.cc libinterp/octave-value/module.mk libinterp/octave-value/ov-base-mat.cc libinterp/octave-value/ov-base-mat.h libinterp/octave-value/ov-base-scalar.h libinterp/octave-value/ov-base.h libinterp/octave-value/ov-cell.h libinterp/octave-value/ov-colon.h libinterp/octave-value/ov-fcn-handle.h libinterp/octave-value/ov-fcn.cc libinterp/octave-value/ov-struct.h libinterp/octave-value/ov-usr-fcn.h libinterp/octave-value/ov.cc
diffstat 85 files changed, 1287 insertions(+), 1034 deletions(-) [+]
line wrap: on
line diff
--- a/libgui/graphics/gl-select.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libgui/graphics/gl-select.h	Sat Apr 06 17:38:27 2024 -0400
@@ -61,7 +61,7 @@
 
   virtual void setup_opengl_transformation (const axes::properties& props);
 
-  virtual void init_marker (const std::string& m, double m_size, float width);
+  virtual void init_marker (const std::string& m, double size, float width);
 
   virtual Matrix render_text (const std::string& txt,
                               double x, double y, double z,
--- a/libinterp/corefcn/data.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/corefcn/data.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -656,8 +656,23 @@
 
 #undef MAKE_INT_BRANCH
 
-        default:
-          panic_impossible ();
+          case btyp_double:
+          case btyp_float:
+          case btyp_complex:
+          case btyp_float_complex:
+          case btyp_bool:
+          case btyp_char:
+          case btyp_struct:
+          case btyp_cell:
+          case btyp_func_handle:
+          case btyp_unknown:
+            error ("rem: unexpected: found %s instead of integer - please report this bug", btyp_class_name[btyp0].c_str ());
+            break;
+
+          // We should have handled all possible enum values above.
+          // Rely on compiler diagnostics to warn if we haven't.  For
+          // example, GCC's -Wswitch option, enabled by -Wall, will
+          // provide a warning.
         }
     }
   else if (args(0).is_single_type () || args(1).is_single_type ())
@@ -840,8 +855,23 @@
 
 #undef MAKE_INT_BRANCH
 
-        default:
-          panic_impossible ();
+          case btyp_double:
+          case btyp_float:
+          case btyp_complex:
+          case btyp_float_complex:
+          case btyp_bool:
+          case btyp_char:
+          case btyp_struct:
+          case btyp_cell:
+          case btyp_func_handle:
+          case btyp_unknown:
+            error ("mod: unexpected: found %s instead of integer - please report this bug", btyp_class_name[btyp0].c_str ());
+            break;
+
+          // We should have handled all possible enum values above.
+          // Rely on compiler diagnostics to warn if we haven't.  For
+          // example, GCC's -Wswitch option, enabled by -Wall, will
+          // provide a warning.
         }
     }
   else if (args(0).is_single_type () || args(1).is_single_type ())
--- a/libinterp/corefcn/graphics.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/corefcn/graphics.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -5310,10 +5310,14 @@
   // Including ones we do don't implement?
 
   // FIXME: This function is probably never called without mode == "reset".
-  //        Verify this is the case with panic_unless() (1/6/2017).
-  //        If there are reports of problems then figure out what code is
-  //        calling it with the mode set to something else.
-  panic_unless (mode == "reset");
+  //        Error if this is not true.  If there are reports of problems
+  //        then figure out what code is calling it with the mode set to
+  //        something else.  It's apparently been this way since
+  //        1/6/2017 without any reports.  Maybe we should eliminate the
+  //        mode argument?
+
+  if (mode != "reset")
+    error (R"(axes::properties::set_defaults: expected mode = "reset", found "%s")", mode.c_str ());
 
   Matrix tlim (1, 2, 0.0);
   tlim(1) = 1;
--- a/libinterp/corefcn/mex.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/corefcn/mex.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -238,6 +238,12 @@
   extern OCTINTERP_API void mxSetImagData (mxArray *ptr, void *pi);
 }
 
+OCTAVE_NORETURN static void
+error_impossible_call (const char *fcn_name)
+{
+  error ("unexpected call to %s - please report this bug", fcn_name);
+}
+
 static void *
 xmalloc (size_t n)
 {
@@ -952,7 +958,7 @@
   void request_mutation () const
   {
     if (m_mutate_flag)
-      panic_impossible ();
+      error ("unexpected: m_mutate_flag is true in mxArray_octave_value::request_mutation - please report this bug");
 
     m_mutate_flag = true;
   }
@@ -1129,9 +1135,9 @@
 
   mxClassID get_class_id () const { return m_id; }
 
-  const char * get_class_name () const
+  static std::string get_class_name (mxClassID id)
   {
-    switch (m_id)
+    switch (id)
       {
       case mxDOUBLE_CLASS: return "double";
       case mxSINGLE_CLASS: return "single";
@@ -1154,6 +1160,11 @@
       }
   }
 
+  const char * get_class_name () const
+  {
+    return m_class_name;
+  }
+
   void set_class_name (const char *name)
   {
     mxFree (m_class_name);
@@ -1621,7 +1632,9 @@
 
     double retval = 0;
 
-    switch (get_class_id ())
+    mxClassID id = get_class_id ();
+
+    switch (id)
       {
       case mxDOUBLE_CLASS:
         retval = *(static_cast<double *> (m_pr));
@@ -1671,8 +1684,21 @@
         retval = *(static_cast<uint64_t *> (m_pr));
         break;
 
-      default:
-        panic_impossible ();
+      case mxCELL_CLASS:
+      case mxFUNCTION_CLASS:
+      case mxSTRUCT_CLASS:
+      case mxUNKNOWN_CLASS:
+      case mxVOID_CLASS:
+        {
+          std::string dest_cname = get_class_name (id);
+          error ("invalid conversion from %s mxArray to %s scalar value", get_class_name (), dest_cname.c_str ());
+        }
+        break;
+
+        // We should have handled all possible enum values above.  Rely
+        // on compiler diagnostics to warn if we haven't.  For example,
+        // GCC's -Wswitch option, enabled by -Wall, will provide a
+        // warning.
       }
 
     return retval;
@@ -1912,8 +1938,18 @@
       case mxUINT64_CLASS:
         return int_to_ov<uint64_t, uint64NDArray, octave_uint64> (dv);
 
-      default:
-        panic_impossible ();
+      case mxCELL_CLASS:
+      case mxFUNCTION_CLASS:
+      case mxSTRUCT_CLASS:
+      case mxUNKNOWN_CLASS:
+      case mxVOID_CLASS:
+        error ("invalid conversion from %s%s mxArray to octave_value", (is_complex () ? "complex " : ""), get_class_name ());
+        break;
+
+        // We should have handled all possible enum values above.  Rely
+        // on compiler diagnostics to warn if we haven't.  For example,
+        // GCC's -Wswitch option, enabled by -Wall, will provide a
+        // warning.
       }
 
     return retval;
@@ -2050,9 +2086,9 @@
 
   ~mxArray_interleaved_full () = default;
 
-  void * get_imag_data () const { panic_impossible (); }
-
-  void set_imag_data (void */*pi*/) { panic_impossible (); }
+  void * get_imag_data () const { error_impossible_call ("mxArray_interleaved_full::get_imag_data"); }
+
+  void set_imag_data (void */*pi*/) { error_impossible_call ("mxArray_interleaved_full::set_imag_data"); }
 
 protected:
 
@@ -2132,55 +2168,55 @@
     set_complexity (m_pi != nullptr);
   }
 
-  mxDouble * get_doubles () const { panic_impossible (); }
-  mxSingle * get_singles () const { panic_impossible (); }
-  mxInt8 * get_int8s () const { panic_impossible (); }
-  mxInt16 * get_int16s () const { panic_impossible (); }
-  mxInt32 * get_int32s () const { panic_impossible (); }
-  mxInt64 * get_int64s () const { panic_impossible (); }
-  mxUint8 * get_uint8s () const { panic_impossible (); }
-  mxUint16 * get_uint16s () const { panic_impossible (); }
-  mxUint32 * get_uint32s () const { panic_impossible (); }
-  mxUint64 * get_uint64s () const { panic_impossible (); }
-
-  mxComplexDouble * get_complex_doubles () const { panic_impossible (); }
-  mxComplexSingle * get_complex_singles () const { panic_impossible (); }
+  mxDouble * get_doubles () const { error_impossible_call ("mxArray_separate_full::get_doubles"); }
+  mxSingle * get_singles () const { error_impossible_call ("mxArray_separate_full::get_singles"); }
+  mxInt8 * get_int8s () const { error_impossible_call ("mxArray_separate_full::get_int8s"); }
+  mxInt16 * get_int16s () const { error_impossible_call ("mxArray_separate_full::get_int16s"); }
+  mxInt32 * get_int32s () const { error_impossible_call ("mxArray_separate_full::get_int32s"); }
+  mxInt64 * get_int64s () const { error_impossible_call ("mxArray_separate_full::get_int64s"); }
+  mxUint8 * get_uint8s () const { error_impossible_call ("mxArray_separate_full::get_uint8s"); }
+  mxUint16 * get_uint16s () const { error_impossible_call ("mxArray_separate_full::get_uint16s"); }
+  mxUint32 * get_uint32s () const { error_impossible_call ("mxArray_separate_full::get_uint32s"); }
+  mxUint64 * get_uint64s () const { error_impossible_call ("mxArray_separate_full::get_uint64s"); }
+
+  mxComplexDouble * get_complex_doubles () const { error_impossible_call ("mxArray_separate_full::get_complex_doubles"); }
+  mxComplexSingle * get_complex_singles () const { error_impossible_call ("mxArray_separate_full::get_complex_singles"); }
 
   // We don't have complex integer types, but for separate storage they
   // still would not work.
-  mxComplexInt8 * get_complex_int8s () const { panic_impossible (); }
-  mxComplexInt16 * get_complex_int16s () const { panic_impossible (); }
-  mxComplexInt32 * get_complex_int32s () const { panic_impossible (); }
-  mxComplexInt64 * get_complex_int64s () const { panic_impossible (); }
-  mxComplexUint8 * get_complex_uint8s () const { panic_impossible (); }
-  mxComplexUint16 * get_complex_uint16s () const { panic_impossible (); }
-  mxComplexUint32 * get_complex_uint32s () const { panic_impossible (); }
-  mxComplexUint64 * get_complex_uint64s () const { panic_impossible (); }
-
-  int set_doubles (mxDouble *) { panic_impossible (); }
-  int set_singles (mxSingle *) { panic_impossible (); }
-  int set_int8s (mxInt8 *) { panic_impossible (); }
-  int set_int16s (mxInt16 *) { panic_impossible (); }
-  int set_int32s (mxInt32 *) { panic_impossible (); }
-  int set_int64s (mxInt64 *) { panic_impossible (); }
-  int set_uint8s (mxUint8 *) { panic_impossible (); }
-  int set_uint16s (mxUint16 *) { panic_impossible (); }
-  int set_uint32s (mxUint32 *) { panic_impossible (); }
-  int set_uint64s (mxUint64 *) { panic_impossible (); }
-
-  int set_complex_doubles (mxComplexDouble *) { panic_impossible (); }
-  int set_complex_singles (mxComplexSingle *) { panic_impossible (); }
+  mxComplexInt8 * get_complex_int8s () const { error_impossible_call ("mxArray_separate_full::get_complex_int8s"); }
+  mxComplexInt16 * get_complex_int16s () const { error_impossible_call ("mxArray_separate_full::get_complex_int16s"); }
+  mxComplexInt32 * get_complex_int32s () const { error_impossible_call ("mxArray_separate_full::get_complex_int32s"); }
+  mxComplexInt64 * get_complex_int64s () const { error_impossible_call ("mxArray_separate_full::get_complex_int64s"); }
+  mxComplexUint8 * get_complex_uint8s () const { error_impossible_call ("mxArray_separate_full::get_complex_uint8s"); }
+  mxComplexUint16 * get_complex_uint16s () const { error_impossible_call ("mxArray_separate_full::get_complex_uint16s"); }
+  mxComplexUint32 * get_complex_uint32s () const { error_impossible_call ("mxArray_separate_full::get_complex_uint32s"); }
+  mxComplexUint64 * get_complex_uint64s () const { error_impossible_call ("mxArray_separate_full::get_complex_uint64s"); }
+
+  int set_doubles (mxDouble *) { error_impossible_call ("mxArray_separate_full::set_doubles"); }
+  int set_singles (mxSingle *) { error_impossible_call ("mxArray_separate_full::set_singles"); }
+  int set_int8s (mxInt8 *) { error_impossible_call ("mxArray_separate_full::set_int8s"); }
+  int set_int16s (mxInt16 *) { error_impossible_call ("mxArray_separate_full::set_int16s"); }
+  int set_int32s (mxInt32 *) { error_impossible_call ("mxArray_separate_full::set_int32s"); }
+  int set_int64s (mxInt64 *) { error_impossible_call ("mxArray_separate_full::set_int64s"); }
+  int set_uint8s (mxUint8 *) { error_impossible_call ("mxArray_separate_full::set_uint8s"); }
+  int set_uint16s (mxUint16 *) { error_impossible_call ("mxArray_separate_full::set_uint16s"); }
+  int set_uint32s (mxUint32 *) { error_impossible_call ("mxArray_separate_full::set_uint32s"); }
+  int set_uint64s (mxUint64 *) { error_impossible_call ("mxArray_separate_full::set_uint64s"); }
+
+  int set_complex_doubles (mxComplexDouble *) { error_impossible_call ("mxArray_separate_full::set_complex_doubles"); }
+  int set_complex_singles (mxComplexSingle *) { error_impossible_call ("mxArray_separate_full::set_complex_singles"); }
 
   // We don't have complex integer types, but for separate storage they
   // still would not work.
-  int set_complex_int8s (mxComplexInt8 *) { panic_impossible (); }
-  int set_complex_int16s (mxComplexInt16 *) { panic_impossible (); }
-  int set_complex_int32s (mxComplexInt32 *) { panic_impossible (); }
-  int set_complex_int64s (mxComplexInt64 *) { panic_impossible (); }
-  int set_complex_uint8s (mxComplexUint8 *) { panic_impossible (); }
-  int set_complex_uint16s (mxComplexUint16 *) { panic_impossible (); }
-  int set_complex_uint32s (mxComplexUint32 *) { panic_impossible (); }
-  int set_complex_uint64s (mxComplexUint64 *) { panic_impossible (); }
+  int set_complex_int8s (mxComplexInt8 *) { error_impossible_call ("mxArray_separate_full::set_complex_int8s"); }
+  int set_complex_int16s (mxComplexInt16 *) { error_impossible_call ("mxArray_separate_full::set_complex_int16s"); }
+  int set_complex_int32s (mxComplexInt32 *) { error_impossible_call ("mxArray_separate_full::set_complex_int32s"); }
+  int set_complex_int64s (mxComplexInt64 *) { error_impossible_call ("mxArray_separate_full::set_complex_int64s"); }
+  int set_complex_uint8s (mxComplexUint8 *) { error_impossible_call ("mxArray_separate_full::set_complex_uint8s"); }
+  int set_complex_uint16s (mxComplexUint16 *) { error_impossible_call ("mxArray_separate_full::set_complex_uint16s"); }
+  int set_complex_uint32s (mxComplexUint32 *) { error_impossible_call ("mxArray_separate_full::set_complex_uint32s"); }
+  int set_complex_uint64s (mxComplexUint64 *) { error_impossible_call ("mxArray_separate_full::set_complex_uint64s"); }
 
   octave_value as_octave_value () const
   {
@@ -2210,8 +2246,19 @@
       case mxUINT64_CLASS:
         error ("complex integer types are not supported");
 
-      default:
-        panic_impossible ();
+      case mxCELL_CLASS:
+      case mxCHAR_CLASS:
+      case mxFUNCTION_CLASS:
+      case mxSTRUCT_CLASS:
+      case mxUNKNOWN_CLASS:
+      case mxVOID_CLASS:
+        error ("invalid conversion from complex %s mxArray to octave_value", get_class_name ());
+        break;
+
+        // We should have handled all possible enum values above.  Rely
+        // on compiler diagnostics to warn if we haven't.  For example,
+        // GCC's -Wswitch option, enabled by -Wall, will provide a
+        // warning.
       }
 
     return retval;
@@ -2374,8 +2421,27 @@
       case mxLOGICAL_CLASS:
         return to_ov<bool> (dv);
 
-      default:
-        panic_impossible ();
+      case mxINT8_CLASS:
+      case mxUINT8_CLASS:
+      case mxINT16_CLASS:
+      case mxUINT16_CLASS:
+      case mxINT32_CLASS:
+      case mxUINT32_CLASS:
+      case mxINT64_CLASS:
+      case mxUINT64_CLASS:
+      case mxCELL_CLASS:
+      case mxCHAR_CLASS:
+      case mxFUNCTION_CLASS:
+      case mxSTRUCT_CLASS:
+      case mxUNKNOWN_CLASS:
+      case mxVOID_CLASS:
+        error ("invalid conversion from %s%s sparse mxArray to octave_value", (is_complex () ? "complex " : ""), get_class_name ());
+        break;
+
+        // We should have handled all possible enum values above.  Rely
+        // on compiler diagnostics to warn if we haven't.  For example,
+        // GCC's -Wswitch option, enabled by -Wall, will provide a
+        // warning.
       }
 
     return retval;
@@ -2476,9 +2542,9 @@
 
   ~mxArray_interleaved_sparse () = default;
 
-  void * get_imag_data () const { panic_impossible (); }
-
-  void set_imag_data (void */*pi*/) { panic_impossible (); }
+  void * get_imag_data () const { error_impossible_call ("mxArray_interleaved_sparse::get_imag_data"); }
+
+  void set_imag_data (void */*pi*/) { error_impossible_call ("mxArray_interleaved_sparse::set_imag_data"); }
 };
 
 class mxArray_separate_sparse : public mxArray_base_sparse
@@ -2532,11 +2598,11 @@
     set_complexity (m_pi != nullptr);
   }
 
-  mxDouble * get_doubles () const { panic_impossible (); }
-  mxComplexDouble * get_complex_doubles () const { panic_impossible (); }
-
-  int set_doubles (mxDouble *) { panic_impossible (); }
-  int set_complex_doubles (mxComplexDouble *) { panic_impossible (); }
+  mxDouble * get_doubles () const { error_impossible_call ("mxArray_separate_sparse::get_doubles"); }
+  mxComplexDouble * get_complex_doubles () const { error_impossible_call ("mxArray_separate_sparse::get_complex_doubles"); }
+
+  int set_doubles (mxDouble *) { error_impossible_call ("mxArray_separate_sparse::set_doubles"); }
+  int set_complex_doubles (mxComplexDouble *) { error_impossible_call ("mxArray_separate_sparse::set_complex_doubles"); }
 
   octave_value as_octave_value () const
   {
@@ -2573,8 +2639,28 @@
       case mxSINGLE_CLASS:
         error ("single precision sparse data type not supported");
 
-      default:
-        panic_impossible ();
+      case mxLOGICAL_CLASS:
+      case mxINT8_CLASS:
+      case mxUINT8_CLASS:
+      case mxINT16_CLASS:
+      case mxUINT16_CLASS:
+      case mxINT32_CLASS:
+      case mxUINT32_CLASS:
+      case mxINT64_CLASS:
+      case mxUINT64_CLASS:
+      case mxCELL_CLASS:
+      case mxCHAR_CLASS:
+      case mxFUNCTION_CLASS:
+      case mxSTRUCT_CLASS:
+      case mxUNKNOWN_CLASS:
+      case mxVOID_CLASS:
+        error ("invalid conversion from complex %s sparse mxArray to octave_value", get_class_name ());
+        break;
+
+        // We should have handled all possible enum values above.  Rely
+        // on compiler diagnostics to warn if we haven't.  For example,
+        // GCC's -Wswitch option, enabled by -Wall, will provide a
+        // warning.
       }
 
     return retval;
--- a/libinterp/corefcn/mx-type-traits.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/corefcn/mx-type-traits.h	Sat Apr 06 17:38:27 2024 -0400
@@ -28,6 +28,8 @@
 
 #include "octave-config.h"
 
+#include <string>
+
 #include "mxtypes.h"
 #include "oct-inttypes-fwd.h"
 
--- a/libinterp/corefcn/ordqz.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/corefcn/ordqz.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -329,9 +329,14 @@
               else
                 select(k) = false;
               break;
-            default:
-              // default: case just here to suppress compiler warning.
-              panic_impossible ();
+            case NONE:
+              error ("ordqz: invalid select mode NONE");
+              break;
+
+              // We should have handled all possible enum values above.
+              // Rely on compiler diagnostics to warn if we haven't.
+              // For example, GCC's -Wswitch option, enabled by -Wall,
+              // will provide a warning.
             }
         }
 
@@ -450,9 +455,14 @@
               else
                 select(k) = false;
               break;
-            default:
-              // default: case just here to suppress compiler warning.
-              panic_impossible();
+            case NONE:
+              error ("ordqz: invalid select mode NONE");
+              break;
+
+              // We should have handled all possible enum values above.
+              // Rely on compiler diagnostics to warn if we haven't.
+              // For example, GCC's -Wswitch option, enabled by -Wall,
+              // will provide a warning.
             }
         }
 
--- a/libinterp/corefcn/stack-frame.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/corefcn/stack-frame.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -3257,7 +3257,7 @@
   // scope_stack_frame objects.  Anything else indicates an error in
   // the implementation.
 
-  panic_impossible ();
+  error ("unexpected call to stack_frame::size () - please report this bug");
 }
 
 void
@@ -3267,7 +3267,7 @@
   // scope_stack_frame objects.  Anything else indicates an error in
   // the implementation.
 
-  panic_impossible ();
+  error ("unexpected call to stack_frame::resize () - please report this bug");
 }
 
 stack_frame::scope_flags
@@ -3277,7 +3277,7 @@
   // scope_stack_frame objects.  Anything else indicates an error in
   // the implementation.
 
-  panic_impossible ();
+  error ("unexpected call to stack_frame::get_scope_flag (std::size_t) - please report this bug");
 }
 
 void
@@ -3287,7 +3287,7 @@
   // scope_stack_frame objects.  Anything else indicates an error in
   // the implementation.
 
-  panic_impossible ();
+  error ("unexpected call to stack_frame::get_scope_flag (std::size_t, scope_flags) - please report this bug");
 }
 
 void
@@ -3344,7 +3344,7 @@
   // scope_stack_frame objects.  Anything else indicates an error in
   // the implementation.
 
-  panic_impossible ();
+  error ("unexpected call to stack_frame::varval (std::size_t) - please report this bug");
 }
 
 octave_value&
@@ -3358,7 +3358,7 @@
   // scope_stack_frame objects.  Anything else indicates an error in
   // the implementation.
 
-  panic_impossible ();
+  error ("unexpected call to stack_frame::varref (std::size_t) - please report this bug");
 }
 
 std::string
@@ -3367,7 +3367,7 @@
   // This function should only be called for user_fcn_stack_frame.
   // Anything else indicates an error in the implementation.
 
-  panic_impossible ();
+  error ("unexpected call to stack_frame::inputname (int, bool) - please report this bug");
 }
 
 void
@@ -3664,7 +3664,8 @@
   // scope.  If the symbol wasn't present before, it should be outside
   // the range so we need to resize then update offsets.
 
-  panic_unless (data_offset >= size ());
+  if (data_offset < size ())
+    error ("unexpected: data_offset < size () in script_stack_frame::resize_and_update_script_offsets - please report this bug");
 
   resize (data_offset+1);
 
@@ -3717,7 +3718,8 @@
 
   if (sym)
     {
-      panic_unless (sym.frame_offset () == 0);
+      if (sym.frame_offset () != 0)
+        error ("unexpected: sym.frame_offset () != 0 in script_stack_frame::lookup_symbol - please report this bug");
 
       return sym;
     }
@@ -3747,7 +3749,9 @@
       // All symbol records in a script scope should have zero offset,
       // which means we redirect our lookup using
       // lexical_frame_offsets and values_offets.
-      panic_unless (sym.frame_offset () == 0);
+
+      if (sym.frame_offset () != 0)
+        error ("unexpected: sym.frame_offset () != 0 in script_stack_frame::insert_symbol - please report this bug");
 
       return sym;
     }
@@ -3757,7 +3761,8 @@
 
   sym = scope.find_symbol (name);
 
-  panic_unless (sym.is_valid ());
+  if (! sym.is_valid ())
+    error ("unexpected: sym is not valid in script_stack_frame::insert_symbol - please report this bug");
 
   resize_and_update_script_offsets (sym);
 
@@ -4309,7 +4314,8 @@
 
   sym = scope.find_symbol (name);
 
-  panic_unless (sym.is_valid ());
+  if (! sym.is_valid ())
+    error ("unexpected: sym is not valid in user_fcn_stack_frame::insert_symbol - please report this bug");
 
   return sym;
 }
@@ -4520,7 +4526,8 @@
 
   sym = m_scope.find_symbol (name);
 
-  panic_unless (sym.is_valid ());
+  if (! sym.is_valid ())
+    error ("unexpected: sym is not valid in scope_stack_frame::insert_symbol - please report this bug");
 
   return sym;
 }
--- a/libinterp/corefcn/symrec.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/corefcn/symrec.h	Sat Apr 06 17:38:27 2024 -0400
@@ -40,9 +40,7 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
-class symbol_scope_rep;
-
-class symbol_record
+class OCTINTERP_API symbol_record
 {
 public:
 
--- a/libinterp/corefcn/symscope.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/corefcn/symscope.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -42,7 +42,7 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
-symbol_record symbol_scope_rep::insert_local (const std::string& name)
+symbol_record symbol_scope::symbol_scope_rep::insert_local (const std::string& name)
 {
   symbol_record sym (name);
 
@@ -52,7 +52,7 @@
 }
 
 void
-symbol_scope_rep::insert_symbol_record (symbol_record& sr)
+symbol_scope::symbol_scope_rep::insert_symbol_record (symbol_record& sr)
 {
   std::size_t data_offset = num_symbols ();
   std::string name = sr.name ();
@@ -63,7 +63,7 @@
 }
 
 symbol_record
-symbol_scope_rep::insert (const std::string& name)
+symbol_scope::symbol_scope_rep::insert (const std::string& name)
 {
   table_iterator p = m_symbols.find (name);
 
@@ -95,7 +95,7 @@
 }
 
 std::list<octave_value>
-symbol_scope_rep::localfunctions () const
+symbol_scope::symbol_scope_rep::localfunctions () const
 {
   std::list<octave_value> retval;
 
@@ -133,7 +133,7 @@
 }
 
 octave_value
-symbol_scope_rep::dump () const
+symbol_scope::symbol_scope_rep::dump () const
 {
   std::map<std::string, octave_value> m
   = {{ "name", m_name },
@@ -148,7 +148,7 @@
 }
 
 octave_value
-symbol_scope_rep::dump_symbols_map () const
+symbol_scope::symbol_scope_rep::dump_symbols_map () const
 {
   std::map<std::string, octave_value> info_map;
 
@@ -163,7 +163,7 @@
 }
 
 std::list<symbol_record>
-symbol_scope_rep::symbol_list () const
+symbol_scope::symbol_scope_rep::symbol_list () const
 {
   std::list<symbol_record> retval;
 
@@ -174,7 +174,7 @@
 }
 
 octave_value
-symbol_scope_rep::find_subfunction (const std::string& name) const
+symbol_scope::symbol_scope_rep::find_subfunction (const std::string& name) const
 {
   subfunctions_const_iterator p = m_subfunctions.find (name);
 
@@ -190,7 +190,7 @@
 }
 
 void
-symbol_scope_rep::mark_subfunctions_in_scope_as_private (const std::string& class_name)
+symbol_scope::symbol_scope_rep::mark_subfunctions_in_scope_as_private (const std::string& class_name)
 {
   for (auto& nm_sf : m_subfunctions)
     {
@@ -202,7 +202,7 @@
 }
 
 std::list<std::string>
-symbol_scope_rep::parent_fcn_names () const
+symbol_scope::symbol_scope_rep::parent_fcn_names () const
 {
   std::list<std::string> retval;
 
@@ -219,25 +219,25 @@
 }
 
 void
-symbol_scope_rep::set_parent (const std::shared_ptr<symbol_scope_rep>& parent)
+symbol_scope::symbol_scope_rep::set_parent (const std::shared_ptr<symbol_scope::symbol_scope_rep>& parent)
 {
-  m_parent = std::weak_ptr<symbol_scope_rep> (parent);
+  m_parent = std::weak_ptr<symbol_scope::symbol_scope_rep> (parent);
 }
 
 void
-symbol_scope_rep::set_primary_parent (const std::shared_ptr<symbol_scope_rep>& parent)
+symbol_scope::symbol_scope_rep::set_primary_parent (const std::shared_ptr<symbol_scope::symbol_scope_rep>& parent)
 {
-  m_primary_parent = std::weak_ptr<symbol_scope_rep> (parent);
+  m_primary_parent = std::weak_ptr<symbol_scope::symbol_scope_rep> (parent);
 }
 
 void
-symbol_scope_rep::cache_dir_name (const std::string& name)
+symbol_scope::symbol_scope_rep::cache_dir_name (const std::string& name)
 {
   m_dir_name = sys::canonicalize_file_name (name);
 }
 
 bool
-symbol_scope_rep::is_relative (const std::shared_ptr<symbol_scope_rep>& scope) const
+symbol_scope::symbol_scope_rep::is_relative (const std::shared_ptr<symbol_scope::symbol_scope_rep>& scope) const
 {
   if (is_nested ())
     {
@@ -276,7 +276,7 @@
 }
 
 void
-symbol_scope_rep::mark_as_variable (const std::string& nm)
+symbol_scope::symbol_scope_rep::mark_as_variable (const std::string& nm)
 {
   table_iterator p = m_symbols.find (nm);
 
@@ -285,14 +285,14 @@
 }
 
 void
-symbol_scope_rep::mark_as_variables (const std::list<std::string>& lst)
+symbol_scope::symbol_scope_rep::mark_as_variables (const std::list<std::string>& lst)
 {
   for (const auto& nm : lst)
     mark_as_variable (nm);
 }
 
 bool
-symbol_scope_rep::is_variable (const std::string& nm) const
+symbol_scope::symbol_scope_rep::is_variable (const std::string& nm) const
 {
   table_const_iterator p = m_symbols.find (nm);
 
@@ -312,7 +312,7 @@
 }
 
 void
-symbol_scope_rep::update_nest ()
+symbol_scope::symbol_scope_rep::update_nest ()
 {
   auto t_parent = m_parent.lock ();
 
@@ -344,9 +344,9 @@
 }
 
 bool
-symbol_scope_rep::look_nonlocal (const std::string& name,
-                                 std::size_t offset,
-                                 symbol_record& result)
+symbol_scope::symbol_scope_rep::look_nonlocal (const std::string& name,
+                                               std::size_t offset,
+                                               symbol_record& result)
 {
   offset++;
 
@@ -388,7 +388,7 @@
   if (is_primary_fcn_scope ())
     return m_rep->localfunctions ();
 
-  std::shared_ptr<symbol_scope_rep> ppsr
+  std::shared_ptr<symbol_scope::symbol_scope_rep> ppsr
     = m_rep->primary_parent_scope_rep ();
 
   if (! ppsr)
--- a/libinterp/corefcn/symscope.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/corefcn/symscope.h	Sat Apr 06 17:38:27 2024 -0400
@@ -48,327 +48,327 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
-class symbol_scope;
-
-class symbol_scope_rep
-  : public std::enable_shared_from_this<symbol_scope_rep>
+class OCTINTERP_API symbol_scope
 {
-public:
-
-  typedef std::map<std::string, symbol_record>::const_iterator
-    table_const_iterator;
-  typedef std::map<std::string, symbol_record>::iterator
-    table_iterator;
-
-  typedef std::map<std::string, octave_value>::const_iterator
-    subfunctions_const_iterator;
-  typedef std::map<std::string, octave_value>::iterator
-    subfunctions_iterator;
-
-  symbol_scope_rep (const std::string& name = "", bool add_ans = true)
-    : m_name (name), m_symbols (), m_subfunctions (),
-      m_persistent_values (), m_code (nullptr), m_fcn_name (),
-      m_fcn_file_name (), m_dir_name (), m_parent (),
-      m_primary_parent (), m_children (), m_nesting_depth (0),
-      m_is_static (false), m_is_primary_fcn_scope (false)
-  {
-    // Most scopes have ans as the first symbol, initially undefined.
-    if (add_ans)
-      insert_local ("ans");
-  }
-
-  OCTAVE_DISABLE_COPY_MOVE (symbol_scope_rep)
-
-  ~symbol_scope_rep () = default;
-
-  std::size_t num_symbols () const { return m_symbols.size (); }
-
-  // Simply inserts symbol.  No non-local searching.
-
-  symbol_record insert_local (const std::string& name);
-
-  void insert_symbol_record (symbol_record& sr);
-
-  bool is_nested () const { return m_nesting_depth > 0; }
-
-  std::size_t nesting_depth () const { return m_nesting_depth; }
-
-  void set_nesting_depth (std::size_t depth) { m_nesting_depth = depth; }
-
-  bool is_parent () const { return ! m_children.empty (); }
-
-  bool is_static () const { return m_is_static; }
-
-  void mark_static () { m_is_static = true; }
-
-  std::shared_ptr<symbol_scope_rep> parent_scope_rep () const
-  {
-    return m_parent.lock ();
-  }
-
-  std::shared_ptr<symbol_scope_rep> primary_parent_scope_rep () const
-  {
-    return m_primary_parent.lock ();
-  }
-
-  std::shared_ptr<symbol_scope_rep> dup () const
-  {
-    std::shared_ptr<symbol_scope_rep> new_sid
-      = std::shared_ptr<symbol_scope_rep> (new symbol_scope_rep (m_name));
-
-    for (const auto& nm_sr : m_symbols)
-      new_sid->m_symbols[nm_sr.first] = nm_sr.second.dup ();
-
-    new_sid->m_subfunctions = m_subfunctions;
-    new_sid->m_persistent_values = m_persistent_values;
-    new_sid->m_subfunction_names = m_subfunction_names;
-    new_sid->m_code = m_code;
-    new_sid->m_fcn_name = m_fcn_name;
-    new_sid->m_fcn_file_name = m_fcn_file_name;
-    new_sid->m_dir_name = m_dir_name;
-    new_sid->m_parent = m_parent;
-    new_sid->m_primary_parent = m_primary_parent;
-    new_sid->m_children = m_children;
-    new_sid->m_nesting_depth = m_nesting_depth;
-    new_sid->m_is_static = m_is_static;
-    new_sid->m_is_primary_fcn_scope = m_is_primary_fcn_scope;
-
-    return new_sid;
-  }
-
-  octave_value& persistent_varref (std::size_t data_offset)
-  {
-    return m_persistent_values[data_offset];
-  }
-
-  octave_value persistent_varval (std::size_t data_offset) const
-  {
-    auto p = m_persistent_values.find (data_offset);
-
-    return p == m_persistent_values.end () ? octave_value () : p->second;
-  }
-
-  symbol_record find_symbol (const std::string& name)
-  {
-    auto p = m_symbols.find (name);
-
-    if (p == m_symbols.end ())
-      return insert (name);
-    else
-      return p->second;
-  }
-
-  symbol_record lookup_symbol (const std::string& name) const
-  {
-    auto p = m_symbols.find (name);
-
-    return p == m_symbols.end () ? symbol_record () : p->second;
-  }
-
-  symbol_record insert (const std::string& name);
-
-  void rename (const std::string& old_name, const std::string& new_name)
-  {
-    auto p = m_symbols.find (old_name);
-
-    if (p != m_symbols.end ())
-      {
-        symbol_record sr = p->second;
-
-        sr.rename (new_name);
-
-        m_symbols.erase (p);
-
-        m_symbols[new_name] = sr;
-      }
-  }
-
-  void install_subfunction (const std::string& name,
-                            const octave_value& fval)
-  {
-    m_subfunctions[name] = fval;
-  }
-
-  void install_nestfunction (const std::string& name,
-                             const octave_value& fval,
-                             const symbol_scope& fcn_scope)
-  {
-    m_subfunctions[name] = fval;
-
-    m_children.push_back (fcn_scope);
-  }
-
-  octave_value find_subfunction (const std::string& name) const;
-
-  void lock_subfunctions ()
-  {
-    for (auto& nm_sf : m_subfunctions)
-      nm_sf.second.lock ();
-  }
-
-  void unlock_subfunctions ()
-  {
-    for (auto& nm_sf : m_subfunctions)
-      nm_sf.second.unlock ();
-  }
-
-  // Pairs of name, function objects.
-  std::map<std::string, octave_value> subfunctions () const
-  {
-    return m_subfunctions;
-  }
-
-  void erase_subfunctions ()
-  {
-    m_subfunctions.clear ();
-  }
-
-  void mark_subfunctions_in_scope_as_private (const std::string& class_name);
-
-  bool has_subfunctions () const
-  {
-    return ! m_subfunction_names.empty ();
-  }
-
-  void stash_subfunction_names (const std::list<std::string>& names)
-  {
-    m_subfunction_names = names;
-  }
-
-  std::list<std::string> subfunction_names () const
-  {
-    return m_subfunction_names;
-  }
-
-  std::list<octave_value> localfunctions () const;
-
-  octave_value dump () const;
-
-  std::string name () const { return m_name; }
-
-  void cache_name (const std::string& name) { m_name = name; }
-
-  std::string fcn_name () const { return m_fcn_name; }
-
-  void cache_fcn_name (const std::string& name) { m_fcn_name = name; }
-
-  std::list<std::string> parent_fcn_names () const;
-
-  octave_user_code * user_code () const { return m_code; }
-
-  void set_user_code (octave_user_code *code) { m_code = code; }
-
-  void set_parent (const std::shared_ptr<symbol_scope_rep>& parent);
-
-  void set_primary_parent (const std::shared_ptr<symbol_scope_rep>& parent);
-
-  void cache_fcn_file_name (const std::string& name)
-  {
-    m_fcn_file_name = name;
-  }
-
-  std::string fcn_file_name () const { return m_fcn_file_name; }
-
-  void cache_dir_name (const std::string& name);
-
-  std::string dir_name () const { return m_dir_name; }
-
-  void mark_primary_fcn_scope () { m_is_primary_fcn_scope = true; }
-
-  bool is_primary_fcn_scope () const { return m_is_primary_fcn_scope; }
-
-  bool is_relative (const std::shared_ptr<symbol_scope_rep>& scope) const;
-
-  void mark_as_variable (const std::string& nm);
-  void mark_as_variables (const std::list<std::string>& lst);
-
-  bool is_variable (const std::string& nm) const;
-
-  void update_nest ();
-
-  bool look_nonlocal (const std::string& name, std::size_t offset,
-                      symbol_record& result);
-
-  octave_value dump_symbols_map () const;
-
-  const std::map<std::string, symbol_record>& symbols () const
-  {
-    return m_symbols;
-  }
-
-  std::map<std::string, symbol_record>& symbols ()
-  {
-    return m_symbols;
-  }
-
-  std::list<symbol_record> symbol_list () const;
-
 private:
 
-  //! Name for this scope (usually the corresponding filename of the
-  //! function corresponding to the scope).
+  class symbol_scope_rep
+    : public std::enable_shared_from_this<symbol_scope_rep>
+  {
+  public:
+
+    typedef std::map<std::string, symbol_record>::const_iterator
+      table_const_iterator;
+    typedef std::map<std::string, symbol_record>::iterator
+      table_iterator;
+
+    typedef std::map<std::string, octave_value>::const_iterator
+      subfunctions_const_iterator;
+    typedef std::map<std::string, octave_value>::iterator
+      subfunctions_iterator;
 
-  std::string m_name;
+    symbol_scope_rep (const std::string& name = "", bool add_ans = true)
+      : m_name (name), m_symbols (), m_subfunctions (),
+        m_persistent_values (), m_code (nullptr), m_fcn_name (),
+        m_fcn_file_name (), m_dir_name (), m_parent (),
+        m_primary_parent (), m_children (), m_nesting_depth (0),
+        m_is_static (false), m_is_primary_fcn_scope (false)
+    {
+      // Most scopes have ans as the first symbol, initially undefined.
+      if (add_ans)
+        insert_local ("ans");
+    }
+
+    OCTAVE_DISABLE_COPY_MOVE (symbol_scope_rep)
+
+    ~symbol_scope_rep () = default;
+
+    std::size_t num_symbols () const { return m_symbols.size (); }
 
-  //! Map from symbol names to symbol info.
+    // Simply inserts symbol.  No non-local searching.
+
+    symbol_record insert_local (const std::string& name);
+
+    void insert_symbol_record (symbol_record& sr);
+
+    bool is_nested () const { return m_nesting_depth > 0; }
 
-  std::map<std::string, symbol_record> m_symbols;
+    std::size_t nesting_depth () const { return m_nesting_depth; }
+
+    void set_nesting_depth (std::size_t depth) { m_nesting_depth = depth; }
+
+    bool is_parent () const { return ! m_children.empty (); }
+
+    bool is_static () const { return m_is_static; }
+
+    void mark_static () { m_is_static = true; }
 
-  //! Map from symbol names to subfunctions.
+    std::shared_ptr<symbol_scope_rep> parent_scope_rep () const
+    {
+      return m_parent.lock ();
+    }
 
-  std::map<std::string, octave_value> m_subfunctions;
+    std::shared_ptr<symbol_scope_rep> primary_parent_scope_rep () const
+    {
+      return m_primary_parent.lock ();
+    }
+
+    std::shared_ptr<symbol_scope_rep> dup () const
+    {
+      std::shared_ptr<symbol_scope_rep> new_sid
+        = std::shared_ptr<symbol_scope_rep> (new symbol_scope_rep (m_name));
+
+      for (const auto& nm_sr : m_symbols)
+        new_sid->m_symbols[nm_sr.first] = nm_sr.second.dup ();
 
-  //! Map from data offset to persistent values in this scope.
-  std::map<std::size_t, octave_value> m_persistent_values;
+      new_sid->m_subfunctions = m_subfunctions;
+      new_sid->m_persistent_values = m_persistent_values;
+      new_sid->m_subfunction_names = m_subfunction_names;
+      new_sid->m_code = m_code;
+      new_sid->m_fcn_name = m_fcn_name;
+      new_sid->m_fcn_file_name = m_fcn_file_name;
+      new_sid->m_dir_name = m_dir_name;
+      new_sid->m_parent = m_parent;
+      new_sid->m_primary_parent = m_primary_parent;
+      new_sid->m_children = m_children;
+      new_sid->m_nesting_depth = m_nesting_depth;
+      new_sid->m_is_static = m_is_static;
+      new_sid->m_is_primary_fcn_scope = m_is_primary_fcn_scope;
+
+      return new_sid;
+    }
 
-  //! The list of subfunctions (if any) in the order they appear in
-  //! the function file.
+    octave_value& persistent_varref (std::size_t data_offset)
+    {
+      return m_persistent_values[data_offset];
+    }
+
+    octave_value persistent_varval (std::size_t data_offset) const
+    {
+      auto p = m_persistent_values.find (data_offset);
+
+      return p == m_persistent_values.end () ? octave_value () : p->second;
+    }
 
-  std::list<std::string> m_subfunction_names;
+    symbol_record find_symbol (const std::string& name)
+    {
+      auto p = m_symbols.find (name);
+
+      if (p == m_symbols.end ())
+        return insert (name);
+      else
+        return p->second;
+    }
+
+    symbol_record lookup_symbol (const std::string& name) const
+    {
+      auto p = m_symbols.find (name);
 
-  //! The associated user code (may be null).
+      return p == m_symbols.end () ? symbol_record () : p->second;
+    }
+
+    symbol_record insert (const std::string& name);
+
+    void rename (const std::string& old_name, const std::string& new_name)
+    {
+      auto p = m_symbols.find (old_name);
 
-  octave_user_code *m_code;
+      if (p != m_symbols.end ())
+        {
+          symbol_record sr = p->second;
+
+          sr.rename (new_name);
+
+          m_symbols.erase (p);
+
+          m_symbols[new_name] = sr;
+        }
+    }
 
-  //! Simple name of the function corresponding to this scope.
+    void install_subfunction (const std::string& name,
+                              const octave_value& fval)
+    {
+      m_subfunctions[name] = fval;
+    }
 
-  std::string m_fcn_name;
+    void install_nestfunction (const std::string& name,
+                               const octave_value& fval,
+                               const symbol_scope& fcn_scope)
+    {
+      m_subfunctions[name] = fval;
 
-  //! The file name associated with m_code.
+      m_children.push_back (fcn_scope);
+    }
+
+    octave_value find_subfunction (const std::string& name) const;
+
+    void lock_subfunctions ()
+    {
+      for (auto& nm_sf : m_subfunctions)
+        nm_sf.second.lock ();
+    }
 
-  std::string m_fcn_file_name;
+    void unlock_subfunctions ()
+    {
+      for (auto& nm_sf : m_subfunctions)
+        nm_sf.second.unlock ();
+    }
 
-  //! The directory associated with m_code.
+    // Pairs of name, function objects.
+    std::map<std::string, octave_value> subfunctions () const
+    {
+      return m_subfunctions;
+    }
+
+    void erase_subfunctions ()
+    {
+      m_subfunctions.clear ();
+    }
+
+    void mark_subfunctions_in_scope_as_private (const std::string& class_name);
 
-  std::string m_dir_name;
+    bool has_subfunctions () const
+    {
+      return ! m_subfunction_names.empty ();
+    }
+
+    void stash_subfunction_names (const std::list<std::string>& names)
+    {
+      m_subfunction_names = names;
+    }
 
-  //! Parent of nested function (may be null).
+    std::list<std::string> subfunction_names () const
+    {
+      return m_subfunction_names;
+    }
+
+    std::list<octave_value> localfunctions () const;
+
+    octave_value dump () const;
+
+    std::string name () const { return m_name; }
 
-  std::weak_ptr<symbol_scope_rep> m_parent;
+    void cache_name (const std::string& name) { m_name = name; }
+
+    std::string fcn_name () const { return m_fcn_name; }
+
+    void cache_fcn_name (const std::string& name) { m_fcn_name = name; }
+
+    std::list<std::string> parent_fcn_names () const;
+
+    octave_user_code * user_code () const { return m_code; }
+
+    void set_user_code (octave_user_code *code) { m_code = code; }
+
+    void set_parent (const std::shared_ptr<symbol_scope_rep>& parent);
+
+    void set_primary_parent (const std::shared_ptr<symbol_scope_rep>& parent);
 
-  //! Primary (top) parent of nested function (may be null).  Used
-  //! to determine whether two nested functions are related.
+    void cache_fcn_file_name (const std::string& name)
+    {
+      m_fcn_file_name = name;
+    }
+
+    std::string fcn_file_name () const { return m_fcn_file_name; }
+
+    void cache_dir_name (const std::string& name);
+
+    std::string dir_name () const { return m_dir_name; }
 
-  std::weak_ptr<symbol_scope_rep> m_primary_parent;
+    void mark_primary_fcn_scope () { m_is_primary_fcn_scope = true; }
+
+    bool is_primary_fcn_scope () const { return m_is_primary_fcn_scope; }
+
+    bool is_relative (const std::shared_ptr<symbol_scope_rep>& scope) const;
 
-  //! Child nested functions.
+    void mark_as_variable (const std::string& nm);
+    void mark_as_variables (const std::list<std::string>& lst);
+
+    bool is_variable (const std::string& nm) const;
+
+    void update_nest ();
 
-  std::vector<symbol_scope> m_children;
+    bool look_nonlocal (const std::string& name, std::size_t offset,
+                        symbol_record& result);
+
+    octave_value dump_symbols_map () const;
 
-  //! If true, then this scope belongs to a nested function.
+    const std::map<std::string, symbol_record>& symbols () const
+    {
+      return m_symbols;
+    }
+
+    std::map<std::string, symbol_record>& symbols ()
+    {
+      return m_symbols;
+    }
+
+    std::list<symbol_record> symbol_list () const;
+
+  private:
 
-  std::size_t m_nesting_depth;
+    //! Name for this scope (usually the corresponding filename of the
+    //! function corresponding to the scope).
+
+    std::string m_name;
+
+    //! Map from symbol names to symbol info.
+
+    std::map<std::string, symbol_record> m_symbols;
 
-  //! If true then no variables can be added.
+    //! Map from symbol names to subfunctions.
+
+    std::map<std::string, octave_value> m_subfunctions;
+
+    //! Map from data offset to persistent values in this scope.
+    std::map<std::size_t, octave_value> m_persistent_values;
+
+    //! The list of subfunctions (if any) in the order they appear in
+    //! the function file.
+
+    std::list<std::string> m_subfunction_names;
 
-  bool m_is_static;
+    //! The associated user code (may be null).
+
+    octave_user_code *m_code;
+
+    //! Simple name of the function corresponding to this scope.
+
+    std::string m_fcn_name;
+
+    //! The file name associated with m_code.
+
+    std::string m_fcn_file_name;
+
+    //! The directory associated with m_code.
+
+    std::string m_dir_name;
+
+    //! Parent of nested function (may be null).
+
+    std::weak_ptr<symbol_scope_rep> m_parent;
 
-  //! If true, this is the scope of a primary function.
-  bool m_is_primary_fcn_scope;
-};
+    //! Primary (top) parent of nested function (may be null).  Used
+    //! to determine whether two nested functions are related.
+
+    std::weak_ptr<symbol_scope_rep> m_primary_parent;
+
+    //! Child nested functions.
+
+    std::vector<symbol_scope> m_children;
+
+    //! If true, then this scope belongs to a nested function.
 
-class symbol_scope
-{
+    std::size_t m_nesting_depth;
+
+    //! If true then no variables can be added.
+
+    bool m_is_static;
+
+    //! If true, this is the scope of a primary function.
+    bool m_is_primary_fcn_scope;
+  };
+
 public:
 
   symbol_scope () = delete;
--- a/libinterp/octave-value/module.mk	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/module.mk	Sat Apr 06 17:38:27 2024 -0400
@@ -99,6 +99,9 @@
   %reldir%/cdef-package.cc \
   %reldir%/cdef-property.cc \
   %reldir%/cdef-utils.cc \
+  %reldir%/ov-base-int-inst.cc \
+  %reldir%/ov-base-mat-inst.cc \
+  %reldir%/ov-base-scalar-inst.cc \
   %reldir%/ov-base.cc \
   %reldir%/ov-bool-mat.cc \
   %reldir%/ov-bool.cc \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/octave-value/ov-base-int-inst.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -0,0 +1,60 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2024 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include "int8NDArray.h"
+#include "int16NDArray.h"
+#include "int32NDArray.h"
+#include "int64NDArray.h"
+#include "uint8NDArray.h"
+#include "uint16NDArray.h"
+#include "uint32NDArray.h"
+#include "uint64NDArray.h"
+
+#include "ov-base-int.cc"
+
+// instantiate template class with types that need to be exported from library
+
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_matrix<int8NDArray>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_matrix<int16NDArray>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_matrix<int32NDArray>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_matrix<int64NDArray>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_matrix<uint8NDArray>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_matrix<uint16NDArray>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_matrix<uint32NDArray>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_matrix<uint64NDArray>;
+
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_scalar<octave_int8>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_scalar<octave_int16>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_scalar<octave_int32>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_scalar<octave_int64>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_scalar<octave_uint8>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_scalar<octave_uint16>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_scalar<octave_uint32>;
+template class OCTINTERP_CLASS_TEMPLATE_INSTANTIATION_API octave_base_int_scalar<octave_uint64>;
+
--- a/libinterp/octave-value/ov-base-int.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-base-int.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -58,10 +58,6 @@
 #include "oct-stream.h"
 #include "ops.h"
 #include "ov-base.h"
-#include "ov-base-mat.h"
-#include "ov-base-mat.cc"
-#include "ov-base-scalar.h"
-#include "ov-base-scalar.cc"
 #include "ov-base-int.h"
 #include "ov-int-traits.h"
 #include "pr-output.h"
--- a/libinterp/octave-value/ov-base-int.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-base-int.h	Sat Apr 06 17:38:27 2024 -0400
@@ -44,8 +44,17 @@
 
 // base int matrix values.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<int8NDArray>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<int16NDArray>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<int32NDArray>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<int64NDArray>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<uint8NDArray>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<uint16NDArray>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<uint32NDArray>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<uint64NDArray>;
+
 template <typename T>
-class OCTINTERP_API octave_base_int_matrix : public octave_base_matrix<T>
+class OCTINTERP_TEMPLATE_API octave_base_int_matrix : public octave_base_matrix<T>
 {
 public:
 
@@ -63,7 +72,7 @@
 
   OCTINTERP_API octave_base_value * try_narrowing_conversion ();
 
-  bool isreal () const { return true; }
+  OCTINTERP_OVERRIDABLE_FUNC_API bool isreal () const { return true; }
 
   //  void increment () { matrix += 1; }
 
@@ -114,29 +123,45 @@
 
 // base int scalar values.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_scalar<octave_int8>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_scalar<octave_int16>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_scalar<octave_int32>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_scalar<octave_int64>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_scalar<octave_uint8>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_scalar<octave_uint16>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_scalar<octave_uint32>;
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_scalar<octave_uint64>;
+
 template <typename T>
-class OCTINTERP_API octave_base_int_scalar : public octave_base_scalar<T>
+class OCTINTERP_TEMPLATE_API octave_base_int_scalar : public octave_base_scalar<T>
 {
 public:
 
+  OCTINTERP_OVERRIDABLE_FUNC_API
   octave_base_int_scalar () : octave_base_scalar<T> () { }
 
+  OCTINTERP_OVERRIDABLE_FUNC_API
   octave_base_int_scalar (const T& s) : octave_base_scalar<T> (s) { }
 
-  ~octave_base_int_scalar () = default;
+  OCTINTERP_OVERRIDABLE_FUNC_API ~octave_base_int_scalar () = default;
 
-  octave_base_value * clone () const
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_base_value * clone () const
   { return new octave_base_int_scalar (*this); }
-  octave_base_value * empty_clone () const
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_base_value * empty_clone () const
   { return new octave_base_int_scalar (); }
 
-  octave_base_value * try_narrowing_conversion () { return nullptr; }
-
-  bool is_maybe_function () const { return false; }
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_base_value *
+  try_narrowing_conversion ()
+  { return nullptr; }
 
-  bool isreal () const { return true; }
+  OCTINTERP_OVERRIDABLE_FUNC_API bool is_maybe_function () const
+  { return false; }
 
-  bool is_real_scalar () const { return true; }
+  OCTINTERP_OVERRIDABLE_FUNC_API bool isreal () const
+  { return true; }
+
+  OCTINTERP_OVERRIDABLE_FUNC_API bool is_real_scalar () const
+  { return true; }
 
   //  void increment () { scalar += 1; }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/octave-value/ov-base-mat-inst.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -0,0 +1,129 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2024 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include "ov-base-mat.cc"
+
+// instantiate template class with types that need to be exported from library
+
+template class OCTINTERP_API octave_base_matrix<boolNDArray>;
+template class OCTINTERP_API octave_base_matrix<charNDArray>;
+template class OCTINTERP_API octave_base_matrix<int8NDArray>;
+template class OCTINTERP_API octave_base_matrix<int16NDArray>;
+template class OCTINTERP_API octave_base_matrix<int32NDArray>;
+template class OCTINTERP_API octave_base_matrix<int64NDArray>;
+template class OCTINTERP_API octave_base_matrix<uint8NDArray>;
+template class OCTINTERP_API octave_base_matrix<uint16NDArray>;
+template class OCTINTERP_API octave_base_matrix<uint32NDArray>;
+template class OCTINTERP_API octave_base_matrix<uint64NDArray>;
+template class OCTINTERP_API octave_base_matrix<ComplexNDArray>;
+template class OCTINTERP_API octave_base_matrix<FloatComplexNDArray>;
+template class OCTINTERP_API octave_base_matrix<FloatNDArray>;
+template class OCTINTERP_API octave_base_matrix<NDArray>;
+
+
+// Cell is able to handle octave_value indexing by itself, so just forward
+// everything.
+
+template <>
+octave_value
+octave_base_matrix<Cell>::do_index_op (const octave_value_list& idx,
+                                       bool resize_ok)
+{
+  return m_matrix.index (idx, resize_ok);
+}
+
+template <>
+void
+octave_base_matrix<Cell>::assign (const octave_value_list& idx, const Cell& rhs)
+{
+  m_matrix.assign (idx, rhs);
+}
+
+template <>
+void
+octave_base_matrix<Cell>::assign (const octave_value_list& idx,
+                                  octave_value rhs)
+{
+  // FIXME: Really?
+  if (rhs.iscell ())
+    m_matrix.assign (idx, rhs.cell_value ());
+  else
+    m_matrix.assign (idx, Cell (rhs));
+}
+
+template <>
+void
+octave_base_matrix<Cell>::delete_elements (const octave_value_list& idx)
+{
+  m_matrix.delete_elements (idx);
+}
+
+// FIXME: this list of specializations is becoming so long that we should
+// really ask whether octave_cell should inherit from octave_base_matrix at all.
+
+template <>
+std::string
+octave_base_matrix<Cell>::edit_display (const float_display_format&,
+                                        octave_idx_type i,
+                                        octave_idx_type j) const
+{
+  octave_value val = m_matrix(i, j);
+
+  std::string tname = val.type_name ();
+  dim_vector dv = val.dims ();
+  std::string dimstr = dv.str ();
+  return "[" + dimstr + " " + tname + "]";
+}
+
+template <>
+octave_value
+octave_base_matrix<Cell>::fast_elem_extract (octave_idx_type n) const
+{
+  if (n < m_matrix.numel ())
+    return Cell (m_matrix(n));
+  else
+    return octave_value ();
+}
+
+template <>
+bool
+octave_base_matrix<Cell>::fast_elem_insert (octave_idx_type n,
+                                            const octave_value& x)
+{
+  const octave_base_matrix<Cell> *xrep
+    = dynamic_cast<const octave_base_matrix<Cell> *> (&x.get_rep ());
+
+  bool retval = xrep && xrep->m_matrix.numel () == 1 && n < m_matrix.numel ();
+  if (retval)
+    m_matrix(n) = xrep->m_matrix(0);
+
+  return retval;
+}
+
+template class OCTINTERP_API octave_base_matrix<Cell>;
--- a/libinterp/octave-value/ov-base-mat.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-base-mat.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -63,7 +63,7 @@
       break;
 
     default:
-      panic_impossible ();
+      error ("unexpected: index not '(', '{', or '.' in octave_base_matrix<T>::simple_subsref - please report this bug");
     }
 }
 
--- a/libinterp/octave-value/ov-base-mat.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-base-mat.h	Sat Apr 06 17:38:27 2024 -0400
@@ -45,15 +45,17 @@
 // Real matrix values.
 
 template <typename MT>
-class OCTINTERP_API octave_base_matrix : public octave_base_value
+class OCTINTERP_TEMPLATE_API octave_base_matrix : public octave_base_value
 {
 public:
 
   typedef MT object_type;
 
+  OCTINTERP_OVERRIDABLE_FUNC_API
   octave_base_matrix ()
     : octave_base_value (), m_matrix (), m_typ (), m_idx_cache () { }
 
+  OCTINTERP_OVERRIDABLE_FUNC_API
   octave_base_matrix (const MT& m, const MatrixType& t = MatrixType ())
     : octave_base_value (), m_matrix (m),
       m_typ (t.is_known () ? new MatrixType (t) : nullptr), m_idx_cache ()
@@ -62,6 +64,7 @@
       m_matrix.resize (dim_vector (0, 0));
   }
 
+  OCTINTERP_OVERRIDABLE_FUNC_API
   octave_base_matrix (const octave_base_matrix& m)
     : octave_base_value (), m_matrix (m.m_matrix),
       m_typ (m.m_typ ? new MatrixType (*m.m_typ) : nullptr),
@@ -69,14 +72,19 @@
                    : nullptr)
   { }
 
+  OCTINTERP_OVERRIDABLE_FUNC_API
   ~octave_base_matrix () { clear_cached_info (); }
 
+  OCTINTERP_OVERRIDABLE_FUNC_API
   std::size_t byte_size () const { return m_matrix.byte_size (); }
 
+  OCTINTERP_OVERRIDABLE_FUNC_API
   octave_value squeeze () const { return MT (m_matrix.squeeze ()); }
 
+  OCTINTERP_OVERRIDABLE_FUNC_API
   octave_value full_value () const { return m_matrix; }
 
+  OCTINTERP_OVERRIDABLE_FUNC_API
   void maybe_economize () { m_matrix.maybe_economize (); }
 
 #if defined (OCTAVE_ENABLE_BYTECODE_EVALUATOR)
@@ -95,11 +103,11 @@
   OCTINTERP_API octave_value
   subsref (const std::string& type, const std::list<octave_value_list>& idx);
 
-  octave_value_list
+  OCTINTERP_API octave_value_list
   simple_subsref (char type, octave_value_list& idx, int nargout);
 
-  octave_value_list subsref (const std::string& type,
-                             const std::list<octave_value_list>& idx, int)
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value_list
+  subsref (const std::string& type, const std::list<octave_value_list>& idx, int)
   { return subsref (type, idx); }
 
   OCTINTERP_API octave_value
@@ -123,58 +131,76 @@
 
   OCTINTERP_API void delete_elements (const octave_value_list& idx);
 
-  dim_vector dims () const { return m_matrix.dims (); }
+  OCTINTERP_OVERRIDABLE_FUNC_API dim_vector
+  dims () const { return m_matrix.dims (); }
 
-  octave_idx_type numel () const { return m_matrix.numel (); }
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_idx_type
+  numel () const { return m_matrix.numel (); }
 
-  int ndims () const { return m_matrix.ndims (); }
+  OCTINTERP_OVERRIDABLE_FUNC_API int
+  ndims () const { return m_matrix.ndims (); }
 
-  octave_idx_type nnz () const { return m_matrix.nnz (); }
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_idx_type
+  nnz () const { return m_matrix.nnz (); }
 
-  octave_value reshape (const dim_vector& new_dims) const
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value
+  reshape (const dim_vector& new_dims) const
   { return MT (m_matrix.reshape (new_dims)); }
 
-  octave_value permute (const Array<int>& vec, bool inv = false) const
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value
+  permute (const Array<int>& vec, bool inv = false) const
   { return MT (m_matrix.permute (vec, inv)); }
 
-  octave_value resize (const dim_vector& dv, bool fill = false) const;
-
-  octave_value all (int dim = 0) const { return m_matrix.all (dim); }
-  octave_value any (int dim = 0) const { return m_matrix.any (dim); }
+  OCTINTERP_API octave_value
+  resize (const dim_vector& dv, bool fill = false) const;
 
-  MatrixType matrix_type () const { return m_typ ? *m_typ : MatrixType (); }
-  MatrixType matrix_type (const MatrixType& _typ) const;
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value all (int dim = 0) const
+  { return m_matrix.all (dim); }
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value any (int dim = 0) const
+  { return m_matrix.any (dim); }
 
-  octave_value diag (octave_idx_type k = 0) const
+  OCTINTERP_OVERRIDABLE_FUNC_API MatrixType matrix_type () const
+  { return m_typ ? *m_typ : MatrixType (); }
+  OCTINTERP_API MatrixType matrix_type (const MatrixType& _typ) const;
+
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value
+  diag (octave_idx_type k = 0) const
   { return octave_value (m_matrix.diag (k)); }
 
-  octave_value diag (octave_idx_type m, octave_idx_type n) const
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value
+  diag (octave_idx_type m, octave_idx_type n) const
   { return octave_value (m_matrix.diag (m, n)); }
 
-  octave_value sort (octave_idx_type dim = 0, sortmode mode = ASCENDING) const
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value
+  sort (octave_idx_type dim = 0, sortmode mode = ASCENDING) const
   { return octave_value (m_matrix.sort (dim, mode)); }
-  octave_value sort (Array<octave_idx_type>& sidx, octave_idx_type dim = 0,
-                     sortmode mode = ASCENDING) const
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value
+  sort (Array<octave_idx_type>& sidx, octave_idx_type dim = 0,
+        sortmode mode = ASCENDING) const
   { return octave_value (m_matrix.sort (sidx, dim, mode)); }
 
-  sortmode issorted (sortmode mode = UNSORTED) const
+  OCTINTERP_OVERRIDABLE_FUNC_API sortmode issorted (sortmode mode = UNSORTED) const
   { return m_matrix.issorted (mode); }
 
-  Array<octave_idx_type> sort_rows_idx (sortmode mode = ASCENDING) const
+  OCTINTERP_OVERRIDABLE_FUNC_API Array<octave_idx_type>
+  sort_rows_idx (sortmode mode = ASCENDING) const
   { return m_matrix.sort_rows_idx (mode); }
 
-  sortmode is_sorted_rows (sortmode mode = UNSORTED) const
+  OCTINTERP_OVERRIDABLE_FUNC_API sortmode
+  is_sorted_rows (sortmode mode = UNSORTED) const
   { return m_matrix.is_sorted_rows (mode); }
 
-  bool is_matrix_type () const { return true; }
+  OCTINTERP_OVERRIDABLE_FUNC_API bool
+  is_matrix_type () const { return true; }
 
-  bool is_full_num_matrix () const { return true; }
+  OCTINTERP_OVERRIDABLE_FUNC_API bool
+  is_full_num_matrix () const { return true; }
 
-  bool isnumeric () const { return true; }
+  OCTINTERP_OVERRIDABLE_FUNC_API bool isnumeric () const { return true; }
 
-  bool is_defined () const { return true; }
+  OCTINTERP_OVERRIDABLE_FUNC_API bool is_defined () const { return true; }
 
-  bool is_constant () const { return true; }
+  OCTINTERP_OVERRIDABLE_FUNC_API bool is_constant () const { return true; }
 
   OCTINTERP_API bool is_true () const;
 
@@ -193,13 +219,13 @@
   edit_display (const float_display_format& fmt,
                 octave_idx_type i, octave_idx_type j) const;
 
-  MT& matrix_ref ()
+  OCTINTERP_OVERRIDABLE_FUNC_API MT& matrix_ref ()
   {
     clear_cached_info ();
     return m_matrix;
   }
 
-  const MT& matrix_ref () const
+  OCTINTERP_OVERRIDABLE_FUNC_API const MT& matrix_ref () const
   {
     return m_matrix;
   }
@@ -212,7 +238,8 @@
 
   // This function exists to support the MEX interface.
   // You should not use it anywhere else.
-  const void * mex_get_data () const { return m_matrix.data (); }
+  OCTINTERP_OVERRIDABLE_FUNC_API const void *
+  mex_get_data () const { return m_matrix.data (); }
 
 #if defined (OCTAVE_ENABLE_BYTECODE_EVALUATOR)
 
@@ -236,14 +263,15 @@
 
   MT m_matrix;
 
-  octave::idx_vector set_idx_cache (const octave::idx_vector& idx) const
+  OCTINTERP_OVERRIDABLE_FUNC_API octave::idx_vector
+  set_idx_cache (const octave::idx_vector& idx) const
   {
     delete m_idx_cache;
     m_idx_cache = new octave::idx_vector (idx);
     return idx;
   }
 
-  void clear_cached_info () const
+  OCTINTERP_OVERRIDABLE_FUNC_API void clear_cached_info () const
   {
     delete m_typ; m_typ = nullptr;
     delete m_idx_cache; m_idx_cache = nullptr;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/octave-value/ov-base-scalar-inst.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -0,0 +1,46 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2024 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include "ov-base-scalar.cc"
+
+// instantiate template class with types that need to be exported from library
+
+template class OCTINTERP_API octave_base_scalar<bool>;
+template class OCTINTERP_API octave_base_scalar<double>;
+template class OCTINTERP_API octave_base_scalar<float>;
+template class OCTINTERP_API octave_base_scalar<Complex>;
+template class OCTINTERP_API octave_base_scalar<FloatComplex>;
+template class OCTINTERP_API octave_base_scalar<octave_int8>;
+template class OCTINTERP_API octave_base_scalar<octave_int16>;
+template class OCTINTERP_API octave_base_scalar<octave_int32>;
+template class OCTINTERP_API octave_base_scalar<octave_int64>;
+template class OCTINTERP_API octave_base_scalar<octave_uint8>;
+template class OCTINTERP_API octave_base_scalar<octave_uint16>;
+template class OCTINTERP_API octave_base_scalar<octave_uint32>;
+template class OCTINTERP_API octave_base_scalar<octave_uint64>;
--- a/libinterp/octave-value/ov-base-scalar.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-base-scalar.h	Sat Apr 06 17:38:27 2024 -0400
@@ -50,20 +50,22 @@
 
   typedef ST scalar_type;
 
-  octave_base_scalar ()
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_base_scalar ()
     : octave_base_value (), scalar () { }
 
-  octave_base_scalar (const ST& s)
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_base_scalar (const ST& s)
     : octave_base_value (), scalar (s) { }
 
-  octave_base_scalar (const octave_base_scalar& s)
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_base_scalar (const octave_base_scalar& s)
     : octave_base_value (), scalar (s.scalar) { }
 
-  ~octave_base_scalar () = default;
+  OCTINTERP_OVERRIDABLE_FUNC_API ~octave_base_scalar () = default;
 
-  octave_value squeeze () const { return scalar; }
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value
+  squeeze () const { return scalar; }
 
-  octave_value full_value () const { return scalar; }
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value
+  full_value () const { return scalar; }
 
   // We don't need to override all three forms of subsref.  The using
   // declaration will avoid warnings about partially-overloaded virtual
@@ -73,71 +75,87 @@
   OCTINTERP_API octave_value
   subsref (const std::string& type, const std::list<octave_value_list>& idx);
 
-  octave_value_list subsref (const std::string& type,
-                             const std::list<octave_value_list>& idx, int)
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value_list
+  subsref (const std::string& type, const std::list<octave_value_list>& idx, int)
   { return subsref (type, idx); }
 
   OCTINTERP_API octave_value
   subsasgn (const std::string& type, const std::list<octave_value_list>& idx,
             const octave_value& rhs);
 
-  bool is_constant () const { return true; }
+  OCTINTERP_OVERRIDABLE_FUNC_API bool is_constant () const { return true; }
 
-  bool is_defined () const { return true; }
+  OCTINTERP_OVERRIDABLE_FUNC_API bool is_defined () const { return true; }
 
   OCTINTERP_API dim_vector dims () const;
 
-  octave_idx_type numel () const { return 1; }
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_idx_type numel () const { return 1; }
 
-  int ndims () const { return 2; }
+  OCTINTERP_OVERRIDABLE_FUNC_API int ndims () const { return 2; }
 
-  octave_idx_type nnz () const { return (scalar != ST () ? 1 : 0); }
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_idx_type
+  nnz () const { return (scalar != ST () ? 1 : 0); }
 
   OCTINTERP_API octave_value permute (const Array<int>&, bool = false) const;
 
   OCTINTERP_API octave_value reshape (const dim_vector& new_dims) const;
 
-  std::size_t byte_size () const { return sizeof (ST); }
+  OCTINTERP_OVERRIDABLE_FUNC_API std::size_t
+  byte_size () const { return sizeof (ST); }
 
-  octave_value all (int = 0) const { return (scalar != ST ()); }
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value
+  all (int = 0) const { return (scalar != ST ()); }
 
-  octave_value any (int = 0) const { return (scalar != ST ()); }
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value
+  any (int = 0) const { return (scalar != ST ()); }
 
   OCTINTERP_API octave_value diag (octave_idx_type k = 0) const;
 
   OCTINTERP_API octave_value diag (octave_idx_type m, octave_idx_type n) const;
 
-  octave_value sort (octave_idx_type, sortmode) const
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value
+  sort (octave_idx_type, sortmode) const
   { return octave_value (scalar); }
-  octave_value sort (Array<octave_idx_type>& sidx, octave_idx_type,
-                     sortmode) const
+
+  OCTINTERP_OVERRIDABLE_FUNC_API octave_value
+  sort (Array<octave_idx_type>& sidx, octave_idx_type, sortmode) const
   {
     sidx.resize (dim_vector (1, 1));
     sidx(0) = 0;
     return octave_value (scalar);
   }
 
-  sortmode issorted (sortmode mode = UNSORTED) const
+  OCTINTERP_OVERRIDABLE_FUNC_API sortmode
+  issorted (sortmode mode = UNSORTED) const
   { return mode == UNSORTED ? ASCENDING : mode; }
 
-  Array<octave_idx_type> sort_rows_idx (sortmode) const
+  OCTINTERP_OVERRIDABLE_FUNC_API Array<octave_idx_type>
+  sort_rows_idx (sortmode) const
   {
     return Array<octave_idx_type> (dim_vector (1, 1),
                                    static_cast<octave_idx_type> (0));
   }
 
-  sortmode is_sorted_rows (sortmode mode = UNSORTED) const
+  OCTINTERP_OVERRIDABLE_FUNC_API sortmode
+  is_sorted_rows (sortmode mode = UNSORTED) const
   { return mode == UNSORTED ? ASCENDING : mode; }
 
-  MatrixType matrix_type () const { return MatrixType::Diagonal; }
-  MatrixType matrix_type (const MatrixType&) const
+  OCTINTERP_OVERRIDABLE_FUNC_API MatrixType
+  matrix_type () const
+  { return MatrixType::Diagonal; }
+
+  OCTINTERP_OVERRIDABLE_FUNC_API MatrixType
+  matrix_type (const MatrixType&) const
   { return matrix_type (); }
 
-  bool is_maybe_function () const { return false; }
+  OCTINTERP_OVERRIDABLE_FUNC_API bool
+  is_maybe_function () const { return false; }
 
-  bool is_scalar_type () const { return true; }
+  OCTINTERP_OVERRIDABLE_FUNC_API bool
+  is_scalar_type () const { return true; }
 
-  bool isnumeric () const { return true; }
+  OCTINTERP_OVERRIDABLE_FUNC_API bool
+  isnumeric () const { return true; }
 
   OCTINTERP_API bool is_true () const;
 
@@ -159,11 +177,14 @@
 
   // This function exists to support the MEX interface.
   // You should not use it anywhere else.
-  const void * mex_get_data () const { return &scalar; }
+  OCTINTERP_OVERRIDABLE_FUNC_API const void *
+  mex_get_data () const { return &scalar; }
 
-  const ST& scalar_ref () const { return scalar; }
+  OCTINTERP_OVERRIDABLE_FUNC_API const ST& scalar_ref () const
+  { return scalar; }
 
-  ST& scalar_ref () { return scalar; }
+  OCTINTERP_OVERRIDABLE_FUNC_API ST& scalar_ref ()
+  { return scalar; }
 
   OCTINTERP_API octave_value fast_elem_extract (octave_idx_type n) const;
 
--- a/libinterp/octave-value/ov-base.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-base.h	Sat Apr 06 17:38:27 2024 -0400
@@ -185,12 +185,19 @@
 #define OCTAVE_EMPTY_CPP_ARG /* empty */
 
 #define DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA                          \
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA2 (OCTAVE_EMPTY_CPP_ARG)
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA2 (OCTAVE_EMPTY_CPP_ARG,        \
+                                         OCTAVE_EMPTY_CPP_ARG)
+
+#define DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API(API)                 \
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA2 (OCTAVE_EMPTY_CPP_ARG, API)
 
 #define DECLARE_OV_BASE_TYPEID_FUNCTIONS_AND_DATA                     \
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA2(virtual)
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA2 (virtual, OCTAVE_EMPTY_CPP_ARG)
 
-#define DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA2(VIRTUAL)                \
+#define DECLARE_OV_BASE_TYPEID_FUNCTIONS_AND_DATA_API(API)            \
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA2 (virtual, API)
+
+#define DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA2(VIRTUAL, API)           \
   public:                                                             \
     VIRTUAL int type_id () const { return s_t_id; }                   \
     VIRTUAL std::string type_name () const { return s_t_name; }       \
@@ -198,20 +205,24 @@
     static int static_type_id () { return s_t_id; }                   \
     static std::string static_type_name () { return s_t_name; }       \
     static std::string static_class_name () { return s_c_name; }      \
-    static void register_type ();                                     \
-    static void register_type (octave::type_info&);                   \
+    API static void register_type ();                                 \
+    API static void register_type (octave::type_info&);               \
                                                                       \
   private:                                                            \
-    static int s_t_id;                                                \
-    static const std::string s_t_name;                                \
-    static const std::string s_c_name;
+    static API int s_t_id;                                            \
+    static API const std::string s_t_name;                            \
+    static API const std::string s_c_name;
 
 #define DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS(cls, type)         \
-  template <> void cls<type>::register_type ();                       \
-  template <> void cls<type>::register_type (octave::type_info&);     \
-  template <> int cls<type>::s_t_id;                                  \
-  template <> const std::string cls<type>::s_t_name;                  \
-  template <> const std::string cls<type>::s_c_name;
+  DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS_API (cls, type,          \
+                                                  OCTAVE_EMPTY_CPP_ARG)
+
+#define DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS_API(cls, type, API) \
+  template <> API void cls<type>::register_type ();                   \
+  template <> API void cls<type>::register_type (octave::type_info&); \
+  template <> API int cls<type>::s_t_id;                              \
+  template <> API const std::string cls<type>::s_t_name;              \
+  template <> API const std::string cls<type>::s_c_name;
 
 // FIXME: The 'new' operator below creates an 8-byte memory leak for every
 // registered data type (of which there are 58 built-in to Octave, plus any
@@ -276,7 +287,7 @@
   friend class octave_value_vm;
 #endif
 
-  octave_base_value ();
+  OCTINTERP_API octave_base_value ();
 
   octave_base_value (const octave_base_value&) : octave_base_value () { }
 
@@ -287,7 +298,7 @@
   clone () const { return new octave_base_value (*this); }
 
   // Empty clone.
-  virtual octave_base_value *
+  virtual OCTINTERP_API octave_base_value *
   empty_clone () const;
 
   // Unique clone.  Usually clones, but may be overridden to fake the
@@ -306,76 +317,77 @@
   numeric_demotion_function () const
   { return type_conv_info (); }
 
-  virtual octave_value squeeze () const;
+  virtual OCTINTERP_API octave_value squeeze () const;
 
-  virtual octave_value full_value () const;
+  virtual OCTINTERP_API octave_value full_value () const;
 
   // Will return a copy of it-self when the representation
   // allready is a scalar (.i.e. double). The const variant
   // as_double () would allocate a new octave value.
-  virtual octave_value as_double_or_copy ();
+  virtual OCTINTERP_API octave_value as_double_or_copy ();
 
-  virtual octave_value as_double () const;
-  virtual octave_value as_single () const;
+  virtual OCTINTERP_API octave_value as_double () const;
+  virtual OCTINTERP_API octave_value as_single () const;
 
-  virtual octave_value as_int8 () const;
-  virtual octave_value as_int16 () const;
-  virtual octave_value as_int32 () const;
-  virtual octave_value as_int64 () const;
+  virtual OCTINTERP_API octave_value as_int8 () const;
+  virtual OCTINTERP_API octave_value as_int16 () const;
+  virtual OCTINTERP_API octave_value as_int32 () const;
+  virtual OCTINTERP_API octave_value as_int64 () const;
 
-  virtual octave_value as_uint8 () const;
-  virtual octave_value as_uint16 () const;
-  virtual octave_value as_uint32 () const;
-  virtual octave_value as_uint64 () const;
+  virtual OCTINTERP_API octave_value as_uint8 () const;
+  virtual OCTINTERP_API octave_value as_uint16 () const;
+  virtual OCTINTERP_API octave_value as_uint32 () const;
+  virtual OCTINTERP_API octave_value as_uint64 () const;
 
   virtual octave_base_value * try_narrowing_conversion ()
   { return nullptr; }
 
   virtual void maybe_economize () { }
 
-  virtual Matrix size ();
+  virtual OCTINTERP_API Matrix size ();
 
-  virtual octave_idx_type xnumel (const octave_value_list&);
+  virtual OCTINTERP_API octave_idx_type xnumel (const octave_value_list&);
 
   // FIXME: Do we really need all three of these versions of subsref?
 
-  virtual octave_value
+  virtual OCTINTERP_API octave_value
   subsref (const std::string& type,
            const std::list<octave_value_list>& idx);
 
-  virtual octave_value_list
+  virtual OCTINTERP_API octave_value_list
   subsref (const std::string& type,
            const std::list<octave_value_list>& idx,
            int nargout);
 
-  virtual octave_value
+  virtual OCTINTERP_API octave_value
   subsref (const std::string& type,
            const std::list<octave_value_list>& idx,
            bool auto_add);
 
-  virtual octave_value_list
+  virtual OCTINTERP_API octave_value_list
   simple_subsref (char type, octave_value_list& idx, int nargout);
 
-  virtual octave_value
+  virtual OCTINTERP_API octave_value
   do_index_op (const octave_value_list& idx, bool resize_ok = false);
 
   virtual void assign (const std::string&, const octave_value&) { }
 
-  virtual octave_value
+  virtual OCTINTERP_API octave_value
   subsasgn (const std::string& type,
             const std::list<octave_value_list>& idx,
             const octave_value& rhs);
 
-  virtual octave_value
+  virtual OCTINTERP_API octave_value
   simple_subsasgn (char type, octave_value_list& idx,
                    const octave_value& rhs);
 
-  virtual octave_value
+  virtual OCTINTERP_API octave_value
   undef_subsasgn (const std::string& type,
                   const std::list<octave_value_list>& idx,
                   const octave_value& rhs);
 
-  virtual octave::idx_vector index_vector (bool require_integers = false) const;
+  virtual OCTINTERP_API octave::idx_vector
+  index_vector (bool require_integers = false) const;
 
   virtual dim_vector dims () const { return dim_vector (); }
 
@@ -400,21 +412,23 @@
 
   virtual std::size_t byte_size () const { return 0; }
 
-  virtual octave_idx_type nnz () const;
+  virtual OCTINTERP_API octave_idx_type nnz () const;
 
-  virtual octave_idx_type nzmax () const;
+  virtual OCTINTERP_API octave_idx_type nzmax () const;
 
-  virtual octave_idx_type nfields () const;
+  virtual OCTINTERP_API octave_idx_type nfields () const;
 
-  virtual octave_value reshape (const dim_vector&) const;
+  virtual OCTINTERP_API octave_value reshape (const dim_vector&) const;
 
-  virtual octave_value permute (const Array<int>& vec, bool = false) const;
-
-  virtual octave_value resize (const dim_vector&, bool fill = false) const;
+  virtual OCTINTERP_API octave_value
+  permute (const Array<int>& vec, bool = false) const;
 
-  virtual MatrixType matrix_type () const;
+  virtual OCTINTERP_API octave_value
+  resize (const dim_vector&, bool fill = false) const;
 
-  virtual MatrixType matrix_type (const MatrixType& typ) const;
+  virtual OCTINTERP_API MatrixType matrix_type () const;
+
+  virtual OCTINTERP_API MatrixType matrix_type (const MatrixType& typ) const;
 
   virtual bool is_defined () const { return false; }
 
@@ -474,9 +488,9 @@
 
   virtual bool is_all_va_args () const { return false; }
 
-  virtual octave_value all (int = 0) const;
+  virtual OCTINTERP_API octave_value all (int = 0) const;
 
-  virtual octave_value any (int = 0) const;
+  virtual OCTINTERP_API octave_value any (int = 0) const;
 
   virtual builtin_type_t builtin_type () const { return btyp_unknown; }
 
@@ -565,27 +579,33 @@
 
   virtual void erase_subfunctions () { }
 
-  virtual short int short_value (bool = false, bool = false) const;
+  virtual OCTINTERP_API short int
+  short_value (bool = false, bool = false) const;
 
-  virtual unsigned short int ushort_value (bool = false, bool = false) const;
+  virtual OCTINTERP_API unsigned short int
+  ushort_value (bool = false, bool = false) const;
 
-  virtual int int_value (bool = false, bool = false) const;
+  virtual OCTINTERP_API int
+  int_value (bool = false, bool = false) const;
 
-  virtual unsigned int uint_value (bool = false, bool = false) const;
+  virtual OCTINTERP_API unsigned int
+  uint_value (bool = false, bool = false) const;
 
-  virtual int nint_value (bool = false) const;
+  virtual OCTINTERP_API int nint_value (bool = false) const;
 
-  virtual long int long_value (bool = false, bool = false) const;
+  virtual OCTINTERP_API long int
+  long_value (bool = false, bool = false) const;
 
-  virtual unsigned long int ulong_value (bool = false, bool = false) const;
-
-  virtual int64_t int64_value (bool = false, bool = false) const;
+  virtual OCTINTERP_API unsigned long int
+  ulong_value (bool = false, bool = false) const;
 
-  virtual uint64_t uint64_value (bool = false, bool = false) const;
+  virtual OCTINTERP_API int64_t int64_value (bool = false, bool = false) const;
+
+  virtual OCTINTERP_API uint64_t uint64_value (bool = false, bool = false) const;
 
-  virtual double double_value (bool = false) const;
+  virtual OCTINTERP_API double double_value (bool = false) const;
 
-  virtual float float_value (bool = false) const;
+  virtual OCTINTERP_API float float_value (bool = false) const;
 
   virtual double scalar_value (bool frc_str_conv = false) const
   { return double_value (frc_str_conv); }
@@ -593,110 +613,110 @@
   virtual float float_scalar_value (bool frc_str_conv = false) const
   { return float_value (frc_str_conv); }
 
-  virtual Cell cell_value () const;
+  virtual OCTINTERP_API Cell cell_value () const;
 
-  virtual Matrix matrix_value (bool = false) const;
+  virtual OCTINTERP_API Matrix matrix_value (bool = false) const;
 
-  virtual FloatMatrix float_matrix_value (bool = false) const;
+  virtual OCTINTERP_API FloatMatrix float_matrix_value (bool = false) const;
 
-  virtual NDArray array_value (bool = false) const;
+  virtual OCTINTERP_API NDArray array_value (bool = false) const;
 
-  virtual FloatNDArray float_array_value (bool = false) const;
+  virtual OCTINTERP_API FloatNDArray float_array_value (bool = false) const;
 
-  virtual Complex complex_value (bool = false) const;
+  virtual OCTINTERP_API Complex complex_value (bool = false) const;
 
-  virtual FloatComplex float_complex_value (bool = false) const;
+  virtual OCTINTERP_API FloatComplex float_complex_value (bool = false) const;
 
-  virtual ComplexMatrix complex_matrix_value (bool = false) const;
+  virtual OCTINTERP_API ComplexMatrix complex_matrix_value (bool = false) const;
 
-  virtual FloatComplexMatrix float_complex_matrix_value (bool = false) const;
+  virtual OCTINTERP_API FloatComplexMatrix float_complex_matrix_value (bool = false) const;
 
-  virtual ComplexNDArray complex_array_value (bool = false) const;
+  virtual OCTINTERP_API ComplexNDArray complex_array_value (bool = false) const;
 
-  virtual FloatComplexNDArray float_complex_array_value (bool = false) const;
+  virtual OCTINTERP_API FloatComplexNDArray float_complex_array_value (bool = false) const;
 
-  virtual bool bool_value (bool = false) const;
+  virtual OCTINTERP_API bool bool_value (bool = false) const;
 
-  virtual boolMatrix bool_matrix_value (bool = false) const;
+  virtual OCTINTERP_API boolMatrix bool_matrix_value (bool = false) const;
 
-  virtual boolNDArray bool_array_value (bool = false) const;
+  virtual OCTINTERP_API boolNDArray bool_array_value (bool = false) const;
 
-  virtual charMatrix char_matrix_value (bool force = false) const;
+  virtual OCTINTERP_API charMatrix char_matrix_value (bool force = false) const;
 
-  virtual charNDArray char_array_value (bool = false) const;
+  virtual OCTINTERP_API charNDArray char_array_value (bool = false) const;
 
-  virtual SparseMatrix sparse_matrix_value (bool = false) const;
+  virtual OCTINTERP_API SparseMatrix sparse_matrix_value (bool = false) const;
 
-  virtual SparseComplexMatrix sparse_complex_matrix_value (bool = false) const;
+  virtual OCTINTERP_API SparseComplexMatrix sparse_complex_matrix_value (bool = false) const;
 
-  virtual SparseBoolMatrix sparse_bool_matrix_value (bool = false) const;
+  virtual OCTINTERP_API SparseBoolMatrix sparse_bool_matrix_value (bool = false) const;
 
-  virtual DiagMatrix diag_matrix_value (bool = false) const;
+  virtual OCTINTERP_API DiagMatrix diag_matrix_value (bool = false) const;
 
-  virtual FloatDiagMatrix float_diag_matrix_value (bool = false) const;
+  virtual OCTINTERP_API FloatDiagMatrix float_diag_matrix_value (bool = false) const;
 
-  virtual ComplexDiagMatrix complex_diag_matrix_value (bool = false) const;
+  virtual OCTINTERP_API ComplexDiagMatrix complex_diag_matrix_value (bool = false) const;
 
-  virtual FloatComplexDiagMatrix
+  virtual OCTINTERP_API FloatComplexDiagMatrix
   float_complex_diag_matrix_value (bool = false) const;
 
-  virtual PermMatrix perm_matrix_value () const;
+  virtual OCTINTERP_API PermMatrix perm_matrix_value () const;
 
-  virtual octave_int8 int8_scalar_value () const;
+  virtual OCTINTERP_API octave_int8 int8_scalar_value () const;
 
-  virtual octave_int16 int16_scalar_value () const;
+  virtual OCTINTERP_API octave_int16 int16_scalar_value () const;
 
-  virtual octave_int32 int32_scalar_value () const;
+  virtual OCTINTERP_API octave_int32 int32_scalar_value () const;
 
-  virtual octave_int64 int64_scalar_value () const;
+  virtual OCTINTERP_API octave_int64 int64_scalar_value () const;
 
-  virtual octave_uint8 uint8_scalar_value () const;
+  virtual OCTINTERP_API octave_uint8 uint8_scalar_value () const;
 
-  virtual octave_uint16 uint16_scalar_value () const;
+  virtual OCTINTERP_API octave_uint16 uint16_scalar_value () const;
 
-  virtual octave_uint32 uint32_scalar_value () const;
+  virtual OCTINTERP_API octave_uint32 uint32_scalar_value () const;
 
-  virtual octave_uint64 uint64_scalar_value () const;
+  virtual OCTINTERP_API octave_uint64 uint64_scalar_value () const;
 
-  virtual int8NDArray int8_array_value () const;
+  virtual OCTINTERP_API int8NDArray int8_array_value () const;
 
-  virtual int16NDArray int16_array_value () const;
+  virtual OCTINTERP_API int16NDArray int16_array_value () const;
 
-  virtual int32NDArray int32_array_value () const;
+  virtual OCTINTERP_API int32NDArray int32_array_value () const;
 
-  virtual int64NDArray int64_array_value () const;
+  virtual OCTINTERP_API int64NDArray int64_array_value () const;
 
-  virtual uint8NDArray uint8_array_value () const;
+  virtual OCTINTERP_API uint8NDArray uint8_array_value () const;
 
-  virtual uint16NDArray uint16_array_value () const;
+  virtual OCTINTERP_API uint16NDArray uint16_array_value () const;
 
-  virtual uint32NDArray uint32_array_value () const;
+  virtual OCTINTERP_API uint32NDArray uint32_array_value () const;
 
-  virtual uint64NDArray uint64_array_value () const;
+  virtual OCTINTERP_API uint64NDArray uint64_array_value () const;
 
-  virtual string_vector string_vector_value (bool pad = false) const;
+  virtual OCTINTERP_API string_vector string_vector_value (bool pad = false) const;
 
-  virtual std::string string_value (bool force = false) const;
+  virtual OCTINTERP_API std::string string_value (bool force = false) const;
 
-  virtual Array<std::string> cellstr_value () const;
+  virtual OCTINTERP_API Array<std::string> cellstr_value () const;
 
-  virtual octave::range<double> range_value () const;
+  virtual OCTINTERP_API octave::range<double> range_value () const;
 
   // For now, enable only range<double>.
 
-  virtual octave_map map_value () const;
+  virtual OCTINTERP_API octave_map map_value () const;
 
-  virtual octave_scalar_map scalar_map_value () const;
+  virtual OCTINTERP_API octave_scalar_map scalar_map_value () const;
 
-  virtual string_vector map_keys () const;
+  virtual OCTINTERP_API string_vector map_keys () const;
 
-  virtual bool isfield (const std::string&) const;
+  virtual OCTINTERP_API bool isfield (const std::string&) const;
 
-  virtual std::size_t nparents () const;
+  virtual OCTINTERP_API std::size_t nparents () const;
 
-  virtual std::list<std::string> parent_class_name_list () const;
+  virtual OCTINTERP_API std::list<std::string> parent_class_name_list () const;
 
-  virtual string_vector parent_class_names () const;
+  virtual OCTINTERP_API string_vector parent_class_names () const;
 
   virtual octave_base_value * find_parent_class (const std::string&)
   { return nullptr; }
@@ -707,72 +727,74 @@
   virtual bool is_instance_of (const std::string&) const
   { return false; }
 
-  virtual octave_classdef * classdef_object_value (bool silent = false);
+  virtual OCTINTERP_API octave_classdef * classdef_object_value (bool silent = false);
 
-  virtual octave_function * function_value (bool silent = false);
+  virtual OCTINTERP_API octave_function * function_value (bool silent = false);
 
-  virtual octave_user_function * user_function_value (bool silent = false);
+  virtual OCTINTERP_API octave_user_function * user_function_value (bool silent = false);
 
-  virtual octave_user_script * user_script_value (bool silent = false);
+  virtual OCTINTERP_API octave_user_script * user_script_value (bool silent = false);
 
-  virtual octave_user_code * user_code_value (bool silent = false);
+  virtual OCTINTERP_API octave_user_code * user_code_value (bool silent = false);
 
-  virtual octave_fcn_handle * fcn_handle_value (bool silent = false);
+  virtual OCTINTERP_API octave_fcn_handle * fcn_handle_value (bool silent = false);
 
-  virtual octave_value_list list_value () const;
+  virtual OCTINTERP_API octave_value_list list_value () const;
 
-  virtual octave_value convert_to_str (bool pad = false, bool force = false,
-                                       char type = '\'') const;
-  virtual octave_value
+  virtual OCTINTERP_API octave_value
+  convert_to_str (bool pad = false, bool force = false, char type = '\'') const;
+  virtual OCTINTERP_API octave_value
   convert_to_str_internal (bool pad, bool force, char type) const;
 
-  virtual void convert_to_row_or_column_vector ();
+  virtual OCTINTERP_API void convert_to_row_or_column_vector ();
 
   // The following extractor functions don't perform any implicit type
   // conversions.
 
-  virtual std::string xstring_value () const;
+  virtual OCTINTERP_API std::string xstring_value () const;
 
   virtual bool print_as_scalar () const { return false; }
 
-  virtual void print (std::ostream& os, bool pr_as_read_syntax = false);
+  virtual OCTINTERP_API void
+  print (std::ostream& os, bool pr_as_read_syntax = false);
 
-  virtual void
+  virtual OCTINTERP_API void
   print_raw (std::ostream& os, bool pr_as_read_syntax = false) const;
 
-  virtual bool
+  virtual OCTINTERP_API bool
   print_name_tag (std::ostream& os, const std::string& name) const;
 
-  virtual void
+  virtual OCTINTERP_API void
   print_with_name (std::ostream& output_buf, const std::string& name,
                    bool print_padding = true);
 
   virtual void short_disp (std::ostream& os) const { os << "..."; }
 
-  virtual float_display_format get_edit_display_format () const;
+  virtual OCTINTERP_API float_display_format get_edit_display_format () const;
 
   virtual std::string edit_display (const float_display_format&,
                                     octave_idx_type, octave_idx_type) const
   { return "#VAL"; }
 
-  virtual void print_info (std::ostream& os, const std::string& prefix) const;
+  virtual OCTINTERP_API void
+  print_info (std::ostream& os, const std::string& prefix) const;
 
-  virtual bool save_ascii (std::ostream& os);
+  virtual OCTINTERP_API bool save_ascii (std::ostream& os);
 
-  virtual bool load_ascii (std::istream& is);
+  virtual OCTINTERP_API bool load_ascii (std::istream& is);
 
-  virtual bool save_binary (std::ostream& os, bool save_as_floats);
+  virtual OCTINTERP_API bool save_binary (std::ostream& os, bool save_as_floats);
 
-  virtual bool load_binary (std::istream& is, bool swap,
-                            octave::mach_info::float_format fmt);
+  virtual OCTINTERP_API bool
+  load_binary (std::istream& is, bool swap, octave::mach_info::float_format fmt);
 
-  virtual bool
+  virtual OCTINTERP_API bool
   save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats);
 
-  virtual bool
+  virtual OCTINTERP_API bool
   load_hdf5 (octave_hdf5_id loc_id, const char *name);
 
-  virtual int
+  virtual OCTINTERP_API int
   write (octave::stream& os, int block_size,
          oct_data_conv::data_type output_type, int skip,
          octave::mach_info::float_format flt_fmt) const;
@@ -783,28 +805,29 @@
 
   virtual const octave_idx_type * mex_get_jc () const { return nullptr; }
 
-  virtual mxArray * as_mxArray (bool interleaved) const;
+  virtual OCTINTERP_API mxArray * as_mxArray (bool interleaved) const;
 
-  virtual octave_value diag (octave_idx_type k = 0) const;
+  virtual OCTINTERP_API octave_value diag (octave_idx_type k = 0) const;
 
-  virtual octave_value diag (octave_idx_type m, octave_idx_type n) const;
+  virtual OCTINTERP_API octave_value diag (octave_idx_type m, octave_idx_type n) const;
 
-  virtual octave_value sort (octave_idx_type dim = 0,
-                             sortmode mode = ASCENDING) const;
-  virtual octave_value sort (Array<octave_idx_type>& sidx,
-                             octave_idx_type dim = 0,
-                             sortmode mode = ASCENDING) const;
+  virtual OCTINTERP_API octave_value
+  sort (octave_idx_type dim = 0, sortmode mode = ASCENDING) const;
 
-  virtual sortmode issorted (sortmode mode = UNSORTED) const;
+  virtual OCTINTERP_API octave_value
+  sort (Array<octave_idx_type>& sidx, octave_idx_type dim = 0,
+        sortmode mode = ASCENDING) const;
 
-  virtual Array<octave_idx_type>
+  virtual OCTINTERP_API sortmode issorted (sortmode mode = UNSORTED) const;
+
+  virtual OCTINTERP_API Array<octave_idx_type>
   sort_rows_idx (sortmode mode = ASCENDING) const;
 
-  virtual sortmode is_sorted_rows (sortmode mode = UNSORTED) const;
+  virtual OCTINTERP_API sortmode is_sorted_rows (sortmode mode = UNSORTED) const;
 
-  virtual void lock ();
+  virtual OCTINTERP_API void lock ();
 
-  virtual void unlock ();
+  virtual OCTINTERP_API void unlock ();
 
   virtual bool islocked () const { return false; }
 
@@ -812,11 +835,11 @@
 
   virtual void maybe_call_dtor () { }
 
-  virtual octave_value dump () const;
+  virtual OCTINTERP_API octave_value dump () const;
 
-  virtual octave_value storable_value ();
+  virtual OCTINTERP_API octave_value storable_value ();
 
-  virtual octave_base_value * make_storable_value ();
+  virtual OCTINTERP_API octave_base_value * make_storable_value ();
 
 #if defined (OCTAVE_ENABLE_BYTECODE_EVALUATOR)
 
@@ -924,26 +947,26 @@
     num_unary_mappers = umap_unknown
   };
 
-  virtual octave_value map (unary_mapper_t) const;
+  virtual OCTINTERP_API octave_value map (unary_mapper_t) const;
 
   // These are fast indexing & assignment shortcuts for extracting
   // or inserting a single scalar from/to an array.
 
   // Extract the n-th element, aka val(n).  Result is undefined if val is not
   // an array type or n is out of range.  Never error.
-  virtual octave_value
+  virtual OCTINTERP_API octave_value
   fast_elem_extract (octave_idx_type n) const;
 
   // Assign the n-th element, aka val(n) = x.  Returns false if val is not an
   // array type, x is not a matching scalar type, or n is out of range.
   // Never error.
-  virtual bool
+  virtual OCTINTERP_API bool
   fast_elem_insert (octave_idx_type n, const octave_value& x);
 
   // This is a helper for the above, to be overridden in scalar types.  The
   // whole point is to handle the insertion efficiently with just *two* VM
   // calls, which is basically the theoretical minimum.
-  virtual bool
+  virtual OCTINTERP_API bool
   fast_elem_insert_self (void *where, builtin_type_t btyp) const;
 
 #if defined (OCTAVE_ENABLE_BYTECODE_EVALUATOR)
@@ -1011,7 +1034,7 @@
   DECLARE_OV_BASE_TYPEID_FUNCTIONS_AND_DATA
 };
 
-class OCTINTERP_API octave_base_dld_value : public octave_base_value
+class OCTINTERP_TEMPLATE_API octave_base_dld_value : public octave_base_value
 {
 public:
 
--- a/libinterp/octave-value/ov-bool-mat.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-bool-mat.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -52,9 +52,6 @@
 #include "ovl.h"
 #include "oct-hdf5.h"
 #include "ops.h"
-#include "ov-base.h"
-#include "ov-base-mat.h"
-#include "ov-base-mat.cc"
 #include "ov-bool.h"
 #include "ov-bool-mat.h"
 #include "ov-re-mat.h"
@@ -65,8 +62,6 @@
 #include "ls-hdf5.h"
 #include "ls-utils.h"
 
-template class octave_base_matrix<boolNDArray>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_bool_matrix,
                                      "bool matrix", "logical");
 
--- a/libinterp/octave-value/ov-bool-mat.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-bool-mat.h	Sat Apr 06 17:38:27 2024 -0400
@@ -48,6 +48,8 @@
 
 // Character matrix values.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<boolNDArray>;
+
 class octave_bool_matrix : public octave_base_matrix<boolNDArray>
 {
 public:
@@ -240,7 +242,7 @@
 
 protected:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-bool-sparse.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-bool-sparse.h	Sat Apr 06 17:38:27 2024 -0400
@@ -150,7 +150,7 @@
 
 protected:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-bool.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-bool.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -42,8 +42,6 @@
 #include "ov-bool.h"
 #include "ov-bool-mat.h"
 #include "ov-base.h"
-#include "ov-base-scalar.h"
-#include "ov-base-scalar.cc"
 #include "ov-re-mat.h"
 #include "ov-scalar.h"
 #include "pr-output.h"
@@ -51,13 +49,6 @@
 #include "ls-oct-text.h"
 #include "ls-hdf5.h"
 
-// Prevent implicit instantiations on some systems (Windows, others?)
-// that can lead to duplicate definitions of static data members.
-
-extern template class octave_base_scalar<double>;
-
-template class octave_base_scalar<bool>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_bool, "bool", "logical");
 
 static octave_base_value *
--- a/libinterp/octave-value/ov-bool.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-bool.h	Sat Apr 06 17:38:27 2024 -0400
@@ -48,6 +48,8 @@
 
 // Real scalar values.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_scalar<bool>;
+
 class OCTINTERP_API octave_bool : public octave_base_scalar<bool>
 {
 public:
@@ -256,7 +258,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-builtin.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-builtin.h	Sat Apr 06 17:38:27 2024 -0400
@@ -116,7 +116,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-cell.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-cell.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -48,8 +48,6 @@
 #include "oct-hdf5.h"
 #include "unwind-prot.h"
 #include "utils.h"
-#include "ov-base-mat.h"
-#include "ov-base-mat.cc"
 #include "ov-fcn-handle.h"
 #include "ov-re-mat.h"
 #include "ov-scalar.h"
@@ -62,85 +60,6 @@
 #include "ls-hdf5.h"
 #include "ls-utils.h"
 
-// Cell is able to handle octave_value indexing by itself, so just forward
-// everything.
-
-template <>
-octave_value
-octave_base_matrix<Cell>::do_index_op (const octave_value_list& idx,
-                                       bool resize_ok)
-{
-  return m_matrix.index (idx, resize_ok);
-}
-
-template <>
-void
-octave_base_matrix<Cell>::assign (const octave_value_list& idx, const Cell& rhs)
-{
-  m_matrix.assign (idx, rhs);
-}
-
-template <>
-void
-octave_base_matrix<Cell>::assign (const octave_value_list& idx,
-                                  octave_value rhs)
-{
-  // FIXME: Really?
-  if (rhs.iscell ())
-    m_matrix.assign (idx, rhs.cell_value ());
-  else
-    m_matrix.assign (idx, Cell (rhs));
-}
-
-template <>
-void
-octave_base_matrix<Cell>::delete_elements (const octave_value_list& idx)
-{
-  m_matrix.delete_elements (idx);
-}
-
-// FIXME: this list of specializations is becoming so long that we should
-// really ask whether octave_cell should inherit from octave_base_matrix at all.
-
-template <>
-std::string
-octave_base_matrix<Cell>::edit_display (const float_display_format&,
-                                        octave_idx_type i,
-                                        octave_idx_type j) const
-{
-  octave_value val = m_matrix(i, j);
-
-  std::string tname = val.type_name ();
-  const dim_vector& dv = val.dims ();
-  std::string dimstr = dv.str ();
-  return "[" + dimstr + " " + tname + "]";
-}
-
-template <>
-octave_value
-octave_base_matrix<Cell>::fast_elem_extract (octave_idx_type n) const
-{
-  if (n < m_matrix.numel ())
-    return Cell (m_matrix(n));
-  else
-    return octave_value ();
-}
-
-template <>
-bool
-octave_base_matrix<Cell>::fast_elem_insert (octave_idx_type n,
-    const octave_value& x)
-{
-  const octave_cell *xrep = dynamic_cast<const octave_cell *> (&x.get_rep ());
-
-  bool retval = xrep && xrep->m_matrix.numel () == 1 && n < m_matrix.numel ();
-  if (retval)
-    m_matrix(n) = xrep->m_matrix(0);
-
-  return retval;
-}
-
-template class octave_base_matrix<Cell>;
 
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_cell, "cell", "cell");
 
--- a/libinterp/octave-value/ov-cell.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-cell.h	Sat Apr 06 17:38:27 2024 -0400
@@ -46,6 +46,8 @@
 
 // Cells.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<Cell>;
+
 class octave_cell : public octave_base_matrix<Cell>
 {
 public:
@@ -198,7 +200,7 @@
 
   mutable std::unique_ptr<Array<std::string>> m_cellstr_cache;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-ch-mat.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-ch-mat.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -48,15 +48,10 @@
 #include "unistr-wrappers.h"
 
 #include "mxarray.h"
-#include "ov-base.h"
-#include "ov-base-mat.h"
-#include "ov-base-mat.cc"
 #include "ov-ch-mat.h"
 #include "errwarn.h"
 #include "pr-output.h"
 
-template class octave_base_matrix<charNDArray>;
-
 octave::idx_vector
 octave_char_matrix::index_vector (bool /* require_integers */) const
 {
@@ -324,7 +319,7 @@
         std::size_t output_length = in_m.numel ();                             \
         charNDArray ch_array = charNDArray (in_m.dims ());                     \
         const uint8_t *in = reinterpret_cast<const uint8_t *> (in_m.data ());  \
-        uint8_t *buf = reinterpret_cast<uint8_t *> (ch_array.rwdata ());  \
+        uint8_t *buf = reinterpret_cast<uint8_t *> (ch_array.rwdata ());       \
         U8_FCN (in, m_matrix.numel (), nullptr, buf, &output_length);          \
         if (output_length != static_cast<std::size_t> (m_matrix.numel ()))     \
           {                                                                    \
--- a/libinterp/octave-value/ov-ch-mat.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-ch-mat.h	Sat Apr 06 17:38:27 2024 -0400
@@ -48,6 +48,8 @@
 
 // Character matrix values.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<charNDArray>;
+
 class octave_char_matrix : public octave_base_matrix<charNDArray>
 {
 protected:
--- a/libinterp/octave-value/ov-colon.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-colon.h	Sat Apr 06 17:38:27 2024 -0400
@@ -86,7 +86,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-complex.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-complex.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -43,8 +43,6 @@
 #include "ov-complex.h"
 #include "ov-flt-complex.h"
 #include "ov-base.h"
-#include "ov-base-scalar.h"
-#include "ov-base-scalar.cc"
 #include "ov-cx-mat.h"
 #include "ov-scalar.h"
 #include "errwarn.h"
@@ -54,14 +52,6 @@
 #include "ls-oct-text.h"
 #include "ls-hdf5.h"
 
-// Prevent implicit instantiations on some systems (Windows, others?)
-// that can lead to duplicate definitions of static data members.
-
-extern template class octave_base_scalar<double>;
-extern template class octave_base_scalar<FloatComplex>;
-
-template class octave_base_scalar<Complex>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_complex,
                                      "complex scalar", "double");
 
--- a/libinterp/octave-value/ov-complex.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-complex.h	Sat Apr 06 17:38:27 2024 -0400
@@ -47,6 +47,8 @@
 
 // Complex scalar values.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_scalar<Complex>;
+
 class OCTINTERP_API octave_complex : public octave_base_scalar<Complex>
 {
 public:
@@ -203,7 +205,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 typedef octave_complex octave_complex_scalar;
--- a/libinterp/octave-value/ov-cs-list.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-cs-list.h	Sat Apr 06 17:38:27 2024 -0400
@@ -90,7 +90,7 @@
   // The list of Octave values.
   octave_value_list m_list;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-cx-diag.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-cx-diag.h	Sat Apr 06 17:38:27 2024 -0400
@@ -97,7 +97,7 @@
 
   OCTINTERP_API bool chk_valid_scalar (const octave_value&, Complex&) const;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-cx-mat.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-cx-mat.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -50,8 +50,6 @@
 #include "oct-stream.h"
 #include "ops.h"
 #include "ov-base.h"
-#include "ov-base-mat.h"
-#include "ov-base-mat.cc"
 #include "ov-complex.h"
 #include "ov-cx-mat.h"
 #include "ov-flt-cx-mat.h"
@@ -65,8 +63,6 @@
 #include "ls-utils.h"
 
 
-template class octave_base_matrix<ComplexNDArray>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_complex_matrix,
                                      "complex matrix", "double");
 
--- a/libinterp/octave-value/ov-cx-mat.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-cx-mat.h	Sat Apr 06 17:38:27 2024 -0400
@@ -48,6 +48,8 @@
 
 // Complex matrix values.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<ComplexNDArray>;
+
 class OCTINTERP_API octave_complex_matrix : public octave_base_matrix<ComplexNDArray>
 {
 public:
@@ -179,7 +181,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-cx-sparse.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-cx-sparse.h	Sat Apr 06 17:38:27 2024 -0400
@@ -142,7 +142,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-dld-fcn.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-dld-fcn.h	Sat Apr 06 17:38:27 2024 -0400
@@ -104,7 +104,7 @@
   // on the file to see if it has changed.
   bool m_system_fcn_file;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-fcn-handle.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-fcn-handle.h	Sat Apr 06 17:38:27 2024 -0400
@@ -412,7 +412,7 @@
 
   octave::base_fcn_handle * get_rep () const { return m_rep.get (); }
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 extern bool
--- a/libinterp/octave-value/ov-fcn.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-fcn.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -44,25 +44,25 @@
 octave_base_value *
 octave_function::clone () const
 {
-  panic_impossible ();
+  error ("unexpected call to octave_function::clone - please report this bug");
 }
 
 octave_base_value *
 octave_function::empty_clone () const
 {
-  panic_impossible ();
+  error ("unexpected call to octave_function::empty_clone - please report this bug");
 }
 
 octave::filepos
 octave_function::beg_pos () const
 {
-  panic_impossible ();
+  error ("unexpected call to octave_function::beg_pos - please report this bug");
 }
 
 octave::filepos
 octave_function::end_pos () const
 {
-  panic_impossible ();
+  error ("unexpected call to octave_function::end_pos - please report this bug");
 }
 
 octave_value_list
--- a/libinterp/octave-value/ov-float.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-float.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -46,8 +46,6 @@
 #include "ov-scalar.h"
 #include "ov-float.h"
 #include "ov-base.h"
-#include "ov-base-scalar.h"
-#include "ov-base-scalar.cc"
 #include "ov-flt-re-mat.h"
 #include "ov-typeinfo.h"
 #include "pr-output.h"
@@ -59,8 +57,6 @@
 #include "ls-hdf5.h"
 
 
-template class octave_base_scalar<float>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_float_scalar, "float scalar",
                                      "single");
 
--- a/libinterp/octave-value/ov-float.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-float.h	Sat Apr 06 17:38:27 2024 -0400
@@ -49,6 +49,8 @@
 
 // Real scalar values.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_scalar<float>;
+
 class OCTINTERP_API octave_float_scalar : public octave_base_scalar<float>
 {
 public:
@@ -272,7 +274,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-flt-complex.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-flt-complex.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -40,9 +40,6 @@
 #include "oct-stream.h"
 #include "ops.h"
 #include "ov-complex.h"
-#include "ov-base.h"
-#include "ov-base-scalar.h"
-#include "ov-base-scalar.cc"
 #include "ov-flt-cx-mat.h"
 #include "ov-float.h"
 #include "ov-flt-complex.h"
@@ -53,13 +50,6 @@
 #include "ls-oct-text.h"
 #include "ls-hdf5.h"
 
-// Prevent implicit instantiations on some systems (Windows, others?)
-// that can lead to duplicate definitions of static data members.
-
-extern template class octave_base_scalar<float>;
-
-template class octave_base_scalar<FloatComplex>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_float_complex,
                                      "float complex scalar", "single");
 
--- a/libinterp/octave-value/ov-flt-complex.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-flt-complex.h	Sat Apr 06 17:38:27 2024 -0400
@@ -47,6 +47,8 @@
 
 // Complex scalar values.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_scalar<FloatComplex>;
+
 class OCTINTERP_API octave_float_complex : public octave_base_scalar<FloatComplex>
 {
 public:
@@ -194,7 +196,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 typedef octave_float_complex octave_float_complex_scalar;
--- a/libinterp/octave-value/ov-flt-cx-diag.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-flt-cx-diag.h	Sat Apr 06 17:38:27 2024 -0400
@@ -93,7 +93,7 @@
   bool chk_valid_scalar (const octave_value&,
                          FloatComplex&) const;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-flt-cx-mat.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-flt-cx-mat.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -50,8 +50,6 @@
 #include "oct-stream.h"
 #include "ops.h"
 #include "ov-base.h"
-#include "ov-base-mat.h"
-#include "ov-base-mat.cc"
 #include "ov-complex.h"
 #include "ov-flt-complex.h"
 #include "ov-cx-mat.h"
@@ -69,8 +67,6 @@
 #include "ls-utils.h"
 
 
-template class octave_base_matrix<FloatComplexNDArray>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_float_complex_matrix,
                                      "float complex matrix", "single");
 
--- a/libinterp/octave-value/ov-flt-cx-mat.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-flt-cx-mat.h	Sat Apr 06 17:38:27 2024 -0400
@@ -48,6 +48,8 @@
 
 // Complex matrix values.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<FloatComplexNDArray>;
+
 class OCTINTERP_API octave_float_complex_matrix : public octave_base_matrix<FloatComplexNDArray>
 {
 public:
@@ -175,7 +177,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-flt-re-diag.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-flt-re-diag.h	Sat Apr 06 17:38:27 2024 -0400
@@ -103,7 +103,7 @@
   bool chk_valid_scalar (const octave_value&,
                          float&) const;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-flt-re-mat.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-flt-re-mat.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -63,8 +63,6 @@
 #include "oct-stream.h"
 #include "ops.h"
 #include "ov-base.h"
-#include "ov-base-mat.h"
-#include "ov-base-mat.cc"
 #include "ov-scalar.h"
 #include "ov-float.h"
 #include "ov-flt-complex.h"
@@ -84,8 +82,6 @@
 #include "ls-hdf5.h"
 
 
-template class octave_base_matrix<FloatNDArray>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_float_matrix, "float matrix",
                                      "single");
 
--- a/libinterp/octave-value/ov-flt-re-mat.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-flt-re-mat.h	Sat Apr 06 17:38:27 2024 -0400
@@ -48,6 +48,8 @@
 
 // Real matrix values.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<FloatNDArray>;
+
 class OCTINTERP_API octave_float_matrix : public octave_base_matrix<FloatNDArray>
 {
 public:
@@ -218,7 +220,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-int16.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-int16.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -45,8 +45,6 @@
 #  define HDF5_SAVE_TYPE 0
 #endif
 
-#include "ov-base-int.h"
-#include "ov-base-int.cc"
 #include "ov-int16.h"
 #include "pr-output.h"
 #include "variables.h"
@@ -59,21 +57,8 @@
 octave_hdf5_id octave_int16_matrix::s_hdf5_save_type = HDF5_SAVE_TYPE;
 octave_hdf5_id octave_int16_scalar::s_hdf5_save_type = HDF5_SAVE_TYPE;
 
-// Prevent implicit instantiations on some systems (Windows, others?)
-// that can lead to duplicate definitions of static data members.
-
-extern template class octave_base_scalar<double>;
-
-template class octave_base_matrix<int16NDArray>;
-
-template class octave_base_int_matrix<int16NDArray>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_int16_matrix,
                                      "int16 matrix", "int16");
 
-template class octave_base_scalar<octave_int16>;
-
-template class octave_base_int_scalar<octave_int16>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_int16_scalar,
                                      "int16 scalar", "int16");
--- a/libinterp/octave-value/ov-int32.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-int32.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -45,8 +45,6 @@
 #  define HDF5_SAVE_TYPE 0
 #endif
 
-#include "ov-base-int.h"
-#include "ov-base-int.cc"
 #include "ov-int32.h"
 #include "pr-output.h"
 #include "variables.h"
@@ -59,21 +57,8 @@
 octave_hdf5_id octave_int32_matrix::s_hdf5_save_type = HDF5_SAVE_TYPE;
 octave_hdf5_id octave_int32_scalar::s_hdf5_save_type = HDF5_SAVE_TYPE;
 
-// Prevent implicit instantiations on some systems (Windows, others?)
-// that can lead to duplicate definitions of static data members.
-
-extern template class octave_base_scalar<double>;
-
-template class octave_base_matrix<int32NDArray>;
-
-template class octave_base_int_matrix<int32NDArray>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_int32_matrix,
                                      "int32 matrix", "int32");
 
-template class octave_base_scalar<octave_int32>;
-
-template class octave_base_int_scalar<octave_int32>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_int32_scalar,
                                      "int32 scalar", "int32");
--- a/libinterp/octave-value/ov-int64.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-int64.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -45,8 +45,6 @@
 #  define HDF5_SAVE_TYPE 0
 #endif
 
-#include "ov-base-int.h"
-#include "ov-base-int.cc"
 #include "ov-int64.h"
 #include "pr-output.h"
 #include "variables.h"
@@ -59,21 +57,8 @@
 octave_hdf5_id octave_int64_matrix::s_hdf5_save_type = HDF5_SAVE_TYPE;
 octave_hdf5_id octave_int64_scalar::s_hdf5_save_type = HDF5_SAVE_TYPE;
 
-// Prevent implicit instantiations on some systems (Windows, others?)
-// that can lead to duplicate definitions of static data members.
-
-extern template class octave_base_scalar<double>;
-
-template class octave_base_matrix<int64NDArray>;
-
-template class octave_base_int_matrix<int64NDArray>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_int64_matrix,
                                      "int64 matrix", "int64");
 
-template class octave_base_scalar<octave_int64>;
-
-template class octave_base_int_scalar<octave_int64>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_int64_scalar,
                                      "int64 scalar", "int64");
--- a/libinterp/octave-value/ov-int8.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-int8.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -45,8 +45,6 @@
 #  define HDF5_SAVE_TYPE 0
 #endif
 
-#include "ov-base-int.h"
-#include "ov-base-int.cc"
 #include "ov-int8.h"
 #include "pr-output.h"
 #include "variables.h"
@@ -59,21 +57,8 @@
 octave_hdf5_id octave_int8_matrix::s_hdf5_save_type = HDF5_SAVE_TYPE;
 octave_hdf5_id octave_int8_scalar::s_hdf5_save_type = HDF5_SAVE_TYPE;
 
-// Prevent implicit instantiations on some systems (Windows, others?)
-// that can lead to duplicate definitions of static data members.
-
-extern template class octave_base_scalar<double>;
-
-template class octave_base_matrix<int8NDArray>;
-
-template class octave_base_int_matrix<int8NDArray>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_int8_matrix,
                                      "int8 matrix", "int8");
 
-template class octave_base_scalar<octave_int8>;
-
-template class octave_base_int_scalar<octave_int8>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_int8_scalar,
                                      "int8 scalar", "int8");
--- a/libinterp/octave-value/ov-intx.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-intx.h	Sat Apr 06 17:38:27 2024 -0400
@@ -49,6 +49,8 @@
 #include "ov-re-mat.h"
 #include "ov-scalar.h"
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_int_matrix<intNDArray<OCTAVE_INT_T>>;
+
 class OCTINTERP_API OCTAVE_VALUE_INT_MATRIX_T
   : public octave_base_int_matrix<intNDArray<OCTAVE_INT_T>>
 {
@@ -381,10 +383,14 @@
 
   static octave_hdf5_id s_hdf5_save_type;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
-class OCTINTERP_API OCTAVE_VALUE_INT_SCALAR_T
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_int_scalar<OCTAVE_INT_T>;
+
+class
+OCTINTERP_API
+OCTAVE_VALUE_INT_SCALAR_T
   : public octave_base_int_scalar<OCTAVE_INT_T>
 {
 public:
@@ -692,5 +698,5 @@
 
   static octave_hdf5_id s_hdf5_save_type;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
--- a/libinterp/octave-value/ov-lazy-idx.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-lazy-idx.h	Sat Apr 06 17:38:27 2024 -0400
@@ -275,7 +275,7 @@
   static octave_base_value *
   numeric_conversion_function (const octave_base_value&);
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-legacy-range.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-legacy-range.h	Sat Apr 06 17:38:27 2024 -0400
@@ -104,7 +104,7 @@
 
   std::unique_ptr<Range> m_range;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-magic-int.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-magic-int.h	Sat Apr 06 17:38:27 2024 -0400
@@ -295,7 +295,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 class OCTINTERP_API octave_magic_int : public octave_base_magic_int<octave_int64>
@@ -319,7 +319,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-mex-fcn.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-mex-fcn.h	Sat Apr 06 17:38:27 2024 -0400
@@ -121,7 +121,7 @@
   // on the file to see if it has changed.
   bool m_is_system_fcn_file;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-null-mat.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-null-mat.h	Sat Apr 06 17:38:27 2024 -0400
@@ -56,7 +56,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 // The special "" value
@@ -78,7 +78,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 // The special '' value
@@ -101,7 +101,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-oncleanup.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-oncleanup.h	Sat Apr 06 17:38:27 2024 -0400
@@ -99,7 +99,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 
 protected:
 
--- a/libinterp/octave-value/ov-perm.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-perm.h	Sat Apr 06 17:38:27 2024 -0400
@@ -263,7 +263,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-range.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-range.h	Sat Apr 06 17:38:27 2024 -0400
@@ -86,7 +86,7 @@
   int m_base = 0;
   int m_increment = 0;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 // For now, we only need ov_range<double> but we don't attempt to
@@ -522,10 +522,10 @@
 
   static octave_hdf5_id hdf5_save_type;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
-DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS (ov_range, double)
+DECLARE_TEMPLATE_OV_TYPEID_SPECIALIZATIONS_API (ov_range, double, OCTINTERP_API)
 
 // For now, enable only ov_range<double>.
 
--- a/libinterp/octave-value/ov-re-diag.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-re-diag.h	Sat Apr 06 17:38:27 2024 -0400
@@ -108,7 +108,7 @@
   bool chk_valid_scalar (const octave_value&,
                          double&) const;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-re-mat.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-re-mat.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -63,8 +63,6 @@
 #include "oct-stream.h"
 #include "ops.h"
 #include "ov-base.h"
-#include "ov-base-mat.h"
-#include "ov-base-mat.cc"
 #include "ov-scalar.h"
 #include "ov-re-mat.h"
 #include "ov-flt-re-mat.h"
@@ -85,8 +83,6 @@
 #include "ls-hdf5.h"
 
 
-template class octave_base_matrix<NDArray>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_matrix, "matrix", "double");
 
 static octave_base_value *
--- a/libinterp/octave-value/ov-re-mat.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-re-mat.h	Sat Apr 06 17:38:27 2024 -0400
@@ -48,6 +48,8 @@
 
 // Real matrix values.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_matrix<NDArray>;
+
 class OCTINTERP_API octave_matrix : public octave_base_matrix<NDArray>
 {
 public:
@@ -244,7 +246,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-re-sparse.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-re-sparse.h	Sat Apr 06 17:38:27 2024 -0400
@@ -148,7 +148,7 @@
 private:
   octave_value map (double (*fcn) (double)) const;
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-scalar.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-scalar.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -46,8 +46,6 @@
 #include "ov-scalar.h"
 #include "ov-float.h"
 #include "ov-base.h"
-#include "ov-base-scalar.h"
-#include "ov-base-scalar.cc"
 #include "ov-re-mat.h"
 #include "ov-typeinfo.h"
 #include "pr-output.h"
@@ -59,13 +57,6 @@
 #include "ls-oct-text.h"
 #include "ls-hdf5.h"
 
-// Prevent implicit instantiations on some systems (Windows, others?)
-// that can lead to duplicate definitions of static data members.
-
-extern template class octave_base_scalar<float>;
-
-template class octave_base_scalar<double>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_scalar, "scalar", "double");
 
 static octave_base_value *
--- a/libinterp/octave-value/ov-scalar.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-scalar.h	Sat Apr 06 17:38:27 2024 -0400
@@ -48,6 +48,8 @@
 
 // Real scalar values.
 
+extern template class OCTINTERP_EXTERN_TEMPLATE_API octave_base_scalar<double>;
+
 class OCTINTERP_API octave_scalar : public octave_base_scalar<double>
 {
 public:
@@ -285,7 +287,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-str-mat.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-str-mat.h	Sat Apr 06 17:38:27 2024 -0400
@@ -179,7 +179,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 typedef octave_char_matrix_str octave_char_matrix_dq_str;
@@ -264,7 +264,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-struct.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-struct.h	Sat Apr 06 17:38:27 2024 -0400
@@ -184,7 +184,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 class octave_scalar_struct : public octave_base_value
@@ -319,7 +319,7 @@
 
   octave_value to_array ();
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov-uint16.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-uint16.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -46,8 +46,6 @@
 #  define HDF5_SAVE_TYPE 0
 #endif
 
-#include "ov-base-int.h"
-#include "ov-base-int.cc"
 #include "ov-uint16.h"
 #include "pr-output.h"
 #include "variables.h"
@@ -60,21 +58,8 @@
 octave_hdf5_id octave_uint16_matrix::s_hdf5_save_type = HDF5_SAVE_TYPE;
 octave_hdf5_id octave_uint16_scalar::s_hdf5_save_type = HDF5_SAVE_TYPE;
 
-// Prevent implicit instantiations on some systems (Windows, others?)
-// that can lead to duplicate definitions of static data members.
-
-extern template class octave_base_scalar<double>;
-
-template class octave_base_matrix<uint16NDArray>;
-
-template class octave_base_int_matrix<uint16NDArray>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_uint16_matrix,
                                      "uint16 matrix", "uint16");
 
-template class octave_base_scalar<octave_uint16>;
-
-template class octave_base_int_scalar<octave_uint16>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_uint16_scalar,
                                      "uint16 scalar", "uint16");
--- a/libinterp/octave-value/ov-uint32.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-uint32.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -45,8 +45,6 @@
 #  define HDF5_SAVE_TYPE 0
 #endif
 
-#include "ov-base-int.h"
-#include "ov-base-int.cc"
 #include "ov-uint32.h"
 #include "pr-output.h"
 #include "variables.h"
@@ -59,21 +57,8 @@
 octave_hdf5_id octave_uint32_matrix::s_hdf5_save_type = HDF5_SAVE_TYPE;
 octave_hdf5_id octave_uint32_scalar::s_hdf5_save_type = HDF5_SAVE_TYPE;
 
-// Prevent implicit instantiations on some systems (Windows, others?)
-// that can lead to duplicate definitions of static data members.
-
-extern template class octave_base_scalar<double>;
-
-template class octave_base_matrix<uint32NDArray>;
-
-template class octave_base_int_matrix<uint32NDArray>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_uint32_matrix,
                                      "uint32 matrix", "uint32");
 
-template class octave_base_scalar<octave_uint32>;
-
-template class octave_base_int_scalar<octave_uint32>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_uint32_scalar,
                                      "uint32 scalar", "uint32");
--- a/libinterp/octave-value/ov-uint64.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-uint64.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -45,8 +45,6 @@
 #  define HDF5_SAVE_TYPE 0
 #endif
 
-#include "ov-base-int.h"
-#include "ov-base-int.cc"
 #include "ov-uint64.h"
 #include "pr-output.h"
 #include "variables.h"
@@ -59,21 +57,8 @@
 octave_hdf5_id octave_uint64_matrix::s_hdf5_save_type = HDF5_SAVE_TYPE;
 octave_hdf5_id octave_uint64_scalar::s_hdf5_save_type = HDF5_SAVE_TYPE;
 
-// Prevent implicit instantiations on some systems (Windows, others?)
-// that can lead to duplicate definitions of static data members.
-
-extern template class octave_base_scalar<double>;
-
-template class octave_base_matrix<uint64NDArray>;
-
-template class octave_base_int_matrix<uint64NDArray>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_uint64_matrix,
                                      "uint64 matrix", "uint64");
 
-template class octave_base_scalar<octave_uint64>;
-
-template class octave_base_int_scalar<octave_uint64>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_uint64_scalar,
                                      "uint64 scalar", "uint64");
--- a/libinterp/octave-value/ov-uint8.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-uint8.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -45,8 +45,6 @@
 #  define HDF5_SAVE_TYPE 0
 #endif
 
-#include "ov-base-int.h"
-#include "ov-base-int.cc"
 #include "ov-uint8.h"
 #include "pr-output.h"
 #include "variables.h"
@@ -59,21 +57,8 @@
 octave_hdf5_id octave_uint8_matrix::s_hdf5_save_type = HDF5_SAVE_TYPE;
 octave_hdf5_id octave_uint8_scalar::s_hdf5_save_type = HDF5_SAVE_TYPE;
 
-// Prevent implicit instantiations on some systems (Windows, others?)
-// that can lead to duplicate definitions of static data members.
-
-extern template class octave_base_scalar<double>;
-
-template class octave_base_matrix<uint8NDArray>;
-
-template class octave_base_int_matrix<uint8NDArray>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_uint8_matrix,
                                      "uint8 matrix", "uint8");
 
-template class octave_base_scalar<octave_uint8>;
-
-template class octave_base_int_scalar<octave_uint8>;
-
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_uint8_scalar,
                                      "uint8 scalar", "uint8");
--- a/libinterp/octave-value/ov-usr-fcn.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov-usr-fcn.h	Sat Apr 06 17:38:27 2024 -0400
@@ -224,7 +224,7 @@
 
 private:
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 // User-defined functions.
@@ -480,7 +480,7 @@
 
   void restore_warning_states ();
 
-  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA_API (OCTINTERP_API)
 };
 
 #endif
--- a/libinterp/octave-value/ov.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/octave-value/ov.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -1058,9 +1058,14 @@
       m_rep = new octave_bool_matrix (mask, idx);
       break;
 
-    default:
-      panic_impossible ();
+    case octave::idx_vector::class_invalid:
+      error ("unexpected: invalid index in conversion to octave_value - please report this bug");
       break;
+
+      // We should have handled all possible enum values above.  Rely
+      // on compiler diagnostics to warn if we haven't.  For example,
+      // GCC's -Wswitch option, enabled by -Wall, will provide a
+      // warning.
     }
 
   // FIXME: needed?
@@ -2584,7 +2589,7 @@
           return octave_scalar_map ();
 
         default:
-          panic_impossible ();
+          error ("unexpected: index not '(', '{', or '.' in octave_value::empty_conv - please report this bug");
         }
     }
   else
--- a/libinterp/operators/ops.h	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/operators/ops.h	Sat Apr 06 17:38:27 2024 -0400
@@ -138,34 +138,37 @@
   }
 
 // FIXME: the following currently don't handle index.
-#define DEFNDASSIGNOP_OP(name, t1, t2, f, op)                           \
-  static octave_value                                                   \
-  CONCAT2 (oct_assignop_, name) (octave_base_value& a1,                 \
-                                 const octave_value_list& idx,          \
-                                 const octave_base_value& a2)           \
-  {                                                                     \
-    OCTAVE_CAST_BASE_VALUE (CONCAT2 (octave_, t1)&, v1, a1);            \
-    OCTAVE_CAST_BASE_VALUE (const CONCAT2 (octave_, t2)&, v2, a2);      \
-                                                                        \
-    panic_unless (idx.empty ());                                        \
-    v1.matrix_ref () op v2.CONCAT2 (f, _value) ();                      \
-                                                                        \
-    return octave_value ();                                             \
+
+#define DEFNDASSIGNOP_OP(name, t1, t2, f, op)                                                           \
+  static octave_value                                                                                   \
+  CONCAT2 (oct_assignop_, name) (octave_base_value& a1,                                                 \
+                                 const octave_value_list& idx,                                          \
+                                 const octave_base_value& a2)                                           \
+  {                                                                                                     \
+    OCTAVE_CAST_BASE_VALUE (CONCAT2 (octave_, t1)&, v1, a1);                                            \
+    OCTAVE_CAST_BASE_VALUE (const CONCAT2 (octave_, t2)&, v2, a2);                                      \
+                                                                                                        \
+    if (! idx.empty ())                                                                                 \
+      error ("unexpected: index is not empty in oct_assignop_ " #name " - please report this bug");     \
+    v1.matrix_ref () op v2.CONCAT2 (f, _value) ();                                                      \
+                                                                                                        \
+    return octave_value ();                                                                             \
   }
 
-#define DEFNDASSIGNOP_FNOP(name, t1, t2, f, fnop)                       \
-  static octave_value                                                   \
-  CONCAT2 (oct_assignop_, name) (octave_base_value& a1,                 \
-                                 const octave_value_list& idx,          \
-                                 const octave_base_value& a2)           \
-  {                                                                     \
-    OCTAVE_CAST_BASE_VALUE (CONCAT2 (octave_, t1)&, v1, a1);            \
-    OCTAVE_CAST_BASE_VALUE (const CONCAT2 (octave_, t2)&, v2, a2);      \
-                                                                        \
-    panic_unless (idx.empty ());                                        \
-    fnop (v1.matrix_ref (), v2.CONCAT2 (f, _value) ());                 \
-                                                                        \
-    return octave_value ();                                             \
+#define DEFNDASSIGNOP_FNOP(name, t1, t2, f, fnop)                                                       \
+  static octave_value                                                                                   \
+  CONCAT2 (oct_assignop_, name) (octave_base_value& a1,                                                 \
+                                 const octave_value_list& idx,                                          \
+                                 const octave_base_value& a2)                                           \
+  {                                                                                                     \
+    OCTAVE_CAST_BASE_VALUE (CONCAT2 (octave_, t1)&, v1, a1);                                            \
+    OCTAVE_CAST_BASE_VALUE (const CONCAT2 (octave_, t2)&, v2, a2);                                      \
+                                                                                                        \
+    if (! idx.empty ())                                                                                 \
+      error ("unexpected: index is not empty in oct_assignop_ " #name " - please report this bug");     \
+    fnop (v1.matrix_ref (), v2.CONCAT2 (f, _value) ());                                                 \
+                                                                                                        \
+    return octave_value ();                                                                             \
   }
 
 #define DEFASSIGNANYOP_FN(name, t1, f)                          \
--- a/libinterp/parse-tree/lex.ll	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/parse-tree/lex.ll	Sat Apr 06 17:38:27 2024 -0400
@@ -2844,12 +2844,14 @@
         }
         break;
 
-      default:
-        panic_impossible ();
+        // We should have handled all possible enum values above.  Rely
+        // on compiler diagnostics to warn if we haven't.  For example,
+        // GCC's -Wswitch option, enabled by -Wall, will provide a
+        // warning.
       }
 
     if (! tok)
-            tok = new token (kw->tok_id, true, m_tok_beg, m_tok_end, get_comment_list ());
+      tok = new token (kw->tok_id, true, m_tok_beg, m_tok_end, get_comment_list ());
 
     return tok;
   }
@@ -3102,14 +3104,10 @@
     *p = '\0';
 
     double value = 0.0;
-    int nread = 0;
-
-    nread = sscanf (tmptxt, "%lf", &value);
-
-    // Panic instead of error because if yytext doesn't contain a valid
-    // number, we are in deep doo doo.
-
-    panic_unless (nread == 1);
+    int nread = sscanf (tmptxt, "%lf", &value);
+
+    if (nread != 1)
+      error ("unexpected: nread != 1 in base_lexer::handle_number<10> - please report this bug");
 
     octave_value ov_value;
 
@@ -3219,7 +3217,9 @@
 
     uintmax_t long_int_val;
     int status = sscanf (yytxt.c_str (), "%jx", &long_int_val);
-    panic_unless (status);
+
+    if (status == 0)
+      error ("unexpected: sscanf failed in base_lexer::handle_number<16> - please report this bug");
 
     octave_value ov_value
       = make_integer_value (long_int_val, unsigned_val, bytes);
--- a/libinterp/parse-tree/oct-lvalue.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/parse-tree/oct-lvalue.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -168,7 +168,7 @@
       break;
 
     default:
-      panic_impossible ();
+      error ("unexpected: index not '(', '{', or '.' in octave_lvalue::numel - please report this bug");
     }
 }
 
--- a/libinterp/parse-tree/pt-idx.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/libinterp/parse-tree/pt-idx.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -141,21 +141,26 @@
   if (n == 0)
     return m_expr->end_pos ();
 
-  char idx_type = m_type[n-1];
-
-  if (idx_type == '(' || idx_type == '{')
+  switch (m_type[n-1])
     {
-      tree_argument_list *args = m_args.back ();
-      return args->end_pos ();
-    }
+    case '(':
+    case '{':
+      {
+        tree_argument_list *args = m_args.back ();
+        return args->end_pos ();
+      }
+      break;
 
-  if (idx_type == '.')
-    {
-      tree_expression *dyn_field = m_dyn_field.back ();
-      return dyn_field->end_pos ();
+    case '.':
+      {
+        tree_expression *dyn_field = m_dyn_field.back ();
+        return dyn_field->end_pos ();
+      }
+
+    default:
+      error ("unexpected: index not '(', '{', or '.' in tree_index_expression::end_pos - please report this bug");
+      break;
     }
-
-  panic_impossible ();
 }
 
 std::string
--- a/liboctave/array/Array-util.cc	Thu Apr 04 22:51:18 2024 -0400
+++ b/liboctave/array/Array-util.cc	Sat Apr 06 17:38:27 2024 -0400
@@ -337,7 +337,9 @@
 
   int n = frozen_lengths.ndims ();
 
-  liboctave_panic_unless (idx_n == n);
+  if (idx_n != n)
+    (*current_liboctave_error_handler)
+      ("unexpected: idx_n != n in all_colon_equiv - please report this bug");
 
   for (octave_idx_type i = 0; i < n; i++)
     {
--- a/scripts/image/getframe.m	Thu Apr 04 22:51:18 2024 -0400
+++ b/scripts/image/getframe.m	Sat Apr 06 17:38:27 2024 -0400
@@ -34,8 +34,9 @@
 ## Without an argument, capture the current axes excluding ticklabels, title,
 ## and x/y/zlabels.  The returned structure @var{frame} has a field
 ## @code{cdata}, which contains the actual image data in the form of an
-## @nospell{NxMx3} (RGB) uint8 matrix, and a field @code{colormap} which is
-## provided for @sc{matlab} compatibility but is always empty.
+## @nospell{NxMx3} (RGB) uint8 matrix in physical screen pixels, and a field
+## @code{colormap} which is provided for @sc{matlab} compatibility but is
+## always empty.
 ##
 ## If the first argument @var{hax} is an axes handle, then capture this axes,
 ## rather than the current axes returned by @code{gca}.
@@ -114,11 +115,12 @@
     set (hf, "units", units)
   end_unwind_protect
 
-  i1 = max (floor (pos(1)), 1);
-  i2 = min (ceil (pos(1)+pos(3)-1), columns (cdata));
+  dpr = get (hf, "__device_pixel_ratio__");
+  i1 = max (floor ((pos(1)-1)*dpr+1), 1);
+  i2 = min (ceil ((pos(1)+pos(3)-1)*dpr), columns (cdata));
   idxx = i1:i2;
-  i1 = max (floor (pos(2)), 1);
-  i2 = min (ceil (pos(2)+pos(4)-1), rows (cdata));
+  i1 = max (floor ((pos(2)-1)*dpr+1), 1);
+  i2 = min (ceil ((pos(2)+pos(4)-1)*dpr), rows (cdata));
   idxy = fliplr (rows (cdata) - (i1:i2) + 1);
 
   frame = struct ("cdata", cdata(idxy,idxx,:), "colormap", []);
@@ -172,7 +174,8 @@
 %! graphics_toolkit (hf, "qt");
 %! unwind_protect
 %!   pos = get (hf, "position");
-%!   assert (size (getframe (hf).cdata)(1:2), pos(4:-1:3));
+%!   dpr = get (hf, "__device_pixel_ratio__");
+%!   assert (size (getframe (hf).cdata)(1:2), pos(4:-1:3)*dpr);
 %! unwind_protect_cleanup
 %!   close (hf);
 %! end_unwind_protect
--- a/scripts/plot/appearance/legend.m	Thu Apr 04 22:51:18 2024 -0400
+++ b/scripts/plot/appearance/legend.m	Sat Apr 06 17:38:27 2024 -0400
@@ -207,6 +207,12 @@
   ##        lock once that bug is properly fixed.
   mlock ();
 
+  ## legend() requires root property to be invisible.
+  if (strcmp (get (groot, 'showhiddenhandles'), 'on'))
+    cleanup = onCleanup (@() set (groot, 'showhiddenhandles', 'on'));
+    set (groot, 'showhiddenhandles', 'off');
+  endif
+
   opts = parse_opts (varargin{:});
 
   hl = opts.legend_handle;