--- /dev/null
+/* 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;
+}
--- /dev/null
+# 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.*"]
# 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
+}