From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 14830 invoked by alias); 11 May 2010 20:38:27 -0000 Received: (qmail 14806 invoked by uid 22791); 11 May 2010 20:38:22 -0000 X-SWARE-Spam-Status: No, hits=-3.5 required=5.0 tests=AWL,BAYES_50,KAM_STOCKGEN,RCVD_IN_DNSWL_HI,SPF_HELO_PASS,TW_BJ,TW_FN,TW_RG,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 11 May 2010 20:38:13 +0000 Received: from int-mx03.intmail.prod.int.phx2.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.16]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o4BKcBc4015915 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Tue, 11 May 2010 16:38:11 -0400 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx03.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o4BKcAIH032565 for ; Tue, 11 May 2010 16:38:10 -0400 Received: from [10.15.16.55] (toner.yyz.redhat.com [10.15.16.55]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id o4BKcAmN005830 for ; Tue, 11 May 2010 16:38:10 -0400 Message-ID: <4BE9BE28.6080800@redhat.com> Date: Tue, 11 May 2010 20:38:00 -0000 From: sami wagiaalla User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.9) Gecko/20100330 Fedora/3.0.4-1.fc12 Thunderbird/3.0.4 MIME-Version: 1.0 To: gdb-patches@sourceware.org Subject: [PATCH] Test and support all cpp operator types Content-Type: multipart/mixed; boundary="------------030400010807090702050405" X-IsSubscribed: yes 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 X-SW-Source: 2010-05/txt/msg00247.txt.bz2 This is a multi-part message in MIME format. --------------030400010807090702050405 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-length: 1114 This patch adds testing and support for the following types of operator lookup: - non-member operators. - imported operators (using directive, anonymous namespaces). - ADL operators. If there are more, please let me know. It also tests for overload resolution in the presence of combination of the above. In the implementation of this I have abandoned the use of value_struct_elt and used find_overload_match instead. This eliminated the duplicate implementation of eval-time lookup and brought the benefits of overload resolution to operator evaluation. Operators have the unique situation where it is not possible to tell from the expression whether the intent is a member or non-member operator eg: class A { int operator==(int){ return 0; } } int operator==(char){ return 1; } A a; (gdb) p a == 1 'find_overload_match' search is mutually exclusive; it performs either method or non-method search if performed. So it had to be changed to support the case above; I added a new search mode which would look for both candidates and compare them to find the best result. Thanks, Sami --------------030400010807090702050405 Content-Type: text/plain; name="operators.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="operators.patch" Content-length: 22259 commit 49803a15a3df378b8441102cc5316704b6f96cb9 Author: Sami Wagiaalla Date: Tue May 11 15:39:00 2010 -0400 test and support all cpp operator types 2010-05-11 Sami Wagiaalla * value.h: Created oload_search_type enum. (find_overload_match): use oload_search_type enum. * valops.c (find_overload_match): Support combined member and non-member search. (oload_method_static): Verify index is a proper value. * valarith.c (value_user_defined_cpp_op): Search for and handle both member and non-member operators. (value_user_defined_cpp_op): New function. (value_user_defined_op): New function. (value_x_unop): Use value_user_defined_op. (value_x_binop): Ditto. * cp-support.c (make_symbol_overload_list_using): Added block iteration. Add check for namespace aliases and imported declarations. 2010-05-11 Sami Wagiaalla * gdb.cp/koenig.exp: Test for ADL operators. * gdb.cp/koenig.cc: Added ADL operators. * gdb.cp/operator.exp: New test. * gdb.cp/operator.cc: New test. diff --git a/gdb/cp-support.c b/gdb/cp-support.c index 8f447ca..87805c9 100644 --- a/gdb/cp-support.c +++ b/gdb/cp-support.c @@ -800,21 +800,27 @@ make_symbol_overload_list_using (const char *func_name, const char *namespace) { const struct using_direct *current; + const struct block *block; /* First, go through the using directives. If any of them apply, look in the appropriate namespaces for new functions to match on. */ - for (current = block_using (get_selected_block (0)); - current != NULL; - current = current->next) - { - if (strcmp (namespace, current->import_dest) == 0) - { - make_symbol_overload_list_using (func_name, - current->import_src); - } - } + for (block = get_selected_block (0); + block != NULL; + block = BLOCK_SUPERBLOCK(block)) + for (current = block_using (block); + current != NULL; + current = current->next) + { + /* If this is a namespace alias or imported declaration ignore it. */ + if (current->alias != NULL || current->declaration !=NULL) + continue; + + if (strcmp (namespace, current->import_dest) == 0) + make_symbol_overload_list_using (func_name, current->import_src); + + } /* Now, add names for this namespace. */ make_symbol_overload_list_namespace (func_name, namespace); diff --git a/gdb/testsuite/gdb.cp/koenig.cc b/gdb/testsuite/gdb.cp/koenig.cc index 6cfa3f5..3d4c7fb 100644 --- a/gdb/testsuite/gdb.cp/koenig.cc +++ b/gdb/testsuite/gdb.cp/koenig.cc @@ -175,6 +175,43 @@ typedef O::A TOA; typedef TOA TTOA; //------------ + +namespace P { + class Q{ + public: + int operator== (int){ + return 24; + } + + int operator== (float){ + return 25; + } + + int operator+ (float){ + return 26; + } + + }; + + int operator!= (Q, int){ + return 27; + } + + int operator!= (Q, double){ + return 28; + } + + int operator+ (Q, int){ + return 29; + } + + int operator++ (Q){ + return 30; + } + +} + +//------------ int main () { @@ -238,6 +275,16 @@ main () TTOA ttoa; foo (ttoa, 'a'); + P::Q q; + q == 5; + q == 5.0f; + q != 5; + q != 5.0f; + q + 5; + q + 5.0f; + + ++q; + return first (0, c) + foo (eo) + foo (eo, eo) + foo (eo, eo, 1) + foo (fo, eo) + foo (1 ,fo, eo) + diff --git a/gdb/testsuite/gdb.cp/koenig.exp b/gdb/testsuite/gdb.cp/koenig.exp index b13ffbc..37c770b 100644 --- a/gdb/testsuite/gdb.cp/koenig.exp +++ b/gdb/testsuite/gdb.cp/koenig.exp @@ -107,3 +107,22 @@ gdb_test "p M::N::bar('a')" "= 22" #test that lookup supports typedef gdb_test "p foo(ttoa, 'a')" "= 23" + +# test lookup of namespace user-defined operators +# and overload resolution: + +# within class +gdb_test "p q == 5" "= 24" +gdb_test "p q == 5.0f" "= 25" + +# within namespace +gdb_test "p q != 5" "= 27" +gdb_test "p q != 5.0f" "= 28" + +# across namespace and class +gdb_test "p q + 5.0f" "= 26" +gdb_test "p q + 5" "= 29" + +# some unary operators for good measure +# Cannot resolve function operator++ to any overloaded instance +gdb_test "p ++q" "= 30" diff --git a/gdb/testsuite/gdb.cp/operator.cc b/gdb/testsuite/gdb.cp/operator.cc new file mode 100644 index 0000000..6063eb1 --- /dev/null +++ b/gdb/testsuite/gdb.cp/operator.cc @@ -0,0 +1,148 @@ +class A {}; + +int operator== (A, int) { + return 11; +} + +int operator== (A, char) { + return 12; +} + +//------------------ + +namespace B{ + class C {}; + + int operator== (C, int) { + return 22; + } + + int operator== (C, char) { + return 23; + } +} + +//------------------ + +class D {}; +namespace { + int operator==(D,int) { + return 33; + } + + int operator==(D,char) { + return 34; + } +} + +int operator==(D,float) { + return 35; +} + +//------------------ + +class E {}; +namespace F { + int operator== (E, int){ + return 44; + } + + int operator== (E, char){ + return 45; + } +} + +int operator== (E, float){ + return 46; +} + +using namespace F; + +//----------------- + +class G { +public: + int operator== (int) { + return 55; + } +}; + +int operator== (G, char){ + return 56; +} + +//------------------ + +class H {}; +namespace I { + int operator== (H, int){ + return 66; + } +} + +namespace ALIAS = I; + +//------------------ + +class J {}; + +namespace K { + int i; + int operator== (J, int){ + return 77; + } +} + +using K::i; + +//------------------ + +class L{}; +namespace M{ + int operator== (L, int){ + return 88; + } +} + +namespace N{ + using namespace M; +} + +using namespace N; + +//------------------ + +int main () { + A a; + a == 1; + a == 'a'; + + B::C bc; + bc == 1; + bc == 'a'; + + D d; + d == 1; + d == 'a'; + d == 1.0f; + + E e; + e == 1; + e == 'a'; + e == 1.0f; + + G g; + g == 1; + g == 'a'; + + H h; + I::operator== (h,1); + + J j; + K::operator== (j,1); + + L l; + l == 1; + + return 0; +} diff --git a/gdb/testsuite/gdb.cp/operator.exp b/gdb/testsuite/gdb.cp/operator.exp new file mode 100644 index 0000000..b9bf157 --- /dev/null +++ b/gdb/testsuite/gdb.cp/operator.exp @@ -0,0 +1,73 @@ +# Copyright 2008 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 . + +if $tracelevel then { + strace $tracelevel +} + +set prms_id 0 +set bug_id 0 + +set testfile operator +set srcfile ${testfile}.cc +set binfile ${objdir}/${subdir}/${testfile} +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } { + untested "Couldn't compile test program" + return -1 +} + +# Get things started. + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +############################################ + +if ![runto_main] then { + perror "couldn't run to breakpoint main" + continue +} + +# Test global operator +gdb_test "p a == 1" "= 11" "global operator" +gdb_test "p a == 'a'" "= 12" "global operator overload" + +# Test ADL operator +gdb_test "p bc == 1" "= 22" "ADL operator" +gdb_test "p bc == 'a'" "= 23" "ADL operator overload" + +# Test operator imported from anonymous namespace +gdb_test "p d == 1" "= 33" "anonymous namespace operator" +gdb_test "p d == 'a'" "= 34" "anonymous namespace operator overload" +gdb_test "p d == 1.0f" "= 35" "anonymous namespace operator overload float" + +# Test operator imported by using directive +gdb_test "p e == 1" "= 44" "imported operator" +gdb_test "p e == 'a'" "= 45" "imported operator overload" +gdb_test "p e == 1.0f" "= 46" "imported operator overload float" + +# Test member operator +gdb_test "p g == 1" "= 55" "member operator" +gdb_test "p g == 'a'" "= 56" "member operator overload" + +# Test that operators are not wrongly imported +# by import declarations and namespace aliases +gdb_test "p h == 1" "Cannot resolve function operator== to any overloaded instance" "namespace alias" +gdb_test "p j == 1" "Cannot resolve function operator== to any overloaded instance" "imported declaration" + +# Test that indirectly imported operators work +gdb_test "p l == 1" "= 88" \ No newline at end of file diff --git a/gdb/valarith.c b/gdb/valarith.c index 4efe936..2dbcb77 100644 --- a/gdb/valarith.c +++ b/gdb/valarith.c @@ -31,6 +31,7 @@ #include "dfp.h" #include #include "infcall.h" +#include "exceptions.h" /* Define whether or not the C operator '/' truncates towards zero for differently signed operands (truncation direction is undefined in C). */ @@ -318,6 +319,67 @@ unop_user_defined_p (enum exp_opcode op, struct value *arg1) } } +/* Try to find an operator named OPERATOR which takes NARGS arguments + specified in ARGS. If the operator found is a static member operator + *STATIC_MEMFUNP will be set to 1, and otherwise 0. + The search if performed through find_overload_match which will handle + member operators, non member operators, operators imported implicitly or + explicitly, and perform correct overload resolution in all of the above + situations or combinations thereof. */ + +static struct value * +value_user_defined_cpp_op (struct value **args, int nargs, char *operator, + int *static_memfuncp) +{ + + struct symbol *symp = NULL; + struct value *valp = NULL; + struct type **arg_types; + int i; + + arg_types = (struct type **) alloca (nargs * (sizeof (struct type *))); + /* Prepare list of argument types for overload resolution */ + for (i = 0; i < nargs; i++) + arg_types[i] = value_type (args[i]); + + find_overload_match (arg_types, nargs, operator, 2 /* could be method */, + 0 /* strict match */, &args[0], /* objp */ + NULL /* pass NULL symbol since symbol is unknown */, + &valp, &symp, static_memfuncp, 0); + + if (valp) + return valp; + + if (symp) + { + /* This is a non member function and does not + expect a reference as its first argument + rather the explicit structure. */ + args[0] = value_ind (args[0]); + return value_of_variable (symp, 0); + } + + return NULL; +} + +/* Lookup user defined operator NAME. Return a value representing the + function, otherwise return NULL. */ + +static struct value * +value_user_defined_op (struct value **argp, struct value **args, char *name, + int *static_memfuncp, int nargs) +{ + struct value *result = NULL; + + if (current_language->la_language == language_cplus) + result = value_user_defined_cpp_op (args, nargs, name, static_memfuncp); + else + result = value_struct_elt (argp, args, name, static_memfuncp, + "structure"); + + return result; +} + /* We know either arg1 or arg2 is a structure, so try to find the right user defined function. Create an argument vector that calls arg1.operator @ (arg1,arg2) and return that value (where '@' is any @@ -458,7 +520,8 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op, error (_("Invalid binary operation specified.")); } - argvec[0] = value_struct_elt (&arg1, argvec + 1, tstr, &static_memfuncp, "structure"); + argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr, + &static_memfuncp, 2); if (argvec[0]) { @@ -555,7 +618,8 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside) error (_("Invalid unary operation specified.")); } - argvec[0] = value_struct_elt (&arg1, argvec + 1, tstr, &static_memfuncp, "structure"); + argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr, + &static_memfuncp, nargs); if (argvec[0]) { diff --git a/gdb/valops.c b/gdb/valops.c index 6f5f684..4d38128 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -2266,7 +2266,6 @@ value_find_oload_method_list (struct value **argp, const char *method, struct type *t; t = check_typedef (value_type (*argp)); - /* Code snarfed from value_struct_elt. */ while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF) { @@ -2293,6 +2292,16 @@ value_find_oload_method_list (struct value **argp, const char *method, matches on the argument types according to the overload resolution rules. + METHOD can be one of three valuse: + NON_METHOD for non-member functions. + METHOD: for member functions. + BOTH: used for overload resolution of operators where the + candidates are expected to be either member or non member + functions. In this case the first argument ARGTYPES + (representing 'this') is expected to be a reference to the + target object, and will be dereferenced when attempting the + non-member search. + In the case of class methods, the parameter OBJ is an object value in which to search for overloaded methods. @@ -2320,16 +2329,21 @@ value_find_oload_method_list (struct value **argp, const char *method, int find_overload_match (struct type **arg_types, int nargs, - const char *name, int method, int lax, - struct value **objp, struct symbol *fsym, + const char *name, enum oload_search_type method, + int lax, struct value **objp, struct symbol *fsym, struct value **valp, struct symbol **symp, int *staticp, const int no_adl) { struct value *obj = (objp ? *objp : NULL); + /* Index of best overloaded function. */ - int oload_champ; + int func_oload_champ = -1; + int method_oload_champ = -1; + /* The measure for the current best match. */ - struct badness_vector *oload_champ_bv = NULL; + struct badness_vector *method_badness = NULL; + struct badness_vector *func_badness = NULL; + struct value *temp = obj; /* For methods, the list of overloaded methods. */ struct fn_field *fns_ptr = NULL; @@ -2345,9 +2359,11 @@ find_overload_match (struct type **arg_types, int nargs, const char *obj_type_name = NULL; const char *func_name = NULL; enum oload_classification match_quality; + enum oload_classification method_match_quality = INCOMPATIBLE; + enum oload_classification func_match_quality = INCOMPATIBLE; /* Get the list of overloaded methods or functions. */ - if (method) + if (method == METHOD || method == BOTH) { gdb_assert (obj); @@ -2370,26 +2386,50 @@ find_overload_match (struct type **arg_types, int nargs, } } + /* Retrieve the list of methods with the name NAME. */ fns_ptr = value_find_oload_method_list (&temp, name, 0, &num_fns, &basetype, &boffset); - if (!fns_ptr || !num_fns) + + /* If this is a method only search, and no methods were found + the search has faild. */ + if (method == METHOD && (!fns_ptr || !num_fns)) error (_("Couldn't find method %s%s%s"), obj_type_name, (obj_type_name && *obj_type_name) ? "::" : "", name); + /* If we are dealing with stub method types, they should have been resolved by find_method_list via value_find_oload_method_list above. */ - gdb_assert (TYPE_DOMAIN_TYPE (fns_ptr[0].type) != NULL); - oload_champ = find_oload_champ (arg_types, nargs, method, - num_fns, fns_ptr, - oload_syms, &oload_champ_bv); + if (fns_ptr) + { + gdb_assert (TYPE_DOMAIN_TYPE (fns_ptr[0].type) != NULL); + method_oload_champ = find_oload_champ (arg_types, nargs, method, + num_fns, fns_ptr, + oload_syms, &method_badness); + + method_match_quality = + classify_oload_match (method_badness, nargs, + oload_method_static (method, fns_ptr, + method_oload_champ)); + + make_cleanup (xfree, method_badness); + } + } - else + + if (method == NON_METHOD || method == BOTH) { + const char *qualified_name = NULL; + /* If the the overload match is being search for both + as a method and non member function, the first argument + must now be dereferenced. */ + if (method == BOTH) + arg_types[0] = TYPE_TARGET_TYPE (arg_types[0]); + if (fsym) { qualified_name = SYMBOL_NATURAL_NAME (fsym); @@ -2432,30 +2472,67 @@ find_overload_match (struct type **arg_types, int nargs, return 0; } - make_cleanup (xfree, oload_syms); - make_cleanup (xfree, oload_champ_bv); + func_oload_champ = find_oload_champ_namespace (arg_types, nargs, + func_name, + qualified_name, + &oload_syms, + &func_badness, + no_adl); + + if (func_oload_champ >= 0) + func_match_quality = classify_oload_match (func_badness, nargs, 0); - oload_champ = find_oload_champ_namespace (arg_types, nargs, - func_name, - qualified_name, - &oload_syms, - &oload_champ_bv, - no_adl); + make_cleanup (xfree, oload_syms); + make_cleanup (xfree, func_badness); } /* Did we find a match ? */ - if (oload_champ == -1) + if (method_oload_champ == -1 && func_oload_champ == -1) error (_("No symbol \"%s\" in current context."), name); - /* Check how bad the best match is. */ - match_quality = - classify_oload_match (oload_champ_bv, nargs, - oload_method_static (method, fns_ptr, - oload_champ)); + /* If we have found both a method match and a function + match, find out which one is better, and calculate match + quality. */ + if (method_oload_champ >= 0 && func_oload_champ >= 0) + { + switch (compare_badness (func_badness, method_badness)) + { + case 0: /* Top two contenders are equally good. */ + /* FIXME: GDB does not support the general ambiguous + case. All candidates should be collected and presented + the the user. */ + error (_("Ambiguous overload resolution")); + break; + case 1: /* Incomparable top contenders. */ + /* This is an error incompatible candidates + should not have been proposed. */ + error (_("Internal error: incompatible overload candidates proposed")); + break; + case 2: /* Function champion. */ + method_oload_champ = -1; + match_quality = func_match_quality; + break; + case 3: /* Method champion. */ + func_oload_champ = -1; + match_quality = method_match_quality; + break; + default: + error (_("Internal error: unexpected overload comparison result")); + break; + } + } + else + { + /* We have either a method match or a function match. */ + if (method_oload_champ >= 0) + match_quality = method_match_quality; + else + match_quality = func_match_quality; + } if (match_quality == INCOMPATIBLE) { - if (method) + if (method == METHOD) error (_("Cannot resolve method %s%s%s to any overloaded instance"), obj_type_name, (obj_type_name && *obj_type_name) ? "::" : "", @@ -2466,7 +2543,7 @@ find_overload_match (struct type **arg_types, int nargs, } else if (match_quality == NON_STANDARD) { - if (method) + if (method == METHOD) warning (_("Using non-standard conversion to match method %s%s%s to supplied arguments"), obj_type_name, (obj_type_name && *obj_type_name) ? "::" : "", @@ -2476,20 +2553,21 @@ find_overload_match (struct type **arg_types, int nargs, func_name); } - if (method) + if (staticp != NULL) + *staticp = oload_method_static (method, fns_ptr, method_oload_champ); + + if (method_oload_champ >= 0) { - if (staticp != NULL) - *staticp = oload_method_static (method, fns_ptr, oload_champ); - if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, oload_champ)) - *valp = value_virtual_fn_field (&temp, fns_ptr, oload_champ, + if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, method_oload_champ)) + *valp = value_virtual_fn_field (&temp, fns_ptr, method_oload_champ, basetype, boffset); else - *valp = value_fn_field (&temp, fns_ptr, oload_champ, + *valp = value_fn_field (&temp, fns_ptr, method_oload_champ, basetype, boffset); } else { - *symp = oload_syms[oload_champ]; + *symp = oload_syms[func_oload_champ]; } if (objp) @@ -2778,7 +2856,8 @@ find_oload_champ (struct type **arg_types, int nargs, int method, static int oload_method_static (int method, struct fn_field *fns_ptr, int index) { - if (method && TYPE_FN_FIELD_STATIC_P (fns_ptr, index)) + if (method && fns_ptr && index >= 0 + && TYPE_FN_FIELD_STATIC_P (fns_ptr, index)) return 1; else return 0; diff --git a/gdb/value.h b/gdb/value.h index 57b4dd7..a7ed135 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -447,8 +447,10 @@ extern struct fn_field *value_find_oload_method_list (struct value **, int, int *, struct type **, int *); +enum oload_search_type { NON_METHOD, METHOD, BOTH }; extern int find_overload_match (struct type **arg_types, int nargs, - const char *name, int method, int lax, + const char *name, + enum oload_search_type method, int lax, struct value **objp, struct symbol *fsym, struct value **valp, struct symbol **symp, int *staticp, const int no_adl); --------------030400010807090702050405--