diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index c04c48e2e90..d49426a0842 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -682,7 +682,7 @@ ada_discrete_type_high_bound (struct type *type)
 	  return high.const_val ();
 	else
 	  {
-	    gdb_assert (high.kind () == PROP_UNDEFINED);
+	    gdb_assert (!high.is_available ());
 
 	    /* This happens when trying to evaluate a type's dynamic bound
 	       without a live target.  There is nothing relevant for us to
@@ -717,7 +717,7 @@ ada_discrete_type_low_bound (struct type *type)
 	  return low.const_val ();
 	else
 	  {
-	    gdb_assert (low.kind () == PROP_UNDEFINED);
+	    gdb_assert (!low.is_available ());
 
 	    /* This happens when trying to evaluate a type's dynamic bound
 	       without a live target.  There is nothing relevant for us to
diff --git a/gdb/c-varobj.c b/gdb/c-varobj.c
index ac840c69493..4a747644909 100644
--- a/gdb/c-varobj.c
+++ b/gdb/c-varobj.c
@@ -192,7 +192,7 @@ c_number_of_children (const struct varobj *var)
     {
     case TYPE_CODE_ARRAY:
       if (type->length () > 0 && target->length () > 0
-	  && (type->bounds ()->high.kind () != PROP_UNDEFINED))
+	  && type->bounds ()->high.is_available ())
 	children = type->length () / target->length ();
       else
 	/* If we don't know how many elements there are, don't display
diff --git a/gdb/eval.c b/gdb/eval.c
index 495effe2d03..4f4582e2d79 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -2819,7 +2819,7 @@ var_value_operation::evaluate_for_sizeof (struct expression *exp,
 	  if (type_not_allocated (type) || type_not_associated (type))
 	    return value::zero (size_type, not_lval);
 	  else if (is_dynamic_type (type->index_type ())
-		   && type->bounds ()->high.kind () == PROP_UNDEFINED)
+		   && !type->bounds ()->high.is_available ())
 	    return value::allocate_optimized_out (size_type);
 	}
     }
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index c35c73a0eb7..7317caecd94 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -1372,7 +1372,7 @@ fortran_undetermined::value_subarray (value *array,
 	     have a known upper bound, so don't error check in that
 	     situation.  */
 	  if (index < lb
-	      || (dim_type->index_type ()->bounds ()->high.kind () != PROP_UNDEFINED
+	      || (dim_type->index_type ()->bounds ()->high.is_available ()
 		  && index > ub)
 	      || (array->lval () != lval_memory
 		  && dim_type->index_type ()->bounds ()->high.kind () == PROP_UNDEFINED))
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 1dc68a99104..1208caf2591 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -2177,21 +2177,35 @@ resolve_dynamic_range (struct type *dyn_range_type,
   gdb_assert (rank >= 0);
 
   const struct dynamic_prop *prop = &dyn_range_type->bounds ()->low;
-  if (resolve_p && dwarf2_evaluate_property (prop, frame, addr_stack, &value,
-					     { (CORE_ADDR) rank }))
-    low_bound.set_const_val (value);
+  if (resolve_p)
+    {
+      if (dwarf2_evaluate_property (prop, frame, addr_stack, &value,
+				    { (CORE_ADDR) rank }))
+	low_bound.set_const_val (value);
+      else if (prop->kind () == PROP_UNDEFINED)
+	low_bound.set_undefined ();
+      else
+	low_bound.set_optimized_out ();
+    }
   else
     low_bound.set_undefined ();
 
   prop = &dyn_range_type->bounds ()->high;
-  if (resolve_p && dwarf2_evaluate_property (prop, frame, addr_stack, &value,
-					     { (CORE_ADDR) rank }))
+  if (resolve_p)
     {
-      high_bound.set_const_val (value);
+      if (dwarf2_evaluate_property (prop, frame, addr_stack, &value,
+				    { (CORE_ADDR) rank }))
+	{
+	  high_bound.set_const_val (value);
 
-      if (dyn_range_type->bounds ()->flag_upper_bound_is_count)
-	high_bound.set_const_val
-	  (low_bound.const_val () + high_bound.const_val () - 1);
+	  if (dyn_range_type->bounds ()->flag_upper_bound_is_count)
+	    high_bound.set_const_val
+	      (low_bound.const_val () + high_bound.const_val () - 1);
+	}
+      else if (prop->kind () == PROP_UNDEFINED)
+	high_bound.set_undefined ();
+      else
+	high_bound.set_optimized_out ();
     }
   else
     high_bound.set_undefined ();
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 2afcf163a81..20f68885d6a 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -271,6 +271,7 @@ enum dynamic_prop_kind
   PROP_VARIANT_PARTS, /* Variant parts.  */
   PROP_TYPE,	   /* Type.  */
   PROP_VARIABLE_NAME, /* Variable name.  */
+  PROP_OPTIMIZED_OUT, /* Optimized out.  */
 };
 
 union dynamic_prop_data
@@ -318,6 +319,18 @@ struct dynamic_prop
     m_kind = PROP_UNDEFINED;
   }
 
+  void set_optimized_out ()
+  {
+    m_kind = PROP_OPTIMIZED_OUT;
+  }
+
+  /* Return true if this property is "available", at least in theory
+     -- meaning it is neither undefined nor optimized out.  */
+  bool is_available () const
+  {
+    return m_kind != PROP_UNDEFINED && m_kind != PROP_OPTIMIZED_OUT;
+  }
+
   LONGEST const_val () const
   {
     gdb_assert (m_kind == PROP_CONST);
@@ -760,6 +773,13 @@ struct range_bounds
       return this->stride.const_val ();
   }
 
+  /* Return true if either bounds is optimized out.  */
+  bool optimized_out () const
+  {
+    return (low.kind () == PROP_OPTIMIZED_OUT
+	    || high.kind () == PROP_OPTIMIZED_OUT);
+  }
+
   /* * Low bound of range.  */
 
   struct dynamic_prop low;
@@ -1135,6 +1155,12 @@ struct type
     this->main_type->flds_bnds.bounds = bounds;
   }
 
