public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [C++ PATCH] PING: Rewrite convert_nontype_argument
@ 2004-10-18 22:07 Giovanni Bajo
  2004-11-03 15:48 ` Jason Merrill
  0 siblings, 1 reply; 2+ messages in thread
From: Giovanni Bajo @ 2004-10-18 22:07 UTC (permalink / raw)
  To: jason; +Cc: gcc-patches, giovannibajo, Mark Mitchell

[-- Attachment #1: Type: text/plain, Size: 2026 bytes --]

Hello Jason,

this is my updated patch of the rewrite of convert_nontype_argument. Some
context for you:

-- First sent here: http://gcc.gnu.org/ml/gcc-patches/2004-07/msg01605.html.
You reviewed it right away and had some questions (see the followup).

-- Take two, addressing your concerns and answering your questions:
http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00226.html

-- Mark said it was OK with him but wanted to hear your opinion:
http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00232.html. He also found
problem in my patch related to division by zeros.

Now this is the (hopefully) final patch, updated against today mainline. It
addresses Mark's concerns by removing the wrong hunk in build_binary_op, and
instead it now directly checks for INTEGER_CST instead of TREE_CONSTANT:


  if (INTEGRAL_TYPE_P (type))
    {
      if (!INTEGRAL_TYPE_P (expr_type))
        return error_mark_node;

      expr = fold_decl_constant_value (expr);
      /* Notice that there are constant expressions like '4 % 0' which
         do not fold into integer constants.  */
      if (TREE_CODE (expr) != INTEGER_CST)
        {
          error ("%qE is not a valid template argument for type %qT "
                 "because it is a non-constant expression", expr, type);
          return NULL_TREE;
        }



There are also two additional differences from take two:

-- fold_decl_constant_value is now public instead of being static. I have
plans to use this function to fold constant expressions within GNU
attributes.

-- I know fold the resolt of the conversion of an integral constant through
fold(). This is needed to not confuse the mangler, which does not strip the
NOPs and thus does not see the constant (see mangle.c:write_template_arg).
Without this call, g++.dg/abi/mangle16.C fails (notice: this was not needed
one month ago, but it is now).

I would be grateful if you could find some time to review this patch, I have
been updating it for 3 months now. Tested on i686-pc-linux-gnu, OK for
mainline?

Giovanni Bajo

[-- Attachment #2: patch-rewrite-cnta.txt --]
[-- Type: text/plain, Size: 40778 bytes --]

cp/
2004-10-XX  Giovanni Bajo  <giovannibajo@gcc.gnu.org>

	DR 49, 100
	* cp-tree.h (TYPE_REF_OBJ_P): New macro.
	(TYPE_PTR_P, TYPE_PTROB_P, TYPE_PTROBV_P, TYPE_PTRFN_P,
	TYPE_REFFN_P): Document.
	(fold_decl_constant_value): New prototype.
	* pt.c (convert_nontype_argument_function): Rewrite and extract
	parts into...
	(fold_decl_constant_value, convert_nontype_argument_function): New.
	(lookup_template_class): Add comment about useless double call.
	* mangle.c (write_expression): Strip conversions before lowering
	pointer to members.
	* cvt.c (ocp_convert): Check LOOKUP_COMPLAIN for a pedwarn. Disallow
	enum to enum conversion.

testsuite/
2004-10-XX  Giovanni Bajo  <giovannibajo@gcc.gnu.org>

	* g++.dg/template/nontype7.C: New test.
	* g++.dg/template/nontype8.C: Likewise.
	* g++.dg/template/nontype9.C: Likewise.
	* g++.dg/template/nontype10.C: Likewise.
	* g++.dg/tc1/dr49.C: Likewise.
	* g++.dg/template/ptrmem8.C: Relax dg-error checks.
	* g++.old-deja/g++.other/null1.C: Remove a buggy error check



Index: cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.1063
diff -c -3 -p -r1.1063 cp-tree.h
*** cp/cp-tree.h	14 Oct 2004 23:15:25 -0000	1.1063
--- cp/cp-tree.h	18 Oct 2004 13:42:48 -0000
*************** struct lang_decl GTY(())
*** 2402,2419 ****
--- 2402,2430 ----
  /* Returns true if NODE is a pointer-to-data-member.  */
  #define TYPE_PTRMEM_P(NODE)			\
    (TREE_CODE (NODE) == OFFSET_TYPE)
+ /* Returns true if NODE is a pointer.  */
  #define TYPE_PTR_P(NODE)			\
    (TREE_CODE (NODE) == POINTER_TYPE)
+ /* Returns true if NODE is a pointer to an object.  */
  #define TYPE_PTROB_P(NODE)				\
    (TYPE_PTR_P (NODE) 					\
     && TREE_CODE (TREE_TYPE (NODE)) != FUNCTION_TYPE	\
     && TREE_CODE (TREE_TYPE (NODE)) != METHOD_TYPE	\
     && TREE_CODE (TREE_TYPE (NODE)) != VOID_TYPE)
+ /* Returns true if NODE is a reference to an object.  */
+ #define TYPE_REF_OBJ_P(NODE)				\
+   (TREE_CODE (NODE) == REFERENCE_TYPE			\
+    && TREE_CODE (TREE_TYPE (NODE)) != FUNCTION_TYPE	\
+    && TREE_CODE (TREE_TYPE (NODE)) != METHOD_TYPE	\
+    && TREE_CODE (TREE_TYPE (NODE)) != VOID_TYPE)
+ /* Returns true if NODE is a pointer to an object, or a pointer to void.  */
  #define TYPE_PTROBV_P(NODE)						\
    (TYPE_PTR_P (NODE) && TREE_CODE (TREE_TYPE (NODE)) != FUNCTION_TYPE)
+ /* Returns true if NODE is a pointer to function.  */
  #define TYPE_PTRFN_P(NODE)				\
    (TREE_CODE (NODE) == POINTER_TYPE			\
     && TREE_CODE (TREE_TYPE (NODE)) == FUNCTION_TYPE)
+ /* Returns true if NODE is a reference to function.  */
  #define TYPE_REFFN_P(NODE)				\
    (TREE_CODE (NODE) == REFERENCE_TYPE			\
     && TREE_CODE (TREE_TYPE (NODE)) == FUNCTION_TYPE)
*************** extern tree build_non_dependent_expr    
*** 3979,3984 ****
--- 3990,3996 ----
  extern tree build_non_dependent_args            (tree);
  extern bool reregister_specialization           (tree, tree, tree);
  extern tree fold_non_dependent_expr             (tree);
+ extern tree fold_decl_constant_value            (tree);
  
  /* in repo.c */
  extern void init_repo (void);
Index: cp/cvt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cvt.c,v
retrieving revision 1.167
diff -c -3 -p -r1.167 cvt.c
*** cp/cvt.c	28 Sep 2004 02:56:06 -0000	1.167
--- cp/cvt.c	18 Oct 2004 13:42:49 -0000
*************** ocp_convert (tree type, tree expr, int c
*** 675,684 ****
        /* enum = enum, enum = int, enum = float, (enum)pointer are all
           errors.  */
        if (TREE_CODE (type) == ENUMERAL_TYPE
! 	  && ((ARITHMETIC_TYPE_P (intype) && ! (convtype & CONV_STATIC))
! 	      || (TREE_CODE (intype) == POINTER_TYPE)))
  	{
! 	  pedwarn ("conversion from `%#T' to `%#T'", intype, type);
  
  	  if (flag_pedantic_errors)
  	    return error_mark_node;
