[review v3] testsuite, cp: increase the coverage of testing pass-by-ref arguments

Luis Machado luis.machado@linaro.org
Mon Jan 13 19:40:00 GMT 2020


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
> 



More information about the Gdb-patches mailing list