+  /* Return true if this type's bounds were optimized out.  */
+  bool bound_optimized_out () const
+  {
+    return bounds ()->optimized_out ();
+  }
+
   ULONGEST bit_stride () const
   {
     if (this->code () == TYPE_CODE_ARRAY && this->field (0).bitsize () != 0)
diff --git a/gdb/testsuite/gdb.dwarf2/arr-opt-out.c b/gdb/testsuite/gdb.dwarf2/arr-opt-out.c
new file mode 100644
index 00000000000..5397911fea6
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/arr-opt-out.c
@@ -0,0 +1,22 @@
+/* Copyright 2023 Free Software Foundation, Inc.
+
+   This program 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.
+
+   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int global_array[] = {0, 0};
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/arr-opt-out.exp b/gdb/testsuite/gdb.dwarf2/arr-opt-out.exp
new file mode 100644
index 00000000000..387ac0ce996
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/arr-opt-out.exp
@@ -0,0 +1,95 @@
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This program 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.
+#
+# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test that an array whose bounds are optimized out is itself marked
+# as optimized out.
+
+load_lib dwarf.exp
+load_lib gdb-python.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+require dwarf2_support
+
+standard_testfile .c -dw.S
+
+# Make some DWARF for the test.
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_language @DW_LANG_Ada95}
+	    {DW_AT_name	    foo.adb}
+	    {DW_AT_comp_dir /tmp}
+	} {
+	    declare_labels integer_label array_label \
+		low_bound_label high_bound_label
+
+	    integer_label: DW_TAG_base_type {
+		{DW_AT_byte_size 4 DW_FORM_sdata}
+		{DW_AT_encoding	 @DW_ATE_signed}
+		{DW_AT_name	 integer}
+	    }
+
+	    # Note that the bounds don't have a location -- they are
+	    # optimized out.  This mimics what it is seen sometimes in
+	    # the wild with optimized Ada code.
+	    low_bound_label: DW_TAG_variable {
+		{DW_AT_name pck__table___L}
+		{DW_AT_type :$integer_label}
+		{DW_AT_declaration 1 flag}
+	    }
+	    high_bound_label: DW_TAG_variable {
+		{DW_AT_name pck__table___U}
+		{DW_AT_type :$integer_label}
+		{DW_AT_declaration 1 flag}
+	    }
+
+	    array_label: DW_TAG_array_type {
+		{DW_AT_name pck__table}
+		{DW_AT_type :$integer_label}
+	    } {
+		DW_TAG_subrange_type {
+		    {DW_AT_type	       :$integer_label}
+		    {DW_AT_lower_bound :$low_bound_label}
+		    {DW_AT_upper_bound :$high_bound_label}
+		}
+	    }
+
+	    DW_TAG_variable {
+		{DW_AT_name the_table}
+		{DW_AT_type :$array_label}
+		{DW_AT_location {
+		    DW_OP_addr [gdb_target_symbol global_array]
+		} SPECIAL_expr}
+		{DW_AT_external 1 flag}
+	    }
+	}
+    }
+}
+
+if {[prepare_for_testing "failed to prepare" ${testfile} \
+	 [list $srcfile $asm_file] {nodebug}]} {
+    return -1
+}
+
+gdb_test_no_output "set language ada"
+
+gdb_test "print the_table" " = <optimized out>"
+
+# The same but in Python.
+if {[allow_python_tests]} {
+    gdb_test "python print(gdb.parse_and_eval('the_table').is_optimized_out)" \
+	True
+}
diff --git a/gdb/value.c b/gdb/value.c
index 4ec9babcce8..04ed243f14d 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -3601,9 +3601,16 @@ value_from_contents_and_address (struct type *type,
   struct type *resolved_type = resolve_dynamic_type (type, view, address,
 						     &frame);
   struct type *resolved_type_no_typedef = check_typedef (resolved_type);
-  struct value *v;
 
-  if (valaddr == NULL)
+  struct value *v;
+  if (resolved_type_no_typedef->code () == TYPE_CODE_ARRAY
+      && resolved_type_no_typedef->bound_optimized_out ())
+    {
+      /* Resolution found that the bounds are optimized out.  In this
+	 case, mark the array itself as optimized-out.  */
+      v = value::allocate_optimized_out (resolved_type);
+    }
+  else if (valaddr == nullptr)
     v = value::allocate_lazy (resolved_type);
   else
     v = value_from_contents (resolved_type, valaddr);