--- 675,687 ----
        /* enum = enum, enum = int, enum = float, (enum)pointer are all
           errors.  */
        if (TREE_CODE (type) == ENUMERAL_TYPE
! 	  && (((INTEGRAL_OR_ENUMERATION_TYPE_P (intype)
! 	        || TREE_CODE (intype) == REAL_TYPE)
! 	       && ! (convtype & CONV_STATIC))
! 	      || TREE_CODE (intype) == POINTER_TYPE))
  	{
! 	  if (flags & LOOKUP_COMPLAIN)
! 	    pedwarn ("conversion from `%#T' to `%#T'", intype, type);
  
  	  if (flag_pedantic_errors)
  	    return error_mark_node;
Index: cp/mangle.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/mangle.c,v
retrieving revision 1.113
diff -c -3 -p -r1.113 mangle.c
*** cp/mangle.c	17 Sep 2004 21:54:56 -0000	1.113
--- cp/mangle.c	18 Oct 2004 13:42:50 -0000
*************** write_expression (tree expr)
*** 1969,1974 ****
--- 1969,1984 ----
  
    code = TREE_CODE (expr);
  
+   /* Skip NOP_EXPRs.  They can occur when (say) a pointer argument
+      is converted (via qualification conversions) to another
+      type.  */
+   while (TREE_CODE (expr) == NOP_EXPR
+ 	 || TREE_CODE (expr) == NON_LVALUE_EXPR)
+     {
+       expr = TREE_OPERAND (expr, 0);
+       code = TREE_CODE (expr);
+     }
+ 
    /* Handle pointers-to-members by making them look like expression
       nodes.  */
    if (code == PTRMEM_CST)
*************** write_expression (tree expr)
*** 1980,1995 ****
        code = TREE_CODE (expr);
      }
  
-   /* Skip NOP_EXPRs.  They can occur when (say) a pointer argument
-      is converted (via qualification conversions) to another
-      type.  */
-   while (TREE_CODE (expr) == NOP_EXPR
- 	 || TREE_CODE (expr) == NON_LVALUE_EXPR)
-     {
-       expr = TREE_OPERAND (expr, 0);
-       code = TREE_CODE (expr);
-     }
- 
    /* Handle template parameters.  */
    if (code == TEMPLATE_TYPE_PARM 
        || code == TEMPLATE_TEMPLATE_PARM
--- 1990,1995 ----
Index: cp/pt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/pt.c,v
retrieving revision 1.936
diff -c -3 -p -r1.936 pt.c
*** cp/pt.c	14 Oct 2004 23:15:26 -0000	1.936
--- cp/pt.c	18 Oct 2004 21:49:12 -0000
*************** static int maybe_adjust_types_for_deduct
*** 110,115 ****
--- 110,116 ----
  static int  type_unification_real (tree, tree, tree, tree,
  				   int, unification_kind_t, int, int);
  static void note_template_header (int);
+ static tree convert_nontype_argument_function (tree, tree);
  static tree convert_nontype_argument (tree, tree);
  static tree convert_template_argument (tree, tree, tree,
  				       tsubst_flags_t, int, tree);
*************** fold_non_dependent_expr (tree expr)
*** 3254,3259 ****
--- 3255,3333 ----
    return expr;
  }
  
