From c1f5e54825e4ac2d64b267578fd87409e0ea901c Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 24 Jan 2022 18:02:38 -0700 Subject: [PATCH] Fix Rust parser bug with function fields In Rust, 'obj.f()' is a method call -- but '(obj.f)()' is a call of a function-valued field 'f' in 'obj'. The Rust parser in gdb currently gets this wrong. This is PR rust/24082. The expression and Rust parser rewrites made this simple to fix -- simply wrapping a parenthesized expression in a new operation handles it. This patch has a slight hack because I didn't want to introduce a new exp_opcode enumeration constant just for this. IMO this doesn't matter, since we should work toward removing dependencies on these opcodes anyway; but let me know what you think of this. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=24082 --- gdb/rust-exp.h | 27 +++++++++++++++++++++ gdb/rust-parse.c | 2 +- gdb/testsuite/gdb.rust/fnfield.exp | 38 +++++++++++++++++++++++++++++ gdb/testsuite/gdb.rust/fnfield.rs | 39 ++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 gdb/testsuite/gdb.rust/fnfield.exp create mode 100644 gdb/testsuite/gdb.rust/fnfield.rs diff --git a/gdb/rust-exp.h b/gdb/rust-exp.h index 6a24f2cbc10..778d87f4078 100644 --- a/gdb/rust-exp.h +++ b/gdb/rust-exp.h @@ -197,6 +197,33 @@ public: { return OP_AGGREGATE; } }; +/* Rust parenthesized operation. This is needed to distinguish + between 'obj.f()', which is a method call, and '(obj.f)()', which + is a call of a function-valued field 'f'. */ +class rust_parenthesized_operation + : public tuple_holding_operation +{ +public: + + explicit rust_parenthesized_operation (operation_up op) + : tuple_holding_operation (std::move (op)) + { + } + + value *evaluate (struct type *expect_type, + struct expression *exp, + enum noside noside) override + { + return std::get<0> (m_storage)->evaluate (expect_type, exp, noside); + } + + enum exp_opcode opcode () const override + { + /* A lie but this isn't worth introducing a new opcode for. */ + return UNOP_PLUS; + } +}; + } /* namespace expr */ #endif /* RUST_EXP_H */ diff --git a/gdb/rust-parse.c b/gdb/rust-parse.c index 31a1ee3b38f..894f2e79d49 100644 --- a/gdb/rust-parse.c +++ b/gdb/rust-parse.c @@ -1105,7 +1105,7 @@ rust_parser::parse_tuple () { /* Parenthesized expression. */ lex (); - return expr; + return make_operation (std::move (expr)); } std::vector ops; diff --git a/gdb/testsuite/gdb.rust/fnfield.exp b/gdb/testsuite/gdb.rust/fnfield.exp new file mode 100644 index 00000000000..45755710626 --- /dev/null +++ b/gdb/testsuite/gdb.rust/fnfield.exp @@ -0,0 +1,38 @@ +# Copyright (C) 2022 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 . + +# Test trait object printing. + +load_lib rust-support.exp +if {[skip_rust_tests]} { + continue +} + +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 +} + +gdb_test "print foo.f()" " = 6" "call impl function" +gdb_test "print (foo.f)()" " = 5" "call function field" +gdb_test "print foo.g()" " = 7" "call impl function g" +gdb_test "print (foo.g)()" "There is no member named g." \ + "cannot call g with parens" diff --git a/gdb/testsuite/gdb.rust/fnfield.rs b/gdb/testsuite/gdb.rust/fnfield.rs new file mode 100644 index 00000000000..b80136c057f --- /dev/null +++ b/gdb/testsuite/gdb.rust/fnfield.rs @@ -0,0 +1,39 @@ +// Copyright (C) 2022 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 . + +#![allow(warnings)] + +fn five() -> i32 { 5 } + +fn main() { + let foo = Foo {x: 5, f: five}; + foo.print(); // set breakpoint here + println!("Hello, world! {}, {}, {}", foo.f(), (foo.f)(), + foo.g ()); +} + +struct Foo { + x :i32, + f: fn () -> i32, +} + +impl Foo { + fn print(&self) { + println!("hello {}", self.x) + } + + fn f(&self) -> i32 { 6 } + fn g(&self) -> i32 { 7 } +} -- 2.30.2