diff mbox

[C++] DR 217 follow up (and more)

Message ID 53DA7E85.1000806@oracle.com
State New
Headers show

Commit Message

Paolo Carlini July 31, 2014, 5:36 p.m. UTC
Hi,

today I noticed that when we implemented the resolution we failed to 
handle static member functions. The below tested x86_64-linux.

While working on this I noticed that we don't use the 
DECL_NONSTATIC_MEMBER_FUNCTION_P macro much, should we apply something 
like the attached?

Finally, if you are wondering instead how I noticed the former ;) I have 
been working on c++/15339, which seems relatively easy to resolve. Ie, 
rejecting in duplicate_decls

template<typename> void fun(int);
template<typename> void fun(int = 0);

however, handling correctly the member case:

class A
{
   template<typename> void fun(int);
};

template<typename> void A::fun(int = 0) { }

is more tricky because we don't want to start rejecting:

class A
{
   void fun(int);
};

void A::fun(int = 0) { }

which is well formed. The problem is that when grokfndecl calls 
duplicate_decls in such member cases it looks through TEMPLATE_DECLs, eg:

       if (TREE_CODE (old_decl) == TEMPLATE_DECL)
         /* Because grokfndecl is always supposed to return a
            FUNCTION_DECL, we pull out the DECL_TEMPLATE_RESULT
            here.  We depend on our callers to figure out that its
            really a template that's being returned.  */
         old_decl = DECL_TEMPLATE_RESULT (old_decl);

and then telling apart the two cases above is tough, both are 
FUNCTION_DECLs :(

Ideas about the best way to handle that? Anything less delicate than 
trying *not* to use DECL_TEMPLATE_RESULT that early and passing the 
TEMPLATE_DECLs as they are to duplicate_decls and then using it only 
right before returning from grokfndecl?

Thanks!
Paolo.

///////////////////////////
/cp
2014-07-31  Paolo Carlini  <paolo.carlini@oracle.com>

	DR 217 again
	* decl.c (duplicate_decls): Handle static member functions too.

/testsuite
2014-07-31  Paolo Carlini  <paolo.carlini@oracle.com>

	DR 217 again
	* g++.dg/tc1/dr217-2.C: New.

Index: call.c
===================================================================
--- call.c	(revision 213379)
+++ call.c	(working copy)
@@ -7038,7 +7038,7 @@ build_over_call (struct z_candidate *cand, int fla
 	}
     }
   /* Bypass access control for 'this' parameter.  */