+ /* EXPR is an expression which is used in a constant-expression context.
+    For instance, it could be a VAR_DECL with a constant initializer.
+    Extract the innest constant expression.
+    
+    This is basically a more powerful version of decl_constant_value, which
+    can be used also in templates where initializers can maintain a
+    syntactic rather than semantic form (even if they are non-dependent, for
+    access-checking purposes).  */
+ 
+ tree
+ fold_decl_constant_value (tree expr)
+ {
+   while (true)
+     {
+       tree const_expr = decl_constant_value (expr);
+       /* In a template, the initializer for a VAR_DECL may not be
+ 	 marked as TREE_CONSTANT, in which case decl_constant_value
+ 	 will not return the initializer.  Handle that special case
+ 	 here.  */
+       if (expr == const_expr
+ 	  && TREE_CODE (expr) == VAR_DECL
+ 	  && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (expr)
+ 	  && CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (expr))
+ 	  /* DECL_INITIAL can be NULL if we are processing a
+ 	     variable initialized to an expression involving itself.
+ 	     We know it is initialized to a constant -- but not what
+ 	     constant, yet.  */
+ 	  && DECL_INITIAL (expr))
+ 	const_expr = DECL_INITIAL (expr);
+       if (expr == const_expr)
+ 	break;
+       expr = fold_non_dependent_expr (const_expr);
+     }
+ 
+     return expr;
+ }
+ 
+ /* Subroutine of convert_nontype_argument. Converts EXPR to TYPE, which
+    must be a function or a pointer-to-function type, as specified
+    in [temp.arg.nontype]: disambiguate EXPR if it is an overload set,
+    and check that the resulting function has external linkage.  */
+ 
+ static tree
+ convert_nontype_argument_function (tree type, tree expr)
+ {
+   tree fns = expr;
+   tree fn, fn_no_ptr;
+ 
+   fn = instantiate_type (type, fns, tf_none);
+   if (fn == error_mark_node)
+     return error_mark_node;
+ 
+   fn_no_ptr = fn;
+   if (TREE_CODE (fn_no_ptr) == ADDR_EXPR)
+     fn_no_ptr = TREE_OPERAND (fn_no_ptr, 0);
+ 
+   /* [temp.arg.nontype]/1
+ 
+      A template-argument for a non-type, non-template template-parameter
+      shall be one of:
+      [...]
+      -- the address of an object or function with external linkage.  */
+   if (!DECL_EXTERNAL_LINKAGE_P (fn_no_ptr))
+     {
+       error ("%qE is not a valid template argument for type %qT "
+ 	     "because function %qD has not external linkage",
+ 	     expr, type, fn_no_ptr);
+       return NULL_TREE;
+     }
+ 
+   return fn;
+ }
+ 
  /* Attempt to convert the non-type template parameter EXPR to the
     indicated TYPE.  If the conversion is successful, return the
     converted value.  If the conversion is unsuccessful, return
*************** fold_non_dependent_expr (tree expr)
*** 3261,3273 ****
     did not.  We issue error messages for out-and-out bad template
     parameters, but not simply because the conversion failed, since we
     might be just trying to do argument deduction.  Both TYPE and EXPR
!    must be non-dependent.  */
  
  static tree
  convert_nontype_argument (tree type, tree expr)
  {
    tree expr_type;
  
    /* If we are in a template, EXPR may be non-dependent, but still
       have a syntactic, rather than semantic, form.  For example, EXPR
       might be a SCOPE_REF, rather than the VAR_DECL to which the
--- 3335,3371 ----
     did not.  We issue error messages for out-and-out bad template
     parameters, but not simply because the conversion failed, since we
     might be just trying to do argument deduction.  Both TYPE and EXPR
!    must be non-dependent.
! 
!    The conversion follows the special rules described in
!    [temp.arg.nontype], and it is much more strict than an implicit
!    conversion.
! 
!    This function is called twice for each template argument (see
!    lookup_template_class for a more accurate description of this
!    problem). This means that we need to handle expressions which
!    are not valid in a C++ source, but can be created from the
!    first call (for instance, casts to perform conversions). These
!    hacks can go away after we fix the double coercion problem.  */
  
  static tree
  convert_nontype_argument (tree type, tree expr)
  {
    tree expr_type;
  
+   /* Detect immediately string literals as invalid non-type argument.
+      This special-case is not needed for correctness (we would easily
+      catch this later), but only to provide better diagnostic for this
+      common user mistake. As suggested by DR 100, we do not mention
+      linkage issues in the diagnostic as this is not the point.  */
+   if (TREE_CODE (expr) == STRING_CST)
+     {
+       error ("%qE is not a valid template argument for type %qT "
+ 	     "because string literals can never be used in this context",
+ 	     expr, type);
+       return NULL_TREE;
+     }
+ 
    /* If we are in a template, EXPR may be non-dependent, but still
       have a syntactic, rather than semantic, form.  For example, EXPR
       might be a SCOPE_REF, rather than the VAR_DECL to which the
*************** convert_nontype_argument (tree type, tre
*** 3278,3651 ****
    expr = fold_non_dependent_expr (expr);
    expr_type = TREE_TYPE (expr);
  
!   /* A template-argument for a non-type, non-template
!      template-parameter shall be one of:
! 
!      --an integral constant-expression of integral or enumeration
!      type; or
!      
!      --the name of a non-type template-parameter; or
!      
!      --the name of an object or function with external linkage,
!      including function templates and function template-ids but
!      excluding non-static class members, expressed as id-expression;
!      or
!      
!      --the address of an object or function with external linkage,
!      including function templates and function template-ids but
!      excluding non-static class members, expressed as & id-expression
!      where the & is optional if the name refers to a function or
!      array; or
!      
!      --a pointer to member expressed as described in _expr.unary.op_.  */
! 
!   /* An integral constant-expression can include const variables or
! .     enumerators.  Simplify things by folding them to their values,
!      unless we're about to bind the declaration to a reference
!      parameter.  */
!   if (INTEGRAL_TYPE_P (expr_type) && TREE_CODE (type) != REFERENCE_TYPE)
!     while (true) 
!       {
! 	tree const_expr = decl_constant_value (expr);
! 	/* In a template, the initializer for a VAR_DECL may not be
! 	   marked as TREE_CONSTANT, in which case decl_constant_value
! 	   will not return the initializer.  Handle that special case
! 	   here.  */
! 	if (expr == const_expr
! 	    && DECL_INTEGRAL_CONSTANT_VAR_P (expr)
! 	    /* DECL_INITIAL can be NULL if we are processing a
! 	       variable initialized to an expression involving itself.
! 	       We know it is initialized to a constant -- but not what
! 	       constant, yet.  */
! 	    && DECL_INITIAL (expr))
! 	  const_expr = DECL_INITIAL (expr);
! 	if (expr == const_expr)
! 	  break;
! 	expr = fold_non_dependent_expr (const_expr);
!       }
! 
!   if (is_overloaded_fn (expr))
!     /* OK for now.  We'll check that it has external linkage later.
!        Check this first since if expr_type is the unknown_type_node
!        we would otherwise complain below.  */
!     ;
!   else if (TYPE_PTR_TO_MEMBER_P (expr_type))
!     {
!       if (TREE_CODE (expr) != PTRMEM_CST)
! 	goto bad_argument;
!     }
!   else if (TYPE_PTR_P (expr_type)
! 	   || TREE_CODE (expr_type) == ARRAY_TYPE
! 	   || TREE_CODE (type) == REFERENCE_TYPE
! 	   /* If expr is the address of an overloaded function, we
! 	      will get the unknown_type_node at this point.  */
! 	   || expr_type == unknown_type_node)
!     {
!       tree referent;
!       tree e = expr;
!       STRIP_NOPS (e);
! 
!       if (TREE_CODE (expr_type) == ARRAY_TYPE
! 	  || (TREE_CODE (type) == REFERENCE_TYPE
! 	      && TREE_CODE (e) != ADDR_EXPR))
! 	referent = e;
!       else
! 	{
! 	  if (TREE_CODE (e) != ADDR_EXPR)
! 	    {
! 	    bad_argument:
! 	      error ("%qE is not a valid template argument", expr);
! 	      if (TYPE_PTR_P (expr_type))
! 		{
! 		  if (TREE_CODE (TREE_TYPE (expr_type)) == FUNCTION_TYPE)
! 		    error ("it must be the address of a function with external linkage");
! 		  else
! 		    error ("it must be the address of an object with external linkage");
! 		}
! 	      else if (TYPE_PTR_TO_MEMBER_P (expr_type))
! 		error ("it must be a pointer-to-member of the form %<&X::Y%>");
! 
! 	      return NULL_TREE;
! 	    }
! 
! 	  referent = TREE_OPERAND (e, 0);
! 	  STRIP_NOPS (referent);
! 	}
! 
!       if (TREE_CODE (referent) == STRING_CST)
! 	{
! 	  error ("string literal %qE is not a valid template argument "
!                  "because it is the address of an object with static linkage", 
!                  referent);
! 	  return NULL_TREE;
! 	}
! 
!       if (TREE_CODE (referent) == SCOPE_REF)
! 	referent = TREE_OPERAND (referent, 1);
! 
!       if (is_overloaded_fn (referent))
! 	/* We'll check that it has external linkage later.  */
! 	;
!       else if (TREE_CODE (referent) != VAR_DECL)
! 	goto bad_argument;
!       else if (!DECL_EXTERNAL_LINKAGE_P (referent))
! 	{
! 	  error ("address of non-extern %qE cannot be used as "
!                  "template argument", referent); 
! 	  return error_mark_node;
! 	}
!     }
!   else if (INTEGRAL_TYPE_P (expr_type) || TYPE_PTR_TO_MEMBER_P (expr_type))
!     {
!       if (! TREE_CONSTANT (expr))
! 	{
! 	non_constant:
! 	  error ("non-constant %qE cannot be used as template argument", expr);
! 	  return NULL_TREE;
  	}
      }
-   else 
-     {
-       if (TYPE_P (expr))
-          error ("type %qT cannot be used as a value for a non-type "
-                "template-parameter", expr);
-       else if (DECL_P (expr))
-         error ("invalid use of %qD as a non-type template-argument", expr);
-       else
-         error ("invalid use of %qE as a non-type template-argument", expr);
  
!       return NULL_TREE;
!     }
  
!   switch (TREE_CODE (type))
      {
-       HOST_WIDE_INT saved_processing_template_decl;
- 
-     case INTEGER_TYPE:
-     case BOOLEAN_TYPE:
-     case ENUMERAL_TYPE:
-       /* For a non-type template-parameter of integral or enumeration
-          type, integral promotions (_conv.prom_) and integral
-          conversions (_conv.integral_) are applied.  */
        if (!INTEGRAL_TYPE_P (expr_type))
  	return error_mark_node;
  
!       /* [conv.integral] does not allow conversions between two different
! 	 enumeration types.  */
!       if (TREE_CODE (type) == ENUMERAL_TYPE
! 	  && TREE_CODE (expr_type) == ENUMERAL_TYPE
! 	  && !same_type_ignoring_top_level_qualifiers_p (type, expr_type))
! 	  return error_mark_node;
! 
!       /* It's safe to call digest_init in this case; we know we're
! 	 just converting one integral constant expression to another.
! 	 */
!       saved_processing_template_decl = processing_template_decl;
!       processing_template_decl = 0;
!       expr = digest_init (type, expr, (tree*) 0);
!       processing_template_decl = saved_processing_template_decl;
! 
        if (TREE_CODE (expr) != INTEGER_CST)
! 	/* Curiously, some TREE_CONSTANT integral expressions do not
! 	   simplify to integer constants.  For example, `3 % 0',
! 	   remains a TRUNC_MOD_EXPR.  */
! 	goto non_constant;
!       
!       return expr;
! 
!     case OFFSET_TYPE:
!       {
! 	tree e;
! 
! 	/* For a non-type template-parameter of type pointer to data
! 	   member, qualification conversions (_conv.qual_) are
! 	   applied.  */
! 	e = perform_qualification_conversions (type, expr);
! 	if (TREE_CODE (e) == NOP_EXPR)
! 	  /* The call to perform_qualification_conversions will
! 	     insert a NOP_EXPR over EXPR to do express conversion,
! 	     if necessary.  But, that will confuse us if we use
! 	     this (converted) template parameter to instantiate
! 	     another template; then the thing will not look like a
! 	     valid template argument.  So, just make a new
! 	     constant, of the appropriate type.  */
! 	  e = make_ptrmem_cst (type, PTRMEM_CST_MEMBER (expr));
! 	return e;
!       }
! 
!     case POINTER_TYPE:
!       {
! 	tree type_pointed_to = TREE_TYPE (type);
!  
! 	if (TREE_CODE (type_pointed_to) == FUNCTION_TYPE)
! 	  { 
! 	    /* For a non-type template-parameter of type pointer to
! 	       function, only the function-to-pointer conversion
! 	       (_conv.func_) is applied.  If the template-argument
! 	       represents a set of overloaded functions (or a pointer to
! 	       such), the matching function is selected from the set
! 	       (_over.over_).  */
! 	    tree fns;
! 	    tree fn;
! 
! 	    if (TREE_CODE (expr) == ADDR_EXPR)
! 	      fns = TREE_OPERAND (expr, 0);
! 	    else
! 	      fns = expr;
! 
! 	    fn = instantiate_type (type_pointed_to, fns, tf_none);
! 
! 	    if (fn == error_mark_node)
! 	      return error_mark_node;
  
! 	    if (!DECL_EXTERNAL_LINKAGE_P (fn))
! 	      {
! 		if (really_overloaded_fn (fns))
! 		  return error_mark_node;
! 		else
! 		  goto bad_argument;
! 	      }
  
! 	    expr = build_unary_op (ADDR_EXPR, fn, 0);
  
! 	    gcc_assert (same_type_p (type, TREE_TYPE (expr)));
! 	    return expr;
! 	  }
! 	else 
! 	  {
! 	    /* For a non-type template-parameter of type pointer to
! 	       object, qualification conversions (_conv.qual_) and the
! 	       array-to-pointer conversion (_conv.array_) are applied.
! 	       [Note: In particular, neither the null pointer conversion
! 	       (_conv.ptr_) nor the derived-to-base conversion
! 	       (_conv.ptr_) are applied.  Although 0 is a valid
! 	       template-argument for a non-type template-parameter of
! 	       integral type, it is not a valid template-argument for a
! 	       non-type template-parameter of pointer type.]  
  	    
! 	       The call to decay_conversion performs the
! 	       array-to-pointer conversion, if appropriate.  */
! 	    expr = decay_conversion (expr);
  
! 	    if (expr == error_mark_node)
! 	      return error_mark_node;
! 	    else
! 	      return perform_qualification_conversions (type, expr);
! 	  }
!       }
!       break;
  
!     case REFERENCE_TYPE:
!       {
! 	tree type_referred_to = TREE_TYPE (type);
  
! 	/* If this expression already has reference type, get the
! 	   underlying object.  */
! 	if (TREE_CODE (expr_type) == REFERENCE_TYPE) 
! 	  {
! 	    if (TREE_CODE (expr) == NOP_EXPR
! 		&& TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR)
! 	      STRIP_NOPS (expr);
! 	    gcc_assert (TREE_CODE (expr) == ADDR_EXPR);
! 	    expr = TREE_OPERAND (expr, 0);
! 	    expr_type = TREE_TYPE (expr);
! 	  }
  
! 	if (TREE_CODE (type_referred_to) == FUNCTION_TYPE)
! 	  {
! 	    /* For a non-type template-parameter of type reference to
! 	       function, no conversions apply.  If the
! 	       template-argument represents a set of overloaded
! 	       functions, the matching function is selected from the
! 	       set (_over.over_).  */
! 	    tree fn;
  
! 	    fn = instantiate_type (type_referred_to, expr, tf_none);
  
! 	    if (fn == error_mark_node)
! 	      return error_mark_node;
  
! 	    if (!DECL_EXTERNAL_LINKAGE_P (fn))
! 	      {
! 		if (really_overloaded_fn (expr))
! 		  /* Don't issue an error here; we might get a different
! 		     function if the overloading had worked out
! 		     differently.  */
! 		  return error_mark_node;
! 		else
! 		  goto bad_argument;
! 	      }
  
! 	    gcc_assert (same_type_p (type_referred_to, TREE_TYPE (fn)));
! 	    expr = fn;
! 	  }
! 	else
! 	  {
! 	    /* For a non-type template-parameter of type reference to
! 	       object, no conversions apply.  The type referred to by the
! 	       reference may be more cv-qualified than the (otherwise
! 	       identical) type of the template-argument.  The
! 	       template-parameter is bound directly to the
! 	       template-argument, which must be an lvalue.  */
! 	    if (!same_type_p (TYPE_MAIN_VARIANT (expr_type),
! 			      TYPE_MAIN_VARIANT (type_referred_to))
! 		|| !at_least_as_qualified_p (type_referred_to,
! 					     expr_type)
! 		|| !real_lvalue_p (expr))
! 	      return error_mark_node;
! 	  }
  
! 	cxx_mark_addressable (expr);
! 	return build_nop (type, build_address (expr));
!       }
!       break;
  
!     case RECORD_TYPE:
!       {
! 	gcc_assert (TYPE_PTRMEMFUNC_P (type));
  
! 	/* For a non-type template-parameter of type pointer to member
! 	   function, no conversions apply.  If the template-argument
! 	   represents a set of overloaded member functions, the
! 	   matching member function is selected from the set
! 	   (_over.over_).  */
  
! 	if (!TYPE_PTRMEMFUNC_P (expr_type) && 
! 	    expr_type != unknown_type_node)
! 	  return error_mark_node;
  
! 	if (TREE_CODE (expr) == PTRMEM_CST)
! 	  {
! 	    /* A ptr-to-member constant.  */
! 	    if (!same_type_p (type, expr_type))
! 	      return error_mark_node;
! 	    else 
! 	      return expr;
! 	  }
  
! 	if (TREE_CODE (expr) != ADDR_EXPR)
! 	  return error_mark_node;
  
! 	expr = instantiate_type (type, expr, tf_none);
! 	
! 	if (expr == error_mark_node)
! 	  return error_mark_node;
  
! 	if (!same_type_p (type, TREE_TYPE (expr)))
! 	  return error_mark_node;
  
  	return expr;
-       }
-       break;
- 
-     default:
-       /* All non-type parameters must have one of these types.  */
-       gcc_unreachable ();
      }
  
!   return error_mark_node;
  }
  
  /* Return 1 if PARM_PARMS and ARG_PARMS matches using rule for 
     template template parameters.  Both PARM_PARMS and ARG_PARMS are 
     vectors of TREE_LIST nodes containing TYPE_DECL, TEMPLATE_DECL 
--- 3376,3637 ----
    expr = fold_non_dependent_expr (expr);
    expr_type = TREE_TYPE (expr);
  
!   /* HACK: Due to double coercion, we can get a
!      NOP_EXPR<REFERENCE_TYPE>(ADDR_EXPR<POINTER_TYPE> (arg)) here,
!      which is the tree that we built on the first call (see
!      below when coercing to reference to object or to reference to
!      function). We just strip everything and get to the arg.
!      See g++.old-deja/g++.oliva/template4.C and g++.dg/template/nontype9.C
!      for examples.  */
!   if (TREE_CODE (expr) == NOP_EXPR)
!     {
!       if (TYPE_REF_OBJ_P (type) || TYPE_REFFN_P (type))
! 	{
! 	  /* ??? Maybe we could use convert_from_reference here, but we
! 	     would need to relax its constraints because the NOP_EXPR
! 	     could actually change the type to something more cv-qualified,
! 	     and this is not folded by convert_from_reference.  */
! 	  tree addr = TREE_OPERAND (expr, 0);
! 	  gcc_assert (TREE_CODE (expr_type) == REFERENCE_TYPE);
! 	  gcc_assert (TREE_CODE (addr) == ADDR_EXPR);
! 	  gcc_assert (TREE_CODE (TREE_TYPE (addr)) == POINTER_TYPE);
! 	  gcc_assert (same_type_ignoring_top_level_qualifiers_p
! 		      (TREE_TYPE (expr_type),
! 		       TREE_TYPE (TREE_TYPE (addr))));
! 
! 	  expr = TREE_OPERAND (addr, 0);
! 	  expr_type = TREE_TYPE (expr);
! 	}
! 
!       /* We could also generate a NOP_EXPR(ADDR_EXPR()) when the
! 	 parameter is a pointer to object, through decay and
! 	 qualification conversion. Let's strip everything.  */
!       else if (TYPE_PTROBV_P (type))
! 	{
! 	  STRIP_NOPS (expr);
! 	  gcc_assert (TREE_CODE (expr) == ADDR_EXPR);
! 	  gcc_assert (TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE);
! 	  /* Skip the ADDR_EXPR only if it is part of the decay for
! 	     an array. Otherwise, it is part of the original argument
! 	     in the source code.  */
! 	  if (TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == ARRAY_TYPE)
! 	    expr = TREE_OPERAND (expr, 0);
! 	  expr_type = TREE_TYPE (expr);
  	}
      }
  
!   /* [temp.arg.nontype]/5, bullet 1
  
!      For a non-type template-parameter of integral or enumeration type,
!      integral promotions (_conv.prom_) and integral conversions
!      (_conv.integral_) are applied.  */
!   if (INTEGRAL_TYPE_P (type))
      {
        if (!INTEGRAL_TYPE_P (expr_type))
  	return error_mark_node;
  
!       expr = fold_decl_constant_value (expr);
!       /* Notice that there are constant expressions like '4 % 0' which
! 	 do not fold into integer constants.  */
        if (TREE_CODE (expr) != INTEGER_CST)
! 	{
! 	  error ("%qE is not a valid template argument for type %qT "
! 		 "because it is a non-constant expression", expr, type);
! 	  return NULL_TREE;
! 	}
  
!       /* At this point, an implicit conversion does what we want,
! 	 because we already know that the expression is of integral
! 	 type.  */
!       expr = ocp_convert (type, expr, CONV_IMPLICIT, LOOKUP_PROTECT);
!       if (expr == error_mark_node)
! 	return error_mark_node;
  
!       /* Conversion was allowed: fold it to a bare integer constant.  */
!       expr = fold (expr);
!     }
!   /* [temp.arg.nontype]/5, bullet 2
  
!      For a non-type template-parameter of type pointer to object,
!      qualification conversions (_conv.qual_) and the array-to-pointer
!      conversion (_conv.array_) are applied.  */
!   else if (TYPE_PTROBV_P (type))
!     {
!       /* [temp.arg.nontype]/1  (TC1 version, DR 49):
! 
! 	 A template-argument for a non-type, non-template template-parameter
! 	 shall be one of: [...]
! 
! 	 -- the name of a non-type template-parameter;
! 	 -- the address of an object or function with external linkage, [...]
! 	    expressed as "& id-expression" where the & is optional if the name
! 	    refers to a function or array, or if the corresponding
! 	    template-parameter is a reference.
  	    
! 	Here, we do not care about functions, as they are invalid anyway
! 	for a parameter of type pointer-to-object.  */
!       bool constant_address_p =
! 	(TREE_CODE (expr) == ADDR_EXPR
! 	 || TREE_CODE (expr_type) == ARRAY_TYPE
! 	 || (DECL_P (expr) && DECL_TEMPLATE_PARM_P (expr)));
  
!       expr = decay_conversion (expr);
!       if (expr == error_mark_node)
! 	return error_mark_node;
  
!       expr = perform_qualification_conversions (type, expr);
!       if (expr == error_mark_node)
! 	return error_mark_node;
  
!       if (!constant_address_p)
! 	{
! 	    error ("%qE is not a valid template argument for type %qT "
! 		  "because it is not a constant pointer", expr, type);
! 	    return NULL_TREE;
! 	}
!     }
!   /* [temp.arg.nontype]/5, bullet 3
  
!      For a non-type template-parameter of type reference to object, no
!      conversions apply. The type referred to by the reference may be more
!      cv-qualified than the (otherwise identical) type of the
!      template-argument. The template-parameter is bound directly to the
!      template-argument, which must be an lvalue.  */
!   else if (TYPE_REF_OBJ_P (type))
!     {
!       if (!same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (type),
! 						      expr_type))
! 	return error_mark_node;
  
!       if (!at_least_as_qualified_p (TREE_TYPE (type), expr_type))
! 	{
! 	  error ("%qE is not a valid template argument for type %qT "
! 		 "because of conflicts in cv-qualification", expr, type);
! 	  return NULL_TREE;
! 	}
! 	
!       if (!real_lvalue_p (expr))
! 	{
! 	  error ("%qE is not a valid template argument for type %qT "
! 	         "because it is not a lvalue", expr, type);
! 	  return NULL_TREE;
! 	}
  
!       /* [temp.arg.nontype]/1
  
! 	 A template-argument for a non-type, non-template template-parameter
! 	 shall be one of: [...]
  
! 	 -- the address of an object or function with external linkage.   */
!       if (!DECL_EXTERNAL_LINKAGE_P (expr))
! 	{
! 	  error ("%qE is not a valid template argument for type %qT "
! 		 "because object %qD has not external linkage",
! 		 expr, type, expr);
! 	  return NULL_TREE;
! 	}
  
!       expr = build_nop (type, build_address (expr));
!     }
!   /* [temp.arg.nontype]/5, bullet 4
  
!      For a non-type template-parameter of type pointer to function, only
!      the function-to-pointer conversion (_conv.func_) is applied. If the
!      template-argument represents a set of overloaded functions (or a
!      pointer to such), the matching function is selected from the set
!      (_over.over_).  */
!   else if (TYPE_PTRFN_P (type))
!     {
!       /* If the argument is a template-id, we might not have enough
!          context information to decay the pointer.
! 	 ??? Why static5.C requires decay and subst1.C works fine
! 	 even without it?  */
!       if (!type_unknown_p (expr_type))
! 	{
! 	  expr = decay_conversion (expr);
! 	  if (expr == error_mark_node)
! 	    return error_mark_node;
! 	}
  
!       expr = convert_nontype_argument_function (type, expr);
!       if (!expr || expr == error_mark_node)
! 	return expr;
!     }
!   /* [temp.arg.nontype]/5, bullet 5
  
!      For a non-type template-parameter of type reference to function, no
!      conversions apply. If the template-argument represents a set of
!      overloaded functions, the matching function is selected from the set
!      (_over.over_).  */
!   else if (TYPE_REFFN_P (type))
!     {
!       if (TREE_CODE (expr) == ADDR_EXPR)
! 	{
! 	  error ("%qE is not a valid template argument for type %qT "
! 		 "because it is a pointer", expr, type);
! 	  inform ("try using %qE instead", TREE_OPERAND (expr, 0));
! 	  return NULL_TREE;
! 	}
  
!       expr = convert_nontype_argument_function (TREE_TYPE (type), expr);
!       if (!expr || expr == error_mark_node)
! 	return expr;
  
!       expr = build_nop(type, build_address (expr));
!     }
!   /* [temp.arg.nontype]/5, bullet 6
  
!      For a non-type template-parameter of type pointer to member function,
!      no conversions apply. If the template-argument represents a set of
!      overloaded member functions, the matching member function is selected
!      from the set (_over.over_).  */
!   else if (TYPE_PTRMEMFUNC_P (type))
!     {
!       expr = instantiate_type (type, expr, tf_none);
!       if (expr == error_mark_node)
! 	return error_mark_node;
  
!       /* There is no way to disable standard conversions in
! 	 resolve_address_of_overloaded_function (called by
! 	 instantiate_type). It is possible that the call succeeded by
! 	 converting &B::I to &D::I (where B is a base of D), so we need
! 	 to reject this conversion here.
! 
! 	 Actually, even if there was a way to disable standard conversions,
! 	 it would still be better to reject them here so that we can
! 	 provide a superior diagnostic.  */
!       if (!same_type_p (TREE_TYPE (expr), type))
! 	{
! 	  /* Make sure we are just one standard conversion off.  */
! 	  gcc_assert (can_convert (type, TREE_TYPE (expr)));
! 	  error ("%qE is not a valid template argument for type %qT "
! 		 "because it is of type %qT", expr, type,
! 		 TREE_TYPE (expr));
! 	  inform ("standard conversions are not allowed in this context");
! 	  return NULL_TREE;
! 	}
!     }
!   /* [temp.arg.nontype]/5, bullet 7
  
+      For a non-type template-parameter of type pointer to data member,
+      qualification conversions (_conv.qual_) are applied.  */
+   else if (TYPE_PTRMEM_P (type))
+     {
+       expr = perform_qualification_conversions (type, expr);
+       if (expr == error_mark_node)
  	return expr;
      }
