@@ -153,9 +153,9 @@ rust_tuple_struct_type_p (struct type *type)
return type->num_fields () > 0 && rust_underscore_fields (type);
}
-/* See rust-lang.h. */
+/* Return true if TYPE is "slice-like"; false otherwise. */
-bool
+static bool
rust_slice_type_p (const struct type *type)
{
if (type->code () == TYPE_CODE_STRUCT
@@ -269,6 +269,125 @@ rust_get_trait_object_pointer (struct value *value)
return value_cast (pointer_type, value_field (value, 1 - vtable_field));
}
+/* Find and possibly rewrite the unsized part of a slice-like type.
+
+ This function has two modes. If the out parameters are both NULL,
+ it will return true if an unsized member of IN_TYPE is found.
+
+ If the out parameters are both non-NULL, it will do the same, but
+ will also rewrite the unsized member's type to be an array of the
+ appropriate type. BOUND is the upper bound of the new array.
+
+ See convert_slice to understand the different kinds of unsized type
+ and how they are represented.
+*/
+static bool
+rewrite_slice_type (struct type *in_type, struct type **new_type,
+ LONGEST bound, ULONGEST *additional_length)
+{
+ if (in_type->code () != TYPE_CODE_STRUCT)
+ return false;
+
+ unsigned nfields = in_type->num_fields ();
+ if (nfields == 0)
+ return false;
+
+ struct type *rewritten;
+ const field &field = in_type->field (nfields - 1);
+ struct type *field_type = field.type ();
+ if (field.loc_kind () == FIELD_LOC_KIND_BITPOS
+ && field.loc_bitpos () == 8 * in_type->length ())
+ {
+ if (additional_length == nullptr)
+ return true;
+ rewritten = lookup_array_range_type (field_type, 0, bound);
+ *additional_length = rewritten->length ();
+ }
+ else
+ {
+ if (!rewrite_slice_type (field_type, &rewritten, bound,
+ additional_length))
+ return false;
+ if (additional_length == nullptr)
+ return true;
+ }
+
+ struct type *result = copy_type (in_type);
+ result->copy_fields (in_type);
+ result->field (nfields - 1).set_type (rewritten);
+ result->set_length (result->length () + *additional_length);
+
+ *new_type = result;
+ return true;
+}
+
+/* Convert a Rust slice to its "true" representation.
+
+ The Rust compiler emits slices as "fat" pointers like:
+
+ struct { payload *data_ptr; usize length }
+
+ Any sort of unsized type is emitted this way.
+
+ If 'payload' is a struct type, then it must be searched to see if
+ the trailing field is unsized. This has to be done recursively (as
+ in, if the final field in the struct type itself has struct type,
+ then that type must be searched). In this scenario, the unsized
+ field can be recognized because it does not contribute to the
+ type's size.
+
+ If 'payload' does not have a trailing unsized type, or if it is not
+ of struct type, then this slice is "array-like". In this case
+ rewriting will return an array.
+*/
+static struct value *
+convert_slice (struct value *val)
+{
+ struct type *type = check_typedef (val->type ());
+ /* This must have been checked by the caller. */
+ gdb_assert (rust_slice_type_p (type));
+
+ struct value *len = value_struct_elt (&val, {}, "length", nullptr,
+ "slice");
+ LONGEST llen = value_as_long (len);
+
+ struct value *ptr = value_struct_elt (&val, {}, "data_ptr", nullptr,
+ "slice");
+ struct type *original_type = ptr->type ()->target_type ();
+ ULONGEST new_length_storage = 0;
+ struct type *new_type = nullptr;
+ if (!rewrite_slice_type (original_type, &new_type, llen - 1,
+ &new_length_storage))
+ new_type = lookup_array_range_type (original_type, 0, llen - 1);
+
+ struct value *result = value::allocate_lazy (new_type);
+ result->set_lval (lval_memory);
+ result->set_address (value_as_address (ptr));
+ result->fetch_lazy ();
+
+ return result;
+}
+
+/* If TYPE is an array-like slice, return the element type; otherwise
+ return NULL. */
+static struct type *
+rust_array_like_element_type (struct type *type)
+{
+ /* Caller must check this. */
+ gdb_assert (rust_slice_type_p (type));
+ for (int i = 0; i < type->num_fields (); ++i)
+ {
+ if (strcmp (type->field (i).name (), "data_ptr") == 0)
+ {
+ struct type *base_type = type->field (i).type ()->target_type ();
+ if (rewrite_slice_type (base_type, nullptr, 0, nullptr))
+ return nullptr;
+ return base_type;
+ }
+ }
+ return nullptr;
+}
+
/* See language.h. */
@@ -324,57 +443,40 @@ static const struct generic_val_print_decorations rust_decorations =
struct value *
rust_slice_to_array (struct value *val)
{
- struct type *type = check_typedef (val->type ());
- /* This must have been checked by the caller. */
- gdb_assert (rust_slice_type_p (type));
-
- struct value *base = value_struct_elt (&val, {}, "data_ptr", NULL,
- "slice");
- struct value *len = value_struct_elt (&val, {}, "length", NULL, "slice");
- LONGEST llen = value_as_long (len);
-
- struct type *elt_type = base->type ()->target_type ();
- struct type *array_type = lookup_array_range_type (elt_type, 0,
- llen - 1);
- struct value *array = value::allocate_lazy (array_type);
- array->set_lval (lval_memory);
- array->set_address (value_as_address (base));
-
- return array;
+ val = convert_slice (val);
+ if (val->type ()->code () != TYPE_CODE_ARRAY)
+ return nullptr;
+ return val;
}
/* Helper function to print a slice. */
-static void
-rust_val_print_slice (struct value *val, struct ui_file *stream, int recurse,
- const struct value_print_options *options)
+void
+rust_language::val_print_slice
+ (struct value *val, struct ui_file *stream, int recurse,
+ const struct value_print_options *options) const
{
- struct value *base = value_struct_elt (&val, {}, "data_ptr", NULL,
- "slice");
- struct value *len = value_struct_elt (&val, {}, "length", NULL, "slice");
+ struct type *orig_type = check_typedef (val->type ());
+ val = convert_slice (val);
struct type *type = check_typedef (val->type ());
- if (strcmp (type->name (), "&str") == 0)
- val_print_string (base->type ()->target_type (), "UTF-8",
- value_as_address (base), value_as_long (len), stream,
- options);
- else
- {
- LONGEST llen = value_as_long (len);
- type_print (val->type (), "", stream, -1);
- gdb_printf (stream, " ");
-
- if (llen == 0)
- gdb_printf (stream, "[]");
- else
+ /* &str is handled here; but for all other slice types it is fine to
+ simply print the contents. */
+ if (orig_type->name () != nullptr
+ && strcmp (orig_type->name (), "&str") == 0)
+ {
+ LONGEST low_bound, high_bound;
+ if (get_array_bounds (type, &low_bound, &high_bound))
{
- struct value *array = rust_slice_to_array (val);
- array->fetch_lazy ();
- generic_value_print (array, stream, recurse, options,
- &rust_decorations);
+ val_print_string (type->target_type (), "UTF-8",
+ val->address (), high_bound - low_bound + 1,
+ stream, options);
+ return;
}
}
+
+ value_print_inner (val, stream, recurse, options);
}
/* See rust-lang.h. */
@@ -390,7 +492,7 @@ rust_language::val_print_struct
if (rust_slice_type_p (type))
{
- rust_val_print_slice (val, stream, recurse, options);
+ val_print_slice (val, stream, recurse, options);
return;
}
@@ -1180,6 +1282,7 @@ rust_subscript (struct type *expect_type, struct expression *exp,
low = value_as_long (rhs);
struct type *type = check_typedef (lhs->type ());
+ struct type *orig_type = type;
if (noside == EVAL_AVOID_SIDE_EFFECTS)
{
struct type *base_type = nullptr;
@@ -1187,16 +1290,9 @@ rust_subscript (struct type *expect_type, struct expression *exp,
base_type = type->target_type ();
else if (rust_slice_type_p (type))
{
- for (int i = 0; i < type->num_fields (); ++i)
- {
- if (strcmp (type->field (i).name (), "data_ptr") == 0)
- {
- base_type = type->field (i).type ()->target_type ();
- break;
- }
- }
+ base_type = rust_array_like_element_type (type);
if (base_type == nullptr)
- error (_("Could not find 'data_ptr' in slice type"));
+ error (_("Cannot subscript non-array-like slice"));
}
else if (type->code () == TYPE_CODE_PTR)
base_type = type->target_type ();
@@ -1227,6 +1323,12 @@ rust_subscript (struct type *expect_type, struct expression *exp,
LONGEST low_bound;
struct value *base;
+ if (rust_slice_type_p (type))
+ {
+ lhs = convert_slice (lhs);
+ type = check_typedef (lhs->type ());
+ }
+
if (type->code () == TYPE_CODE_ARRAY)
{
base = lhs;
@@ -1236,15 +1338,6 @@ rust_subscript (struct type *expect_type, struct expression *exp,
error (_("Found array with non-zero lower bound"));
++high_bound;
}
- else if (rust_slice_type_p (type))
- {
- struct value *len;
-
- base = value_struct_elt (&lhs, {}, "data_ptr", NULL, "slice");
- len = value_struct_elt (&lhs, {}, "length", NULL, "slice");
- low_bound = 0;
- high_bound = value_as_long (len);
- }
else if (type->code () == TYPE_CODE_PTR)
{
base = lhs;
@@ -1284,9 +1377,11 @@ rust_subscript (struct type *expect_type, struct expression *exp,
usize = language_lookup_primitive_type (exp->language_defn,
exp->gdbarch,
"usize");
- const char *new_name = ((type != nullptr
- && rust_slice_type_p (type))
- ? type->name () : "&[*gdb*]");
+ /* Preserve the name for slice-of-slice; this lets
+ string-printing work a bit more nicely. */
+ const char *new_name = ((orig_type != nullptr
+ && rust_slice_type_p (orig_type))
+ ? orig_type->name () : "&[*gdb*]");
slice = rust_slice_type (new_name, result->type (), usize);
@@ -1477,7 +1572,11 @@ rust_structop::evaluate (struct type *expect_type,
}
}
else
- result = value_struct_elt (&lhs, {}, field_name, NULL, "structure");
+ {
+ if (rust_slice_type_p (type))
+ lhs = convert_slice (lhs);
+ result = value_struct_elt (&lhs, {}, field_name, NULL, "structure");
+ }
if (noside == EVAL_AVOID_SIDE_EFFECTS)
result = value::zero (result->type (), result->lval ());
return result;
@@ -1677,6 +1776,16 @@ rust_language::emitchar (int ch, struct type *chtype,
/* See language.h. */
+bool
+rust_language::is_array_like (struct type *type) const
+{
+ if (!rust_slice_type_p (type))
+ return false;
+ return rust_array_like_element_type (type) != nullptr;
+}
+
+/* See language.h. */
+
bool
rust_language::is_string_type_p (struct type *type) const
{
@@ -34,9 +34,6 @@ extern bool rust_tuple_type_p (struct type *type);
/* Return true if TYPE is a tuple struct type; otherwise false. */
extern bool rust_tuple_struct_type_p (struct type *type);
-/* Return true if TYPE is a slice type, otherwise false. */
-extern bool rust_slice_type_p (const struct type *type);
-
/* Given a block, find the name of the block's crate. Returns an empty
stringif no crate name can be found. */
extern std::string rust_crate_for_block (const struct block *block);
@@ -196,8 +193,7 @@ class rust_language : public language_defn
/* See language.h. */
- bool is_array_like (struct type *type) const override
- { return rust_slice_type_p (type); }
+ bool is_array_like (struct type *type) const override;
/* See language.h. */
@@ -211,6 +207,13 @@ class rust_language : public language_defn
private:
+ /* Helper for value_print_inner, arguments are as for that function.
+ Prints a slice. */
+
+ void val_print_slice (struct value *val, struct ui_file *stream,
+ int recurse,
+ const struct value_print_options *options) const;
+
/* Helper for value_print_inner, arguments are as for that function.
Prints structs and untagged unions. */
@@ -317,7 +317,7 @@ proc test_one_slice {svar length base range} {
global hex
# Just accept any array here.
- set result " = &\\\[.*\\\] \\\[.*\\\]"
+ set result " = \\\[.*\\\]"
gdb_test "print $svar" $result
gdb_test "print &${base}\[${range}\]" $result
@@ -33,6 +33,6 @@ if {![runto ${srcfile}:$line]} {
gdb_test "ptype us" " = .*V<\\\[u8\\\]>.*"
if {[rust_at_least 1.61]} {
- gdb_test "print us2" " = .*Box<.*> \\\[1, 2, 3\\\]"
+ gdb_test "print us2" " = \\\[1, 2, 3\\\]"
gdb_test "ptype us2" "type = .*"
}
new file mode 100644
@@ -0,0 +1,59 @@
+# Copyright (C) 2024 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 the handling of unsized types.
+
+load_lib rust-support.exp
+require allow_rust_tests
+require {can_compile rust}
+require {rust_at_least 1.61}
+
+standard_testfile .rs
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug rust}]} {
+ return -1
+}
+
+set line [gdb_get_line_number "set breakpoint here"]
+if {![runto ${srcfile}:$line]} {
+ untested "could not run to breakpoint"
+ return -1
+}
+
+set base_value \
+ [string cat \
+ "MaybeUnsizedStruct<.*?>" \
+ [string_to_regexp " {regular: 23, rest: \[5, 6, 7\]}"]]
+
+gdb_test "print *sized_struct" \
+ " = .*$base_value"
+gdb_test "print *nested_sized_struct" \
+ " = .*MaybeUnsizedStruct<.*?> {regular: 91, rest: .*$base_value}"
+
+gdb_test "print unsized_struct" \
+ " = .*$base_value"
+gdb_test "print *reference" \
+ " = .*$base_value"
+
+gdb_test "print nested_unsized_struct" \
+ " = .*MaybeUnsizedStruct<.*?> {regular: 91, rest: .*$base_value}"
+
+gdb_test "print alpha" \
+ " = .*MaybeUnsizedStruct2<.*?> {value: \\\[97, 98, 99, 0\\\]}"
+gdb_test "print beta" \
+ " = .*MaybeUnsizedStruct2<.*?> {value: \\\[97, 98, 99, 0\\\]}"
+
+gdb_test "print sized_struct.regular" " = 23"
+gdb_test "print nested_unsized_struct.regular" " = 91"
+gdb_test "print unsized_struct.rest\[1\]" " = 6"
new file mode 100644
@@ -0,0 +1,67 @@
+// Copyright (C) 2024 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/>.
+
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+
+fn ignore<T>(x: T) { }
+
+// A generic struct that is unsized if T is unsized.
+pub struct MaybeUnsizedStruct<T: ?Sized> {
+ pub regular: u32,
+ pub rest: T,
+}
+
+// Same but without an ordinary part.
+pub struct MaybeUnsizedStruct2<T: ?Sized> {
+ value: T,
+}
+
+fn main() {
+ // This struct is still sized because T is a fixed-length array
+ let sized_struct = &MaybeUnsizedStruct {
+ regular: 23,
+ rest: [5, 6, 7],
+ };
+
+ // This struct is still sized because T is sized
+ let nested_sized_struct = &MaybeUnsizedStruct {
+ regular: 91,
+ rest: MaybeUnsizedStruct {
+ regular: 23,
+ rest: [5, 6, 7],
+ },
+ };
+
+ // This will be a fat pointer, containing the length of the final
+ // field.
+ let unsized_struct: &MaybeUnsizedStruct<[u32]> = sized_struct;
+
+ // This will also be a fat pointer, containing the length of the
+ // final field.
+ let nested_unsized_struct:
+ &MaybeUnsizedStruct<MaybeUnsizedStruct<[u32]>> = nested_sized_struct;
+
+ let alpha: MaybeUnsizedStruct2<[u8; 4]> = MaybeUnsizedStruct2 { value: *b"abc\0" };
+ let beta: &MaybeUnsizedStruct2<[u8]> = α
+
+ let reference = &unsized_struct;
+
+ ignore(sized_struct); // set breakpoint here
+ ignore(nested_sized_struct);
+ ignore(unsized_struct);
+ ignore(nested_unsized_struct);
+}