-  else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
+  else if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn))
     {
       tree parmtype = TREE_VALUE (parm);
       tree arg = build_this (first_arg != NULL_TREE
@@ -8026,7 +8026,7 @@ build_new_method_call_1 (tree instance, tree fns,
 			 fn);
 	    }
 
-	  if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE
+	  if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)
 	      && !DECL_CONSTRUCTOR_P (fn)
 	      && is_dummy_object (instance))
 	    {
@@ -8071,7 +8071,7 @@ build_new_method_call_1 (tree instance, tree fns,
 	      /* In an expression of the form `a->f()' where `f' turns
 		 out to be a static member function, `a' is
 		 none-the-less evaluated.  */
-	      if (TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE
+	      if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)
 		  && !is_dummy_object (instance)
 		  && TREE_SIDE_EFFECTS (instance))
 		call = build2 (COMPOUND_EXPR, TREE_TYPE (call),
Index: class.c
===================================================================
--- class.c	(revision 213379)
+++ class.c	(working copy)
@@ -7306,7 +7306,7 @@ resolve_address_of_overloaded_function (tree targe
      pointer-to-member types, not the internal POINTER_TYPE to
      METHOD_TYPE representation.  */
   gcc_assert (!TYPE_PTR_P (target_type)
-	      || TREE_CODE (TREE_TYPE (target_type)) != METHOD_TYPE);
+	      || !DECL_NONSTATIC_MEMBER_FUNCTION_P (target_type));
 
   gcc_assert (is_overloaded_fn (overload));
 
@@ -7355,8 +7355,7 @@ resolve_address_of_overloaded_function (tree targe
 	    /* We're not looking for templates just yet.  */
 	    continue;
 
-	  if ((TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
-	      != is_ptrmem)
+	  if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) != is_ptrmem)
 	    /* We're looking for a non-static member, and this isn't
 	       one, or vice versa.  */
 	    continue;
@@ -7405,8 +7404,7 @@ resolve_address_of_overloaded_function (tree targe
 	    /* We're only looking for templates.  */
 	    continue;
 
-	  if ((TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
-	      != is_ptrmem)
+	  if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) != is_ptrmem)
 	    /* We're not looking for a non-static member, and this is
 	       one, or vice versa.  */
 	    continue;
Index: cp-tree.h
===================================================================
--- cp-tree.h	(revision 213379)
+++ cp-tree.h	(working copy)
@@ -3555,7 +3555,7 @@ more_aggr_init_expr_args_p (const aggr_init_expr_a
 #define TYPE_PTROBV_P(NODE)					\
   (TYPE_PTR_P (NODE)						\
    && !(TREE_CODE (TREE_TYPE (NODE)) == FUNCTION_TYPE		\
-	|| TREE_CODE (TREE_TYPE (NODE)) == METHOD_TYPE))
+	|| DECL_NONSTATIC_MEMBER_FUNCTION_P (NODE)))
 
 /* Returns true if NODE is a pointer to function.  */
 #define TYPE_PTRFN_P(NODE)				\
Index: cvt.c
===================================================================
--- cvt.c	(revision 213379)
+++ cvt.c	(working copy)
@@ -1475,7 +1475,7 @@ convert_force (tree type, tree expr, int convtype,
 
   /* From typeck.c convert_for_assignment */
   if (((TYPE_PTR_P (TREE_TYPE (e)) && TREE_CODE (e) == ADDR_EXPR
-	&& TREE_CODE (TREE_TYPE (TREE_TYPE (e))) == METHOD_TYPE)
+	&& DECL_NONSTATIC_MEMBER_FUNCTION_P (TREE_TYPE (e)))
        || integer_zerop (e)
        || TYPE_PTRMEMFUNC_P (TREE_TYPE (e)))
       && TYPE_PTRMEMFUNC_P (type))
Index: decl.c
===================================================================
--- decl.c	(revision 213379)
+++ decl.c	(working copy)
@@ -7849,7 +7849,7 @@ grokfndecl (tree ctype,
 	    old_decl = DECL_TEMPLATE_RESULT (old_decl);
 
 	  if (DECL_STATIC_FUNCTION_P (old_decl)
-	      && TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
+	      && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
 	    {
 	      /* Remove the `this' parm added by grokclassfn.  */
 	      revert_static_member_fn (decl);
@@ -11525,7 +11525,7 @@ grok_op_properties (tree decl, bool complain)
 {
   tree argtypes = TYPE_ARG_TYPES (TREE_TYPE (decl));
   tree argtype;
-  int methodp = (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE);
+  int methodp = DECL_NONSTATIC_MEMBER_FUNCTION_P (decl);
   tree name = DECL_NAME (decl);
   enum tree_code operator_code;
   int arity;
@@ -13205,7 +13205,7 @@ start_preparsed_function (tree decl1, tree attrs,
   /* Sometimes we don't notice that a function is a static member, and
      build a METHOD_TYPE for it.  Fix that up now.  */
   gcc_assert (!(ctype != NULL_TREE && DECL_STATIC_FUNCTION_P (decl1)
-		&& TREE_CODE (TREE_TYPE (decl1)) == METHOD_TYPE));
+		&& DECL_NONSTATIC_MEMBER_FUNCTION_P (decl1)));
 
   /* Set up current_class_type, and enter the scope of the class, if
      appropriate.  */
Index: decl2.c
===================================================================
--- decl2.c	(revision 213379)
+++ decl2.c	(working copy)
@@ -691,7 +691,7 @@ check_classfn (tree ctype, tree function, tree tem
 	   /* Get rid of the this parameter on functions that become
 	      static.  */
 	  if (DECL_STATIC_FUNCTION_P (fndecl)
-	      && TREE_CODE (TREE_TYPE (function)) == METHOD_TYPE)
+	      && DECL_NONSTATIC_MEMBER_FUNCTION_P (function))
 	    p1 = TREE_CHAIN (p1);
 
 	  /* A member template definition only matches a member template
@@ -957,7 +957,7 @@ grokfield (const cp_declarator *declarator,
 	    }
 	  else if (TREE_CODE (init) == DEFAULT_ARG)
 	    error ("invalid initializer for member function %qD", value);
-	  else if (TREE_CODE (TREE_TYPE (value)) == METHOD_TYPE)
+	  else if (DECL_NONSTATIC_MEMBER_FUNCTION_P (value))
 	    {
 	      if (integer_zerop (init))
 		DECL_PURE_VIRTUAL_P (value) = 1;
@@ -4728,7 +4728,7 @@ build_offset_ref_call_from_tree (tree fn, vec<tree
 	 because we depend on the form of FN.  */
       make_args_non_dependent (*args);
       object = build_non_dependent_expr (object);
-      if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
+      if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn))
 	{
 	  if (TREE_CODE (fn) == DOTSTAR_EXPR)
 	    object = cp_build_addr_expr (object, complain);
@@ -4772,7 +4772,7 @@ check_default_args (tree x)
 {
   tree arg = TYPE_ARG_TYPES (TREE_TYPE (x));
   bool saw_def = false;
-  int i = 0 - (TREE_CODE (TREE_TYPE (x)) == METHOD_TYPE);
+  int i = 0 - DECL_NONSTATIC_MEMBER_FUNCTION_P (x);
   for (; arg && arg != void_list_node; arg = TREE_CHAIN (arg), ++i)
     {
       if (TREE_PURPOSE (arg))
Index: semantics.c
===================================================================
--- semantics.c	(revision 213379)
+++ semantics.c	(working copy)
@@ -1438,7 +1438,7 @@ finish_asm_stmt (int volatile_p, tree string, tree
 		  /* Functions are not modifiable, even though they are
 		     lvalues.  */
 		  || TREE_CODE (TREE_TYPE (operand)) == FUNCTION_TYPE
-		  || TREE_CODE (TREE_TYPE (operand)) == METHOD_TYPE
+		  || DECL_NONSTATIC_MEMBER_FUNCTION_P (operand)
 		  /* If it's an aggregate and any field is const, then it is
 		     effectively const.  */
 		  || (CLASS_TYPE_P (TREE_TYPE (operand))
@@ -3807,7 +3807,7 @@ finish_offsetof (tree expr)
       return error_mark_node;
     }
   if (TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE
-      || TREE_CODE (TREE_TYPE (expr)) == METHOD_TYPE
+      || DECL_NONSTATIC_MEMBER_FUNCTION_P (expr)
       || TREE_TYPE (expr) == unknown_type_node)
     {
       if (INDIRECT_REF_P (expr))
Index: tree.c
===================================================================
--- tree.c	(revision 213379)
+++ tree.c	(working copy)
@@ -1130,7 +1130,7 @@ cp_build_qualified_type_real (tree type,
      between the unqualified and qualified types.  */
   if (result != type
       && TYPE_PTR_P (type)
-      && TREE_CODE (TREE_TYPE (type)) == METHOD_TYPE
+      && DECL_NONSTATIC_MEMBER_FUNCTION_P (type)
       && TYPE_LANG_SPECIFIC (result) == TYPE_LANG_SPECIFIC (type))
     TYPE_LANG_SPECIFIC (result) = NULL;
 
@@ -1139,7 +1139,7 @@ cp_build_qualified_type_real (tree type,
      sharing problem described above.  */
   if (TYPE_CANONICAL (result) != TYPE_CANONICAL (type)
       && TYPE_PTR_P (type)
-      && TREE_CODE (TREE_TYPE (type)) == METHOD_TYPE
+      && DECL_NONSTATIC_MEMBER_FUNCTION_P (type)
       && (TYPE_LANG_SPECIFIC (TYPE_CANONICAL (result)) 
           == TYPE_LANG_SPECIFIC (TYPE_CANONICAL (type))))
     TYPE_LANG_SPECIFIC (TYPE_CANONICAL (result)) = NULL;
Index: typeck.c
===================================================================
--- typeck.c	(revision 213379)
+++ typeck.c	(working copy)
@@ -3495,7 +3495,7 @@ cp_build_function_call_vec (tree function, vec<tre
     }
 
   is_method = (TYPE_PTR_P (fntype)
-	       && TREE_CODE (TREE_TYPE (fntype)) == METHOD_TYPE);
+	       && DECL_NONSTATIC_MEMBER_FUNCTION_P (fntype));
 
   if (!(TYPE_PTRFN_P (fntype)
 	|| is_method
@@ -3554,7 +3554,7 @@ warn_args_num (location_t loc, tree fndecl, bool t
 {
   if (fndecl)
     {
-      if (TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)
+      if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl))
 	{
 	  if (DECL_NAME (fndecl) == NULL_TREE
 	      || IDENTIFIER_HAS_TYPE_VALUE (DECL_NAME (fndecl)))
@@ -3651,7 +3651,7 @@ convert_arguments (tree typelist, vec<tree, va_gc>
 	{
 	  if (TREE_CODE (TREE_TYPE (val)) == ARRAY_TYPE
 	      || TREE_CODE (TREE_TYPE (val)) == FUNCTION_TYPE
-	      || TREE_CODE (TREE_TYPE (val)) == METHOD_TYPE)
+	      || DECL_NONSTATIC_MEMBER_FUNCTION_P (val))
 	    val = decay_conversion (val, complain);
 	}
 
@@ -5151,7 +5151,7 @@ build_x_unary_op (location_t loc, enum tree_code c
 
       /* A pointer to member-function can be formed only by saying
 	 &X::mf.  */
-      if (!flag_ms_extensions && TREE_CODE (TREE_TYPE (xarg)) == METHOD_TYPE
+      if (!flag_ms_extensions && DECL_NONSTATIC_MEMBER_FUNCTION_P (xarg)
 	  && (TREE_CODE (xarg) != OFFSET_REF || !PTRMEM_OK_P (xarg)))
 	{
 	  if (TREE_CODE (xarg) != OFFSET_REF
@@ -5183,7 +5183,7 @@ build_x_unary_op (location_t loc, enum tree_code c
 	  ptrmem = PTRMEM_OK_P (xarg);
 
 	  if (!ptrmem && !flag_ms_extensions
-	      && TREE_CODE (TREE_TYPE (TREE_OPERAND (xarg, 1))) == METHOD_TYPE)
+	      && DECL_NONSTATIC_MEMBER_FUNCTION_P (TREE_OPERAND (xarg, 1)))
 	    {
 	      /* A single non-static member, make sure we don't allow a
 		 pointer-to-member.  */
@@ -5545,7 +5545,7 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue
     }
 
   if (TYPE_PTR_P (argtype)
-      && TREE_CODE (TREE_TYPE (argtype)) == METHOD_TYPE)
+      && DECL_NONSTATIC_MEMBER_FUNCTION_P (argtype))
     {
       build_ptrmemfunc_type (argtype);
       val = build_ptrmemfunc (argtype, val, 0,
@@ -5937,7 +5937,7 @@ unary_complex_lvalue (enum tree_code code, tree ar
     }
 
   if (TREE_CODE (TREE_TYPE (arg)) == FUNCTION_TYPE
-      || TREE_CODE (TREE_TYPE (arg)) == METHOD_TYPE
+      || DECL_NONSTATIC_MEMBER_FUNCTION_P (arg)
       || TREE_CODE (arg) == OFFSET_REF)
     return NULL_TREE;
 
@@ -7473,7 +7473,7 @@ cp_build_modify_expr (tree lhs, enum tree_code mod
 	  /* Functions are not modifiable, even though they are
 	     lvalues.  */
 	  || TREE_CODE (TREE_TYPE (lhs)) == FUNCTION_TYPE
-	  || TREE_CODE (TREE_TYPE (lhs)) == METHOD_TYPE
+	  || DECL_NONSTATIC_MEMBER_FUNCTION_P (lhs)
 	  /* If it's an aggregate and any field is const, then it is
 	     effectively const.  */
 	  || (CLASS_TYPE_P (lhstype)
@@ -8245,7 +8245,7 @@ convert_for_initialization (tree exp, tree type, t
 	   || TREE_CODE (TREE_TYPE (type)) != ARRAY_TYPE))
       || (TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE
 	  && !TYPE_REFFN_P (type))
-      || TREE_CODE (TREE_TYPE (rhs)) == METHOD_TYPE)
+      || DECL_NONSTATIC_MEMBER_FUNCTION_P (rhs))
     rhs = decay_conversion (rhs, complain);
 
   rhstype = TREE_TYPE (rhs);
Index: typeck2.c
===================================================================
--- typeck2.c	(revision 213379)
+++ typeck2.c	(working copy)
@@ -338,7 +338,7 @@ abstract_virtuals_error_sfinae (tree decl, tree ty
 	error ("cannot declare field %q+D to be of abstract type %qT",
 	       decl, type);
       else if (TREE_CODE (decl) == FUNCTION_DECL
-	       && TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
+	       && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
 	error ("invalid abstract return type for member function %q+#D", decl);
       else if (TREE_CODE (decl) == FUNCTION_DECL)
 	error ("invalid abstract return type for function %q+#D", decl);

Comments

Jason Merrill Aug. 1, 2014, 6:30 p.m. UTC | #1
On 07/31/2014 01:36 PM, Paolo Carlini wrote:
> The problem is that when grokfndecl calls duplicate_decls in such member cases it looks through TEMPLATE_DECLs
> and then telling apart the two cases above is tough, both are FUNCTION_DECLs
>
> Ideas about the best way to handle that?

Could you just condition it on DECL_TEMPLATE_INFO?

>  	  tree t2 = TYPE_ARG_TYPES (TREE_TYPE (newdecl));
>  	  int i = 1;
>
> -	  if (TREE_CODE (TREE_TYPE (newdecl)) == METHOD_TYPE)
> +	  if (DECL_NONSTATIC_MEMBER_FUNCTION_P (newdecl))
>  	    t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2);

Let's use FUNCTION_FIRST_USER_PARMTYPE instead.

> -	  if (TREE_CODE (TREE_TYPE (newdecl)) == METHOD_TYPE
> +	  if (DECL_FUNCTION_MEMBER_P (newdecl)

This change is OK.

and in the predicate patch:

> -	      || TREE_CODE (TREE_TYPE (target_type)) != METHOD_TYPE);
> +	      || !DECL_NONSTATIC_MEMBER_FUNCTION_P (target_type));

This is wrong because target_type is not a FUNCTION_DECL.  We should 
probably either add FUNCTION_DECL_CHECK to 
DECL_NONSTATIC_MEMBER_FUNCTION_P or make it false if the argument isn't 
a FUNCTION_DECL.

There are a bunch of instances of this in the patch.

>    if (TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE
> -      || TREE_CODE (TREE_TYPE (expr)) == METHOD_TYPE
> +      || DECL_NONSTATIC_MEMBER_FUNCTION_P (expr)

Changing this instance and similar ones doesn't make much sense since 
the condition is looking for any kind of function, not just a non-static 
member function.

Jason
diff mbox

Patch

Index: cp/decl.c
===================================================================
--- cp/decl.c	(revision 213379)
+++ cp/decl.c	(working copy)
@@ -1710,10 +1710,10 @@  duplicate_decls (tree newdecl, tree olddecl, bool
 	  tree t2 = TYPE_ARG_TYPES (TREE_TYPE (newdecl));
 	  int i = 1;
 
-	  if (TREE_CODE (TREE_TYPE (newdecl)) == METHOD_TYPE)
+	  if (DECL_NONSTATIC_MEMBER_FUNCTION_P (newdecl))
 	    t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2);
 
-	  if (TREE_CODE (TREE_TYPE (newdecl)) == METHOD_TYPE
+	  if (DECL_FUNCTION_MEMBER_P (newdecl)
 	      && CLASSTYPE_TEMPLATE_INFO (CP_DECL_CONTEXT (newdecl)))
 	    {
 	      /* C++11 8.3.6/6.
Index: testsuite/g++.dg/tc1/dr217-2.C
===================================================================
--- testsuite/g++.dg/tc1/dr217-2.C	(revision 0)
+++ testsuite/g++.dg/tc1/dr217-2.C	(working copy)
@@ -0,0 +1,13 @@ 
+// { dg-do compile }
+// DR217: Default arguments for non-template member functions of class 
+//  templates 
+
+template <class T>
+struct S
+{
+  static void foo (int);
+};
+
+template <class T>
+void S<T>::foo (int = 0)  // { dg-error "" "default arguments for parameters of member functions of class templates can be specified in the initial declaration only" }
+{ }