This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[C++ PATCH] Fix bug 4672 (possible defect)


Hi,
This fixes bug 4672, which turns out to be what I think is a defect
in partial template function ordering.  I've attached the defect
report which explains the problem, and which I will be submitting.

This patch implements option 3.

built & tested on i686-pc-linux-gnu
One possibility would be to commit this but disable the final
check. comments?

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-12-30  Nathan Sidwell  <nathan@codesourcery.com>

	PR c++/4672
	* call.c (tourney): Add EXPLICIT_TARGS parameter. Adjust.
	(joust): Likewise.
	(build_over_call): Likewise.
	(convert_class_to_reference): Adjust.
	(build_user_type_conversion_1): Adjust.
	(build_new_function_call): Adjust.
	(build_object_call): Adjust.
	(build_conditional_expr): Adjust.
	(build_new_op): Adjust.
	(convert_like_real): Adjust.
	(build_new_method_call): Adjust.
	* cp-tree.h (more_specialized): Add EXPLICIT_TARGS parameter.
	* pt.c (get_bindings_real): Add EXPLICIT_TARGS parameter. Adjust.
	(fn_type_unificiation): Only coerce EXPLICIT_TARGS when not
	DEDUCE_ORDER. If undeduced parameters remain and DEDUCE_ORDER,
	check EXPLICIT_TARGS.
	(more_specialized): Add EXPLICIT_TARGS parameter. Adjust.
	(most_specialized_instantiation): Adjust.

Index: cp/call.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/call.c,v
retrieving revision 1.300
diff -c -3 -p -r1.300 call.c
*** call.c	2001/12/29 17:24:59	1.300
--- call.c	2001/12/30 20:28:50
*************** extern int inhibit_warnings;
*** 41,51 ****
  static tree build_new_method_call PARAMS ((tree, tree, tree, tree, int));
  
  static tree build_field_call PARAMS ((tree, tree, tree, tree));
! static struct z_candidate * tourney PARAMS ((struct z_candidate *));
  static int equal_functions PARAMS ((tree, tree));
! static int joust PARAMS ((struct z_candidate *, struct z_candidate *, int));
  static int compare_ics PARAMS ((tree, tree));
! static tree build_over_call PARAMS ((struct z_candidate *, tree, int));
  static tree build_java_interface_fn_ref PARAMS ((tree, tree));
  #define convert_like(CONV, EXPR) convert_like_real (CONV, EXPR, NULL_TREE, 0, 0)
  #define convert_like_with_context(CONV, EXPR, FN, ARGNO) convert_like_real (CONV, EXPR, FN, ARGNO, 0)
--- 41,52 ----
  static tree build_new_method_call PARAMS ((tree, tree, tree, tree, int));
  
  static tree build_field_call PARAMS ((tree, tree, tree, tree));
! static struct z_candidate * tourney PARAMS ((struct z_candidate *, tree));
  static int equal_functions PARAMS ((tree, tree));
! static int joust PARAMS ((struct z_candidate *, struct z_candidate *,
! 			  tree, int));
  static int compare_ics PARAMS ((tree, tree));
! static tree build_over_call PARAMS ((struct z_candidate *, tree, tree, int));
  static tree build_java_interface_fn_ref PARAMS ((tree, tree));
  #define convert_like(CONV, EXPR) convert_like_real (CONV, EXPR, NULL_TREE, 0, 0)
  #define convert_like_with_context(CONV, EXPR, FN, ARGNO) convert_like_real (CONV, EXPR, FN, ARGNO, 0)
*************** convert_class_to_reference (t, s, expr)
*** 1025,1031 ****
      return NULL_TREE;
    
    candidates = splice_viable (candidates);
!   cand = tourney (candidates);
    if (!cand)
      return NULL_TREE;
  
--- 1026,1032 ----
      return NULL_TREE;
    
    candidates = splice_viable (candidates);
!   cand = tourney (candidates, NULL_TREE);
    if (!cand)
      return NULL_TREE;
  
*************** add_template_candidate_real (candidates,
*** 2259,2265 ****
      cand->template = tree_cons (tmpl, targs, NULL_TREE);
    else
      cand->template = DECL_TEMPLATE_INFO (fn);
! 
    return cand;
  }
  