+   /* A template non-type parameter must be one of the above.  */
+   else
+     gcc_unreachable ();
  
!   /* Sanity check: did we actually convert the argument to the
!      right type?  */
!   gcc_assert (same_type_p (type, TREE_TYPE (expr)));
!   return expr;
  }
  
+ 
  /* Return 1 if PARM_PARMS and ARG_PARMS matches using rule for 
     template template parameters.  Both PARM_PARMS and ARG_PARMS are 
     vectors of TREE_LIST nodes containing TYPE_DECL, TEMPLATE_DECL 
*************** maybe_get_template_decl_from_type_decl (
*** 4252,4258 ****
  
     If the template class is really a local class in a template
     function, then the FUNCTION_CONTEXT is the function in which it is
!    being instantiated.  */
  
  tree
  lookup_template_class (tree d1, 
--- 4238,4251 ----
  
     If the template class is really a local class in a template
     function, then the FUNCTION_CONTEXT is the function in which it is
!    being instantiated.  
! 
!    ??? Note that this function is currently called *twice* for each
!    template-id: the first time from the parser, while creating the
!    incomplete type (finish_template_type), and the second type during the
!    real instantiation (instantiate_template_class). This is surely something
!    that we want to avoid. It also causes some problems with argument
!    coercion (see convert_nontype_argument for more information on this).  */
  
  tree
  lookup_template_class (tree d1, 
Index: testsuite/g++.dg/template/ptrmem8.C
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/g++.dg/template/ptrmem8.C,v
retrieving revision 1.1
diff -c -3 -p -r1.1 ptrmem8.C
*** testsuite/g++.dg/template/ptrmem8.C	2 Dec 2003 15:50:44 -0000	1.1
--- testsuite/g++.dg/template/ptrmem8.C	2 Sep 2004 10:13:12 -0000
*************** template <int (D::*fun)() const> int Get
*** 15,20 ****
  
  int main () 
  {
!   Get<&B::I>();   // { dg-error "no matching function" }
!   Get<&D::I>();   // { dg-error "no matching function" }
  }
--- 15,20 ----
  
  int main () 
  {
!   Get<&B::I>();   // { dg-error "" }
!   Get<&D::I>();   // { dg-error "" }
  }
Index: testsuite/g++.old-deja/g++.other/null1.C
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/g++.old-deja/g++.other/null1.C,v
retrieving revision 1.7
diff -c -3 -p -r1.7 null1.C
*** testsuite/g++.old-deja/g++.other/null1.C	1 May 2003 02:02:50 -0000	1.7
--- testsuite/g++.old-deja/g++.other/null1.C	2 Sep 2004 10:13:12 -0000
*************** int main()
*** 38,44 ****
    z = NULL;  // { dg-warning "" } converting NULL to non-pointer type
    k(NULL);   // { dg-warning "" } converting NULL to int
    g(NULL);   // { dg-warning "" } converting NULL to int
!   h<NULL>(); // { dg-warning "" } NULL bound to integer template parameter
    l(NULL);   // { dg-warning "" } converting NULL to int
    NULL && NULL; // No warning: converting NULL to bool is OK
  }
--- 38,44 ----
    z = NULL;  // { dg-warning "" } converting NULL to non-pointer type
    k(NULL);   // { dg-warning "" } converting NULL to int
    g(NULL);   // { dg-warning "" } converting NULL to int
!   h<NULL>(); // No warning: NULL bound to integer template parameter
    l(NULL);   // { dg-warning "" } converting NULL to int
    NULL && NULL; // No warning: converting NULL to bool is OK
  }



