Message ID | 20191214095325.3864520AF6@gnutoolchain-gerrit.osci.io |
---|---|
State | New |
Headers | show |
Hi, I noticed these new tests fail (in large numbers) for GCC 5.4.x. Is it expected? If so, it may be worth making this test conditional on newer GCC versions. On 12/14/19 6:53 AM, Tankut Baris Aktemur (Code Review) wrote: > Change URL: https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/142 > ...................................................................... > > testsuite, cp: increase the coverage of testing pass-by-ref arguments > > Extend testcases for GDB's infcall of call-by-value functions that > take aggregate values as parameters. In particular, existing test has > been substantially extended with class definitions whose definitions > of copy constructor, destructor, and move constructor functions are a > combination of > > (1) explicitly defined by the user, > (2) defaulted inside the class declaration, > (3) defaulted outside the class declaration, > (4) deleted > (5) not defined in the source. > > For each combination, a small and a large class is generated as well > as a derived class and a container class. Additionally, the following > manually-written cases are provided: > > - a dynamic class (i.e. class with a virtual method) > - classes that contain an array field > - a class whose copy ctor is inlined > - a class whose destructor is deleted > - classes with multiple copy and/or move ctors > > Test cases check whether GDB makes the right decision to pass an > object by value or implicitly by reference, whether really a copy of > the argument is passed, and whether the copy constructor and > destructor of the clone of the argument are invoked properly. > > The input program pass-by-ref.cc is generated in the test's output > directory. The input program pass-by-ref-2.cc is manually-written. > > Tests have been verified on the X86_64 architecture with > GCC 7.4.0, 8.2.0, and 9.2.1. > > gdb/testsuite/ChangeLog: > 2019-11-07 Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> > > * gdb.cp/pass-by-ref.cc: Delete. Generated in the output > directory instead. > * gdb.cp/pass-by-ref.exp: Extend with more cases. > * gdb.cp/pass-by-ref-2.cc: New file. > * gdb.cp/pass-by-ref-2.exp: New file. > > Change-Id: Ie8ab1f260c6ad5ee4eb34b2c1597ce24af04abb6 > --- > A gdb/testsuite/gdb.cp/pass-by-ref-2.cc > A gdb/testsuite/gdb.cp/pass-by-ref-2.exp > D gdb/testsuite/gdb.cp/pass-by-ref.cc > M gdb/testsuite/gdb.cp/pass-by-ref.exp > 4 files changed, 791 insertions(+), 86 deletions(-) > > > > diff --git a/gdb/testsuite/gdb.cp/pass-by-ref-2.cc b/gdb/testsuite/gdb.cp/pass-by-ref-2.cc > new file mode 100644 > index 0000000..1cd5a16 > --- /dev/null > +++ b/gdb/testsuite/gdb.cp/pass-by-ref-2.cc > @@ -0,0 +1,295 @@ > +/* This testcase is part of GDB, the GNU debugger. > + > + Copyright 2019 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/>. */ > + > +class ByVal { > +public: > + ByVal (void); > + > + int x; > +}; > + > +ByVal::ByVal (void) > +{ > + x = 2; > +} > + > +class ByRef { > +public: > + ByRef (void); > + > + ByRef (const ByRef &rhs); > + > + int x; > +}; > + > +ByRef::ByRef (void) > +{ > + x = 2; > +} > + > +ByRef::ByRef (const ByRef &rhs) > +{ > + x = 3; /* ByRef-cctor */ > +} > + > +class ArrayContainerByVal { > +public: > + ByVal items[2]; > +}; > + > +int > +cbvArrayContainerByVal (ArrayContainerByVal arg) > +{ > + arg.items[0].x += 4; // intentionally modify > + return arg.items[0].x; > +} > + > +class ArrayContainerByRef { > +public: > + ByRef items[2]; > +}; > + > +int > +cbvArrayContainerByRef (ArrayContainerByRef arg) > +{ > + arg.items[0].x += 4; // intentionally modify > + return arg.items[0].x; > +} > + > +class DynamicBase { > +public: > + DynamicBase (void); > + > + virtual int get (void); > + > + int x; > +}; > + > +DynamicBase::DynamicBase (void) > +{ > + x = 2; > +} > + > +int > +DynamicBase::get (void) > +{ > + return 42; > +} > + > +class Dynamic : public DynamicBase { > +public: > + virtual int get (void); > +}; > + > +int > +Dynamic::get (void) > +{ > + return 9999; > +} > + > +int > +cbvDynamic (DynamicBase arg) > +{ > + arg.x += 4; // intentionally modify > + return arg.x + arg.get (); > +} > + > +class Inlined { > +public: > + Inlined (void); > + > + __attribute__((always_inline)) > + Inlined (const Inlined &rhs) > + { > + x = 3; > + } > + > + int x; > +}; > + > +Inlined::Inlined (void) > +{ > + x = 2; > +} > + > +int > +cbvInlined (Inlined arg) > +{ > + arg.x += 4; // intentionally modify > + return arg.x; > +} > + > +class DtorDel { > +public: > + DtorDel (void); > + > + ~DtorDel (void) = delete; > + > + int x; > +}; > + > +DtorDel::DtorDel (void) > +{ > + x = 2; > +} > + > +int > +cbvDtorDel (DtorDel arg) > +{ > + // Calling this method should be rejected > + return arg.x; > +} > + > +class FourCCtor { > +public: > + FourCCtor (void); > + > + FourCCtor (FourCCtor &rhs); > + FourCCtor (const FourCCtor &rhs); > + FourCCtor (volatile FourCCtor &rhs); > + FourCCtor (const volatile FourCCtor &rhs); > + > + int x; > +}; > + > +FourCCtor::FourCCtor (void) > +{ > + x = 2; > +} > + > +FourCCtor::FourCCtor (FourCCtor &rhs) > +{ > + x = 3; > +} > + > +FourCCtor::FourCCtor (const FourCCtor &rhs) > +{ > + x = 4; > +} > + > +FourCCtor::FourCCtor (volatile FourCCtor &rhs) > +{ > + x = 5; > +} > + > +FourCCtor::FourCCtor (const volatile FourCCtor &rhs) > +{ > + x = 6; > +} > + > +int > +cbvFourCCtor (FourCCtor arg) > +{ > + arg.x += 10; // intentionally modify > + return arg.x; > +} > + > +class TwoMCtor { > +public: > + TwoMCtor (void); > + > + /* Even though one move ctor is defaulted, the other > + is explicit. */ > + TwoMCtor (const TwoMCtor &&rhs); > + TwoMCtor (TwoMCtor &&rhs) = default; > + > + int x; > +}; > + > +TwoMCtor::TwoMCtor (void) > +{ > + x = 2; > +} > + > +TwoMCtor::TwoMCtor (const TwoMCtor &&rhs) > +{ > + x = 3; > +} > + > +int > +cbvTwoMCtor (TwoMCtor arg) > +{ > + arg.x += 10; // intentionally modify > + return arg.x; > +} > + > +class TwoMCtorAndCCtor { > +public: > + TwoMCtorAndCCtor (void); > + > + TwoMCtorAndCCtor (const TwoMCtorAndCCtor &rhs) = default; > + > + /* Even though one move ctor is defaulted, the other > + is explicit. This makes the type pass-by-ref. */ > + TwoMCtorAndCCtor (const TwoMCtorAndCCtor &&rhs); > + TwoMCtorAndCCtor (TwoMCtorAndCCtor &&rhs) = default; > + > + int x; > +}; > + > +TwoMCtorAndCCtor::TwoMCtorAndCCtor (void) > +{ > + x = 2; > +} > + > +TwoMCtorAndCCtor::TwoMCtorAndCCtor (const TwoMCtorAndCCtor &&rhs) > +{ > + x = 4; > +} > + > +int > +cbvTwoMCtorAndCCtor (TwoMCtorAndCCtor arg) > +{ > + arg.x += 10; // intentionally modify > + return arg.x; > +} > + > +ArrayContainerByVal arrayContainerByVal; > +ArrayContainerByRef arrayContainerByRef; > +Dynamic dynamic; > +Inlined inlined; > +// Cannot stack-allocate DtorDel > +DtorDel *dtorDel; > +FourCCtor fourCctor_c0v0; > +const FourCCtor fourCctor_c1v0; > +volatile FourCCtor fourCctor_c0v1; > +const volatile FourCCtor fourCctor_c1v1; > +TwoMCtor twoMctor; > +TwoMCtorAndCCtor twoMctorAndCctor; > + > +int > +main (void) > +{ > + int v; > + dtorDel = new DtorDel; > + /* Explicitly call the cbv function to make sure the compiler > + will not omit any code in the binary. */ > + v = cbvArrayContainerByVal (arrayContainerByVal); > + v = cbvArrayContainerByRef (arrayContainerByRef); > + v = cbvDynamic (dynamic); > + v = cbvInlined (inlined); > + v = cbvFourCCtor (fourCctor_c0v0); > + v = cbvFourCCtor (fourCctor_c1v0); > + v = cbvFourCCtor (fourCctor_c0v1); > + v = cbvFourCCtor (fourCctor_c1v1); > + /* v = cbvTwoMCtor (twoMctor); */ // This is illegal, cctor is deleted > + v = cbvTwoMCtorAndCCtor (twoMctorAndCctor); > + > + /* stop here */ > + > + return 0; > +} > diff --git a/gdb/testsuite/gdb.cp/pass-by-ref-2.exp b/gdb/testsuite/gdb.cp/pass-by-ref-2.exp > new file mode 100644 > index 0000000..7cce886 > --- /dev/null > +++ b/gdb/testsuite/gdb.cp/pass-by-ref-2.exp > @@ -0,0 +1,114 @@ > +# Copyright 2019 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/>. > + > +# Check that GDB can call C++ functions whose parameters have > +# object type, and are either passed by value or implicitly by reference. > +# > +# This is a companion test to pass-by-ref.exp. In this test, the input > +# is manually-written. In pass-by-ref.exp, the test input is generated. > +# > +# We include tests for classes that > +# - contain arrays as fields, > +# - are dynamic (i.e. have virtual methods) > +# - have inlined copy ctor > +# - have deleted destructor > + > +if {[skip_cplus_tests]} { > + untested "c++ test skipped" > + continue > +} > + > +standard_testfile .cc > + > +set options {debug c++ additional_flags=-std=c++11} > +if {[prepare_for_testing "failed to prepare" $testfile $srcfile $options]} { > + return -1 > +} > + > +if {![runto_main]} { > + untested "failed to run to main" > + return -1 > +} > + > +set bp_location [gdb_get_line_number "stop here"] > +gdb_breakpoint $bp_location > +gdb_continue_to_breakpoint "end of main" ".*return .*;" > + > +gdb_test "print cbvArrayContainerByVal (arrayContainerByVal)" "6" \ > + "call cbvArrayContainerByVal" > +gdb_test "print arrayContainerByVal.items\[0\].x" "2" \ > + "cbv argument 'arrayContainerByVal' should not change" > + > +gdb_test "print cbvArrayContainerByRef (arrayContainerByRef)" "7" \ > + "call cbvArrayContainerByRef" > +gdb_test "print arrayContainerByRef.items\[0\].x" "2" \ > + "cbv argument 'arrayContainerByRef' should not change" > + > +gdb_test "print cbvDynamic (dynamic)" "48" \ > + "call cbvDynamic" > +gdb_test "print dynamic.x" "2" \ > + "cbv argument 'dynamic' should not change" > + > +set sig "\"Inlined\:\:Inlined\\(.*Inlined const\&\\)\"" > +gdb_test "print cbvInlined (inlined)" \ > + "expression cannot be evaluated .* \\(maybe inlined\\?\\)" > + > +gdb_test "print cbvDtorDel (*dtorDel)" \ > + ".* cannot be evaluated .* 'DtorDel' is not destructible" \ > + "type not destructible" > + > +# Test that GDB calls the correct copy ctor > +gdb_test "print cbvFourCCtor (fourCctor_c0v0)" "13" \ > + "call cbvFourCCtor (c0v0)" > +gdb_test "print fourCctor_c0v0.x" "2" \ > + "cbv argument 'twoCctor_c0v0' should not change" > + > +gdb_test "print cbvFourCCtor (fourCctor_c1v0)" "14" \ > + "call cbvFourCCtor (c1v0)" > +gdb_test "print fourCctor_c1v0.x" "2" \ > + "cbv argument 'twoCctor_c1v0' should not change" > + > +gdb_test "print cbvFourCCtor (fourCctor_c0v1)" "15" \ > + "call cbvFourCCtor (c0v1)" > +gdb_test "print fourCctor_c0v1.x" "2" \ > + "cbv argument 'twoCctor_c0v1' should not change" > + > +gdb_test "print cbvFourCCtor (fourCctor_c1v1)" "16" \ > + "call cbvFourCCtor (c1v1)" > +gdb_test "print fourCctor_c1v1.x" "2" \ > + "cbv argument 'twoCctor_c1v1' should not change" > + > +gdb_test "print cbvTwoMCtor (twoMctor)" \ > + ".* cannot be evaluated .* 'TwoMCtor' is not copy constructible" \ > + "copy ctor is implicitly deleted" > + > +gdb_test "print cbvTwoMCtorAndCCtor (twoMctorAndCctor)" "12" \ > + "call cbvTwoMCtorAndCCtor" > +gdb_test "print twoMctorAndCctor.x" "2" \ > + "cbv argument 'twoMctorAndCtor' should not change" > + > +# Test that we get a breakpoint from the cctor during infcall and > +# we can examine arguments. This is a test that the dummy frame > +# of the copy constructor is set up correctly by the infcall mechanism. > +set bp_location [gdb_get_line_number "ByRef-cctor"] > +gdb_breakpoint $bp_location > +gdb_test "print cbvArrayContainerByRef (arrayContainerByRef)" \ > + ".*The program being debugged stopped.*" \ > + "call cbvArrayContainerByRef with BP" > +gdb_test "backtrace" [multi_line \ > + "#0 ByRef\:\:ByRef .* at .*$srcfile:$bp_location" \ > + "#1 .* ArrayContainerByRef::ArrayContainerByRef .*" \ > + "#2 <function called from gdb>" \ > + "#3 main.*"] > diff --git a/gdb/testsuite/gdb.cp/pass-by-ref.cc b/gdb/testsuite/gdb.cp/pass-by-ref.cc > deleted file mode 100644 > index bbe450a..0000000 > --- a/gdb/testsuite/gdb.cp/pass-by-ref.cc > +++ /dev/null > @@ -1,79 +0,0 @@ > -/* This testcase is part of GDB, the GNU debugger. > - > - Copyright 2007-2019 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/>. */ > - > -class Obj { > -public: > - Obj (); > - Obj (const Obj &); > - ~Obj (); > - int var[2]; > -}; > - > -int foo (Obj arg) > -{ > - return arg.var[0] + arg.var[1]; > -} > - > -Obj::Obj () > -{ > - var[0] = 1; > - var[1] = 2; > -} > - > -Obj::Obj (const Obj &obj) > -{ > - var[0] = obj.var[0]; > - var[1] = obj.var[1]; > -} > - > -Obj::~Obj () > -{ > - > -} > - > -struct Derived : public Obj > -{ > - int other; > -}; > - > -int blap (Derived arg) > -{ > - return foo (arg); > -} > - > -struct Container > -{ > - Obj obj; > -}; > - > -int blip (Container arg) > -{ > - return foo (arg.obj); > -} > - > -Obj global_obj; > -Derived global_derived; > -Container global_container; > - > -int > -main () > -{ > - int bar = foo (global_obj); > - blap (global_derived); > - blip (global_container); > - return bar; > -} > diff --git a/gdb/testsuite/gdb.cp/pass-by-ref.exp b/gdb/testsuite/gdb.cp/pass-by-ref.exp > index 94dd345..f44be77 100644 > --- a/gdb/testsuite/gdb.cp/pass-by-ref.exp > +++ b/gdb/testsuite/gdb.cp/pass-by-ref.exp > @@ -14,20 +14,395 @@ > # along with this program. If not, see <http://www.gnu.org/licenses/>. > > # Check that GDB can call C++ functions whose parameters have > -# object type, but are passed by reference. > +# object type, and are either passed by value or implicitly by reference. > +# > +# Suppose F is a function that has a call-by-value parameter whose > +# type is class C. When calling F with an argument A, a copy of A should > +# be created and passed to F. If C is a trivially-copyable type, A can > +# be copied by a straightforward memory copy. However, roughly speaking, > +# if C has a user-defined copy constructor and/or a user-defined > +# destructor, the copy ctor should be used to initialize the copy of A > +# before calling F, and a reference to that copy is passed to F. After > +# the function returns, the destructor should be called to destruct the > +# copy. In this case, C is said to be a 'pass-by-reference' type. > +# Determining whether C is pass-by-ref depends on > +# how the copy ctor, destructor, and the move ctor of C are defined. > +# First of all, C is not copy constructible if its copy constructor is > +# explicitly or implicitly deleted. In this case, it would be illegal > +# to pass values of type C to a function. C is pass-by-value, if all of > +# its copy ctor, dtor, and move ctor are trivially defined. > +# Otherwise, it is pass-by-ref. > +# > +# To cover the many possible combinations, this test generates classes > +# that contain three special functions: > +# (1) a copy constructor, > +# (2) a destructor, and > +# (3) a move constructor. > +# A special function is in one of the following states: > +# * explicit: The function is explicitly defined by the user. > +# * defaultedIn: The function is defaulted inside the class decl, > +# using the 'default' keyword. > +# * defaultedOut: The function is declared inside the class decl, > +# and defaulted outside using the 'default' keyword. > +# * deleted: The function is explicitly deleted by the user, > +# using the 'delete' keyword. > +# * absent: The function is not declared by the user (i.e. it does not > +# exist in the source. The compiler generates (or deletes) the > +# definition in this case. > +# > +# The C++ ABI decides if a class is pass-by-value or pass-by-ref > +# (i.e. trivially copyable or not) first at the language level, based > +# on the state of the special functions. Then, at the target level, a > +# class may be determined to be pass-by-ref because of its size > +# (e.g. if it is too large to fit on registers). For this reason, this > +# test generates both a small and a large version for the same > +# combination of special function states. > +# > +# A class is not trivially-copyable if a base class or a field is not > +# trivially-copyable, even though the class definition itself seems > +# trivial. To test these cases, we also generate derived classes and > +# container classes. > +# > +# The generated code is placed in the test output directory. > +# > +# The companion test file pass-by-ref-2.exp also contains > +# manually-written cases. > > -if { [skip_cplus_tests] } { continue } > +if {[skip_cplus_tests]} { > + untested "c++ test skipped" > + continue > +} > > +# The program source is generated in the output directory. > +# We use standard_testfile here to set convenience variables. > standard_testfile .cc > > -if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} { > +# Some constant values used when generating the source > + > +set SMALL 2 > +set LARGE 150 > +set ORIGINAL 2 > +set CUSTOM 3 > +set ADDED 4 > +set TRACE 5 > + > + > +# Return 1 if the class whose special function states are STATES > +# is copyable. Otherwise return 0. > + > +proc is_copy_constructible { states } { > + set cctor [lindex $states 0] > + set dtor [lindex $states 1] > + set mctor [lindex $states 2] > + > + if {$cctor == "deleted" || ($cctor == "absent" && $mctor != "absent")} { > + return 0 > + } > + return 1 > +} > + > +# Generate a declaration and an out-of-class definition for a function > +# with the provided signature. The STATE should be one of the following: > +# - explicit, defaultedIn, defaultedOut, deleted, absent > + > +proc generate_member_function { classname signature length state } { > + set declaration "" > + set definition "" > + > + global CUSTOM > + global TRACE > + > + switch $state { > + explicit { > + set declaration "$signature;\n" > + set definition "$classname\:\:$signature > + { > + data\[0\] = $CUSTOM; > + data\[[expr $length - 1]\] = $CUSTOM; > + tracer = $TRACE; > + }\n" > + } > + defaultedIn { > + set declaration "$signature = default;\n" > + } > + defaultedOut { > + set declaration "$signature;\n" > + set definition "$classname\:\:$signature = default;\n" > + } > + deleted { > + set declaration "$signature = delete;\n" > + } > + default { > + # function is not user-defined in this case > + } > + } > + > + return [list $declaration $definition] > +} > + > +# Generate a C++ class with the given CLASSNAME and LENGTH-many > +# integer elements. The STATES is an array of 3 items > +# containing the desired state of the special functions > +# in this order: > +# copy constructor, destructor, move constructor > + > +proc generate_class { classname length states } { > + set declarations "" > + set definitions "" > + set classname "${classname}_[join $states _]" > + > + for {set i 0} {$i < [llength $states]} {incr i} { > + set sig "" > + switch $i { > + 0 {set sig "$classname (const $classname \&rhs)"} > + 1 {set sig "\~$classname (void)"} > + 2 {set sig "$classname ($classname \&\&rhs)"} > + } > + > + set state [lindex $states $i] > + set code [generate_member_function $classname $sig $length $state] > + append declarations [lindex $code 0] > + append definitions [lindex $code 1] > + } > + > + global ORIGINAL > + > + return " > + /*** C++ class $classname ***/ > + class ${classname} { > + public: > + $classname (void); > + $declarations > + > + int data\[$length\]; > + }; > + > + $classname\:\:$classname (void) > + { > + data\[0\] = $ORIGINAL; > + data\[[expr $length - 1]\] = $ORIGINAL; > + } > + > + $definitions > + > + $classname ${classname}_var; /* global var */ > + > + template int cbv<$classname> ($classname arg);" > +} > + > +# Generate a small C++ class > + > +proc generate_small_class { states } { > + global SMALL > + return [generate_class Small $SMALL $states]; > +} > + > +# Generate a large C++ class > + > +proc generate_large_class { states } { > + global LARGE > + return [generate_class Large $LARGE $states]; > +} > + > +# Generate a class that derives from a small class > + > +proc generate_derived_class { states } { > + set base "Small_[join $states _]" > + set classname "Derived_[join $states _]" > + > + return " > + /*** Class derived from $base ***/ > + class $classname : public $base { > + public: > + }; > + > + $classname ${classname}_var; /* global var */ > + > + template int cbv<$classname> ($classname arg);" > +} > + > +# Generate a class that contains a small class item > + > +proc generate_container_class { states } { > + set contained "Small_[join $states _]" > + set classname "Container_[join $states _]" > + > + return " > + /*** Class that contains $contained ***/ > + class $classname { > + public: > + $contained item; > + }; > + > + $classname ${classname}_var; /* global var */ > + > + template int cbv_container<$classname> ($classname arg);" > +} > + > +# Generate useful statements that use a class in the debugee program > + > +proc generate_stmts { classprefix states {cbvfun "cbv"}} { > + set classname "${classprefix}_[join $states _]" > + > + # Having an explicit call to the cbv function in the debugee program > + # ensures that the compiler will emit necessary function in the binary. > + if {[is_copy_constructible $states]} { > + set cbvcall "$cbvfun<$classname> (${classname}_var);\n" > + } else { > + set cbvcall "" > + } > + > + return "$cbvcall" > +} > + > +# Generate the complete debugee program > + > +proc generate_program { classes stmts } { > + global ADDED > + > + return " > + /*** THIS FILE IS GENERATED BY THE TEST. ***/ > + > + static int tracer = 0; > + > + /* The call-by-value function. */ > + template <class T> > + int > + cbv (T arg) > + { > + arg.data\[0\] += $ADDED; // intentionally modify the arg > + return arg.data\[0\]; > + } > + > + template <class T> > + int > + cbv_container (T arg) > + { > + arg.item.data\[0\] += $ADDED; // intentionally modify > + return arg.item.data\[0\]; > + } > + > + $classes > + > + int > + main (void) > + { > + $stmts > + > + /* stop here */ > + > + return 0; > + }" > +} > + > +# Compute all the combinations of special function states. > +# We do not contain the 'deleted' state for the destructor, > +# because it is illegal to have stack-allocated objects > +# whose destructor have been deleted. This case is covered > +# in pass-by-ref-2 via heap-allocated objects. > + > +set options_nodelete [list absent explicit defaultedIn defaultedOut] > +set options [concat $options_nodelete {deleted}] > +set all_combinations {} > + > +foreach cctor $options { > + foreach dtor $options_nodelete { > + foreach mctor $options { > + lappend all_combinations [list $cctor $dtor $mctor] > + } > + } > +} > + > +# Generate the classes. > + > +set classes "" > +set stmts "" > + > +foreach state $all_combinations { > + append classes [generate_small_class $state] > + append stmts [generate_stmts "Small" $state] > + > + append classes [generate_large_class $state] > + append stmts [generate_stmts "Large" $state] > + > + append classes [generate_derived_class $state] > + append stmts [generate_stmts "Derived" $state] > + > + append classes [generate_container_class $state] > + append stmts [generate_stmts "Container" $state "cbv_container"] > +} > + > +# Generate the program code and compile > +set program [generate_program $classes $stmts] > +set srcfile [standard_output_file ${srcfile}] > +gdb_produce_source $srcfile $program > + > +set options {debug c++ additional_flags=-std=c++11} > +if {[prepare_for_testing "failed to prepare" $testfile $srcfile $options]} { > return -1 > } > > -if ![runto_main] then { > +if {![runto_main]} { > + untested "failed to run to main" > return -1 > } > > -gdb_test "print foo (global_obj)" " = 3" "call function in obj" > -gdb_test "print blap (global_derived)" " = 3" "call function in derived" > -gdb_test "print blip (global_container)" " = 3" "call function in container" > +set bp_location [gdb_get_line_number "stop here"] > +gdb_breakpoint $bp_location > +gdb_continue_to_breakpoint "end of main" ".*return .*;" > + > +# Do the checks for a given class whose name is prefixed with PREFIX, > +# and whose special functions have the states given in STATES. > +# The name of the call-by-value function and the expression to access > +# the data field can be specified explicitly if the default values > +# do not work. > + > +proc test_for_class { prefix states cbvfun data_field length} { > + set name "${prefix}_[join $states _]" > + > + set cctor [lindex $states 0] > + set dtor [lindex $states 1] > + set mctor [lindex $states 2] > + > + global ORIGINAL > + global CUSTOM > + global ADDED > + global TRACE > + > + with_test_prefix $name { > + if {[is_copy_constructible $states]} { > + set expected [expr {$ORIGINAL + $ADDED}] > + if {$cctor == "explicit"} { > + set expected [expr {$CUSTOM + $ADDED}] > + } > + if {$dtor == "explicit"} { > + gdb_test "print tracer = 0" " = 0" "reset the tracer" > + } > + gdb_test "print ${cbvfun}<$name> (${name}_var)" " = $expected" \ > + "call '$cbvfun'" > + gdb_test "print ${name}_var.${data_field}\[0\]" " = $ORIGINAL" \ > + "cbv argument should not change (item 0)" > + if {$length > 1} { > + set last_index [expr $length - 1] > + gdb_test "print ${name}_var.${data_field}\[$last_index\]" \ > + " = $ORIGINAL" \ > + "cbv argument should not change (item $last_index)" > + } > + if {$dtor == "explicit"} { > + gdb_test "print tracer" " = $TRACE" \ > + "destructor should be called" > + } > + } else { > + gdb_test "print ${cbvfun}<$name> (${name}_var)" \ > + ".* cannot be evaluated .* '${name}' is not copy constructible" \ > + "calling '$cbvfun' should be refused" > + } > + } > +} > + > +foreach state $all_combinations { > + test_for_class "Small" $state "cbv" "data" $SMALL > + test_for_class "Large" $state "cbv" "data" $LARGE > + test_for_class "Derived" $state "cbv" "data" 1 > + test_for_class "Container" $state "cbv_container" "item.data" 1 > +} >
On Monday, January 13, 2020 7:21 PM, Luis Machado wrote: > > Hi, > > I noticed these new tests fail (in large numbers) for GCC 5.4.x. Is it > expected? If so, it may be worth making this test conditional on newer > GCC versions. > Yes, this is expected. Older GCC versions did not emit certain DWARF attributes (DW_AT_deleted, DW_AT_defaulted). This prevents GDB from making the right pass-by-reference decision. I'll submit a patch for this. Thanks for the suggestion. -Baris > On 12/14/19 6:53 AM, Tankut Baris Aktemur (Code Review) wrote: > > Change URL: https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/142 > > ...................................................................... > > > > testsuite, cp: increase the coverage of testing pass-by-ref arguments > > > > Extend testcases for GDB's infcall of call-by-value functions that > > take aggregate values as parameters. In particular, existing test has > > been substantially extended with class definitions whose definitions > > of copy constructor, destructor, and move constructor functions are a > > combination of > > > > (1) explicitly defined by the user, > > (2) defaulted inside the class declaration, > > (3) defaulted outside the class declaration, > > (4) deleted > > (5) not defined in the source. > > > > For each combination, a small and a large class is generated as well > > as a derived class and a container class. Additionally, the following > > manually-written cases are provided: > > > > - a dynamic class (i.e. class with a virtual method) > > - classes that contain an array field > > - a class whose copy ctor is inlined > > - a class whose destructor is deleted > > - classes with multiple copy and/or move ctors > > > > Test cases check whether GDB makes the right decision to pass an > > object by value or implicitly by reference, whether really a copy of > > the argument is passed, and whether the copy constructor and > > destructor of the clone of the argument are invoked properly. > > > > The input program pass-by-ref.cc is generated in the test's output > > directory. The input program pass-by-ref-2.cc is manually-written. > > > > Tests have been verified on the X86_64 architecture with > > GCC 7.4.0, 8.2.0, and 9.2.1. > > > > gdb/testsuite/ChangeLog: > > 2019-11-07 Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> > > > > * gdb.cp/pass-by-ref.cc: Delete. Generated in the output > > directory instead. > > * gdb.cp/pass-by-ref.exp: Extend with more cases. > > * gdb.cp/pass-by-ref-2.cc: New file. > > * gdb.cp/pass-by-ref-2.exp: New file. > > > > Change-Id: Ie8ab1f260c6ad5ee4eb34b2c1597ce24af04abb6 > > --- > > A gdb/testsuite/gdb.cp/pass-by-ref-2.cc > > A gdb/testsuite/gdb.cp/pass-by-ref-2.exp > > D gdb/testsuite/gdb.cp/pass-by-ref.cc > > M gdb/testsuite/gdb.cp/pass-by-ref.exp > > 4 files changed, 791 insertions(+), 86 deletions(-) > > > > > > > > diff --git a/gdb/testsuite/gdb.cp/pass-by-ref-2.cc b/gdb/testsuite/gdb.cp/pass-by-ref-2.cc > > new file mode 100644 > > index 0000000..1cd5a16 > > --- /dev/null > > +++ b/gdb/testsuite/gdb.cp/pass-by-ref-2.cc > > @@ -0,0 +1,295 @@ > > +/* This testcase is part of GDB, the GNU debugger. > > + > > + Copyright 2019 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/>. */ > > + > > +class ByVal { > > +public: > > + ByVal (void); > > + > > + int x; > > +}; > > + > > +ByVal::ByVal (void) > > +{ > > + x = 2; > > +} > > + > > +class ByRef { > > +public: > > + ByRef (void); > > + > > + ByRef (const ByRef &rhs); > > + > > + int x; > > +}; > > + > > +ByRef::ByRef (void) > > +{ > > + x = 2; > > +} > > + > > +ByRef::ByRef (const ByRef &rhs) > > +{ > > + x = 3; /* ByRef-cctor */ > > +} > > + > > +class ArrayContainerByVal { > > +public: > > + ByVal items[2]; > > +}; > > + > > +int > > +cbvArrayContainerByVal (ArrayContainerByVal arg) > > +{ > > + arg.items[0].x += 4; // intentionally modify > > + return arg.items[0].x; > > +} > > + > > +class ArrayContainerByRef { > > +public: > > + ByRef items[2]; > > +}; > > + > > +int > > +cbvArrayContainerByRef (ArrayContainerByRef arg) > > +{ > > + arg.items[0].x += 4; // intentionally modify > > + return arg.items[0].x; > > +} > > + > > +class DynamicBase { > > +public: > > + DynamicBase (void); > > + > > + virtual int get (void); > > + > > + int x; > > +}; > > + > > +DynamicBase::DynamicBase (void) > > +{ > > + x = 2; > > +} > > + > > +int > > +DynamicBase::get (void) > > +{ > > + return 42; > > +} > > + > > +class Dynamic : public DynamicBase { > > +public: > > + virtual int get (void); > > +}; > > + > > +int > > +Dynamic::get (void) > > +{ > > + return 9999; > > +} > > + > > +int > > +cbvDynamic (DynamicBase arg) > > +{ > > + arg.x += 4; // intentionally modify > > + return arg.x + arg.get (); > > +} > > + > > +class Inlined { > > +public: > > + Inlined (void); > > + > > + __attribute__((always_inline)) > > + Inlined (const Inlined &rhs) > > + { > > + x = 3; > > + } > > + > > + int x; > > +}; > > + > > +Inlined::Inlined (void) > > +{ > > + x = 2; > > +} > > + > > +int > > +cbvInlined (Inlined arg) > > +{ > > + arg.x += 4; // intentionally modify > > + return arg.x; > > +} > > + > > +class DtorDel { > > +public: > > + DtorDel (void); > > + > > + ~DtorDel (void) = delete; > > + > > + int x; > > +}; > > + > > +DtorDel::DtorDel (void) > > +{ > > + x = 2; > > +} > > + > > +int > > +cbvDtorDel (DtorDel arg) > > +{ > > + // Calling this method should be rejected > > + return arg.x; > > +} > > + > > +class FourCCtor { > > +public: > > + FourCCtor (void); > > + > > + FourCCtor (FourCCtor &rhs); > > + FourCCtor (const FourCCtor &rhs); > > + FourCCtor (volatile FourCCtor &rhs); > > + FourCCtor (const volatile FourCCtor &rhs); > > + > > + int x; > > +}; > > + > > +FourCCtor::FourCCtor (void) > > +{ > > + x = 2; > > +} > > + > > +FourCCtor::FourCCtor (FourCCtor &rhs) > > +{ > > + x = 3; > > +} > > + > > +FourCCtor::FourCCtor (const FourCCtor &rhs) > > +{ > > + x = 4; > > +} > > + > > +FourCCtor::FourCCtor (volatile FourCCtor &rhs) > > +{ > > + x = 5; > > +} > > + > > +FourCCtor::FourCCtor (const volatile FourCCtor &rhs) > > +{ > > + x = 6; > > +} > > + > > +int > > +cbvFourCCtor (FourCCtor arg) > > +{ > > + arg.x += 10; // intentionally modify > > + return arg.x; > > +} > > + > > +class TwoMCtor { > > +public: > > + TwoMCtor (void); > > + > > + /* Even though one move ctor is defaulted, the other > > + is explicit. */ > > + TwoMCtor (const TwoMCtor &&rhs); > > + TwoMCtor (TwoMCtor &&rhs) = default; > > + > > + int x; > > +}; > > + > > +TwoMCtor::TwoMCtor (void) > > +{ > > + x = 2; > > +} > > + > > +TwoMCtor::TwoMCtor (const TwoMCtor &&rhs) > > +{ > > + x = 3; > > +} > > + > > +int > > +cbvTwoMCtor (TwoMCtor arg) > > +{ > > + arg.x += 10; // intentionally modify > > + return arg.x; > > +} > > + > > +class TwoMCtorAndCCtor { > > +public: > > + TwoMCtorAndCCtor (void); > > + > > + TwoMCtorAndCCtor (const TwoMCtorAndCCtor &rhs) = default; > > + > > + /* Even though one move ctor is defaulted, the other > > + is explicit. This makes the type pass-by-ref. */ > > + TwoMCtorAndCCtor (const TwoMCtorAndCCtor &&rhs); > > + TwoMCtorAndCCtor (TwoMCtorAndCCtor &&rhs) = default; > > + > > + int x; > > +}; > > + > > +TwoMCtorAndCCtor::TwoMCtorAndCCtor (void) > > +{ > > + x = 2; > > +} > > + > > +TwoMCtorAndCCtor::TwoMCtorAndCCtor (const TwoMCtorAndCCtor &&rhs) > > +{ > > + x = 4; > > +} > > + > > +int > > +cbvTwoMCtorAndCCtor (TwoMCtorAndCCtor arg) > > +{ > > + arg.x += 10; // intentionally modify > > + return arg.x; > > +} > > + > > +ArrayContainerByVal arrayContainerByVal; > > +ArrayContainerByRef arrayContainerByRef; > > +Dynamic dynamic; > > +Inlined inlined; > > +// Cannot stack-allocate DtorDel > > +DtorDel *dtorDel; > > +FourCCtor fourCctor_c0v0; > > +const FourCCtor fourCctor_c1v0; > > +volatile FourCCtor fourCctor_c0v1; > > +const volatile FourCCtor fourCctor_c1v1; > > +TwoMCtor twoMctor; > > +TwoMCtorAndCCtor twoMctorAndCctor; > > + > > +int > > +main (void) > > +{ > > + int v; > > + dtorDel = new DtorDel; > > + /* Explicitly call the cbv function to make sure the compiler > > + will not omit any code in the binary. */ > > + v = cbvArrayContainerByVal (arrayContainerByVal); > > + v = cbvArrayContainerByRef (arrayContainerByRef); > > + v = cbvDynamic (dynamic); > > + v = cbvInlined (inlined); > > + v = cbvFourCCtor (fourCctor_c0v0); > > + v = cbvFourCCtor (fourCctor_c1v0); > > + v = cbvFourCCtor (fourCctor_c0v1); > > + v = cbvFourCCtor (fourCctor_c1v1); > > + /* v = cbvTwoMCtor (twoMctor); */ // This is illegal, cctor is deleted > > + v = cbvTwoMCtorAndCCtor (twoMctorAndCctor); > > + > > + /* stop here */ > > + > > + return 0; > > +} > > diff --git a/gdb/testsuite/gdb.cp/pass-by-ref-2.exp b/gdb/testsuite/gdb.cp/pass-by-ref-2.exp > > new file mode 100644 > > index 0000000..7cce886 > > --- /dev/null > > +++ b/gdb/testsuite/gdb.cp/pass-by-ref-2.exp > > @@ -0,0 +1,114 @@ > > +# Copyright 2019 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/>. > > + > > +# Check that GDB can call C++ functions whose parameters have > > +# object type, and are either passed by value or implicitly by reference. > > +# > > +# This is a companion test to pass-by-ref.exp. In this test, the input > > +# is manually-written. In pass-by-ref.exp, the test input is generated. > > +# > > +# We include tests for classes that > > +# - contain arrays as fields, > > +# - are dynamic (i.e. have virtual methods) > > +# - have inlined copy ctor > > +# - have deleted destructor > > + > > +if {[skip_cplus_tests]} { > > + untested "c++ test skipped" > > + continue > > +} > > + > > +standard_testfile .cc > > + > > +set options {debug c++ additional_flags=-std=c++11} > > +if {[prepare_for_testing "failed to prepare" $testfile $srcfile $options]} { > > + return -1 > > +} > > + > > +if {![runto_main]} { > > + untested "failed to run to main" > > + return -1 > > +} > > + > > +set bp_location [gdb_get_line_number "stop here"] > > +gdb_breakpoint $bp_location > > +gdb_continue_to_breakpoint "end of main" ".*return .*;" > > + > > +gdb_test "print cbvArrayContainerByVal (arrayContainerByVal)" "6" \ > > + "call cbvArrayContainerByVal" > > +gdb_test "print arrayContainerByVal.items\[0\].x" "2" \ > > + "cbv argument 'arrayContainerByVal' should not change" > > + > > +gdb_test "print cbvArrayContainerByRef (arrayContainerByRef)" "7" \ > > + "call cbvArrayContainerByRef" > > +gdb_test "print arrayContainerByRef.items\[0\].x" "2" \ > > + "cbv argument 'arrayContainerByRef' should not change" > > + > > +gdb_test "print cbvDynamic (dynamic)" "48" \ > > + "call cbvDynamic" > > +gdb_test "print dynamic.x" "2" \ > > + "cbv argument 'dynamic' should not change" > > + > > +set sig "\"Inlined\:\:Inlined\\(.*Inlined const\&\\)\"" > > +gdb_test "print cbvInlined (inlined)" \ > > + "expression cannot be evaluated .* \\(maybe inlined\\?\\)" > > + > > +gdb_test "print cbvDtorDel (*dtorDel)" \ > > + ".* cannot be evaluated .* 'DtorDel' is not destructible" \ > > + "type not destructible" > > + > > +# Test that GDB calls the correct copy ctor > > +gdb_test "print cbvFourCCtor (fourCctor_c0v0)" "13" \ > > + "call cbvFourCCtor (c0v0)" > > +gdb_test "print fourCctor_c0v0.x" "2" \ > > + "cbv argument 'twoCctor_c0v0' should not change" > > + > > +gdb_test "print cbvFourCCtor (fourCctor_c1v0)" "14" \ > > + "call cbvFourCCtor (c1v0)" > > +gdb_test "print fourCctor_c1v0.x" "2" \ > > + "cbv argument 'twoCctor_c1v0' should not change" > > + > > +gdb_test "print cbvFourCCtor (fourCctor_c0v1)" "15" \ > > + "call cbvFourCCtor (c0v1)" > > +gdb_test "print fourCctor_c0v1.x" "2" \ > > + "cbv argument 'twoCctor_c0v1' should not change" > > + > > +gdb_test "print cbvFourCCtor (fourCctor_c1v1)" "16" \ > > + "call cbvFourCCtor (c1v1)" > > +gdb_test "print fourCctor_c1v1.x" "2" \ > > + "cbv argument 'twoCctor_c1v1' should not change" > > + > > +gdb_test "print cbvTwoMCtor (twoMctor)" \ > > + ".* cannot be evaluated .* 'TwoMCtor' is not copy constructible" \ > > + "copy ctor is implicitly deleted" > > + > > +gdb_test "print cbvTwoMCtorAndCCtor (twoMctorAndCctor)" "12" \ > > + "call cbvTwoMCtorAndCCtor" > > +gdb_test "print twoMctorAndCctor.x" "2" \ > > + "cbv argument 'twoMctorAndCtor' should not change" > > + > > +# Test that we get a breakpoint from the cctor during infcall and > > +# we can examine arguments. This is a test that the dummy frame > > +# of the copy constructor is set up correctly by the infcall mechanism. > > +set bp_location [gdb_get_line_number "ByRef-cctor"] > > +gdb_breakpoint $bp_location > > +gdb_test "print cbvArrayContainerByRef (arrayContainerByRef)" \ > > + ".*The program being debugged stopped.*" \ > > + "call cbvArrayContainerByRef with BP" > > +gdb_test "backtrace" [multi_line \ > > + "#0 ByRef\:\:ByRef .* at .*$srcfile:$bp_location" \ > > + "#1 .* ArrayContainerByRef::ArrayContainerByRef .*" \ > > + "#2 <function called from gdb>" \ > > + "#3 main.*"] > > diff --git a/gdb/testsuite/gdb.cp/pass-by-ref.cc b/gdb/testsuite/gdb.cp/pass-by-ref.cc > > deleted file mode 100644 > > index bbe450a..0000000 > > --- a/gdb/testsuite/gdb.cp/pass-by-ref.cc > > +++ /dev/null > > @@ -1,79 +0,0 @@ > > -/* This testcase is part of GDB, the GNU debugger. > > - > > - Copyright 2007-2019 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/>. */ > > - > > -class Obj { > > -public: > > - Obj (); > > - Obj (const Obj &); > > - ~Obj (); > > - int var[2]; > > -}; > > - > > -int foo (Obj arg) > > -{ > > - return arg.var[0] + arg.var[1]; > > -} > > - > > -Obj::Obj () > > -{ > > - var[0] = 1; > > - var[1] = 2; > > -} > > - > > -Obj::Obj (const Obj &obj) > > -{ > > - var[0] = obj.var[0]; > > - var[1] = obj.var[1]; > > -} > > - > > -Obj::~Obj () > > -{ > > - > > -} > > - > > -struct Derived : public Obj > > -{ > > - int other; > > -}; > > - > > -int blap (Derived arg) > > -{ > > - return foo (arg); > > -} > > - > > -struct Container > > -{ > > - Obj obj; > > -}; > > - > > -int blip (Container arg) > > -{ > > - return foo (arg.obj); > > -} > > - > > -Obj global_obj; > > -Derived global_derived; > > -Container global_container; > > - > > -int > > -main () > > -{ > > - int bar = foo (global_obj); > > - blap (global_derived); > > - blip (global_container); > > - return bar; > > -} > > diff --git a/gdb/testsuite/gdb.cp/pass-by-ref.exp b/gdb/testsuite/gdb.cp/pass-by-ref.exp > > index 94dd345..f44be77 100644 > > --- a/gdb/testsuite/gdb.cp/pass-by-ref.exp > > +++ b/gdb/testsuite/gdb.cp/pass-by-ref.exp > > @@ -14,20 +14,395 @@ > > # along with this program. If not, see <http://www.gnu.org/licenses/>. > > > > # Check that GDB can call C++ functions whose parameters have > > -# object type, but are passed by reference. > > +# object type, and are either passed by value or implicitly by reference. > > +# > > +# Suppose F is a function that has a call-by-value parameter whose > > +# type is class C. When calling F with an argument A, a copy of A should > > +# be created and passed to F. If C is a trivially-copyable type, A can > > +# be copied by a straightforward memory copy. However, roughly speaking, > > +# if C has a user-defined copy constructor and/or a user-defined > > +# destructor, the copy ctor should be used to initialize the copy of A > > +# before calling F, and a reference to that copy is passed to F. After > > +# the function returns, the destructor should be called to destruct the > > +# copy. In this case, C is said to be a 'pass-by-reference' type. > > +# Determining whether C is pass-by-ref depends on > > +# how the copy ctor, destructor, and the move ctor of C are defined. > > +# First of all, C is not copy constructible if its copy constructor is > > +# explicitly or implicitly deleted. In this case, it would be illegal > > +# to pass values of type C to a function. C is pass-by-value, if all of > > +# its copy ctor, dtor, and move ctor are trivially defined. > > +# Otherwise, it is pass-by-ref. > > +# > > +# To cover the many possible combinations, this test generates classes > > +# that contain three special functions: > > +# (1) a copy constructor, > > +# (2) a destructor, and > > +# (3) a move constructor. > > +# A special function is in one of the following states: > > +# * explicit: The function is explicitly defined by the user. > > +# * defaultedIn: The function is defaulted inside the class decl, > > +# using the 'default' keyword. > > +# * defaultedOut: The function is declared inside the class decl, > > +# and defaulted outside using the 'default' keyword. > > +# * deleted: The function is explicitly deleted by the user, > > +# using the 'delete' keyword. > > +# * absent: The function is not declared by the user (i.e. it does not > > +# exist in the source. The compiler generates (or deletes) the > > +# definition in this case. > > +# > > +# The C++ ABI decides if a class is pass-by-value or pass-by-ref > > +# (i.e. trivially copyable or not) first at the language level, based > > +# on the state of the special functions. Then, at the target level, a > > +# class may be determined to be pass-by-ref because of its size > > +# (e.g. if it is too large to fit on registers). For this reason, this > > +# test generates both a small and a large version for the same > > +# combination of special function states. > > +# > > +# A class is not trivially-copyable if a base class or a field is not > > +# trivially-copyable, even though the class definition itself seems > > +# trivial. To test these cases, we also generate derived classes and > > +# container classes. > > +# > > +# The generated code is placed in the test output directory. > > +# > > +# The companion test file pass-by-ref-2.exp also contains > > +# manually-written cases. > > > > -if { [skip_cplus_tests] } { continue } > > +if {[skip_cplus_tests]} { > > + untested "c++ test skipped" > > + continue > > +} > > > > +# The program source is generated in the output directory. > > +# We use standard_testfile here to set convenience variables. > > standard_testfile .cc > > > > -if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} { > > +# Some constant values used when generating the source > > + > > +set SMALL 2 > > +set LARGE 150 > > +set ORIGINAL 2 > > +set CUSTOM 3 > > +set ADDED 4 > > +set TRACE 5 > > + > > + > > +# Return 1 if the class whose special function states are STATES > > +# is copyable. Otherwise return 0. > > + > > +proc is_copy_constructible { states } { > > + set cctor [lindex $states 0] > > + set dtor [lindex $states 1] > > + set mctor [lindex $states 2] > > + > > + if {$cctor == "deleted" || ($cctor == "absent" && $mctor != "absent")} { > > + return 0 > > + } > > + return 1 > > +} > > + > > +# Generate a declaration and an out-of-class definition for a function > > +# with the provided signature. The STATE should be one of the following: > > +# - explicit, defaultedIn, defaultedOut, deleted, absent > > + > > +proc generate_member_function { classname signature length state } { > > + set declaration "" > > + set definition "" > > + > > + global CUSTOM > > + global TRACE > > + > > + switch $state { > > + explicit { > > + set declaration "$signature;\n" > > + set definition "$classname\:\:$signature > > + { > > + data\[0\] = $CUSTOM; > > + data\[[expr $length - 1]\] = $CUSTOM; > > + tracer = $TRACE; > > + }\n" > > + } > > + defaultedIn { > > + set declaration "$signature = default;\n" > > + } > > + defaultedOut { > > + set declaration "$signature;\n" > > + set definition "$classname\:\:$signature = default;\n" > > + } > > + deleted { > > + set declaration "$signature = delete;\n" > > + } > > + default { > > + # function is not user-defined in this case > > + } > > + } > > + > > + return [list $declaration $definition] > > +} > > + > > +# Generate a C++ class with the given CLASSNAME and LENGTH-many > > +# integer elements. The STATES is an array of 3 items > > +# containing the desired state of the special functions > > +# in this order: > > +# copy constructor, destructor, move constructor > > + > > +proc generate_class { classname length states } { > > + set declarations "" > > + set definitions "" > > + set classname "${classname}_[join $states _]" > > + > > + for {set i 0} {$i < [llength $states]} {incr i} { > > + set sig "" > > + switch $i { > > + 0 {set sig "$classname (const $classname \&rhs)"} > > + 1 {set sig "\~$classname (void)"} > > + 2 {set sig "$classname ($classname \&\&rhs)"} > > + } > > + > > + set state [lindex $states $i] > > + set code [generate_member_function $classname $sig $length $state] > > + append declarations [lindex $code 0] > > + append definitions [lindex $code 1] > > + } > > + > > + global ORIGINAL > > + > > + return " > > + /*** C++ class $classname ***/ > > + class ${classname} { > > + public: > > + $classname (void); > > + $declarations > > + > > + int data\[$length\]; > > + }; > > + > > + $classname\:\:$classname (void) > > + { > > + data\[0\] = $ORIGINAL; > > + data\[[expr $length - 1]\] = $ORIGINAL; > > + } > > + > > + $definitions > > + > > + $classname ${classname}_var; /* global var */ > > + > > + template int cbv<$classname> ($classname arg);" > > +} > > + > > +# Generate a small C++ class > > + > > +proc generate_small_class { states } { > > + global SMALL > > + return [generate_class Small $SMALL $states]; > > +} > > + > > +# Generate a large C++ class > > + > > +proc generate_large_class { states } { > > + global LARGE > > + return [generate_class Large $LARGE $states]; > > +} > > + > > +# Generate a class that derives from a small class > > + > > +proc generate_derived_class { states } { > > + set base "Small_[join $states _]" > > + set classname "Derived_[join $states _]" > > + > > + return " > > + /*** Class derived from $base ***/ > > + class $classname : public $base { > > + public: > > + }; > > + > > + $classname ${classname}_var; /* global var */ > > + > > + template int cbv<$classname> ($classname arg);" > > +} > > + > > +# Generate a class that contains a small class item > > + > > +proc generate_container_class { states } { > > + set contained "Small_[join $states _]" > > + set classname "Container_[join $states _]" > > + > > + return " > > + /*** Class that contains $contained ***/ > > + class $classname { > > + public: > > + $contained item; > > + }; > > + > > + $classname ${classname}_var; /* global var */ > > + > > + template int cbv_container<$classname> ($classname arg);" > > +} > > + > > +# Generate useful statements that use a class in the debugee program > > + > > +proc generate_stmts { classprefix states {cbvfun "cbv"}} { > > + set classname "${classprefix}_[join $states _]" > > + > > + # Having an explicit call to the cbv function in the debugee program > > + # ensures that the compiler will emit necessary function in the binary. > > + if {[is_copy_constructible $states]} { > > + set cbvcall "$cbvfun<$classname> (${classname}_var);\n" > > + } else { > > + set cbvcall "" > > + } > > + > > + return "$cbvcall" > > +} > > + > > +# Generate the complete debugee program > > + > > +proc generate_program { classes stmts } { > > + global ADDED > > + > > + return " > > + /*** THIS FILE IS GENERATED BY THE TEST. ***/ > > + > > + static int tracer = 0; > > + > > + /* The call-by-value function. */ > > + template <class T> > > + int > > + cbv (T arg) > > + { > > + arg.data\[0\] += $ADDED; // intentionally modify the arg > > + return arg.data\[0\]; > > + } > > + > > + template <class T> > > + int > > + cbv_container (T arg) > > + { > > + arg.item.data\[0\] += $ADDED; // intentionally modify > > + return arg.item.data\[0\]; > > + } > > + > > + $classes > > + > > + int > > + main (void) > > + { > > + $stmts > > + > > + /* stop here */ > > + > > + return 0; > > + }" > > +} > > + > > +# Compute all the combinations of special function states. > > +# We do not contain the 'deleted' state for the destructor, > > +# because it is illegal to have stack-allocated objects > > +# whose destructor have been deleted. This case is covered > > +# in pass-by-ref-2 via heap-allocated objects. > > + > > +set options_nodelete [list absent explicit defaultedIn defaultedOut] > > +set options [concat $options_nodelete {deleted}] > > +set all_combinations {} > > + > > +foreach cctor $options { > > + foreach dtor $options_nodelete { > > + foreach mctor $options { > > + lappend all_combinations [list $cctor $dtor $mctor] > > + } > > + } > > +} > > + > > +# Generate the classes. > > + > > +set classes "" > > +set stmts "" > > + > > +foreach state $all_combinations { > > + append classes [generate_small_class $state] > > + append stmts [generate_stmts "Small" $state] > > + > > + append classes [generate_large_class $state] > > + append stmts [generate_stmts "Large" $state] > > + > > + append classes [generate_derived_class $state] > > + append stmts [generate_stmts "Derived" $state] > > + > > + append classes [generate_container_class $state] > > + append stmts [generate_stmts "Container" $state "cbv_container"] > > +} > > + > > +# Generate the program code and compile > > +set program [generate_program $classes $stmts] > > +set srcfile [standard_output_file ${srcfile}] > > +gdb_produce_source $srcfile $program > > + > > +set options {debug c++ additional_flags=-std=c++11} > > +if {[prepare_for_testing "failed to prepare" $testfile $srcfile $options]} { > > return -1 > > } > > > > -if ![runto_main] then { > > +if {![runto_main]} { > > + untested "failed to run to main" > > return -1 > > } > > > > -gdb_test "print foo (global_obj)" " = 3" "call function in obj" > > -gdb_test "print blap (global_derived)" " = 3" "call function in derived" > > -gdb_test "print blip (global_container)" " = 3" "call function in container" > > +set bp_location [gdb_get_line_number "stop here"] > > +gdb_breakpoint $bp_location > > +gdb_continue_to_breakpoint "end of main" ".*return .*;" > > + > > +# Do the checks for a given class whose name is prefixed with PREFIX, > > +# and whose special functions have the states given in STATES. > > +# The name of the call-by-value function and the expression to access > > +# the data field can be specified explicitly if the default values > > +# do not work. > > + > > +proc test_for_class { prefix states cbvfun data_field length} { > > + set name "${prefix}_[join $states _]" > > + > > + set cctor [lindex $states 0] > > + set dtor [lindex $states 1] > > + set mctor [lindex $states 2] > > + > > + global ORIGINAL > > + global CUSTOM > > + global ADDED > > + global TRACE > > + > > + with_test_prefix $name { > > + if {[is_copy_constructible $states]} { > > + set expected [expr {$ORIGINAL + $ADDED}] > > + if {$cctor == "explicit"} { > > + set expected [expr {$CUSTOM + $ADDED}] > > + } > > + if {$dtor == "explicit"} { > > + gdb_test "print tracer = 0" " = 0" "reset the tracer" > > + } > > + gdb_test "print ${cbvfun}<$name> (${name}_var)" " = $expected" \ > > + "call '$cbvfun'" > > + gdb_test "print ${name}_var.${data_field}\[0\]" " = $ORIGINAL" \ > > + "cbv argument should not change (item 0)" > > + if {$length > 1} { > > + set last_index [expr $length - 1] > > + gdb_test "print ${name}_var.${data_field}\[$last_index\]" \ > > + " = $ORIGINAL" \ > > + "cbv argument should not change (item $last_index)" > > + } > > + if {$dtor == "explicit"} { > > + gdb_test "print tracer" " = $TRACE" \ > > + "destructor should be called" > > + } > > + } else { > > + gdb_test "print ${cbvfun}<$name> (${name}_var)" \ > > + ".* cannot be evaluated .* '${name}' is not copy constructible" \ > > + "calling '$cbvfun' should be refused" > > + } > > + } > > +} > > + > > +foreach state $all_combinations { > > + test_for_class "Small" $state "cbv" "data" $SMALL > > + test_for_class "Large" $state "cbv" "data" $LARGE > > + test_for_class "Derived" $state "cbv" "data" 1 > > + test_for_class "Container" $state "cbv_container" "item.data" 1 > > +} > > Intel Deutschland GmbH Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany Tel: +49 89 99 8853-0, www.intel.de Managing Directors: Christin Eisenschmid, Gary Kershaw Chairperson of the Supervisory Board: Nicole Lau Registered Office: Munich Commercial Register: Amtsgericht Muenchen HRB 186928
On 1/13/20 4:35 PM, Aktemur, Tankut Baris wrote: > On Monday, January 13, 2020 7:21 PM, Luis Machado wrote: >> >> Hi, >> >> I noticed these new tests fail (in large numbers) for GCC 5.4.x. Is it >> expected? If so, it may be worth making this test conditional on newer >> GCC versions. >> > > Yes, this is expected. Older GCC versions did not emit certain DWARF attributes > (DW_AT_deleted, DW_AT_defaulted). This prevents GDB from making the right > pass-by-reference decision. I'll submit a patch for this. Thanks for clarifying this. I can submit that patch if you like. I have a box running an older GCC, so i noticed that and thought i'd check. Luis > > Thanks for the suggestion. > > -Baris > >> On 12/14/19 6:53 AM, Tankut Baris Aktemur (Code Review) wrote: >>> Change URL: https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/142 >>> ...................................................................... >>> >>> testsuite, cp: increase the coverage of testing pass-by-ref arguments >>> >>> Extend testcases for GDB's infcall of call-by-value functions that >>> take aggregate values as parameters. In particular, existing test has >>> been substantially extended with class definitions whose definitions >>> of copy constructor, destructor, and move constructor functions are a >>> combination of >>> >>> (1) explicitly defined by the user, >>> (2) defaulted inside the class declaration, >>> (3) defaulted outside the class declaration, >>> (4) deleted >>> (5) not defined in the source. >>> >>> For each combination, a small and a large class is generated as well >>> as a derived class and a container class. Additionally, the following >>> manually-written cases are provided: >>> >>> - a dynamic class (i.e. class with a virtual method) >>> - classes that contain an array field >>> - a class whose copy ctor is inlined >>> - a class whose destructor is deleted >>> - classes with multiple copy and/or move ctors >>> >>> Test cases check whether GDB makes the right decision to pass an >>> object by value or implicitly by reference, whether really a copy of >>> the argument is passed, and whether the copy constructor and >>> destructor of the clone of the argument are invoked properly. >>> >>> The input program pass-by-ref.cc is generated in the test's output >>> directory. The input program pass-by-ref-2.cc is manually-written. >>> >>> Tests have been verified on the X86_64 architecture with >>> GCC 7.4.0, 8.2.0, and 9.2.1. >>> >>> gdb/testsuite/ChangeLog: >>> 2019-11-07 Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> >>> >>> * gdb.cp/pass-by-ref.cc: Delete. Generated in the output >>> directory instead. >>> * gdb.cp/pass-by-ref.exp: Extend with more cases. >>> * gdb.cp/pass-by-ref-2.cc: New file. >>> * gdb.cp/pass-by-ref-2.exp: New file. >>> >>> Change-Id: Ie8ab1f260c6ad5ee4eb34b2c1597ce24af04abb6 >>> --- >>> A gdb/testsuite/gdb.cp/pass-by-ref-2.cc >>> A gdb/testsuite/gdb.cp/pass-by-ref-2.exp >>> D gdb/testsuite/gdb.cp/pass-by-ref.cc >>> M gdb/testsuite/gdb.cp/pass-by-ref.exp >>> 4 files changed, 791 insertions(+), 86 deletions(-) >>> >>> >>> >>> diff --git a/gdb/testsuite/gdb.cp/pass-by-ref-2.cc b/gdb/testsuite/gdb.cp/pass-by-ref-2.cc >>> new file mode 100644 >>> index 0000000..1cd5a16 >>> --- /dev/null >>> +++ b/gdb/testsuite/gdb.cp/pass-by-ref-2.cc >>> @@ -0,0 +1,295 @@ >>> +/* This testcase is part of GDB, the GNU debugger. >>> + >>> + Copyright 2019 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/>. */ >>> + >>> +class ByVal { >>> +public: >>> + ByVal (void); >>> + >>> + int x; >>> +}; >>> + >>> +ByVal::ByVal (void) >>> +{ >>> + x = 2; >>> +} >>> + >>> +class ByRef { >>> +public: >>> + ByRef (void); >>> + >>> + ByRef (const ByRef &rhs); >>> + >>> + int x; >>> +}; >>> + >>> +ByRef::ByRef (void) >>> +{ >>> + x = 2; >>> +} >>> + >>> +ByRef::ByRef (const ByRef &rhs) >>> +{ >>> + x = 3; /* ByRef-cctor */ >>> +} >>> + >>> +class ArrayContainerByVal { >>> +public: >>> + ByVal items[2]; >>> +}; >>> + >>> +int >>> +cbvArrayContainerByVal (ArrayContainerByVal arg) >>> +{ >>> + arg.items[0].x += 4; // intentionally modify >>> + return arg.items[0].x; >>> +} >>> + >>> +class ArrayContainerByRef { >>> +public: >>> + ByRef items[2]; >>> +}; >>> + >>> +int >>> +cbvArrayContainerByRef (ArrayContainerByRef arg) >>> +{ >>> + arg.items[0].x += 4; // intentionally modify >>> + return arg.items[0].x; >>> +} >>> + >>> +class DynamicBase { >>> +public: >>> + DynamicBase (void); >>> + >>> + virtual int get (void); >>> + >>> + int x; >>> +}; >>> + >>> +DynamicBase::DynamicBase (void) >>> +{ >>> + x = 2; >>> +} >>> + >>> +int >>> +DynamicBase::get (void) >>> +{ >>> + return 42; >>> +} >>> + >>> +class Dynamic : public DynamicBase { >>> +public: >>> + virtual int get (void); >>> +}; >>> + >>> +int >>> +Dynamic::get (void) >>> +{ >>> + return 9999; >>> +} >>> + >>> +int >>> +cbvDynamic (DynamicBase arg) >>> +{ >>> + arg.x += 4; // intentionally modify >>> + return arg.x + arg.get (); >>> +} >>> + >>> +class Inlined { >>> +public: >>> + Inlined (void); >>> + >>> + __attribute__((always_inline)) >>> + Inlined (const Inlined &rhs) >>> + { >>> + x = 3; >>> + } >>> + >>> + int x; >>> +}; >>> + >>> +Inlined::Inlined (void) >>> +{ >>> + x = 2; >>> +} >>> + >>> +int >>> +cbvInlined (Inlined arg) >>> +{ >>> + arg.x += 4; // intentionally modify >>> + return arg.x; >>> +} >>> + >>> +class DtorDel { >>> +public: >>> + DtorDel (void); >>> + >>> + ~DtorDel (void) = delete; >>> + >>> + int x; >>> +}; >>> + >>> +DtorDel::DtorDel (void) >>> +{ >>> + x = 2; >>> +} >>> + >>> +int >>> +cbvDtorDel (DtorDel arg) >>> +{ >>> + // Calling this method should be rejected >>> + return arg.x; >>> +} >>> + >>> +class FourCCtor { >>> +public: >>> + FourCCtor (void); >>> + >>> + FourCCtor (FourCCtor &rhs); >>> + FourCCtor (const FourCCtor &rhs); >>> + FourCCtor (volatile FourCCtor &rhs); >>> + FourCCtor (const volatile FourCCtor &rhs); >>> + >>> + int x; >>> +}; >>> + >>> +FourCCtor::FourCCtor (void) >>> +{ >>> + x = 2; >>> +} >>> + >>> +FourCCtor::FourCCtor (FourCCtor &rhs) >>> +{ >>> + x = 3; >>> +} >>> + >>> +FourCCtor::FourCCtor (const FourCCtor &rhs) >>> +{ >>> + x = 4; >>> +} >>> + >>> +FourCCtor::FourCCtor (volatile FourCCtor &rhs) >>> +{ >>> + x = 5; >>> +} >>> + >>> +FourCCtor::FourCCtor (const volatile FourCCtor &rhs) >>> +{ >>> + x = 6; >>> +} >>> + >>> +int >>> +cbvFourCCtor (FourCCtor arg) >>> +{ >>> + arg.x += 10; // intentionally modify >>> + return arg.x; >>> +} >>> + >>> +class TwoMCtor { >>> +public: >>> + TwoMCtor (void); >>> + >>> + /* Even though one move ctor is defaulted, the other >>> + is explicit. */ >>> + TwoMCtor (const TwoMCtor &&rhs); >>> + TwoMCtor (TwoMCtor &&rhs) = default; >>> + >>> + int x; >>> +}; >>> + >>> +TwoMCtor::TwoMCtor (void) >>> +{ >>> + x = 2; >>> +} >>> + >>> +TwoMCtor::TwoMCtor (const TwoMCtor &&rhs) >>> +{ >>> + x = 3; >>> +} >>> + >>> +int >>> +cbvTwoMCtor (TwoMCtor arg) >>> +{ >>> + arg.x += 10; // intentionally modify >>> + return arg.x; >>> +} >>> + >>> +class TwoMCtorAndCCtor { >>> +public: >>> + TwoMCtorAndCCtor (void); >>> + >>> + TwoMCtorAndCCtor (const TwoMCtorAndCCtor &rhs) = default; >>> + >>> + /* Even though one move ctor is defaulted, the other >>> + is explicit. This makes the type pass-by-ref. */ >>> + TwoMCtorAndCCtor (const TwoMCtorAndCCtor &&rhs); >>> + TwoMCtorAndCCtor (TwoMCtorAndCCtor &&rhs) = default; >>> + >>> + int x; >>> +}; >>> + >>> +TwoMCtorAndCCtor::TwoMCtorAndCCtor (void) >>> +{ >>> + x = 2; >>> +} >>> + >>> +TwoMCtorAndCCtor::TwoMCtorAndCCtor (const TwoMCtorAndCCtor &&rhs) >>> +{ >>> + x = 4; >>> +} >>> + >>> +int >>> +cbvTwoMCtorAndCCtor (TwoMCtorAndCCtor arg) >>> +{ >>> + arg.x += 10; // intentionally modify >>> + return arg.x; >>> +} >>> + >>> +ArrayContainerByVal arrayContainerByVal; >>> +ArrayContainerByRef arrayContainerByRef; >>> +Dynamic dynamic; >>> +Inlined inlined; >>> +// Cannot stack-allocate DtorDel >>> +DtorDel *dtorDel; >>> +FourCCtor fourCctor_c0v0; >>> +const FourCCtor fourCctor_c1v0; >>> +volatile FourCCtor fourCctor_c0v1; >>> +const volatile FourCCtor fourCctor_c1v1; >>> +TwoMCtor twoMctor; >>> +TwoMCtorAndCCtor twoMctorAndCctor; >>> + >>> +int >>> +main (void) >>> +{ >>> + int v; >>> + dtorDel = new DtorDel; >>> + /* Explicitly call the cbv function to make sure the compiler >>> + will not omit any code in the binary. */ >>> + v = cbvArrayContainerByVal (arrayContainerByVal); >>> + v = cbvArrayContainerByRef (arrayContainerByRef); >>> + v = cbvDynamic (dynamic); >>> + v = cbvInlined (inlined); >>> + v = cbvFourCCtor (fourCctor_c0v0); >>> + v = cbvFourCCtor (fourCctor_c1v0); >>> + v = cbvFourCCtor (fourCctor_c0v1); >>> + v = cbvFourCCtor (fourCctor_c1v1); >>> + /* v = cbvTwoMCtor (twoMctor); */ // This is illegal, cctor is deleted >>> + v = cbvTwoMCtorAndCCtor (twoMctorAndCctor); >>> + >>> + /* stop here */ >>> + >>> + return 0; >>> +} >>> diff --git a/gdb/testsuite/gdb.cp/pass-by-ref-2.exp b/gdb/testsuite/gdb.cp/pass-by-ref-2.exp >>> new file mode 100644 >>> index 0000000..7cce886 >>> --- /dev/null >>> +++ b/gdb/testsuite/gdb.cp/pass-by-ref-2.exp >>> @@ -0,0 +1,114 @@ >>> +# Copyright 2019 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/>. >>> + >>> +# Check that GDB can call C++ functions whose parameters have >>> +# object type, and are either passed by value or implicitly by reference. >>> +# >>> +# This is a companion test to pass-by-ref.exp. In this test, the input >>> +# is manually-written. In pass-by-ref.exp, the test input is generated. >>> +# >>> +# We include tests for classes that >>> +# - contain arrays as fields, >>> +# - are dynamic (i.e. have virtual methods) >>> +# - have inlined copy ctor >>> +# - have deleted destructor >>> + >>> +if {[skip_cplus_tests]} { >>> + untested "c++ test skipped" >>> + continue >>> +} >>> + >>> +standard_testfile .cc >>> + >>> +set options {debug c++ additional_flags=-std=c++11} >>> +if {[prepare_for_testing "failed to prepare" $testfile $srcfile $options]} { >>> + return -1 >>> +} >>> + >>> +if {![runto_main]} { >>> + untested "failed to run to main" >>> + return -1 >>> +} >>> + >>> +set bp_location [gdb_get_line_number "stop here"] >>> +gdb_breakpoint $bp_location >>> +gdb_continue_to_breakpoint "end of main" ".*return .*;" >>> + >>> +gdb_test "print cbvArrayContainerByVal (arrayContainerByVal)" "6" \ >>> + "call cbvArrayContainerByVal" >>> +gdb_test "print arrayContainerByVal.items\[0\].x" "2" \ >>> + "cbv argument 'arrayContainerByVal' should not change" >>> + >>> +gdb_test "print cbvArrayContainerByRef (arrayContainerByRef)" "7" \ >>> + "call cbvArrayContainerByRef" >>> +gdb_test "print arrayContainerByRef.items\[0\].x" "2" \ >>> + "cbv argument 'arrayContainerByRef' should not change" >>> + >>> +gdb_test "print cbvDynamic (dynamic)" "48" \ >>> + "call cbvDynamic" >>> +gdb_test "print dynamic.x" "2" \ >>> + "cbv argument 'dynamic' should not change" >>> + >>> +set sig "\"Inlined\:\:Inlined\\(.*Inlined const\&\\)\"" >>> +gdb_test "print cbvInlined (inlined)" \ >>> + "expression cannot be evaluated .* \\(maybe inlined\\?\\)" >>> + >>> +gdb_test "print cbvDtorDel (*dtorDel)" \ >>> + ".* cannot be evaluated .* 'DtorDel' is not destructible" \ >>> + "type not destructible" >>> + >>> +# Test that GDB calls the correct copy ctor >>> +gdb_test "print cbvFourCCtor (fourCctor_c0v0)" "13" \ >>> + "call cbvFourCCtor (c0v0)" >>> +gdb_test "print fourCctor_c0v0.x" "2" \ >>> + "cbv argument 'twoCctor_c0v0' should not change" >>> + >>> +gdb_test "print cbvFourCCtor (fourCctor_c1v0)" "14" \ >>> + "call cbvFourCCtor (c1v0)" >>> +gdb_test "print fourCctor_c1v0.x" "2" \ >>> + "cbv argument 'twoCctor_c1v0' should not change" >>> + >>> +gdb_test "print cbvFourCCtor (fourCctor_c0v1)" "15" \ >>> + "call cbvFourCCtor (c0v1)" >>> +gdb_test "print fourCctor_c0v1.x" "2" \ >>> + "cbv argument 'twoCctor_c0v1' should not change" >>> + >>> +gdb_test "print cbvFourCCtor (fourCctor_c1v1)" "16" \ >>> + "call cbvFourCCtor (c1v1)" >>> +gdb_test "print fourCctor_c1v1.x" "2" \ >>> + "cbv argument 'twoCctor_c1v1' should not change" >>> + >>> +gdb_test "print cbvTwoMCtor (twoMctor)" \ >>> + ".* cannot be evaluated .* 'TwoMCtor' is not copy constructible" \ >>> + "copy ctor is implicitly deleted" >>> + >>> +gdb_test "print cbvTwoMCtorAndCCtor (twoMctorAndCctor)" "12" \ >>> + "call cbvTwoMCtorAndCCtor" >>> +gdb_test "print twoMctorAndCctor.x" "2" \ >>> + "cbv argument 'twoMctorAndCtor' should not change" >>> + >>> +# Test that we get a breakpoint from the cctor during infcall and >>> +# we can examine arguments. This is a test that the dummy frame >>> +# of the copy constructor is set up correctly by the infcall mechanism. >>> +set bp_location [gdb_get_line_number "ByRef-cctor"] >>> +gdb_breakpoint $bp_location >>> +gdb_test "print cbvArrayContainerByRef (arrayContainerByRef)" \ >>> + ".*The program being debugged stopped.*" \ >>> + "call cbvArrayContainerByRef with BP" >>> +gdb_test "backtrace" [multi_line \ >>> + "#0 ByRef\:\:ByRef .* at .*$srcfile:$bp_location" \ >>> + "#1 .* ArrayContainerByRef::ArrayContainerByRef .*" \ >>> + "#2 <function called from gdb>" \ >>> + "#3 main.*"] >>> diff --git a/gdb/testsuite/gdb.cp/pass-by-ref.cc b/gdb/testsuite/gdb.cp/pass-by-ref.cc >>> deleted file mode 100644 >>> index bbe450a..0000000 >>> --- a/gdb/testsuite/gdb.cp/pass-by-ref.cc >>> +++ /dev/null >>> @@ -1,79 +0,0 @@ >>> -/* This testcase is part of GDB, the GNU debugger. >>> - >>> - Copyright 2007-2019 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/>. */ >>> - >>> -class Obj { >>> -public: >>> - Obj (); >>> - Obj (const Obj &); >>> - ~Obj (); >>> - int var[2]; >>> -}; >>> - >>> -int foo (Obj arg) >>> -{ >>> - return arg.var[0] + arg.var[1]; >>> -} >>> - >>> -Obj::Obj () >>> -{ >>> - var[0] = 1; >>> - var[1] = 2; >>> -} >>> - >>> -Obj::Obj (const Obj &obj) >>> -{ >>> - var[0] = obj.var[0]; >>> - var[1] = obj.var[1]; >>> -} >>> - >>> -Obj::~Obj () >>> -{ >>> - >>> -} >>> - >>> -struct Derived : public Obj >>> -{ >>> - int other; >>> -}; >>> - >>> -int blap (Derived arg) >>> -{ >>> - return foo (arg); >>> -} >>> - >>> -struct Container >>> -{ >>> - Obj obj; >>> -}; >>> - >>> -int blip (Container arg) >>> -{ >>> - return foo (arg.obj); >>> -} >>> - >>> -Obj global_obj; >>> -Derived global_derived; >>> -Container global_container; >>> - >>> -int >>> -main () >>> -{ >>> - int bar = foo (global_obj); >>> - blap (global_derived); >>> - blip (global_container); >>> - return bar; >>> -} >>> diff --git a/gdb/testsuite/gdb.cp/pass-by-ref.exp b/gdb/testsuite/gdb.cp/pass-by-ref.exp >>> index 94dd345..f44be77 100644 >>> --- a/gdb/testsuite/gdb.cp/pass-by-ref.exp >>> +++ b/gdb/testsuite/gdb.cp/pass-by-ref.exp >>> @@ -14,20 +14,395 @@ >>> # along with this program. If not, see <http://www.gnu.org/licenses/>. >>> >>> # Check that GDB can call C++ functions whose parameters have >>> -# object type, but are passed by reference. >>> +# object type, and are either passed by value or implicitly by reference. >>> +# >>> +# Suppose F is a function that has a call-by-value parameter whose >>> +# type is class C. When calling F with an argument A, a copy of A should >>> +# be created and passed to F. If C is a trivially-copyable type, A can >>> +# be copied by a straightforward memory copy. However, roughly speaking, >>> +# if C has a user-defined copy constructor and/or a user-defined >>> +# destructor, the copy ctor should be used to initialize the copy of A >>> +# before calling F, and a reference to that copy is passed to F. After >>> +# the function returns, the destructor should be called to destruct the >>> +# copy. In this case, C is said to be a 'pass-by-reference' type. >>> +# Determining whether C is pass-by-ref depends on >>> +# how the copy ctor, destructor, and the move ctor of C are defined. >>> +# First of all, C is not copy constructible if its copy constructor is >>> +# explicitly or implicitly deleted. In this case, it would be illegal >>> +# to pass values of type C to a function. C is pass-by-value, if all of >>> +# its copy ctor, dtor, and move ctor are trivially defined. >>> +# Otherwise, it is pass-by-ref. >>> +# >>> +# To cover the many possible combinations, this test generates classes >>> +# that contain three special functions: >>> +# (1) a copy constructor, >>> +# (2) a destructor, and >>> +# (3) a move constructor. >>> +# A special function is in one of the following states: >>> +# * explicit: The function is explicitly defined by the user. >>> +# * defaultedIn: The function is defaulted inside the class decl, >>> +# using the 'default' keyword. >>> +# * defaultedOut: The function is declared inside the class decl, >>> +# and defaulted outside using the 'default' keyword. >>> +# * deleted: The function is explicitly deleted by the user, >>> +# using the 'delete' keyword. >>> +# * absent: The function is not declared by the user (i.e. it does not >>> +# exist in the source. The compiler generates (or deletes) the >>> +# definition in this case. >>> +# >>> +# The C++ ABI decides if a class is pass-by-value or pass-by-ref >>> +# (i.e. trivially copyable or not) first at the language level, based >>> +# on the state of the special functions. Then, at the target level, a >>> +# class may be determined to be pass-by-ref because of its size >>> +# (e.g. if it is too large to fit on registers). For this reason, this >>> +# test generates both a small and a large version for the same >>> +# combination of special function states. >>> +# >>> +# A class is not trivially-copyable if a base class or a field is not >>> +# trivially-copyable, even though the class definition itself seems >>> +# trivial. To test these cases, we also generate derived classes and >>> +# container classes. >>> +# >>> +# The generated code is placed in the test output directory. >>> +# >>> +# The companion test file pass-by-ref-2.exp also contains >>> +# manually-written cases. >>> >>> -if { [skip_cplus_tests] } { continue } >>> +if {[skip_cplus_tests]} { >>> + untested "c++ test skipped" >>> + continue >>> +} >>> >>> +# The program source is generated in the output directory. >>> +# We use standard_testfile here to set convenience variables. >>> standard_testfile .cc >>> >>> -if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} { >>> +# Some constant values used when generating the source >>> + >>> +set SMALL 2 >>> +set LARGE 150 >>> +set ORIGINAL 2 >>> +set CUSTOM 3 >>> +set ADDED 4 >>> +set TRACE 5 >>> + >>> + >>> +# Return 1 if the class whose special function states are STATES >>> +# is copyable. Otherwise return 0. >>> + >>> +proc is_copy_constructible { states } { >>> + set cctor [lindex $states 0] >>> + set dtor [lindex $states 1] >>> + set mctor [lindex $states 2] >>> + >>> + if {$cctor == "deleted" || ($cctor == "absent" && $mctor != "absent")} { >>> + return 0 >>> + } >>> + return 1 >>> +} >>> + >>> +# Generate a declaration and an out-of-class definition for a function >>> +# with the provided signature. The STATE should be one of the following: >>> +# - explicit, defaultedIn, defaultedOut, deleted, absent >>> + >>> +proc generate_member_function { classname signature length state } { >>> + set declaration "" >>> + set definition "" >>> + >>> + global CUSTOM >>> + global TRACE >>> + >>> + switch $state { >>> + explicit { >>> + set declaration "$signature;\n" >>> + set definition "$classname\:\:$signature >>> + { >>> + data\[0\] = $CUSTOM; >>> + data\[[expr $length - 1]\] = $CUSTOM; >>> + tracer = $TRACE; >>> + }\n" >>> + } >>> + defaultedIn { >>> + set declaration "$signature = default;\n" >>> + } >>> + defaultedOut { >>> + set declaration "$signature;\n" >>> + set definition "$classname\:\:$signature = default;\n" >>> + } >>> + deleted { >>> + set declaration "$signature = delete;\n" >>> + } >>> + default { >>> + # function is not user-defined in this case >>> + } >>> + } >>> + >>> + return [list $declaration $definition] >>> +} >>> + >>> +# Generate a C++ class with the given CLASSNAME and LENGTH-many >>> +# integer elements. The STATES is an array of 3 items >>> +# containing the desired state of the special functions >>> +# in this order: >>> +# copy constructor, destructor, move constructor >>> + >>> +proc generate_class { classname length states } { >>> + set declarations "" >>> + set definitions "" >>> + set classname "${classname}_[join $states _]" >>> + >>> + for {set i 0} {$i < [llength $states]} {incr i} { >>> + set sig "" >>> + switch $i { >>> + 0 {set sig "$classname (const $classname \&rhs)"} >>> + 1 {set sig "\~$classname (void)"} >>> + 2 {set sig "$classname ($classname \&\&rhs)"} >>> + } >>> + >>> + set state [lindex $states $i] >>> + set code [generate_member_function $classname $sig $length $state] >>> + append declarations [lindex $code 0] >>> + append definitions [lindex $code 1] >>> + } >>> + >>> + global ORIGINAL >>> + >>> + return " >>> + /*** C++ class $classname ***/ >>> + class ${classname} { >>> + public: >>> + $classname (void); >>> + $declarations >>> + >>> + int data\[$length\]; >>> + }; >>> + >>> + $classname\:\:$classname (void) >>> + { >>> + data\[0\] = $ORIGINAL; >>> + data\[[expr $length - 1]\] = $ORIGINAL; >>> + } >>> + >>> + $definitions >>> + >>> + $classname ${classname}_var; /* global var */ >>> + >>> + template int cbv<$classname> ($classname arg);" >>> +} >>> + >>> +# Generate a small C++ class >>> + >>> +proc generate_small_class { states } { >>> + global SMALL >>> + return [generate_class Small $SMALL $states]; >>> +} >>> + >>> +# Generate a large C++ class >>> + >>> +proc generate_large_class { states } { >>> + global LARGE >>> + return [generate_class Large $LARGE $states]; >>> +} >>> + >>> +# Generate a class that derives from a small class >>> + >>> +proc generate_derived_class { states } { >>> + set base "Small_[join $states _]" >>> + set classname "Derived_[join $states _]" >>> + >>> + return " >>> + /*** Class derived from $base ***/ >>> + class $classname : public $base { >>> + public: >>> + }; >>> + >>> + $classname ${classname}_var; /* global var */ >>> + >>> + template int cbv<$classname> ($classname arg);" >>> +} >>> + >>> +# Generate a class that contains a small class item >>> + >>> +proc generate_container_class { states } { >>> + set contained "Small_[join $states _]" >>> + set classname "Container_[join $states _]" >>> + >>> + return " >>> + /*** Class that contains $contained ***/ >>> + class $classname { >>> + public: >>> + $contained item; >>> + }; >>> + >>> + $classname ${classname}_var; /* global var */ >>> + >>> + template int cbv_container<$classname> ($classname arg);" >>> +} >>> + >>> +# Generate useful statements that use a class in the debugee program >>> + >>> +proc generate_stmts { classprefix states {cbvfun "cbv"}} { >>> + set classname "${classprefix}_[join $states _]" >>> + >>> + # Having an explicit call to the cbv function in the debugee program >>> + # ensures that the compiler will emit necessary function in the binary. >>> + if {[is_copy_constructible $states]} { >>> + set cbvcall "$cbvfun<$classname> (${classname}_var);\n" >>> + } else { >>> + set cbvcall "" >>> + } >>> + >>> + return "$cbvcall" >>> +} >>> + >>> +# Generate the complete debugee program >>> + >>> +proc generate_program { classes stmts } { >>> + global ADDED >>> + >>> + return " >>> + /*** THIS FILE IS GENERATED BY THE TEST. ***/ >>> + >>> + static int tracer = 0; >>> + >>> + /* The call-by-value function. */ >>> + template <class T> >>> + int >>> + cbv (T arg) >>> + { >>> + arg.data\[0\] += $ADDED; // intentionally modify the arg >>> + return arg.data\[0\]; >>> + } >>> + >>> + template <class T> >>> + int >>> + cbv_container (T arg) >>> + { >>> + arg.item.data\[0\] += $ADDED; // intentionally modify >>> + return arg.item.data\[0\]; >>> + } >>> + >>> + $classes >>> + >>> + int >>> + main (void) >>> + { >>> + $stmts >>> + >>> + /* stop here */ >>> + >>> + return 0; >>> + }" >>> +} >>> + >>> +# Compute all the combinations of special function states. >>> +# We do not contain the 'deleted' state for the destructor, >>> +# because it is illegal to have stack-allocated objects >>> +# whose destructor have been deleted. This case is covered >>> +# in pass-by-ref-2 via heap-allocated objects. >>> + >>> +set options_nodelete [list absent explicit defaultedIn defaultedOut] >>> +set options [concat $options_nodelete {deleted}] >>> +set all_combinations {} >>> + >>> +foreach cctor $options { >>> + foreach dtor $options_nodelete { >>> + foreach mctor $options { >>> + lappend all_combinations [list $cctor $dtor $mctor] >>> + } >>> + } >>> +} >>> + >>> +# Generate the classes. >>> + >>> +set classes "" >>> +set stmts "" >>> + >>> +foreach state $all_combinations { >>> + append classes [generate_small_class $state] >>> + append stmts [generate_stmts "Small" $state] >>> + >>> + append classes [generate_large_class $state] >>> + append stmts [generate_stmts "Large" $state] >>> + >>> + append classes [generate_derived_class $state] >>> + append stmts [generate_stmts "Derived" $state] >>> + >>> + append classes [generate_container_class $state] >>> + append stmts [generate_stmts "Container" $state "cbv_container"] >>> +} >>> + >>> +# Generate the program code and compile >>> +set program [generate_program $classes $stmts] >>> +set srcfile [standard_output_file ${srcfile}] >>> +gdb_produce_source $srcfile $program >>> + >>> +set options {debug c++ additional_flags=-std=c++11} >>> +if {[prepare_for_testing "failed to prepare" $testfile $srcfile $options]} { >>> return -1 >>> } >>> >>> -if ![runto_main] then { >>> +if {![runto_main]} { >>> + untested "failed to run to main" >>> return -1 >>> } >>> >>> -gdb_test "print foo (global_obj)" " = 3" "call function in obj" >>> -gdb_test "print blap (global_derived)" " = 3" "call function in derived" >>> -gdb_test "print blip (global_container)" " = 3" "call function in container" >>> +set bp_location [gdb_get_line_number "stop here"] >>> +gdb_breakpoint $bp_location >>> +gdb_continue_to_breakpoint "end of main" ".*return .*;" >>> + >>> +# Do the checks for a given class whose name is prefixed with PREFIX, >>> +# and whose special functions have the states given in STATES. >>> +# The name of the call-by-value function and the expression to access >>> +# the data field can be specified explicitly if the default values >>> +# do not work. >>> + >>> +proc test_for_class { prefix states cbvfun data_field length} { >>> + set name "${prefix}_[join $states _]" >>> + >>> + set cctor [lindex $states 0] >>> + set dtor [lindex $states 1] >>> + set mctor [lindex $states 2] >>> + >>> + global ORIGINAL >>> + global CUSTOM >>> + global ADDED >>> + global TRACE >>> + >>> + with_test_prefix $name { >>> + if {[is_copy_constructible $states]} { >>> + set expected [expr {$ORIGINAL + $ADDED}] >>> + if {$cctor == "explicit"} { >>> + set expected [expr {$CUSTOM + $ADDED}] >>> + } >>> + if {$dtor == "explicit"} { >>> + gdb_test "print tracer = 0" " = 0" "reset the tracer" >>> + } >>> + gdb_test "print ${cbvfun}<$name> (${name}_var)" " = $expected" \ >>> + "call '$cbvfun'" >>> + gdb_test "print ${name}_var.${data_field}\[0\]" " = $ORIGINAL" \ >>> + "cbv argument should not change (item 0)" >>> + if {$length > 1} { >>> + set last_index [expr $length - 1] >>> + gdb_test "print ${name}_var.${data_field}\[$last_index\]" \ >>> + " = $ORIGINAL" \ >>> + "cbv argument should not change (item $last_index)" >>> + } >>> + if {$dtor == "explicit"} { >>> + gdb_test "print tracer" " = $TRACE" \ >>> + "destructor should be called" >>> + } >>> + } else { >>> + gdb_test "print ${cbvfun}<$name> (${name}_var)" \ >>> + ".* cannot be evaluated .* '${name}' is not copy constructible" \ >>> + "calling '$cbvfun' should be refused" >>> + } >>> + } >>> +} >>> + >>> +foreach state $all_combinations { >>> + test_for_class "Small" $state "cbv" "data" $SMALL >>> + test_for_class "Large" $state "cbv" "data" $LARGE >>> + test_for_class "Derived" $state "cbv" "data" 1 >>> + test_for_class "Container" $state "cbv_container" "item.data" 1 >>> +} >>> > Intel Deutschland GmbH > Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany > Tel: +49 89 99 8853-0, www.intel.de > Managing Directors: Christin Eisenschmid, Gary Kershaw > Chairperson of the Supervisory Board: Nicole Lau > Registered Office: Munich > Commercial Register: Amtsgericht Muenchen HRB 186928 >
On Monday, January 13, 2020 8:38 PM, Luis Machado wrote: > > On 1/13/20 4:35 PM, Aktemur, Tankut Baris wrote: > > On Monday, January 13, 2020 7:21 PM, Luis Machado wrote: > >> > >> Hi, > >> > >> I noticed these new tests fail (in large numbers) for GCC 5.4.x. Is it > >> expected? If so, it may be worth making this test conditional on newer > >> GCC versions. > >> > > > > Yes, this is expected. Older GCC versions did not emit certain DWARF attributes > > (DW_AT_deleted, DW_AT_defaulted). This prevents GDB from making the right > > pass-by-reference decision. I'll submit a patch for this. > > Thanks for clarifying this. > > I can submit that patch if you like. I have a box running an older GCC, > so i noticed that and thought i'd check. > > Luis Yes, sure. Thank you. -Baris > > > > > Thanks for the suggestion. > > > > -Baris > > > >> On 12/14/19 6:53 AM, Tankut Baris Aktemur (Code Review) wrote: > >>> Change URL: https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/142 > >>> ...................................................................... > >>> > >>> testsuite, cp: increase the coverage of testing pass-by-ref arguments > >>> > >>> Extend testcases for GDB's infcall of call-by-value functions that > >>> take aggregate values as parameters. In particular, existing test has > >>> been substantially extended with class definitions whose definitions > >>> of copy constructor, destructor, and move constructor functions are a > >>> combination of > >>> > >>> (1) explicitly defined by the user, > >>> (2) defaulted inside the class declaration, > >>> (3) defaulted outside the class declaration, > >>> (4) deleted > >>> (5) not defined in the source. > >>> > >>> For each combination, a small and a large class is generated as well > >>> as a derived class and a container class. Additionally, the following > >>> manually-written cases are provided: > >>> > >>> - a dynamic class (i.e. class with a virtual method) > >>> - classes that contain an array field > >>> - a class whose copy ctor is inlined > >>> - a class whose destructor is deleted > >>> - classes with multiple copy and/or move ctors > >>> > >>> Test cases check whether GDB makes the right decision to pass an > >>> object by value or implicitly by reference, whether really a copy of > >>> the argument is passed, and whether the copy constructor and > >>> destructor of the clone of the argument are invoked properly. > >>> > >>> The input program pass-by-ref.cc is generated in the test's output > >>> directory. The input program pass-by-ref-2.cc is manually-written. > >>> > >>> Tests have been verified on the X86_64 architecture with > >>> GCC 7.4.0, 8.2.0, and 9.2.1. > >>> > >>> gdb/testsuite/ChangeLog: > >>> 2019-11-07 Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> > >>> > >>> * gdb.cp/pass-by-ref.cc: Delete. Generated in the output > >>> directory instead. > >>> * gdb.cp/pass-by-ref.exp: Extend with more cases. > >>> * gdb.cp/pass-by-ref-2.cc: New file. > >>> * gdb.cp/pass-by-ref-2.exp: New file. > >>> > >>> Change-Id: Ie8ab1f260c6ad5ee4eb34b2c1597ce24af04abb6 > >>> --- > >>> A gdb/testsuite/gdb.cp/pass-by-ref-2.cc > >>> A gdb/testsuite/gdb.cp/pass-by-ref-2.exp > >>> D gdb/testsuite/gdb.cp/pass-by-ref.cc > >>> M gdb/testsuite/gdb.cp/pass-by-ref.exp > >>> 4 files changed, 791 insertions(+), 86 deletions(-) > >>> > >>> > >>> > >>> diff --git a/gdb/testsuite/gdb.cp/pass-by-ref-2.cc b/gdb/testsuite/gdb.cp/pass-by-ref-2.cc > >>> new file mode 100644 > >>> index 0000000..1cd5a16 > >>> --- /dev/null > >>> +++ b/gdb/testsuite/gdb.cp/pass-by-ref-2.cc > >>> @@ -0,0 +1,295 @@ > >>> +/* This testcase is part of GDB, the GNU debugger. > >>> + > >>> + Copyright 2019 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/>. */ > >>> + > >>> +class ByVal { > >>> +public: > >>> + ByVal (void); > >>> + > >>> + int x; > >>> +}; > >>> + > >>> +ByVal::ByVal (void) > >>> +{ > >>> + x = 2; > >>> +} > >>> + > >>> +class ByRef { > >>> +public: > >>> + ByRef (void); > >>> + > >>> + ByRef (const ByRef &rhs); > >>> + > >>> + int x; > >>> +}; > >>> + > >>> +ByRef::ByRef (void) > >>> +{ > >>> + x = 2; > >>> +} > >>> + > >>> +ByRef::ByRef (const ByRef &rhs) > >>> +{ > >>> + x = 3; /* ByRef-cctor */ > >>> +} > >>> + > >>> +class ArrayContainerByVal { > >>> +public: > >>> + ByVal items[2]; > >>> +}; > >>> + > >>> +int > >>> +cbvArrayContainerByVal (ArrayContainerByVal arg) > >>> +{ > >>> + arg.items[0].x += 4; // intentionally modify > >>> + return arg.items[0].x; > >>> +} > >>> + > >>> +class ArrayContainerByRef { > >>> +public: > >>> + ByRef items[2]; > >>> +}; > >>> + > >>> +int > >>> +cbvArrayContainerByRef (ArrayContainerByRef arg) > >>> +{ > >>> + arg.items[0].x += 4; // intentionally modify > >>> + return arg.items[0].x; > >>> +} > >>> + > >>> +class DynamicBase { > >>> +public: > >>> + DynamicBase (void); > >>> + > >>> + virtual int get (void); > >>> + > >>> + int x; > >>> +}; > >>> + > >>> +DynamicBase::DynamicBase (void) > >>> +{ > >>> + x = 2; > >>> +} > >>> + > >>> +int > >>> +DynamicBase::get (void) > >>> +{ > >>> + return 42; > >>> +} > >>> + > >>> +class Dynamic : public DynamicBase { > >>> +public: > >>> + virtual int get (void); > >>> +}; > >>> + > >>> +int > >>> +Dynamic::get (void) > >>> +{ > >>> + return 9999; > >>> +} > >>> + > >>> +int > >>> +cbvDynamic (DynamicBase arg) > >>> +{ > >>> + arg.x += 4; // intentionally modify > >>> + return arg.x + arg.get (); > >>> +} > >>> + > >>> +class Inlined { > >>> +public: > >>> + Inlined (void); > >>> + > >>> + __attribute__((always_inline)) > >>> + Inlined (const Inlined &rhs) > >>> + { > >>> + x = 3; > >>> + } > >>> + > >>> + int x; > >>> +}; > >>> + > >>> +Inlined::Inlined (void) > >>> +{ > >>> + x = 2; > >>> +} > >>> + > >>> +int > >>> +cbvInlined (Inlined arg) > >>> +{ > >>> + arg.x += 4; // intentionally modify > >>> + return arg.x; > >>> +} > >>> + > >>> +class DtorDel { > >>> +public: > >>> + DtorDel (void); > >>> + > >>> + ~DtorDel (void) = delete; > >>> + > >>> + int x; > >>> +}; > >>> + > >>> +DtorDel::DtorDel (void) > >>> +{ > >>> + x = 2; > >>> +} > >>> + > >>> +int > >>> +cbvDtorDel (DtorDel arg) > >>> +{ > >>> + // Calling this method should be rejected > >>> + return arg.x; > >>> +} > >>> + > >>> +class FourCCtor { > >>> +public: > >>> + FourCCtor (void); > >>> + > >>> + FourCCtor (FourCCtor &rhs); > >>> + FourCCtor (const FourCCtor &rhs); > >>> + FourCCtor (volatile FourCCtor &rhs); > >>> + FourCCtor (const volatile FourCCtor &rhs); > >>> + > >>> + int x; > >>> +}; > >>> + > >>> +FourCCtor::FourCCtor (void) > >>> +{ > >>> + x = 2; > >>> +} > >>> + > >>> +FourCCtor::FourCCtor (FourCCtor &rhs) > >>> +{ > >>> + x = 3; > >>> +} > >>> + > >>> +FourCCtor::FourCCtor (const FourCCtor &rhs) > >>> +{ > >>> + x = 4; > >>> +} > >>> + > >>> +FourCCtor::FourCCtor (volatile FourCCtor &rhs) > >>> +{ > >>> + x = 5; > >>> +} > >>> + > >>> +FourCCtor::FourCCtor (const volatile FourCCtor &rhs) > >>> +{ > >>> + x = 6; > >>> +} > >>> + > >>> +int > >>> +cbvFourCCtor (FourCCtor arg) > >>> +{ > >>> + arg.x += 10; // intentionally modify > >>> + return arg.x; > >>> +} > >>> + > >>> +class TwoMCtor { > >>> +public: > >>> + TwoMCtor (void); > >>> + > >>> + /* Even though one move ctor is defaulted, the other > >>> + is explicit. */ > >>> + TwoMCtor (const TwoMCtor &&rhs); > >>> + TwoMCtor (TwoMCtor &&rhs) = default; > >>> + > >>> + int x; > >>> +}; > >>> + > >>> +TwoMCtor::TwoMCtor (void) > >>> +{ > >>> + x = 2; > >>> +} > >>> + > >>> +TwoMCtor::TwoMCtor (const TwoMCtor &&rhs) > >>> +{ > >>> + x = 3; > >>> +} > >>> + > >>> +int > >>> +cbvTwoMCtor (TwoMCtor arg) > >>> +{ > >>> + arg.x += 10; // intentionally modify > >>> + return arg.x; > >>> +} > >>> + > >>> +class TwoMCtorAndCCtor { > >>> +public: > >>> + TwoMCtorAndCCtor (void); > >>> + > >>> + TwoMCtorAndCCtor (const TwoMCtorAndCCtor &rhs) = default; > >>> + > >>> + /* Even though one move ctor is defaulted, the other > >>> + is explicit. This makes the type pass-by-ref. */ > >>> + TwoMCtorAndCCtor (const TwoMCtorAndCCtor &&rhs); > >>> + TwoMCtorAndCCtor (TwoMCtorAndCCtor &&rhs) = default; > >>> + > >>> + int x; > >>> +}; > >>> + > >>> +TwoMCtorAndCCtor::TwoMCtorAndCCtor (void) > >>> +{ > >>> + x = 2; > >>> +} > >>> + > >>> +TwoMCtorAndCCtor::TwoMCtorAndCCtor (const TwoMCtorAndCCtor &&rhs) > >>> +{ > >>> + x = 4; > >>> +} > >>> + > >>> +int > >>> +cbvTwoMCtorAndCCtor (TwoMCtorAndCCtor arg) > >>> +{ > >>> + arg.x += 10; // intentionally modify > >>> + return arg.x; > >>> +} > >>> + > >>> +ArrayContainerByVal arrayContainerByVal; > >>> +ArrayContainerByRef arrayContainerByRef; > >>> +Dynamic dynamic; > >>> +Inlined inlined; > >>> +// Cannot stack-allocate DtorDel > >>> +DtorDel *dtorDel; > >>> +FourCCtor fourCctor_c0v0; > >>> +const FourCCtor fourCctor_c1v0; > >>> +volatile FourCCtor fourCctor_c0v1; > >>> +const volatile FourCCtor fourCctor_c1v1; > >>> +TwoMCtor twoMctor; > >>> +TwoMCtorAndCCtor twoMctorAndCctor; > >>> + > >>> +int > >>> +main (void) > >>> +{ > >>> + int v; > >>> + dtorDel = new DtorDel; > >>> + /* Explicitly call the cbv function to make sure the compiler > >>> + will not omit any code in the binary. */ > >>> + v = cbvArrayContainerByVal (arrayContainerByVal); > >>> + v = cbvArrayContainerByRef (arrayContainerByRef); > >>> + v = cbvDynamic (dynamic); > >>> + v = cbvInlined (inlined); > >>> + v = cbvFourCCtor (fourCctor_c0v0); > >>> + v = cbvFourCCtor (fourCctor_c1v0); > >>> + v = cbvFourCCtor (fourCctor_c0v1); > >>> + v = cbvFourCCtor (fourCctor_c1v1); > >>> + /* v = cbvTwoMCtor (twoMctor); */ // This is illegal, cctor is deleted > >>> + v = cbvTwoMCtorAndCCtor (twoMctorAndCctor); > >>> + > >>> + /* stop here */ > >>> + > >>> + return 0; > >>> +} > >>> diff --git a/gdb/testsuite/gdb.cp/pass-by-ref-2.exp b/gdb/testsuite/gdb.cp/pass-by-ref-2.exp > >>> new file mode 100644 > >>> index 0000000..7cce886 > >>> --- /dev/null > >>> +++ b/gdb/testsuite/gdb.cp/pass-by-ref-2.exp > >>> @@ -0,0 +1,114 @@ > >>> +# Copyright 2019 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/>. > >>> + > >>> +# Check that GDB can call C++ functions whose parameters have > >>> +# object type, and are either passed by value or implicitly by reference. > >>> +# > >>> +# This is a companion test to pass-by-ref.exp. In this test, the input > >>> +# is manually-written. In pass-by-ref.exp, the test input is generated. > >>> +# > >>> +# We include tests for classes that > >>> +# - contain arrays as fields, > >>> +# - are dynamic (i.e. have virtual methods) > >>> +# - have inlined copy ctor > >>> +# - have deleted destructor > >>> + > >>> +if {[skip_cplus_tests]} { > >>> + untested "c++ test skipped" > >>> + continue > >>> +} > >>> + > >>> +standard_testfile .cc > >>> + > >>> +set options {debug c++ additional_flags=-std=c++11} > >>> +if {[prepare_for_testing "failed to prepare" $testfile $srcfile $options]} { > >>> + return -1 > >>> +} > >>> + > >>> +if {![runto_main]} { > >>> + untested "failed to run to main" > >>> + return -1 > >>> +} > >>> + > >>> +set bp_location [gdb_get_line_number "stop here"] > >>> +gdb_breakpoint $bp_location > >>> +gdb_continue_to_breakpoint "end of main" ".*return .*;" > >>> + > >>> +gdb_test "print cbvArrayContainerByVal (arrayContainerByVal)" "6" \ > >>> + "call cbvArrayContainerByVal" > >>> +gdb_test "print arrayContainerByVal.items\[0\].x" "2" \ > >>> + "cbv argument 'arrayContainerByVal' should not change" > >>> + > >>> +gdb_test "print cbvArrayContainerByRef (arrayContainerByRef)" "7" \ > >>> + "call cbvArrayContainerByRef" > >>> +gdb_test "print arrayContainerByRef.items\[0\].x" "2" \ > >>> + "cbv argument 'arrayContainerByRef' should not change" > >>> + > >>> +gdb_test "print cbvDynamic (dynamic)" "48" \ > >>> + "call cbvDynamic" > >>> +gdb_test "print dynamic.x" "2" \ > >>> + "cbv argument 'dynamic' should not change" > >>> + > >>> +set sig "\"Inlined\:\:Inlined\\(.*Inlined const\&\\)\"" > >>> +gdb_test "print cbvInlined (inlined)" \ > >>> + "expression cannot be evaluated .* \\(maybe inlined\\?\\)" > >>> + > >>> +gdb_test "print cbvDtorDel (*dtorDel)" \ > >>> + ".* cannot be evaluated .* 'DtorDel' is not destructible" \ > >>> + "type not destructible" > >>> + > >>> +# Test that GDB calls the correct copy ctor > >>> +gdb_test "print cbvFourCCtor (fourCctor_c0v0)" "13" \ > >>> + "call cbvFourCCtor (c0v0)" > >>> +gdb_test "print fourCctor_c0v0.x" "2" \ > >>> + "cbv argument 'twoCctor_c0v0' should not change" > >>> + > >>> +gdb_test "print cbvFourCCtor (fourCctor_c1v0)" "14" \ > >>> + "call cbvFourCCtor (c1v0)" > >>> +gdb_test "print fourCctor_c1v0.x" "2" \ > >>> + "cbv argument 'twoCctor_c1v0' should not change" > >>> + > >>> +gdb_test "print cbvFourCCtor (fourCctor_c0v1)" "15" \ > >>> + "call cbvFourCCtor (c0v1)" > >>> +gdb_test "print fourCctor_c0v1.x" "2" \ > >>> + "cbv argument 'twoCctor_c0v1' should not change" > >>> + > >>> +gdb_test "print cbvFourCCtor (fourCctor_c1v1)" "16" \ > >>> + "call cbvFourCCtor (c1v1)" > >>> +gdb_test "print fourCctor_c1v1.x" "2" \ > >>> + "cbv argument 'twoCctor_c1v1' should not change" > >>> + > >>> +gdb_test "print cbvTwoMCtor (twoMctor)" \ > >>> + ".* cannot be evaluated .* 'TwoMCtor' is not copy constructible" \ > >>> + "copy ctor is implicitly deleted" > >>> + > >>> +gdb_test "print cbvTwoMCtorAndCCtor (twoMctorAndCctor)" "12" \ > >>> + "call cbvTwoMCtorAndCCtor" > >>> +gdb_test "print twoMctorAndCctor.x" "2" \ > >>> + "cbv argument 'twoMctorAndCtor' should not change" > >>> + > >>> +# Test that we get a breakpoint from the cctor during infcall and > >>> +# we can examine arguments. This is a test that the dummy frame > >>> +# of the copy constructor is set up correctly by the infcall mechanism. > >>> +set bp_location [gdb_get_line_number "ByRef-cctor"] > >>> +gdb_breakpoint $bp_location > >>> +gdb_test "print cbvArrayContainerByRef (arrayContainerByRef)" \ > >>> + ".*The program being debugged stopped.*" \ > >>> + "call cbvArrayContainerByRef with BP" > >>> +gdb_test "backtrace" [multi_line \ > >>> + "#0 ByRef\:\:ByRef .* at .*$srcfile:$bp_location" \ > >>> + "#1 .* ArrayContainerByRef::ArrayContainerByRef .*" \ > >>> + "#2 <function called from gdb>" \ > >>> + "#3 main.*"] > >>> diff --git a/gdb/testsuite/gdb.cp/pass-by-ref.cc b/gdb/testsuite/gdb.cp/pass-by-ref.cc > >>> deleted file mode 100644 > >>> index bbe450a..0000000 > >>> --- a/gdb/testsuite/gdb.cp/pass-by-ref.cc > >>> +++ /dev/null > >>> @@ -1,79 +0,0 @@ > >>> -/* This testcase is part of GDB, the GNU debugger. > >>> - > >>> - Copyright 2007-2019 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/>. */ > >>> - > >>> -class Obj { > >>> -public: > >>> - Obj (); > >>> - Obj (const Obj &); > >>> - ~Obj (); > >>> - int var[2]; > >>> -}; > >>> - > >>> -int foo (Obj arg) > >>> -{ > >>> - return arg.var[0] + arg.var[1]; > >>> -} > >>> - > >>> -Obj::Obj () > >>> -{ > >>> - var[0] = 1; > >>> - var[1] = 2; > >>> -} > >>> - > >>> -Obj::Obj (const Obj &obj) > >>> -{ > >>> - var[0] = obj.var[0]; > >>> - var[1] = obj.var[1]; > >>> -} > >>> - > >>> -Obj::~Obj () > >>> -{ > >>> - > >>> -} > >>> - > >>> -struct Derived : public Obj > >>> -{ > >>> - int other; > >>> -}; > >>> - > >>> -int blap (Derived arg) > >>> -{ > >>> - return foo (arg); > >>> -} > >>> - > >>> -struct Container > >>> -{ > >>> - Obj obj; > >>> -}; > >>> - > >>> -int blip (Container arg) > >>> -{ > >>> - return foo (arg.obj); > >>> -} > >>> - > >>> -Obj global_obj; > >>> -Derived global_derived; > >>> -Container global_container; > >>> - > >>> -int > >>> -main () > >>> -{ > >>> - int bar = foo (global_obj); > >>> - blap (global_derived); > >>> - blip (global_container); > >>> - return bar; > >>> -} > >>> diff --git a/gdb/testsuite/gdb.cp/pass-by-ref.exp b/gdb/testsuite/gdb.cp/pass-by-ref.exp > >>> index 94dd345..f44be77 100644 > >>> --- a/gdb/testsuite/gdb.cp/pass-by-ref.exp > >>> +++ b/gdb/testsuite/gdb.cp/pass-by-ref.exp > >>> @@ -14,20 +14,395 @@ > >>> # along with this program. If not, see <http://www.gnu.org/licenses/>. > >>> > >>> # Check that GDB can call C++ functions whose parameters have > >>> -# object type, but are passed by reference. > >>> +# object type, and are either passed by value or implicitly by reference. > >>> +# > >>> +# Suppose F is a function that has a call-by-value parameter whose > >>> +# type is class C. When calling F with an argument A, a copy of A should > >>> +# be created and passed to F. If C is a trivially-copyable type, A can > >>> +# be copied by a straightforward memory copy. However, roughly speaking, > >>> +# if C has a user-defined copy constructor and/or a user-defined > >>> +# destructor, the copy ctor should be used to initialize the copy of A > >>> +# before calling F, and a reference to that copy is passed to F. After > >>> +# the function returns, the destructor should be called to destruct the > >>> +# copy. In this case, C is said to be a 'pass-by-reference' type. > >>> +# Determining whether C is pass-by-ref depends on > >>> +# how the copy ctor, destructor, and the move ctor of C are defined. > >>> +# First of all, C is not copy constructible if its copy constructor is > >>> +# explicitly or implicitly deleted. In this case, it would be illegal > >>> +# to pass values of type C to a function. C is pass-by-value, if all of > >>> +# its copy ctor, dtor, and move ctor are trivially defined. > >>> +# Otherwise, it is pass-by-ref. > >>> +# > >>> +# To cover the many possible combinations, this test generates classes > >>> +# that contain three special functions: > >>> +# (1) a copy constructor, > >>> +# (2) a destructor, and > >>> +# (3) a move constructor. > >>> +# A special function is in one of the following states: > >>> +# * explicit: The function is explicitly defined by the user. > >>> +# * defaultedIn: The function is defaulted inside the class decl, > >>> +# using the 'default' keyword. > >>> +# * defaultedOut: The function is declared inside the class decl, > >>> +# and defaulted outside using the 'default' keyword. > >>> +# * deleted: The function is explicitly deleted by the user, > >>> +# using the 'delete' keyword. > >>> +# * absent: The function is not declared by the user (i.e. it does not > >>> +# exist in the source. The compiler generates (or deletes) the > >>> +# definition in this case. > >>> +# > >>> +# The C++ ABI decides if a class is pass-by-value or pass-by-ref > >>> +# (i.e. trivially copyable or not) first at the language level, based > >>> +# on the state of the special functions. Then, at the target level, a > >>> +# class may be determined to be pass-by-ref because of its size > >>> +# (e.g. if it is too large to fit on registers). For this reason, this > >>> +# test generates both a small and a large version for the same > >>> +# combination of special function states. > >>> +# > >>> +# A class is not trivially-copyable if a base class or a field is not > >>> +# trivially-copyable, even though the class definition itself seems > >>> +# trivial. To test these cases, we also generate derived classes and > >>> +# container classes. > >>> +# > >>> +# The generated code is placed in the test output directory. > >>> +# > >>> +# The companion test file pass-by-ref-2.exp also contains > >>> +# manually-written cases. > >>> > >>> -if { [skip_cplus_tests] } { continue } > >>> +if {[skip_cplus_tests]} { > >>> + untested "c++ test skipped" > >>> + continue > >>> +} > >>> > >>> +# The program source is generated in the output directory. > >>> +# We use standard_testfile here to set convenience variables. > >>> standard_testfile .cc > >>> > >>> -if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} { > >>> +# Some constant values used when generating the source > >>> + > >>> +set SMALL 2 > >>> +set LARGE 150 > >>> +set ORIGINAL 2 > >>> +set CUSTOM 3 > >>> +set ADDED 4 > >>> +set TRACE 5 > >>> + > >>> + > >>> +# Return 1 if the class whose special function states are STATES > >>> +# is copyable. Otherwise return 0. > >>> + > >>> +proc is_copy_constructible { states } { > >>> + set cctor [lindex $states 0] > >>> + set dtor [lindex $states 1] > >>> + set mctor [lindex $states 2] > >>> + > >>> + if {$cctor == "deleted" || ($cctor == "absent" && $mctor != "absent")} { > >>> + return 0 > >>> + } > >>> + return 1 > >>> +} > >>> + > >>> +# Generate a declaration and an out-of-class definition for a function > >>> +# with the provided signature. The STATE should be one of the following: > >>> +# - explicit, defaultedIn, defaultedOut, deleted, absent > >>> + > >>> +proc generate_member_function { classname signature length state } { > >>> + set declaration "" > >>> + set definition "" > >>> + > >>> + global CUSTOM > >>> + global TRACE > >>> + > >>> + switch $state { > >>> + explicit { > >>> + set declaration "$signature;\n" > >>> + set definition "$classname\:\:$signature > >>> + { > >>> + data\[0\] = $CUSTOM; > >>> + data\[[expr $length - 1]\] = $CUSTOM; > >>> + tracer = $TRACE; > >>> + }\n" > >>> + } > >>> + defaultedIn { > >>> + set declaration "$signature = default;\n" > >>> + } > >>> + defaultedOut { > >>> + set declaration "$signature;\n" > >>> + set definition "$classname\:\:$signature = default;\n" > >>> + } > >>> + deleted { > >>> + set declaration "$signature = delete;\n" > >>> + } > >>> + default { > >>> + # function is not user-defined in this case > >>> + } > >>> + } > >>> + > >>> + return [list $declaration $definition] > >>> +} > >>> + > >>> +# Generate a C++ class with the given CLASSNAME and LENGTH-many > >>> +# integer elements. The STATES is an array of 3 items > >>> +# containing the desired state of the special functions > >>> +# in this order: > >>> +# copy constructor, destructor, move constructor > >>> + > >>> +proc generate_class { classname length states } { > >>> + set declarations "" > >>> + set definitions "" > >>> + set classname "${classname}_[join $states _]" > >>> + > >>> + for {set i 0} {$i < [llength $states]} {incr i} { > >>> + set sig "" > >>> + switch $i { > >>> + 0 {set sig "$classname (const $classname \&rhs)"} > >>> + 1 {set sig "\~$classname (void)"} > >>> + 2 {set sig "$classname ($classname \&\&rhs)"} > >>> + } > >>> + > >>> + set state [lindex $states $i] > >>> + set code [generate_member_function $classname $sig $length $state] > >>> + append declarations [lindex $code 0] > >>> + append definitions [lindex $code 1] > >>> + } > >>> + > >>> + global ORIGINAL > >>> + > >>> + return " > >>> + /*** C++ class $classname ***/ > >>> + class ${classname} { > >>> + public: > >>> + $classname (void); > >>> + $declarations > >>> + > >>> + int data\[$length\]; > >>> + }; > >>> + > >>> + $classname\:\:$classname (void) > >>> + { > >>> + data\[0\] = $ORIGINAL; > >>> + data\[[expr $length - 1]\] = $ORIGINAL; > >>> + } > >>> + > >>> + $definitions > >>> + > >>> + $classname ${classname}_var; /* global var */ > >>> + > >>> + template int cbv<$classname> ($classname arg);" > >>> +} > >>> + > >>> +# Generate a small C++ class > >>> + > >>> +proc generate_small_class { states } { > >>> + global SMALL > >>> + return [generate_class Small $SMALL $states]; > >>> +} > >>> + > >>> +# Generate a large C++ class > >>> + > >>> +proc generate_large_class { states } { > >>> + global LARGE > >>> + return [generate_class Large $LARGE $states]; > >>> +} > >>> + > >>> +# Generate a class that derives from a small class > >>> + > >>> +proc generate_derived_class { states } { > >>> + set base "Small_[join $states _]" > >>> + set classname "Derived_[join $states _]" > >>> + > >>> + return " > >>> + /*** Class derived from $base ***/ > >>> + class $classname : public $base { > >>> + public: > >>> + }; > >>> + > >>> + $classname ${classname}_var; /* global var */ > >>> + > >>> + template int cbv<$classname> ($classname arg);" > >>> +} > >>> + > >>> +# Generate a class that contains a small class item > >>> + > >>> +proc generate_container_class { states } { > >>> + set contained "Small_[join $states _]" > >>> + set classname "Container_[join $states _]" > >>> + > >>> + return " > >>> + /*** Class that contains $contained ***/ > >>> + class $classname { > >>> + public: > >>> + $contained item; > >>> + }; > >>> + > >>> + $classname ${classname}_var; /* global var */ > >>> + > >>> + template int cbv_container<$classname> ($classname arg);" > >>> +} > >>> + > >>> +# Generate useful statements that use a class in the debugee program > >>> + > >>> +proc generate_stmts { classprefix states {cbvfun "cbv"}} { > >>> + set classname "${classprefix}_[join $states _]" > >>> + > >>> + # Having an explicit call to the cbv function in the debugee program > >>> + # ensures that the compiler will emit necessary function in the binary. > >>> + if {[is_copy_constructible $states]} { > >>> + set cbvcall "$cbvfun<$classname> (${classname}_var);\n" > >>> + } else { > >>> + set cbvcall "" > >>> + } > >>> + > >>> + return "$cbvcall" > >>> +} > >>> + > >>> +# Generate the complete debugee program > >>> + > >>> +proc generate_program { classes stmts } { > >>> + global ADDED > >>> + > >>> + return " > >>> + /*** THIS FILE IS GENERATED BY THE TEST. ***/ > >>> + > >>> + static int tracer = 0; > >>> + > >>> + /* The call-by-value function. */ > >>> + template <class T> > >>> + int > >>> + cbv (T arg) > >>> + { > >>> + arg.data\[0\] += $ADDED; // intentionally modify the arg > >>> + return arg.data\[0\]; > >>> + } > >>> + > >>> + template <class T> > >>> + int > >>> + cbv_container (T arg) > >>> + { > >>> + arg.item.data\[0\] += $ADDED; // intentionally modify > >>> + return arg.item.data\[0\]; > >>> + } > >>> + > >>> + $classes > >>> + > >>> + int > >>> + main (void) > >>> + { > >>> + $stmts > >>> + > >>> + /* stop here */ > >>> + > >>> + return 0; > >>> + }" > >>> +} > >>> + > >>> +# Compute all the combinations of special function states. > >>> +# We do not contain the 'deleted' state for the destructor, > >>> +# because it is illegal to have stack-allocated objects > >>> +# whose destructor have been deleted. This case is covered > >>> +# in pass-by-ref-2 via heap-allocated objects. > >>> + > >>> +set options_nodelete [list absent explicit defaultedIn defaultedOut] > >>> +set options [concat $options_nodelete {deleted}] > >>> +set all_combinations {} > >>> + > >>> +foreach cctor $options { > >>> + foreach dtor $options_nodelete { > >>> + foreach mctor $options { > >>> + lappend all_combinations [list $cctor $dtor $mctor] > >>> + } > >>> + } > >>> +} > >>> + > >>> +# Generate the classes. > >>> + > >>> +set classes "" > >>> +set stmts "" > >>> + > >>> +foreach state $all_combinations { > >>> + append classes [generate_small_class $state] > >>> + append stmts [generate_stmts "Small" $state] > >>> + > >>> + append classes [generate_large_class $state] > >>> + append stmts [generate_stmts "Large" $state] > >>> + > >>> + append classes [generate_derived_class $state] > >>> + append stmts [generate_stmts "Derived" $state] > >>> + > >>> + append classes [generate_container_class $state] > >>> + append stmts [generate_stmts "Container" $state "cbv_container"] > >>> +} > >>> + > >>> +# Generate the program code and compile > >>> +set program [generate_program $classes $stmts] > >>> +set srcfile [standard_output_file ${srcfile}] > >>> +gdb_produce_source $srcfile $program > >>> + > >>> +set options {debug c++ additional_flags=-std=c++11} > >>> +if {[prepare_for_testing "failed to prepare" $testfile $srcfile $options]} { > >>> return -1 > >>> } > >>> > >>> -if ![runto_main] then { > >>> +if {![runto_main]} { > >>> + untested "failed to run to main" > >>> return -1 > >>> } > >>> > >>> -gdb_test "print foo (global_obj)" " = 3" "call function in obj" > >>> -gdb_test "print blap (global_derived)" " = 3" "call function in derived" > >>> -gdb_test "print blip (global_container)" " = 3" "call function in container" > >>> +set bp_location [gdb_get_line_number "stop here"] > >>> +gdb_breakpoint $bp_location > >>> +gdb_continue_to_breakpoint "end of main" ".*return .*;" > >>> + > >>> +# Do the checks for a given class whose name is prefixed with PREFIX, > >>> +# and whose special functions have the states given in STATES. > >>> +# The name of the call-by-value function and the expression to access > >>> +# the data field can be specified explicitly if the default values > >>> +# do not work. > >>> + > >>> +proc test_for_class { prefix states cbvfun data_field length} { > >>> + set name "${prefix}_[join $states _]" > >>> + > >>> + set cctor [lindex $states 0] > >>> + set dtor [lindex $states 1] > >>> + set mctor [lindex $states 2] > >>> + > >>> + global ORIGINAL > >>> + global CUSTOM > >>> + global ADDED > >>> + global TRACE > >>> + > >>> + with_test_prefix $name { > >>> + if {[is_copy_constructible $states]} { > >>> + set expected [expr {$ORIGINAL + $ADDED}] > >>> + if {$cctor == "explicit"} { > >>> + set expected [expr {$CUSTOM + $ADDED}] > >>> + } > >>> + if {$dtor == "explicit"} { > >>> + gdb_test "print tracer = 0" " = 0" "reset the tracer" > >>> + } > >>> + gdb_test "print ${cbvfun}<$name> (${name}_var)" " = $expected" \ > >>> + "call '$cbvfun'" > >>> + gdb_test "print ${name}_var.${data_field}\[0\]" " = $ORIGINAL" \ > >>> + "cbv argument should not change (item 0)" > >>> + if {$length > 1} { > >>> + set last_index [expr $length - 1] > >>> + gdb_test "print ${name}_var.${data_field}\[$last_index\]" \ > >>> + " = $ORIGINAL" \ > >>> + "cbv argument should not change (item $last_index)" > >>> + } > >>> + if {$dtor == "explicit"} { > >>> + gdb_test "print tracer" " = $TRACE" \ > >>> + "destructor should be called" > >>> + } > >>> + } else { > >>> + gdb_test "print ${cbvfun}<$name> (${name}_var)" \ > >>> + ".* cannot be evaluated .* '${name}' is not copy constructible" \ > >>> + "calling '$cbvfun' should be refused" > >>> + } > >>> + } > >>> +} > >>> + > >>> +foreach state $all_combinations { > >>> + test_for_class "Small" $state "cbv" "data" $SMALL > >>> + test_for_class "Large" $state "cbv" "data" $LARGE > >>> + test_for_class "Derived" $state "cbv" "data" 1 > >>> + test_for_class "Container" $state "cbv_container" "item.data" 1 > >>> +} > >>> > > Intel Deutschland GmbH > > Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany > > Tel: +49 89 99 8853-0, www.intel.de > > Managing Directors: Christin Eisenschmid, Gary Kershaw > > Chairperson of the Supervisory Board: Nicole Lau > > Registered Office: Munich > > Commercial Register: Amtsgericht Muenchen HRB 186928 > > Intel Deutschland GmbH Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany Tel: +49 89 99 8853-0, www.intel.de Managing Directors: Christin Eisenschmid, Gary Kershaw Chairperson of the Supervisory Board: Nicole Lau Registered Office: Munich Commercial Register: Amtsgericht Muenchen HRB 186928
diff --git a/gdb/testsuite/gdb.cp/pass-by-ref-2.cc b/gdb/testsuite/gdb.cp/pass-by-ref-2.cc new file mode 100644 index 0000000..1cd5a16 --- /dev/null +++ b/gdb/testsuite/gdb.cp/pass-by-ref-2.cc @@ -0,0 +1,295 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2019 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/>. */ + +class ByVal { +public: + ByVal (void); + + int x; +}; + +ByVal::ByVal (void) +{ + x = 2; +} + +class ByRef { +public: + ByRef (void); + + ByRef (const ByRef &rhs); + + int x; +}; + +ByRef::ByRef (void) +{ + x = 2; +} + +ByRef::ByRef (const ByRef &rhs) +{ + x = 3; /* ByRef-cctor */ +} + +class ArrayContainerByVal { +public: + ByVal items[2]; +}; + +int +cbvArrayContainerByVal (ArrayContainerByVal arg) +{ + arg.items[0].x += 4; // intentionally modify + return arg.items[0].x; +} + +class ArrayContainerByRef { +public: + ByRef items[2]; +}; + +int +cbvArrayContainerByRef (ArrayContainerByRef arg) +{ + arg.items[0].x += 4; // intentionally modify + return arg.items[0].x; +} + +class DynamicBase { +public: + DynamicBase (void); + + virtual int get (void); + + int x; +}; + +DynamicBase::DynamicBase (void) +{ + x = 2; +} + +int +DynamicBase::get (void) +{ + return 42; +} + +class Dynamic : public DynamicBase { +public: + virtual int get (void); +}; + +int +Dynamic::get (void) +{ + return 9999; +} + +int +cbvDynamic (DynamicBase arg) +{ + arg.x += 4; // intentionally modify + return arg.x + arg.get (); +} + +class Inlined { +public: + Inlined (void); + + __attribute__((always_inline)) + Inlined (const Inlined &rhs) + { + x = 3; + } + + int x; +}; + +Inlined::Inlined (void) +{ + x = 2; +} + +int +cbvInlined (Inlined arg) +{ + arg.x += 4; // intentionally modify + return arg.x; +} + +class DtorDel { +public: + DtorDel (void); + + ~DtorDel (void) = delete; + + int x; +}; + +DtorDel::DtorDel (void) +{ + x = 2; +} + +int +cbvDtorDel (DtorDel arg) +{ + // Calling this method should be rejected + return arg.x; +} + +class FourCCtor { +public: + FourCCtor (void); + + FourCCtor (FourCCtor &rhs); + FourCCtor (const FourCCtor &rhs); + FourCCtor (volatile FourCCtor &rhs); + FourCCtor (const volatile FourCCtor &rhs); + + int x; +}; + +FourCCtor::FourCCtor (void) +{ + x = 2; +} + +FourCCtor::FourCCtor (FourCCtor &rhs) +{ + x = 3; +} + +FourCCtor::FourCCtor (const FourCCtor &rhs) +{ + x = 4; +} + +FourCCtor::FourCCtor (volatile FourCCtor &rhs) +{ + x = 5; +} + +FourCCtor::FourCCtor (const volatile FourCCtor &rhs) +{ + x = 6; +} + +int +cbvFourCCtor (FourCCtor arg) +{ + arg.x += 10; // intentionally modify + return arg.x; +} + +class TwoMCtor { +public: + TwoMCtor (void); + + /* Even though one move ctor is defaulted, the other + is explicit. */ + TwoMCtor (const TwoMCtor &&rhs); + TwoMCtor (TwoMCtor &&rhs) = default; + + int x; +}; + +TwoMCtor::TwoMCtor (void) +{ + x = 2; +} + +TwoMCtor::TwoMCtor (const TwoMCtor &&rhs) +{ + x = 3; +} + +int +cbvTwoMCtor (TwoMCtor arg) +{ + arg.x += 10; // intentionally modify + return arg.x; +} + +class TwoMCtorAndCCtor { +public: + TwoMCtorAndCCtor (void); + + TwoMCtorAndCCtor (const TwoMCtorAndCCtor &rhs) = default; + + /* Even though one move ctor is defaulted, the other + is explicit. This makes the type pass-by-ref. */ + TwoMCtorAndCCtor (const TwoMCtorAndCCtor &&rhs); + TwoMCtorAndCCtor (TwoMCtorAndCCtor &&rhs) = default; + + int x; +}; + +TwoMCtorAndCCtor::TwoMCtorAndCCtor (void) +{ + x = 2; +} + +TwoMCtorAndCCtor::TwoMCtorAndCCtor (const TwoMCtorAndCCtor &&rhs) +{ + x = 4; +} + +int +cbvTwoMCtorAndCCtor (TwoMCtorAndCCtor arg) +{ + arg.x += 10; // intentionally modify + return arg.x; +} + +ArrayContainerByVal arrayContainerByVal; +ArrayContainerByRef arrayContainerByRef; +Dynamic dynamic; +Inlined inlined; +// Cannot stack-allocate DtorDel +DtorDel *dtorDel; +FourCCtor fourCctor_c0v0; +const FourCCtor fourCctor_c1v0; +volatile FourCCtor fourCctor_c0v1; +const volatile FourCCtor fourCctor_c1v1; +TwoMCtor twoMctor; +TwoMCtorAndCCtor twoMctorAndCctor; + +int +main (void) +{ + int v; + dtorDel = new DtorDel; + /* Explicitly call the cbv function to make sure the compiler + will not omit any code in the binary. */ + v = cbvArrayContainerByVal (arrayContainerByVal); + v = cbvArrayContainerByRef (arrayContainerByRef); + v = cbvDynamic (dynamic); + v = cbvInlined (inlined); + v = cbvFourCCtor (fourCctor_c0v0); + v = cbvFourCCtor (fourCctor_c1v0); + v = cbvFourCCtor (fourCctor_c0v1); + v = cbvFourCCtor (fourCctor_c1v1); + /* v = cbvTwoMCtor (twoMctor); */ // This is illegal, cctor is deleted + v = cbvTwoMCtorAndCCtor (twoMctorAndCctor); + + /* stop here */ + + return 0; +} diff --git a/gdb/testsuite/gdb.cp/pass-by-ref-2.exp b/gdb/testsuite/gdb.cp/pass-by-ref-2.exp new file mode 100644 index 0000000..7cce886 --- /dev/null +++ b/gdb/testsuite/gdb.cp/pass-by-ref-2.exp @@ -0,0 +1,114 @@ +# Copyright 2019 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/>. + +# Check that GDB can call C++ functions whose parameters have +# object type, and are either passed by value or implicitly by reference. +# +# This is a companion test to pass-by-ref.exp. In this test, the input +# is manually-written. In pass-by-ref.exp, the test input is generated. +# +# We include tests for classes that +# - contain arrays as fields, +# - are dynamic (i.e. have virtual methods) +# - have inlined copy ctor +# - have deleted destructor + +if {[skip_cplus_tests]} { + untested "c++ test skipped" + continue +} + +standard_testfile .cc + +set options {debug c++ additional_flags=-std=c++11} +if {[prepare_for_testing "failed to prepare" $testfile $srcfile $options]} { + return -1 +} + +if {![runto_main]} { + untested "failed to run to main" + return -1 +} + +set bp_location [gdb_get_line_number "stop here"] +gdb_breakpoint $bp_location +gdb_continue_to_breakpoint "end of main" ".*return .*;" + +gdb_test "print cbvArrayContainerByVal (arrayContainerByVal)" "6" \ + "call cbvArrayContainerByVal" +gdb_test "print arrayContainerByVal.items\[0\].x" "2" \ + "cbv argument 'arrayContainerByVal' should not change" + +gdb_test "print cbvArrayContainerByRef (arrayContainerByRef)" "7" \ + "call cbvArrayContainerByRef" +gdb_test "print arrayContainerByRef.items\[0\].x" "2" \ + "cbv argument 'arrayContainerByRef' should not change" + +gdb_test "print cbvDynamic (dynamic)" "48" \ + "call cbvDynamic" +gdb_test "print dynamic.x" "2" \ + "cbv argument 'dynamic' should not change" + +set sig "\"Inlined\:\:Inlined\\(.*Inlined const\&\\)\"" +gdb_test "print cbvInlined (inlined)" \ + "expression cannot be evaluated .* \\(maybe inlined\\?\\)" + +gdb_test "print cbvDtorDel (*dtorDel)" \ + ".* cannot be evaluated .* 'DtorDel' is not destructible" \ + "type not destructible" + +# Test that GDB calls the correct copy ctor +gdb_test "print cbvFourCCtor (fourCctor_c0v0)" "13" \ + "call cbvFourCCtor (c0v0)" +gdb_test "print fourCctor_c0v0.x" "2" \ + "cbv argument 'twoCctor_c0v0' should not change" + +gdb_test "print cbvFourCCtor (fourCctor_c1v0)" "14" \ + "call cbvFourCCtor (c1v0)" +gdb_test "print fourCctor_c1v0.x" "2" \ + "cbv argument 'twoCctor_c1v0' should not change" + +gdb_test "print cbvFourCCtor (fourCctor_c0v1)" "15" \ + "call cbvFourCCtor (c0v1)" +gdb_test "print fourCctor_c0v1.x" "2" \ + "cbv argument 'twoCctor_c0v1' should not change" + +gdb_test "print cbvFourCCtor (fourCctor_c1v1)" "16" \ + "call cbvFourCCtor (c1v1)" +gdb_test "print fourCctor_c1v1.x" "2" \ + "cbv argument 'twoCctor_c1v1' should not change" + +gdb_test "print cbvTwoMCtor (twoMctor)" \ + ".* cannot be evaluated .* 'TwoMCtor' is not copy constructible" \ + "copy ctor is implicitly deleted" + +gdb_test "print cbvTwoMCtorAndCCtor (twoMctorAndCctor)" "12" \ + "call cbvTwoMCtorAndCCtor" +gdb_test "print twoMctorAndCctor.x" "2" \ + "cbv argument 'twoMctorAndCtor' should not change" + +# Test that we get a breakpoint from the cctor during infcall and +# we can examine arguments. This is a test that the dummy frame +# of the copy constructor is set up correctly by the infcall mechanism. +set bp_location [gdb_get_line_number "ByRef-cctor"] +gdb_breakpoint $bp_location +gdb_test "print cbvArrayContainerByRef (arrayContainerByRef)" \ + ".*The program being debugged stopped.*" \ + "call cbvArrayContainerByRef with BP" +gdb_test "backtrace" [multi_line \ + "#0 ByRef\:\:ByRef .* at .*$srcfile:$bp_location" \ + "#1 .* ArrayContainerByRef::ArrayContainerByRef .*" \ + "#2 <function called from gdb>" \ + "#3 main.*"] diff --git a/gdb/testsuite/gdb.cp/pass-by-ref.cc b/gdb/testsuite/gdb.cp/pass-by-ref.cc deleted file mode 100644 index bbe450a..0000000 --- a/gdb/testsuite/gdb.cp/pass-by-ref.cc +++ /dev/null @@ -1,79 +0,0 @@ -/* This testcase is part of GDB, the GNU debugger. - - Copyright 2007-2019 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/>. */ - -class Obj { -public: - Obj (); - Obj (const Obj &); - ~Obj (); - int var[2]; -}; - -int foo (Obj arg) -{ - return arg.var[0] + arg.var[1]; -} - -Obj::Obj () -{ - var[0] = 1; - var[1] = 2; -} - -Obj::Obj (const Obj &obj) -{ - var[0] = obj.var[0]; - var[1] = obj.var[1]; -} - -Obj::~Obj () -{ - -} - -struct Derived : public Obj -{ - int other; -}; - -int blap (Derived arg) -{ - return foo (arg); -} - -struct Container -{ - Obj obj; -}; - -int blip (Container arg) -{ - return foo (arg.obj); -} - -Obj global_obj; -Derived global_derived; -Container global_container; - -int -main () -{ - int bar = foo (global_obj); - blap (global_derived); - blip (global_container); - return bar; -} diff --git a/gdb/testsuite/gdb.cp/pass-by-ref.exp b/gdb/testsuite/gdb.cp/pass-by-ref.exp index 94dd345..f44be77 100644 --- a/gdb/testsuite/gdb.cp/pass-by-ref.exp +++ b/gdb/testsuite/gdb.cp/pass-by-ref.exp @@ -14,20 +14,395 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # Check that GDB can call C++ functions whose parameters have -# object type, but are passed by reference. +# object type, and are either passed by value or implicitly by reference. +# +# Suppose F is a function that has a call-by-value parameter whose +# type is class C. When calling F with an argument A, a copy of A should +# be created and passed to F. If C is a trivially-copyable type, A can +# be copied by a straightforward memory copy. However, roughly speaking, +# if C has a user-defined copy constructor and/or a user-defined +# destructor, the copy ctor should be used to initialize the copy of A +# before calling F, and a reference to that copy is passed to F. After +# the function returns, the destructor should be called to destruct the +# copy. In this case, C is said to be a 'pass-by-reference' type. +# Determining whether C is pass-by-ref depends on +# how the copy ctor, destructor, and the move ctor of C are defined. +# First of all, C is not copy constructible if its copy constructor is +# explicitly or implicitly deleted. In this case, it would be illegal +# to pass values of type C to a function. C is pass-by-value, if all of +# its copy ctor, dtor, and move ctor are trivially defined. +# Otherwise, it is pass-by-ref. +# +# To cover the many possible combinations, this test generates classes +# that contain three special functions: +# (1) a copy constructor, +# (2) a destructor, and +# (3) a move constructor. +# A special function is in one of the following states: +# * explicit: The function is explicitly defined by the user. +# * defaultedIn: The function is defaulted inside the class decl, +# using the 'default' keyword. +# * defaultedOut: The function is declared inside the class decl, +# and defaulted outside using the 'default' keyword. +# * deleted: The function is explicitly deleted by the user, +# using the 'delete' keyword. +# * absent: The function is not declared by the user (i.e. it does not +# exist in the source. The compiler generates (or deletes) the +# definition in this case. +# +# The C++ ABI decides if a class is pass-by-value or pass-by-ref +# (i.e. trivially copyable or not) first at the language level, based +# on the state of the special functions. Then, at the target level, a +# class may be determined to be pass-by-ref because of its size +# (e.g. if it is too large to fit on registers). For this reason, this +# test generates both a small and a large version for the same +# combination of special function states. +# +# A class is not trivially-copyable if a base class or a field is not +# trivially-copyable, even though the class definition itself seems +# trivial. To test these cases, we also generate derived classes and +# container classes. +# +# The generated code is placed in the test output directory. +# +# The companion test file pass-by-ref-2.exp also contains +# manually-written cases. -if { [skip_cplus_tests] } { continue } +if {[skip_cplus_tests]} { + untested "c++ test skipped" + continue +} +# The program source is generated in the output directory. +# We use standard_testfile here to set convenience variables. standard_testfile .cc -if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} { +# Some constant values used when generating the source + +set SMALL 2 +set LARGE 150 +set ORIGINAL 2 +set CUSTOM 3 +set ADDED 4 +set TRACE 5 + + +# Return 1 if the class whose special function states are STATES +# is copyable. Otherwise return 0. + +proc is_copy_constructible { states } { + set cctor [lindex $states 0] + set dtor [lindex $states 1] + set mctor [lindex $states 2] + + if {$cctor == "deleted" || ($cctor == "absent" && $mctor != "absent")} { + return 0 + } + return 1 +} + +# Generate a declaration and an out-of-class definition for a function +# with the provided signature. The STATE should be one of the following: +# - explicit, defaultedIn, defaultedOut, deleted, absent + +proc generate_member_function { classname signature length state } { + set declaration "" + set definition "" + + global CUSTOM + global TRACE + + switch $state { + explicit { + set declaration "$signature;\n" + set definition "$classname\:\:$signature + { + data\[0\] = $CUSTOM; + data\[[expr $length - 1]\] = $CUSTOM; + tracer = $TRACE; + }\n" + } + defaultedIn { + set declaration "$signature = default;\n" + } + defaultedOut { + set declaration "$signature;\n" + set definition "$classname\:\:$signature = default;\n" + } + deleted { + set declaration "$signature = delete;\n" + } + default { + # function is not user-defined in this case + } + } + + return [list $declaration $definition] +} + +# Generate a C++ class with the given CLASSNAME and LENGTH-many +# integer elements. The STATES is an array of 3 items +# containing the desired state of the special functions +# in this order: +# copy constructor, destructor, move constructor + +proc generate_class { classname length states } { + set declarations "" + set definitions "" + set classname "${classname}_[join $states _]" + + for {set i 0} {$i < [llength $states]} {incr i} { + set sig "" + switch $i { + 0 {set sig "$classname (const $classname \&rhs)"} + 1 {set sig "\~$classname (void)"} + 2 {set sig "$classname ($classname \&\&rhs)"} + } + + set state [lindex $states $i] + set code [generate_member_function $classname $sig $length $state] + append declarations [lindex $code 0] + append definitions [lindex $code 1] + } + + global ORIGINAL + + return " + /*** C++ class $classname ***/ + class ${classname} { + public: + $classname (void); + $declarations + + int data\[$length\]; + }; + + $classname\:\:$classname (void) + { + data\[0\] = $ORIGINAL; + data\[[expr $length - 1]\] = $ORIGINAL; + } + + $definitions + + $classname ${classname}_var; /* global var */ + + template int cbv<$classname> ($classname arg);" +} + +# Generate a small C++ class + +proc generate_small_class { states } { + global SMALL + return [generate_class Small $SMALL $states]; +} + +# Generate a large C++ class + +proc generate_large_class { states } { + global LARGE + return [generate_class Large $LARGE $states]; +} + +# Generate a class that derives from a small class + +proc generate_derived_class { states } { + set base "Small_[join $states _]" + set classname "Derived_[join $states _]" + + return " + /*** Class derived from $base ***/ + class $classname : public $base { + public: + }; + + $classname ${classname}_var; /* global var */ + + template int cbv<$classname> ($classname arg);" +} + +# Generate a class that contains a small class item + +proc generate_container_class { states } { + set contained "Small_[join $states _]" + set classname "Container_[join $states _]" + + return " + /*** Class that contains $contained ***/ + class $classname { + public: + $contained item; + }; + + $classname ${classname}_var; /* global var */ + + template int cbv_container<$classname> ($classname arg);" +} + +# Generate useful statements that use a class in the debugee program + +proc generate_stmts { classprefix states {cbvfun "cbv"}} { + set classname "${classprefix}_[join $states _]" + + # Having an explicit call to the cbv function in the debugee program + # ensures that the compiler will emit necessary function in the binary. + if {[is_copy_constructible $states]} { + set cbvcall "$cbvfun<$classname> (${classname}_var);\n" + } else { + set cbvcall "" + } + + return "$cbvcall" +} + +# Generate the complete debugee program + +proc generate_program { classes stmts } { + global ADDED + + return " + /*** THIS FILE IS GENERATED BY THE TEST. ***/ + + static int tracer = 0; + + /* The call-by-value function. */ + template <class T> + int + cbv (T arg) + { + arg.data\[0\] += $ADDED; // intentionally modify the arg + return arg.data\[0\]; + } + + template <class T> + int + cbv_container (T arg) + { + arg.item.data\[0\] += $ADDED; // intentionally modify + return arg.item.data\[0\]; + } + + $classes + + int + main (void) + { + $stmts + + /* stop here */ + + return 0; + }" +} + +# Compute all the combinations of special function states. +# We do not contain the 'deleted' state for the destructor, +# because it is illegal to have stack-allocated objects +# whose destructor have been deleted. This case is covered +# in pass-by-ref-2 via heap-allocated objects. + +set options_nodelete [list absent explicit defaultedIn defaultedOut] +set options [concat $options_nodelete {deleted}] +set all_combinations {} + +foreach cctor $options { + foreach dtor $options_nodelete { + foreach mctor $options { + lappend all_combinations [list $cctor $dtor $mctor] + } + } +} + +# Generate the classes. + +set classes "" +set stmts "" + +foreach state $all_combinations { + append classes [generate_small_class $state] + append stmts [generate_stmts "Small" $state] + + append classes [generate_large_class $state] + append stmts [generate_stmts "Large" $state] + + append classes [generate_derived_class $state] + append stmts [generate_stmts "Derived" $state] + + append classes [generate_container_class $state] + append stmts [generate_stmts "Container" $state "cbv_container"] +} + +# Generate the program code and compile +set program [generate_program $classes $stmts] +set srcfile [standard_output_file ${srcfile}] +gdb_produce_source $srcfile $program + +set options {debug c++ additional_flags=-std=c++11} +if {[prepare_for_testing "failed to prepare" $testfile $srcfile $options]} { return -1 } -if ![runto_main] then { +if {![runto_main]} { + untested "failed to run to main" return -1 } -gdb_test "print foo (global_obj)" " = 3" "call function in obj" -gdb_test "print blap (global_derived)" " = 3" "call function in derived" -gdb_test "print blip (global_container)" " = 3" "call function in container" +set bp_location [gdb_get_line_number "stop here"] +gdb_breakpoint $bp_location +gdb_continue_to_breakpoint "end of main" ".*return .*;" + +# Do the checks for a given class whose name is prefixed with PREFIX, +# and whose special functions have the states given in STATES. +# The name of the call-by-value function and the expression to access +# the data field can be specified explicitly if the default values +# do not work. + +proc test_for_class { prefix states cbvfun data_field length} { + set name "${prefix}_[join $states _]" + + set cctor [lindex $states 0] + set dtor [lindex $states 1] + set mctor [lindex $states 2] + + global ORIGINAL + global CUSTOM + global ADDED + global TRACE + + with_test_prefix $name { + if {[is_copy_constructible $states]} { + set expected [expr {$ORIGINAL + $ADDED}] + if {$cctor == "explicit"} { + set expected [expr {$CUSTOM + $ADDED}] + } + if {$dtor == "explicit"} { + gdb_test "print tracer = 0" " = 0" "reset the tracer" + } + gdb_test "print ${cbvfun}<$name> (${name}_var)" " = $expected" \ + "call '$cbvfun'" + gdb_test "print ${name}_var.${data_field}\[0\]" " = $ORIGINAL" \ + "cbv argument should not change (item 0)" + if {$length > 1} { + set last_index [expr $length - 1] + gdb_test "print ${name}_var.${data_field}\[$last_index\]" \ + " = $ORIGINAL" \ + "cbv argument should not change (item $last_index)" + } + if {$dtor == "explicit"} { + gdb_test "print tracer" " = $TRACE" \ + "destructor should be called" + } + } else { + gdb_test "print ${cbvfun}<$name> (${name}_var)" \ + ".* cannot be evaluated .* '${name}' is not copy constructible" \ + "calling '$cbvfun' should be refused" + } + } +} + +foreach state $all_combinations { + test_for_class "Small" $state "cbv" "data" $SMALL + test_for_class "Large" $state "cbv" "data" $LARGE + test_for_class "Derived" $state "cbv" "data" 1 + test_for_class "Container" $state "cbv_container" "item.data" 1 +}