--- 2260,2266 ----
      cand->template = tree_cons (tmpl, targs, NULL_TREE);
    else
      cand->template = DECL_TEMPLATE_INFO (fn);
!   
    return cand;
  }
  
*************** build_user_type_conversion_1 (totype, ex
*** 2515,2521 ****
      }
  
    candidates = splice_viable (candidates);
!   cand = tourney (candidates);
  
    if (cand == 0)
      {
--- 2516,2522 ----
      }
  
    candidates = splice_viable (candidates);
!   cand = tourney (candidates, NULL_TREE);
  
    if (cand == 0)
      {
*************** build_new_function_call (fn, args)
*** 2645,2651 ****
  	  return error_mark_node;
  	}
        candidates = splice_viable (candidates);
!       cand = tourney (candidates);
  
        if (cand == 0)
  	{
--- 2646,2652 ----
  	  return error_mark_node;
  	}
        candidates = splice_viable (candidates);
!       cand = tourney (candidates, explicit_targs);
  
        if (cand == 0)
  	{
*************** build_new_function_call (fn, args)
*** 2655,2661 ****
  	  return error_mark_node;
  	}
  
!       return build_over_call (cand, args, LOOKUP_NORMAL);
      }
  
    /* This is not really overloaded. */
--- 2656,2662 ----
  	  return error_mark_node;
  	}
  
!       return build_over_call (cand, explicit_targs, args, LOOKUP_NORMAL);
      }
  
    /* This is not really overloaded. */
*************** build_object_call (obj, args)
*** 2751,2757 ****
      }
  
    candidates = splice_viable (candidates);
!   cand = tourney (candidates);
  
    if (cand == 0)
      {
--- 2752,2758 ----
      }
  
    candidates = splice_viable (candidates);