g++.dg/template/nontype7.C:
----------------------------------------------------------------
// { dg-do compile }
// Origin: C++ standard, [temp.arg.nontype]/2

template<class T, char* p> struct X {
  X();
  X(const char* q) { /* ... */ }
};

char p[] = "Vivisectionist";

X<int,"Studebaker"> x1;    // { dg-error "string literal" }
X<int, p> x2;

// { dg-bogus "" "additional errors" { xfail *-*-* } 11 }
----------------------------------------------------------------


g++.dg/template/nontype8.C:
----------------------------------------------------------------
// { dg-do compile }
// Origin: C++ standard, [temp.arg.nontype]/3

template<int* p> class X { };

int a[10];
struct S { int m; static int s; } s;

X<&a[2]> x3;                    // { dg-error "" } address of array element
X<&s.m> x4;                     // { dg-error "" } address of non-static member
X<&s.s> x5;                     // { dg-error "" } &S::s must be used
X<&S::s> x6;                    // OK: address of static member
----------------------------------------------------------------


g++.dg/template/nontype9.C:
----------------------------------------------------------------
// { dg-do compile  }
// Contributed by: Giovanni Bajo <giovannibajo at gcc dot gnu dot org>
int i;

template <void (&FN)()>
struct g {
  void foo(void) {
    FN ();
  }
};

