This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[C++ PATCH]: Bug 1617
- To: gcc-patches at gcc dot gnu dot org
- Subject: [C++ PATCH]: Bug 1617
- From: Nathan Sidwell <nathan at codesourcery dot com>
- Date: Fri, 19 Jan 2001 14:30:28 +0000
- CC: Martin Sebor <sebor at roguewave dot com>, mark at codesourcery dot com
- Organization: Codesourcery LLC
Hi,
This is a patch for bug 1617.
That bug report relates to DR 214
(http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#214). Which talks
about the underspecified nature of partial template function ordering.
Martin Sebor has been kind enough to compare various compilers with the
attached sebor.C and sebor.sh script. His results are
+-------------+---+---+---+---++---+---++---+---+
|call --> | a | b | c | d || e | f || g | h |
+=============+===+===+===+===++===+===++===+===+
|EDG eccp 2.44| 1 | 1 | 3 | 2 || 6 | 6 || 7 | 7 |
+-------------+---+---+---+---++---+---++---+---+
|gcc 2.95.2 | E | E | 3 | 2 || E | E || 7 | 7 |
+-------------+---+---+---+---++---+---++---+---+
|HP aCC 3.26 | E | E | E | 2 || E | E || 7 | 7 |
+-------------+---+---+---+---++---+---++---+---+
|IBM xlC 5.0 | E | E | 3 | 2 || E | E || 7 | 7 |
+-------------+---+---+---+---++---+---++---+---+
|MSVC 6.0 | E | E | E | 2 || 6 | 6 || E | E |
+-------------+---+---+---+---++---+---++---+---+
|SunPro 5.2 | 2 | 2 | 3 | 2 || 5 | 5 || 7 | 7 |
+-------------+---+---+---+---++---+---++---+---+
+-------------+---+---+---+---++---+---++---+---+
|patch * | 1 | 1 | 3 | 2 || 4 | E || 7 | 7 |
+-------------+---+---+---+---++---+---++---+---+
|gedanken * | 1 | 1 | 3 | 2 || 4 | E || 7 | 7 |
+-------------+---+---+---+---++---+---++---+---+
The interesting cases are a, b, e, f
The final two rows are my additions. The first `patch' is the results
after this patch is applied. The second is my thought experiment on the
examples. If I understand Martin's email (archived in gnats), he also
agrees with that row. The columns e and f where we differ from EDG
concern
template <typename T> int bar (T const* const&); //4
template <typename T> int bar (T* const&); //5
template <typename T> int bar (T*); //6
bar ((int const *)0); // e
bar ((int *)0); // f
For 'e', the choices of the three templates are `int', `int const' and `int const'
The first is more specialized, so should be selected.
For 'f', the choice is `int' for each template, however the first template
involves a qualification conversion, so overload resolution prefers the second
and third. They are unordered, in exactly the same way as `T' and `T const &'
is unordered.
I'd obviously like this patch in, as it makes 'T *' more specialized than 'T &'
for any pointer type 'S *'. It is safe in that it only serves to disambiguate
currently ambigous templates (cannot make a well formed program ill formed). It
is dangerous because it may be accepting ill-formed programs.
nathan
--
Dr Nathan Sidwell :: http://www.codesourcery.com :: CodeSourcery LLC
'But that's a lie.' - 'Yes it is. What's your point?'
nathan@codesourcery.com : http://www.cs.bris.ac.uk/~nathan/ : nathan@acm.org
2001-01-19 Nathan Sidwell <nathan@codesourcery.com>
* cp-tree.h (unification_kind_t): Add DEDUCE_ORDER.
* pt.c (UNIFY_ALLOW_OUTER_MORE_CV_QUAL,
UNIFY_ALLOW_OUTER_LESS_CV_QUAL): New unify flags.
(maybe_adjust_types_for_deduction): Return extra unify flags. Do
REFERENCE_TYPE jig for DEDUCE_ORDER.
(type_unification_real): Deal with DEDUCE_ORDER. Use result of
maybe_adjust_types_for_deduction.
(try_one_overload): Use result of maybe_adjust_types_for_deduction.
(check_cv_quals_for_unify): Use new unify qualifier flags.
(unify): Clear new unify qualifier flags.
(get_bindings_real): Use LEN parameter to distinguish EXACT or
ORDERing deduction.
(most_specialized_instantiation): Specify large number of
parameters to more_specialized.
Index: cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/cp-tree.h,v
retrieving revision 1.564
diff -c -3 -p -r1.564 cp-tree.h
*** cp-tree.h 2001/01/19 09:24:11 1.564
--- cp-tree.h 2001/01/19 14:06:38
*************** extern int function_depth;
*** 3209,3222 ****
/* in pt.c */
! /* These values are used for the `STRICT' parameter to type_unfication and
fn_type_unification. Their meanings are described with the
documentation for fn_type_unification. */
typedef enum unification_kind_t {
DEDUCE_CALL,
DEDUCE_CONV,
! DEDUCE_EXACT
} unification_kind_t;
/* Macros for operating on a template instantation level node, represented
--- 3209,3223 ----
/* in pt.c */
! /* These values are used for the `STRICT' parameter to type_unification and
fn_type_unification. Their meanings are described with the
documentation for fn_type_unification. */
typedef enum unification_kind_t {
DEDUCE_CALL,
DEDUCE_CONV,
! DEDUCE_EXACT,
! DEDUCE_ORDER
} unification_kind_t;
/* Macros for operating on a template instantation level node, represented
Index: cp/pt.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/pt.c,v
retrieving revision 1.508
diff -c -3 -p -r1.508 pt.c
*** pt.c 2001/01/19 09:24:14 1.508
--- pt.c 2001/01/19 14:06:41
*************** static htab_t local_specializations;
*** 87,92 ****
--- 87,94 ----
#define UNIFY_ALLOW_DERIVED 4
#define UNIFY_ALLOW_INTEGER 8
#define UNIFY_ALLOW_OUTER_LEVEL 16
+ #define UNIFY_ALLOW_OUTER_MORE_CV_QUAL 32
+ #define UNIFY_ALLOW_OUTER_LESS_CV_QUAL 64
#define GTB_VIA_VIRTUAL 1 /* The base class we are examining is
virtual, or a base class of a virtual
*************** static tree coerce_template_parms PARAMS
*** 111,117 ****
static void tsubst_enum PARAMS ((tree, tree, tree));
static tree add_to_template_args PARAMS ((tree, tree));
static tree add_outermost_template_args PARAMS ((tree, tree));
! static void maybe_adjust_types_for_deduction PARAMS ((unification_kind_t, tree*,
tree*));
static int type_unification_real PARAMS ((tree, tree, tree, tree,
int, unification_kind_t, int, int));
--- 113,119 ----
static void tsubst_enum PARAMS ((tree, tree, tree));
static tree add_to_template_args PARAMS ((tree, tree));
static tree add_outermost_template_args PARAMS ((tree, tree));
! static int maybe_adjust_types_for_deduction PARAMS ((unification_kind_t, tree*,
tree*));
static int type_unification_real PARAMS ((tree, tree, tree, tree,
int, unification_kind_t, int, int));
*************** instantiate_template (tmpl, targ_ptr)
*** 7681,7693 ****
[temp.deduct.conv].
DEDUCE_EXACT:
We are deducing arguments when calculating the partial
ordering between specializations of function or class
! templates, as in [temp.func.order] and [temp.class.order],
! when doing an explicit instantiation as in [temp.explicit],
! when determining an explicit specialization as in
! [temp.expl.spec], or when taking the address of a function
! template, as in [temp.deduct.funcaddr].
LEN is the number of parms to consider before returning success, or -1
for all. This is used in partial ordering to avoid comparing parms for
--- 7683,7697 ----
[temp.deduct.conv].
DEDUCE_EXACT:
+ We are deducing arguments when doing an explicit instantiation
+ as in [temp.explicit], when determining an explicit specialization
+ as in [temp.expl.spec], or when taking the address of a function
+ template, as in [temp.deduct.funcaddr].
+
+ DEDUCE_ORDER:
We are deducing arguments when calculating the partial
ordering between specializations of function or class
! templates, as in [temp.func.order] and [temp.class.order].
LEN is the number of parms to consider before returning success, or -1
for all. This is used in partial ordering to avoid comparing parms for
*************** fn_type_unification (fn, explicit_targs,
*** 7793,7804 ****
the argument passed to the call, or the type of the value
initialized with the result of the conversion function. */
! static void
maybe_adjust_types_for_deduction (strict, parm, arg)
unification_kind_t strict;
tree* parm;
tree* arg;
{
switch (strict)
{
case DEDUCE_CALL:
--- 7797,7810 ----
the argument passed to the call, or the type of the value
initialized with the result of the conversion function. */
! static int
maybe_adjust_types_for_deduction (strict, parm, arg)
unification_kind_t strict;
tree* parm;
tree* arg;
{
+ int result = 0;
+
switch (strict)
{
case DEDUCE_CALL:
*************** maybe_adjust_types_for_deduction (strict
*** 7817,7824 ****
case DEDUCE_EXACT:
/* There is nothing to do in this case. */
! return;
default:
my_friendly_abort (0);
}
--- 7823,7865 ----
case DEDUCE_EXACT:
/* There is nothing to do in this case. */
! return 0;
+ case DEDUCE_ORDER:
+ /* DR 214. [temp.func.order] is underspecified, and leads to no
+ ordering between things like `T *' and `T const &' for `U *'.
+ The former has T=U and the latter T=U*. The former looks more
+ specialized and John Spicer considers it well-formed (the EDG
+ compiler accepts it).
+
+ John also confirms that deduction should proceed as in a function
+ call. Which implies the usual ARG and PARM bashing as DEDUCE_CALL.
+ However, in ordering, ARG can have REFERENCE_TYPE, but no argument
+ to an actual call can have such a type.
+
+ When deducing against a REFERENCE_TYPE, we can either not do any PARM
+ pashing, or we can bash the ARG too. The latter, though seemingly
+ more safe, turns out to give the following quirk. Consider
+ deducing a call to a `const int *' with the following template
+ function parameters
+ #1; T const *const & ; T = int
+ #2; T *const & ; T = const int
+ #3; T * ; T = const int
+ It looks like #1 is the more specialized. Taken pairwise, #1 is
+ more specialized than #2 and #2 is more specialized than #3, yet
+ there is no ordering between #1 and #3.
+
+ So we bash a reference ARG too, if the PARM is not a reference. If
+ both are REFERENCE_TYPE, we don't bash them. */
+ if (TREE_CODE (*arg) == REFERENCE_TYPE)
+ {
+ if (TREE_CODE (*parm) == REFERENCE_TYPE)
+ return 0;
+ *arg = TREE_TYPE (*arg);
+ result |= UNIFY_ALLOW_OUTER_LESS_CV_QUAL;
+ goto skip_arg;
+ }
+ break;
default:
my_friendly_abort (0);
}
*************** maybe_adjust_types_for_deduction (strict
*** 7849,7854 ****
--- 7890,7896 ----
*arg = TYPE_MAIN_VARIANT (*arg);
}
+ skip_arg:;
/* [temp.deduct.call]
If P is a cv-qualified type, the top level cv-qualifiers
*************** maybe_adjust_types_for_deduction (strict
*** 7857,7863 ****
type deduction. */
*parm = TYPE_MAIN_VARIANT (*parm);
if (TREE_CODE (*parm) == REFERENCE_TYPE)
! *parm = TREE_TYPE (*parm);
}
/* Most parms like fn_type_unification.
--- 7899,7909 ----
type deduction. */
*parm = TYPE_MAIN_VARIANT (*parm);
if (TREE_CODE (*parm) == REFERENCE_TYPE)
! {
! *parm = TREE_TYPE (*parm);
! result |= UNIFY_ALLOW_OUTER_MORE_CV_QUAL;
! }
! return result;
}
/* Most parms like fn_type_unification.
*************** type_unification_real (tparms, targs, pa
*** 7902,7907 ****
--- 7948,7957 ----
case DEDUCE_EXACT:
sub_strict = UNIFY_ALLOW_NONE;
break;
+
+ case DEDUCE_ORDER:
+ sub_strict = UNIFY_ALLOW_NONE;
+ break;
default:
my_friendly_abort (0);
*************** type_unification_real (tparms, targs, pa
*** 7943,7949 ****
arg = NULL_TREE;
}
! if (strict == DEDUCE_EXACT)
{
if (same_type_p (parm, type))
continue;
--- 7993,7999 ----
arg = NULL_TREE;
}
! if (strict == DEDUCE_EXACT || strict == DEDUCE_ORDER)
{
if (same_type_p (parm, type))
continue;
*************** type_unification_real (tparms, targs, pa
*** 7976,7987 ****
}
arg = TREE_TYPE (arg);
}
!
! if (!subr)
! maybe_adjust_types_for_deduction (strict, &parm, &arg);
! if (unify (tparms, targs, parm, arg, sub_strict))
! return 1;
/* Are we done with the interesting parms? */
if (--len == 0)
--- 8026,8041 ----
}
arg = TREE_TYPE (arg);
}
!
! {
! int arg_strict = sub_strict;
!
! if (!subr)
! arg_strict |= maybe_adjust_types_for_deduction (strict, &parm, &arg);
! if (unify (tparms, targs, parm, arg, arg_strict))
! return 1;
! }
/* Are we done with the interesting parms? */
if (--len == 0)
*************** try_one_overload (tparms, orig_targs, ta
*** 8129,8135 ****
if (uses_template_parms (arg))
return 1;
! maybe_adjust_types_for_deduction (strict, &parm, &arg);
/* We don't copy orig_targs for this because if we have already deduced
some template args from previous args, unify would complain when we
--- 8183,8189 ----
if (uses_template_parms (arg))
return 1;
! sub_strict |= maybe_adjust_types_for_deduction (strict, &parm, &arg);
/* We don't copy orig_targs for this because if we have already deduced
some template args from previous args, unify would complain when we
*************** check_cv_quals_for_unify (strict, arg, p
*** 8413,8423 ****
tree arg;
tree parm;
{
! if (!(strict & UNIFY_ALLOW_MORE_CV_QUAL)
&& !at_least_as_qualified_p (arg, parm))
return 0;
! if (!(strict & UNIFY_ALLOW_LESS_CV_QUAL)
&& !at_least_as_qualified_p (parm, arg))
return 0;
--- 8467,8477 ----
tree arg;
tree parm;
{
! if (!(strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
&& !at_least_as_qualified_p (arg, parm))
return 0;
! if (!(strict & (UNIFY_ALLOW_LESS_CV_QUAL | UNIFY_ALLOW_OUTER_LESS_CV_QUAL))
&& !at_least_as_qualified_p (parm, arg))
return 0;
*************** check_cv_quals_for_unify (strict, arg, p
*** 8448,8454 ****
have const qualified pointers leading up to the inner type which
requires additional CV quals, except at the outer level, where const
is not required [conv.qual]. It would be normal to set this flag in
! addition to setting UNIFY_ALLOW_MORE_CV_QUAL. */
static int
unify (tparms, targs, parm, arg, strict)
--- 8502,8514 ----
have const qualified pointers leading up to the inner type which
requires additional CV quals, except at the outer level, where const
is not required [conv.qual]. It would be normal to set this flag in
! addition to setting UNIFY_ALLOW_MORE_CV_QUAL.
! UNIFY_ALLOW_OUTER_MORE_CV_QUAL:
! This is the outermost level of a deduction, and PARM can be more CV
! qualified at this point.
! UNIFY_ALLOW_OUTER_LESS_CV_QUAL:
! This is the outermost level of a deduction, and PARM can be less CV
! qualified at this point. */
static int
unify (tparms, targs, parm, arg, strict)
*************** unify (tparms, targs, parm, arg, strict)
*** 8498,8503 ****
--- 8558,8565 ----
strict &= ~UNIFY_ALLOW_MORE_CV_QUAL;
strict &= ~UNIFY_ALLOW_OUTER_LEVEL;
strict &= ~UNIFY_ALLOW_DERIVED;
+ strict &= ~UNIFY_ALLOW_OUTER_MORE_CV_QUAL;
+ strict &= ~UNIFY_ALLOW_OUTER_LESS_CV_QUAL;
switch (TREE_CODE (parm))
{
*************** more_specialized_class (pat1, pat2)
*** 9018,9024 ****
DECL from the function template FN, with the explicit template
arguments EXPLICIT_ARGS. If CHECK_RETTYPE is 1, the return type must
also match. Return NULL_TREE if no satisfactory arguments could be
! found. LEN is passed through to fn_type_unification. */
static tree
get_bindings_real (fn, decl, explicit_args, check_rettype, len)
--- 9080,9087 ----
DECL from the function template FN, with the explicit template
arguments EXPLICIT_ARGS. If CHECK_RETTYPE is 1, the return type must
also match. Return NULL_TREE if no satisfactory arguments could be
! found. LEN determines if we are doing exact deduction (<0) or
! ordering deduction (>=0), and is passed through to fn_type_unification. */
static tree
get_bindings_real (fn, decl, explicit_args, check_rettype, len)
*************** get_bindings_real (fn, decl, explicit_ar
*** 9069,9075 ****
decl_arg_types,
(check_rettype || DECL_CONV_FN_P (fn)
? TREE_TYPE (decl_type) : NULL_TREE),
! DEDUCE_EXACT, len);
if (i != 0)
return NULL_TREE;
--- 9132,9138 ----
decl_arg_types,
(check_rettype || DECL_CONV_FN_P (fn)
? TREE_TYPE (decl_type) : NULL_TREE),
! len >= 0 ? DEDUCE_ORDER : DEDUCE_EXACT, len);
if (i != 0)
return NULL_TREE;
*************** most_specialized_instantiation (instanti
*** 9162,9168 ****
champ = instantiations;
for (fn = TREE_CHAIN (instantiations); fn; fn = TREE_CHAIN (fn))
{
! fate = more_specialized (TREE_VALUE (champ), TREE_VALUE (fn), -1);
if (fate == 1)
;
else
--- 9225,9231 ----
champ = instantiations;
for (fn = TREE_CHAIN (instantiations); fn; fn = TREE_CHAIN (fn))
{
! fate = more_specialized (TREE_VALUE (champ), TREE_VALUE (fn), 99999);
if (fate == 1)
;
else
*************** most_specialized_instantiation (instanti
*** 9179,9185 ****
for (fn = instantiations; fn && fn != champ; fn = TREE_CHAIN (fn))
{
! fate = more_specialized (TREE_VALUE (champ), TREE_VALUE (fn), -1);
if (fate != 1)
return error_mark_node;
}
--- 9242,9248 ----
for (fn = instantiations; fn && fn != champ; fn = TREE_CHAIN (fn))
{
! fate = more_specialized (TREE_VALUE (champ), TREE_VALUE (fn), 99999);
if (fate != 1)
return error_mark_node;
}
// Copyright (C) 2000 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 18 Jan 2001 <nathan@codesourcery.com>
// Bug 1617. We didn't resolve partial ordering properly. The std is rather
// vague about it anyway, DR 214 talks about this.
extern "C" int puts (char const *);
template <typename T> int Foo (T *) {puts (__PRETTY_FUNCTION__); return 1;}
template <typename T> int Foo (T &) {puts (__PRETTY_FUNCTION__); return 2;}
template <typename T> int Foo (T const &) {puts (__PRETTY_FUNCTION__); return 3;}
template <typename T> int Bar (T const *const &) {puts (__PRETTY_FUNCTION__); return 4;}
template <typename T> int Bar (T *const &) {puts (__PRETTY_FUNCTION__); return 5;}
template <typename T> int Bar (T *) {puts (__PRETTY_FUNCTION__); return 6;}
template <typename T> int Quux (T *const &) {puts (__PRETTY_FUNCTION__); return 7;}
template <typename T> int Quux (T const &) {puts (__PRETTY_FUNCTION__); return 8;}
int Baz (int const *ptr, int *ptr2)
{
if (Foo (ptr) != 1)
return 1;
if (Foo (ptr2) != 1)
return 2;
if (Foo (*ptr) != 3)
return 3;
if (Foo (*ptr2) != 2)
return 4;
if (Bar (ptr) != 4)
return 5;
if (Quux (ptr) != 7)
return 5;
if (Quux (ptr2) != 7)
return 6;
return 0;
}
int main ()
{
return Baz (0, 0);
}
// Build don't link:
//
// Copyright (C) 2000 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 18 Jan 2001 <nathan@codesourcery.com>
// Bug 1617. We didn't resolve partial ordering properly. The std is rather
// vague about it anyway, DR 214 talks about this.
extern "C" int puts (char const *);
template <typename T> int Foo (T); // ERROR - candidate
template <typename T> int Foo (T &); // ERROR - candidate
template <typename T> int Qux (T); // ERROR - candidate
template <typename T> int Qux (T const &); // ERROR - candidate
template <typename T> int Bar (T const *const &); // ERROR - candidate
template <typename T> int Bar (T *const &); // ERROR - candidate
template <typename T> int Bar (T *); // ERROR - candidate
template <typename T> int Baz (T *const &); // ERROR - candidate
template <typename T> int Baz (T *); // ERROR - candidate
int Baz (int const *ptr, int *ptr2)
{
Baz (ptr2); // ERROR - ambiguous
Bar (ptr2); // ERROR - ambiguous
Foo (ptr2); // ERROR - ambiguous
Qux (ptr2); // ERROR - ambiguous
return 0;
}
extern "C" int puts (const char*);
#define TEMPLATE(name, arg, n) \
template <class T> const char* name (arg) { return #name "(" #arg ") #" #n; }
#if defined (a) || defined (b) || defined (c) || defined (d)
TEMPLATE (foo, T*, 1)
TEMPLATE (foo, T&, 2)
TEMPLATE (foo, T const&, 3)
#endif
#if defined (e) || defined (f)
TEMPLATE (bar, T const* const&, 4)
TEMPLATE (bar, T* const&, 5)
TEMPLATE (bar, T*, 6)
#endif
#if defined (g) || defined (h)
TEMPLATE (quux, T* const&, 7)
TEMPLATE (quux, T const&, 8)
#endif
void baz (int const *ptr, int *ptr2)
{
#ifdef a
puts (foo (ptr));
#endif
#ifdef b
puts (foo (ptr2));
#endif
#ifdef c
puts (foo (*ptr));
#endif
#ifdef d
puts (foo (*ptr2));
#endif
#ifdef e
puts (bar (ptr));
#endif
#ifdef f
puts (bar (ptr2));
#endif
#ifdef g
puts (quux (ptr));
#endif
#ifdef h
puts (quux (ptr2));
#endif
}
int main ()
{
baz (0, 0);
}
sebor.sh