!   cand = tourney (candidates, NULL_TREE);
  
    if (cand == 0)
      {
*************** build_object_call (obj, args)
*** 2765,2771 ****
       DECL_NAME here.  */
    if (TREE_CODE (cand->fn) == FUNCTION_DECL
        && DECL_OVERLOADED_OPERATOR_P (cand->fn) == CALL_EXPR)
!     return build_over_call (cand, mem_args, LOOKUP_NORMAL);
  
    obj = convert_like_with_context
            (TREE_VEC_ELT (cand->convs, 0), obj, cand->fn, -1);
--- 2766,2772 ----
       DECL_NAME here.  */
    if (TREE_CODE (cand->fn) == FUNCTION_DECL
        && DECL_OVERLOADED_OPERATOR_P (cand->fn) == CALL_EXPR)
!     return build_over_call (cand, NULL_TREE, mem_args, LOOKUP_NORMAL);
  
    obj = convert_like_with_context
            (TREE_VEC_ELT (cand->convs, 0), obj, cand->fn, -1);
*************** build_conditional_expr (arg1, arg2, arg3
*** 3067,3073 ****
  	  return error_mark_node;
  	}
        candidates = splice_viable (candidates);
!       cand = tourney (candidates);
        if (!cand)
  	{
  	  op_error (COND_EXPR, NOP_EXPR, arg1, arg2, arg3, "no match");
--- 3068,3074 ----
  	  return error_mark_node;
  	}
        candidates = splice_viable (candidates);
!       cand = tourney (candidates, NULL_TREE);
        if (!cand)
  	{
  	  op_error (COND_EXPR, NOP_EXPR, arg1, arg2, arg3, "no match");
*************** build_new_op (code, flags, arg1, arg2, a
*** 3421,3427 ****
        return error_mark_node;
      }
    candidates = splice_viable (candidates);
!   cand = tourney (candidates);
  
    if (cand == 0)
      {
--- 3422,3428 ----
        return error_mark_node;
      }
    candidates = splice_viable (candidates);
!   cand = tourney (candidates, NULL_TREE);
  
    if (cand == 0)
      {
*************** build_new_op (code, flags, arg1, arg2, a
*** 3451,3457 ****
  	}
  
        return build_over_call
! 	(cand,
  	 TREE_CODE (TREE_TYPE (cand->fn)) == METHOD_TYPE
  	 ? mem_arglist : arglist,
  	 LOOKUP_NORMAL);
--- 3452,3458 ----
  	}
  
        return build_over_call
! 	(cand, NULL_TREE,
  	 TREE_CODE (TREE_TYPE (cand->fn)) == METHOD_TYPE
  	 ? mem_arglist : arglist,
  	 LOOKUP_NORMAL);
*************** convert_like_real (convs, expr, fn, argn
*** 3803,3809 ****
  	  }
  	else
  	  args = build_this (expr);
! 	expr = build_over_call (cand, args, LOOKUP_NORMAL);
  
  	/* If this is a constructor or a function returning an aggr type,
  	   we need to build up a TARGET_EXPR.  */
--- 3804,3810 ----
  	  }
  	else
  	  args = build_this (expr);
! 	expr = build_over_call (cand, NULL_TREE, args, LOOKUP_NORMAL);
  
  	/* If this is a constructor or a function returning an aggr type,
  	   we need to build up a TARGET_EXPR.  */
*************** convert_default_arg (type, arg, fn, parm
*** 4099,4106 ****
     bitmask of various LOOKUP_* flags which apply to the call itself.  */
  
  static tree
! build_over_call (cand, args, flags)
       struct z_candidate *cand;
       tree args;
       int flags;
  {
--- 4100,4108 ----
     bitmask of various LOOKUP_* flags which apply to the call itself.  */
  
  static tree
! build_over_call (cand, explicit_targs, args, flags)
       struct z_candidate *cand;
+      tree explicit_targs;
       tree args;
       int flags;
  {
*************** build_over_call (cand, args, flags)
*** 4115,4121 ****
    /* Give any warnings we noticed during overload resolution.  */
    if (cand->warnings)
      for (val = cand->warnings; val; val = TREE_CHAIN (val))
!       joust (cand, WRAPPER_PTR (TREE_VALUE (val)), 1);
  
    if (DECL_FUNCTION_MEMBER_P (fn))
      enforce_access (cand->basetype_path, fn);
--- 4117,4123 ----
    /* Give any warnings we noticed during overload resolution.  */
    if (cand->warnings)
      for (val = cand->warnings; val; val = TREE_CHAIN (val))
!       joust (cand, WRAPPER_PTR (TREE_VALUE (val)), explicit_targs, 1);
  
    if (DECL_FUNCTION_MEMBER_P (fn))
      enforce_access (cand->basetype_path, fn);
*************** build_new_method_call (instance, name, a
*** 4631,4637 ****
        return error_mark_node;
      }
    candidates = splice_viable (candidates);
!   cand = tourney (candidates);
  
    if (cand == 0)
      {
--- 4633,4639 ----
        return error_mark_node;
      }
    candidates = splice_viable (candidates);
!   cand = tourney (candidates, explicit_targs);
  
    if (cand == 0)
      {
*************** build_new_method_call (instance, name, a
*** 4663,4672 ****
      flags |= LOOKUP_NONVIRTUAL;
  
    if (TREE_CODE (TREE_TYPE (cand->fn)) == METHOD_TYPE)
!     call = build_over_call (cand, mem_args, flags);
    else
      {
!       call = build_over_call (cand, args, flags);
        /* Do evaluate the object parameter in a call to a static member
  	 function.  */
        if (TREE_SIDE_EFFECTS (instance))
--- 4665,4674 ----
      flags |= LOOKUP_NONVIRTUAL;
  
    if (TREE_CODE (TREE_TYPE (cand->fn)) == METHOD_TYPE)
!     call = build_over_call (cand, explicit_targs, mem_args, flags);
    else
      {
!       call = build_over_call (cand, explicit_targs, args, flags);
        /* Do evaluate the object parameter in a call to a static member
  	 function.  */
        if (TREE_SIDE_EFFECTS (instance))
*************** equal_functions (fn1, fn2)
*** 5187,5194 ****
        0: cand1 and cand2 are indistinguishable */
  
  static int
! joust (cand1, cand2, warn)
       struct z_candidate *cand1, *cand2;
       int warn;
  {
    int winner = 0;
--- 5189,5197 ----
        0: cand1 and cand2 are indistinguishable */
  
  static int
! joust (cand1, cand2, explicit_targs, warn)
       struct z_candidate *cand1, *cand2;
+      tree explicit_targs;
       int warn;
  {
    int winner = 0;
*************** joust (cand1, cand2, warn)
*** 5349,5354 ****
--- 5352,5358 ----
      {
        winner = more_specialized
          (TI_TEMPLATE (cand1->template), TI_TEMPLATE (cand2->template),
+ 	 explicit_targs,
           DEDUCE_ORDER,
           /* Tell the deduction code how many real function arguments
  	    we saw, not counting the implicit 'this' argument.  But,
*************** tweak:
*** 5463,5470 ****
     algorithm.  */
  
  static struct z_candidate *
! tourney (candidates)
       struct z_candidate *candidates;
  {
    struct z_candidate *champ = candidates, *challenger;
    int fate;
--- 5467,5475 ----
     algorithm.  */
  
  static struct z_candidate *
! tourney (candidates, explicit_targs)
       struct z_candidate *candidates;
+      tree explicit_targs;
  {
    struct z_candidate *champ = candidates, *challenger;
    int fate;
*************** tourney (candidates)
*** 5475,5481 ****
  
    for (challenger = champ->next; challenger; )
      {
!       fate = joust (champ, challenger, 0);
        if (fate == 1)
  	challenger = challenger->next;
        else
--- 5480,5486 ----
  
    for (challenger = champ->next; challenger; )
      {
!       fate = joust (champ, challenger, explicit_targs, 0);
        if (fate == 1)
  	challenger = challenger->next;
        else
*************** tourney (candidates)
*** 5505,5511 ****
  	 && !(champ_compared_to_predecessor && challenger->next == champ);
         challenger = challenger->next)
      {
!       fate = joust (champ, challenger, 0);
        if (fate != 1)
  	return 0;
      }
--- 5510,5516 ----
  	 && !(champ_compared_to_predecessor && challenger->next == champ);
         challenger = challenger->next)
      {
!       fate = joust (champ, challenger, explicit_targs, 0);
        if (fate != 1)
  	return 0;
      }
Index: cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.667
diff -c -3 -p -r1.667 cp-tree.h
*** cp-tree.h	2001/12/18 03:35:25	1.667
--- cp-tree.h	2001/12/30 20:31:24
*************** extern tree instantiate_template		PARAMS
*** 3939,3945 ****
  extern int fn_type_unification                  PARAMS ((tree, tree, tree, tree, tree, unification_kind_t, int));
  extern tree tinst_for_decl			PARAMS ((void));
  extern void mark_decl_instantiated		PARAMS ((tree, int));
! extern int more_specialized			PARAMS ((tree, tree, int, int));
  extern void mark_class_instantiated		PARAMS ((tree, int));
  extern void do_decl_instantiation		PARAMS ((tree, tree, tree));
  extern void do_type_instantiation		PARAMS ((tree, tree, int));
--- 3939,3945 ----
  extern int fn_type_unification                  PARAMS ((tree, tree, tree, tree, tree, unification_kind_t, int));
  extern tree tinst_for_decl			PARAMS ((void));
  extern void mark_decl_instantiated		PARAMS ((tree, int));
! extern int more_specialized			PARAMS ((tree, tree, tree, unification_kind_t, int));
  extern void mark_class_instantiated		PARAMS ((tree, int));
  extern void do_decl_instantiation		PARAMS ((tree, tree, tree));
  extern void do_type_instantiation		PARAMS ((tree, tree, int));
Index: cp/pt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/pt.c,v
retrieving revision 1.568
diff -c -3 -p -r1.568 pt.c
*** pt.c	2001/12/29 17:24:59	1.568
--- pt.c	2001/12/30 20:34:39
*************** static tree build_template_decl PARAMS (
*** 132,138 ****
  static int mark_template_parm PARAMS ((tree, void *));
  static tree tsubst_friend_function PARAMS ((tree, tree));
  static tree tsubst_friend_class PARAMS ((tree, tree));
! static tree get_bindings_real PARAMS ((tree, tree, tree, int, int, int));
  static int template_decl_level PARAMS ((tree));
  static tree maybe_get_template_decl_from_type_decl PARAMS ((tree));
  static int check_cv_quals_for_unify PARAMS ((int, tree, tree));
--- 132,139 ----
  static int mark_template_parm PARAMS ((tree, void *));
  static tree tsubst_friend_function PARAMS ((tree, tree));
  static tree tsubst_friend_class PARAMS ((tree, tree));
! static tree get_bindings_real PARAMS ((tree, tree, tree, int,
! 				       unification_kind_t, int));
  static int template_decl_level PARAMS ((tree));
  static tree maybe_get_template_decl_from_type_decl PARAMS ((tree));
  static int check_cv_quals_for_unify PARAMS ((int, tree, tree));
*************** fn_type_unification (fn, explicit_targs,
*** 7680,7686 ****
    my_friendly_assert (TREE_CODE (fn) == TEMPLATE_DECL, 0);
    
    fntype = TREE_TYPE (fn);
!   if (explicit_targs)
      {
        /* [temp.deduct]
  	  
--- 7681,7687 ----
    my_friendly_assert (TREE_CODE (fn) == TEMPLATE_DECL, 0);
    
    fntype = TREE_TYPE (fn);
!   if (explicit_targs && (strict != DEDUCE_ORDER))
      {
        /* [temp.deduct]
  	  
*************** fn_type_unification (fn, explicit_targs,
*** 7740,7745 ****
--- 7741,7779 ----
  				  targs, parms, args, /*subr=*/0,
  				  strict, /*allow_incomplete*/1, len);
  
+   if (result == 2 && strict == DEDUCE_ORDER && explicit_targs)
+     {
+       /* Undeduced arguments remain, but we were given some explicit
+          arguments and are determining partial ordering.  If all the
+          undeduced arguments would be provided be the explicit
+          arguments, all is ok.  In this case EXPLICIT_TARGS will
+          always be a TREE_LIST of the innermost arguments.  */
+       int arg_n;
+       int explicit_length = list_length (explicit_targs);
+       tree inner_parms = DECL_INNERMOST_TEMPLATE_PARMS (fn);
+       
+       for (arg_n = 0; arg_n != TREE_VEC_LENGTH (targs); arg_n++)
+ 	{
+ 	  if (TREE_VEC_ELT (targs, arg_n))
+ 	    /*OK*/;
+ 	  else if (arg_n >= explicit_length)
+ 	    return result;
+ 	  else
+ 	    {
+ 	      /* Fill in a dummy deduction for the argument, so that the
+ 	         subsequent tsubst doesn't get confused.  */
+ 	      tree dummy = TREE_VALUE (TREE_VEC_ELT (inner_parms, arg_n));
+ 
+ 	      if (TREE_CODE (dummy) == TYPE_DECL)
+ 		dummy = TREE_TYPE (dummy);
+ 	      
+ 	      TREE_VEC_ELT (targs, arg_n) = dummy;
+ 	    }
+ 	}
+       
+       result = 0;
+     }
+   
    if (result == 0) 
      /* All is well so far.  Now, check:
         
*************** mark_decl_instantiated (result, extern_p
*** 9032,9061 ****
  
  /* Given two function templates PAT1 and PAT2, return:
  
-    DEDUCE should be DEDUCE_EXACT or DEDUCE_ORDER.
-    
     1 if PAT1 is more specialized than PAT2 as described in [temp.func.order].
     -1 if PAT2 is more specialized than PAT1.
     0 if neither is more specialized.
  
!    LEN is passed through to fn_type_unification.  */
     
  int
! more_specialized (pat1, pat2, deduce, len)
       tree pat1, pat2;
!      int deduce;
       int len;
  {
    tree targs;
    int winner = 0;
  
    targs = get_bindings_real (pat1, DECL_TEMPLATE_RESULT (pat2),
!                              NULL_TREE, 0, deduce, len);
    if (targs)
      --winner;
  
    targs = get_bindings_real (pat2, DECL_TEMPLATE_RESULT (pat1),
!                              NULL_TREE, 0, deduce, len);
    if (targs)
      ++winner;
  
--- 9066,9114 ----
  
  /* Given two function templates PAT1 and PAT2, return:
  
     1 if PAT1 is more specialized than PAT2 as described in [temp.func.order].
     -1 if PAT2 is more specialized than PAT1.
     0 if neither is more specialized.
  
!    EXPLICIT_TARGS, if non-null, is the explicit arguments given in a
!    call with a template-id, these are used for the defect mentioned below.
!    DEDUCE should be DEDUCE_EXACT or DEDUCE_ORDER.
!    LEN is passed through to fn_type_unification.
     
+    Defect. [temp.func.order] only does return type deduction for
+    conversion operators. What if one of the templates has a return
+    type dependent on template parameters (and non-deducible from other
+    arguments)? For instance,
+ 
+      template <typename R, typename T> R Foo (T); 		#1
+      template <typename R, typename T> R Foo (T const *);	#2
+ 
+    #2 looks more specialized than #1, but deduction would fail both
+    ways.  We could detect this situation by noting that a function's
+    return type uses template parameters, and then forcing return type
+    deduction.  This unfortunately makes some legal programs
+    ill-formed.  Fortunately, functions such as #1 & #2 can only be
+    called with a template-id-expr providing a value for the R
+    parameter.  We make use of this to determine whether undeduced
+    paramters would be filled by the explicitly provided arguments.  */
+ 
  int
! more_specialized (pat1, pat2, explicit_targs, deduce, len)
       tree pat1, pat2;
!      tree explicit_targs;
!      unification_kind_t deduce;
       int len;
  {
    tree targs;
    int winner = 0;
  
    targs = get_bindings_real (pat1, DECL_TEMPLATE_RESULT (pat2),
! 			     explicit_targs, 0, deduce, len);
    if (targs)
      --winner;
  
    targs = get_bindings_real (pat2, DECL_TEMPLATE_RESULT (pat1),
! 			     explicit_targs, 0, deduce, len);
    if (targs)
      ++winner;
  
*************** more_specialized_class (pat1, pat2)
*** 9097,9103 ****
  static tree
  get_bindings_real (fn, decl, explicit_args, check_rettype, deduce, len)
       tree fn, decl, explicit_args;
!      int check_rettype, deduce, len;
  {
    int ntparms = DECL_NTPARMS (fn);
    tree targs = make_tree_vec (ntparms);
--- 9150,9158 ----
  static tree
  get_bindings_real (fn, decl, explicit_args, check_rettype, deduce, len)
       tree fn, decl, explicit_args;
!      int check_rettype;
!      unification_kind_t deduce;
!      int len;
  {
    int ntparms = DECL_NTPARMS (fn);
    tree targs = make_tree_vec (ntparms);
*************** get_bindings_real (fn, decl, explicit_ar
*** 9109,9115 ****
       The call to fn_type_unification will handle substitution into the
       FN.  */
    decl_type = TREE_TYPE (decl);
!   if (explicit_args && uses_template_parms (decl_type))
      {
        tree tmpl;
        tree converted_args;
--- 9164,9171 ----
       The call to fn_type_unification will handle substitution into the
       FN.  */
    decl_type = TREE_TYPE (decl);
!   if (explicit_args && (deduce != DEDUCE_ORDER)
!       && uses_template_parms (decl_type))
      {
        tree tmpl;
        tree converted_args;
*************** most_specialized_instantiation (instanti
*** 9227,9233 ****
    for (fn = TREE_CHAIN (instantiations); fn; fn = TREE_CHAIN (fn))
      {
        fate = more_specialized (TREE_VALUE (champ), TREE_VALUE (fn),
!                                DEDUCE_EXACT, -1);
        if (fate == 1)
  	;
        else
--- 9283,9289 ----
    for (fn = TREE_CHAIN (instantiations); fn; fn = TREE_CHAIN (fn))
      {
        fate = more_specialized (TREE_VALUE (champ), TREE_VALUE (fn),
!                                NULL_TREE, DEDUCE_EXACT, -1);
        if (fate == 1)
  	;
        else
*************** most_specialized_instantiation (instanti
*** 9245,9251 ****
    for (fn = instantiations; fn && fn != champ; fn = TREE_CHAIN (fn))
      {
        fate = more_specialized (TREE_VALUE (champ), TREE_VALUE (fn),
!                                DEDUCE_EXACT, -1);
        if (fate != 1)
  	return error_mark_node;
      }
--- 9301,9307 ----
    for (fn = instantiations; fn && fn != champ; fn = TREE_CHAIN (fn))
      {
        fate = more_specialized (TREE_VALUE (champ), TREE_VALUE (fn),
!                                NULL_TREE, DEDUCE_EXACT, -1);
        if (fate != 1)
  	return error_mark_node;
      }
// { dg-do compile }

// Copyright (C) 2001 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 29 Dec 2001 <nathan@codesourcery.com>

// PR 4672. Defect report concerning function partial ordering.

template <typename T, class U> T checked_cast(U & from);

template <typename T, class U> T checked_cast(U * from);

class C {};

void foo (int *arg)
{
  checked_cast <C const &> (*arg);
  checked_cast <C const *> (arg);
}
Submitter: Nathan Sidwell

Description:
[temp.func.order] describes the partial ordering of function
templates. Paragraph 5 states,
	 'A template is more specialized than another if, and
	 only if, it is at least as specialized as the other
	 template and that template is not at least as
	 specialized as the first.'
To paraphrase, given two templates A & B, if A's template
parameters can be deduced by B, but B's cannot be deduced by
A, then A is more specialized than B. Deduction is done as
if for a function call. In particular, except for conversion
operators, the return type is not involved in deduction.
This leads to the following templates and use being
unordered. (This example is culled from G++ bug report 4672
http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view&pr=4672)

template <typename T, class U> T checked_cast(U & from); //#1
template <typename T, class U> T checked_cast(U * from); //#2
class C {};

void foo (int *arg)
{
  checked_cast <C const *> (arg);
}

In the call,
#1 can be deduced with T = 'C const *' and U = 'int *'
#2 can be deduced with T = 'C const *' and U = 'int'

It looks like #2 is more specialized that #1, but
[temp.func.order] does not make it so, as neither template can
deduce 'T' from the other template's function parameters.

Possible Resolutions:
There are several possible solutions, however through
experimentation I have discounted two of them.

Option 1:
When deducing function ordering, if the return type of one of
the templates uses a template parameter, then return types
should be used for deduction.  This, unfortunately, makes
existing well formed programs ill formed. For example

template <class T> class X {};

template <class T> X<T> Foo (T *);	// #1
template <class T> int Foo (T const *); // #2

void Baz (int *p1, int const *p2)
{
  int j = Foo (p2); //#3
}

Here, neither #1 nor #2 can deduce the other, as the return
types fail to match. Considering only the function parameters
gives #2 more specialized than #1, and hence makes the call
#3 well formed.

Option 2:
As option 1, but only consider the return type when deducing
the template whose return type involves template parameters.
This has the same flaw as option 1, and that example is
similarly ill formed, as #1's return type 'X<T,0>' fails to
match 'int' so #1 cannot deduce #2. In the converse direction,
return types are not considered, but the function parameters
fail to deduce.

Option 3:
It is observed that the orginal example is only callable
with a template-id-expr to supply a value for the first,
undeducible, parameter.  If that parameter were deducible it
would also appear within at least one of the function
parameters.  We can alter paragraph 4 of [temp.func.order]
to indicate that it is not necessary to deduce the parameters
which are provided explicitly, when the call has the form
of a template-id-expr.  This is a safe extension as it only
serves to make ill formed programs well formed.

Suggested resolution:
Insert after the first sentance of paragraph 4 in
[temp.function.order]
	'Should any template parameters remain undeduced, and
	the function call be of the form of a template-id-expr,
	those template parameters provided in the
	template-id-expr may be arbitrarily synthesized prior
	to determining whether the deduced arguments generate
	a valid function type.'

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]