void h ()
{
  i = 7;
}

template struct g<h>;
----------------------------------------------------------------


g++.dg/template/nontype10.C:
----------------------------------------------------------------
// { dg-do compile }
// Contributed by: Giovanni Bajo <giovannibajo at gcc dot gnu dot org>
#include <cstddef>

template <int  T> struct A {};
template <void* T> struct B {};

A<NULL> a;
B<NULL> b;  // { dg-error "" }
----------------------------------------------------------------


g++.dg/tc1/dr49.C:
----------------------------------------------------------------
// { dg-do compile }
// Contributed by: Giovanni Bajo <giovannibajo at gcc dot gnu dot org>
// DR 49: Non-constant pointers are invalid template arguments.

template<int *a> struct R { /* ... */ };
template<int b[5]> struct S { /* ... */ };

int p;
template struct R<&p>; // OK
template struct S<&p>; // OK due to parameter adjustment

int *ptr;
template struct R<ptr>; // { dg-error "constant" }
template struct S<ptr>; // { dg-error "constant" }

int v[5];
template struct R<v>; // OK due to implicit argument conversion
template struct S<v>; // OK due to both adjustment and conversion
----------------------------------------------------------------

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [C++ PATCH] PING: Rewrite convert_nontype_argument
  2004-10-18 22:07 [C++ PATCH] PING: Rewrite convert_nontype_argument Giovanni Bajo
@ 2004-11-03 15:48 ` Jason Merrill
  0 siblings, 0 replies; 2+ messages in thread
From: Jason Merrill @ 2004-11-03 15:48 UTC (permalink / raw)
  To: Giovanni Bajo; +Cc: gcc-patches, giovannibajo, Mark Mitchell

OK, thanks.

Jason

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2004-11-03 15:48 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-10-18 22:07 [C++ PATCH] PING: Rewrite convert_nontype_argument Giovanni Bajo
2004-11-03 15:48 ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).