From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 44939 invoked by alias); 13 Jan 2020 18:21:35 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 44926 invoked by uid 89); 13 Jan 2020 18:21:35 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-22.9 required=5.0 tests=AWL,BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_SHORT,RCVD_IN_DNSWL_NONE,SPAM_BODY,SPF_PASS autolearn=ham version=3.3.1 spammy=STATES, refused, tankutbarisaktemurintelcom, U*tankut.baris.aktemur X-HELO: mail-qv1-f49.google.com Received: from mail-qv1-f49.google.com (HELO mail-qv1-f49.google.com) (209.85.219.49) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 13 Jan 2020 18:21:30 +0000 Received: by mail-qv1-f49.google.com with SMTP id dp13so4428691qvb.7 for ; Mon, 13 Jan 2020 10:21:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=subject:to:references:from:message-id:date:user-agent:mime-version :in-reply-to:content-language:content-transfer-encoding; bh=azvuiKZKzknsKby6O1X+Zp+4kd5ykUy8CKyEReFXJCU=; b=I4CCeE5UTLCjFfvzUeRmrkH7CicyJuI+EvKZ4ORfGETNHwfefOcafnN/SgPO33HbT5 IqB8kytH0/GaobxHlXGgetMg5BhvY7wuqGXaTVCZLp1KWYCx37QGGyZDAO3f2y1mnZ1n hY+927aIEo6AycH2VfNa3IAm/z9MjgQZhGu3Fe3WkbV17h9SamuFKKlc44te8cxG+IUy DCDYuLachLTe7rHdOUZhyYM6uUEFqFpLKS+g5+NcwaXeNq2brAMvwzTuYorzagkY/Qy4 nsXdf95GiCb0CmGYzuVW6pZmiMKrPppVUKbTAKu+McMSB1Vr2sd/XSNQpCICj4mr0whO sHjA== Return-Path: Received: from [192.168.0.185] ([179.183.9.215]) by smtp.gmail.com with ESMTPSA id g81sm5363893qkb.70.2020.01.13.10.21.25 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 13 Jan 2020 10:21:28 -0800 (PST) Subject: Re: [review v3] testsuite, cp: increase the coverage of testing pass-by-ref arguments To: tankut.baris.aktemur@intel.com, tromey@sourceware.org, gdb-patches@sourceware.org, "Tankut Baris Aktemur (Code Review)" References: <20191214095325.3864520AF6@gnutoolchain-gerrit.osci.io> From: Luis Machado Message-ID: <80c8502f-a350-6a2b-0b72-bbc903f53bc3@linaro.org> Date: Mon, 13 Jan 2020 18:58:00 -0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.2.2 MIME-Version: 1.0 In-Reply-To: <20191214095325.3864520AF6@gnutoolchain-gerrit.osci.io> Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit X-IsSubscribed: yes X-SW-Source: 2020-01/txt/msg00342.txt.bz2 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 > > * 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 . */ > + > +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 . > + > +# 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 " \ > + "#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 . */ > - > -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 . > > # 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 > + int > + cbv (T arg) > + { > + arg.data\[0\] += $ADDED; // intentionally modify the arg > + return arg.data\[0\]; > + } > + > + template > + 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 > +} >