diff options
-rw-r--r-- | gdb/testsuite/ChangeLog | 8 | ||||
-rw-r--r-- | gdb/testsuite/gdb.cp/pass-by-ref-2.cc | 295 | ||||
-rw-r--r-- | gdb/testsuite/gdb.cp/pass-by-ref-2.exp | 114 | ||||
-rw-r--r-- | gdb/testsuite/gdb.cp/pass-by-ref.cc | 79 | ||||
-rw-r--r-- | gdb/testsuite/gdb.cp/pass-by-ref.exp | 389 |
5 files changed, 799 insertions, 86 deletions
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 4476549..c93edc6 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2019-12-20 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. + 2019-12-20 Tom Tromey <tom@tromey.com> * gdb.tui/list-before.exp: New file. 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 +} |