DR 49, 100
authorGiovanni Bajo <giovannibajo@gcc.gnu.org>
Thu, 4 Nov 2004 13:07:35 +0000 (13:07 +0000)
committerGiovanni Bajo <giovannibajo@gcc.gnu.org>
Thu, 4 Nov 2004 13:07:35 +0000 (13:07 +0000)
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.

* 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

From-SVN: r90059

13 files changed:
gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/cvt.c
gcc/cp/mangle.c
gcc/cp/pt.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/tc1/dr49.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/nontype10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/nontype7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/nontype8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/nontype9.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/ptrmem8.C
gcc/testsuite/g++.old-deja/g++.other/null1.C

index ef855e3..a531bf0 100644 (file)
@@ -1,3 +1,19 @@
+2004-11-04  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.
+
 2004-11-02  Mark Mitchell  <mark@codesourcery.com>
 
        PR c++/18124
index 307809f..6a69eb7 100644 (file)
@@ -2398,18 +2398,29 @@ struct lang_decl GTY(())
 /* 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)
@@ -3976,6 +3987,7 @@ extern tree build_non_dependent_expr            (tree);
 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 3e84ed0..f1968fd 100644 (file)
@@ -662,10 +662,13 @@ ocp_convert (tree type, tree expr, int convtype, int flags)
       /* 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)))
+         && (((INTEGRAL_OR_ENUMERATION_TYPE_P (intype)
+               || TREE_CODE (intype) == REAL_TYPE)
+              && ! (convtype & CONV_STATIC))
+             || TREE_CODE (intype) == POINTER_TYPE))
        {
-         pedwarn ("conversion from %q#T to %q#T", intype, type);
+         if (flags & LOOKUP_COMPLAIN)
+           pedwarn ("conversion from %q#T to %q#T", intype, type);
 
          if (flag_pedantic_errors)
            return error_mark_node;
index 5d9995f..1aeb3be 100644 (file)
@@ -1969,6 +1969,16 @@ write_expression (tree expr)
 
   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)
@@ -1980,16 +1990,6 @@ write_expression (tree expr)
       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
index 56f55ed..9614884 100644 (file)
@@ -110,6 +110,7 @@ static int maybe_adjust_types_for_deduction (unification_kind_t, tree*, tree*);
 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);
@@ -3302,6 +3303,79 @@ fold_non_dependent_expr (tree expr)
   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
@@ -3309,13 +3383,37 @@ fold_non_dependent_expr (tree expr)
    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.  */
+   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
@@ -3326,374 +3424,262 @@ convert_nontype_argument (tree type, tree expr)
   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)
+  /* 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))
        {
-         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;
+         /* ??? 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);
        }
 
-      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))
+      /* 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))
        {
-         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;
+         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);
        }
     }
-  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;
-    }
+  /* [temp.arg.nontype]/5, bullet 1
 
-  switch (TREE_CODE (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 (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;
-
+      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)
-       /* 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;
+       {
+         error ("%qE is not a valid template argument for type %qT "
+                "because it is a non-constant expression", expr, type);
+         return NULL_TREE;
+       }
 
-           fn = instantiate_type (type_pointed_to, fns, tf_none);
+      /* 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;
 
-           if (fn == 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
 
-           if (!DECL_EXTERNAL_LINKAGE_P (fn))
-             {
-               if (really_overloaded_fn (fns))
-                 return error_mark_node;
-               else
-                 goto bad_argument;
-             }
+     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):
 
-           expr = build_unary_op (ADDR_EXPR, fn, 0);
+        A template-argument for a non-type, non-template template-parameter
+        shall be one of: [...]
 
-           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 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.
            
-              The call to decay_conversion performs the
-              array-to-pointer conversion, if appropriate.  */
-           expr = decay_conversion (expr);
+       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;
 
-           if (expr == error_mark_node)
-             return error_mark_node;
-           else
-             return perform_qualification_conversions (type, expr);
-         }
-      }
-      break;
+      expr = perform_qualification_conversions (type, expr);
+      if (expr == error_mark_node)
+       return error_mark_node;
 
-    case REFERENCE_TYPE:
-      {
-       tree type_referred_to = TREE_TYPE (type);
+      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
 
-       /* 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);
-         }
+     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 (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;
+      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;
+       }
 
-           fn = instantiate_type (type_referred_to, expr, tf_none);
+      /* [temp.arg.nontype]/1
 
-           if (fn == error_mark_node)
-             return error_mark_node;
+        A template-argument for a non-type, non-template template-parameter
+        shall be one of: [...]
 
-           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;
-             }
+        -- 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;
+       }
 
-           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;
-         }
+      expr = build_nop (type, build_address (expr));
+    }
+  /* [temp.arg.nontype]/5, bullet 4
 
-       cxx_mark_addressable (expr);
-       return build_nop (type, build_address (expr));
-      }
-      break;
+     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;
+       }
 
-    case RECORD_TYPE:
-      {
-       gcc_assert (TYPE_PTRMEMFUNC_P (type));
+      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 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_).  */
+     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;
+       }
 
-       if (!TYPE_PTRMEMFUNC_P (expr_type) && 
-           expr_type != unknown_type_node)
-         return error_mark_node;
+      expr = convert_nontype_argument_function (TREE_TYPE (type), expr);
+      if (!expr || expr == error_mark_node)
+       return expr;
 
-       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;
-         }
+      expr = build_nop(type, build_address (expr));
+    }
+  /* [temp.arg.nontype]/5, bullet 6
 
-       if (TREE_CODE (expr) != ADDR_EXPR)
-         return error_mark_node;
+     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;
 
-       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.
 
-       if (!same_type_p (type, TREE_TYPE (expr)))
-         return error_mark_node;
+        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;
-      }
-      break;
-
-    default:
-      /* All non-type parameters must have one of these types.  */
-      gcc_unreachable ();
     }
+  /* A template non-type parameter must be one of the above.  */
+  else
+    gcc_unreachable ();
 
-  return error_mark_node;
+  /* 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 
@@ -4300,7 +4286,14 @@ maybe_get_template_decl_from_type_decl (tree decl)
 
    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.  */
+   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 2e16bf9..770171a 100644 (file)
@@ -1,3 +1,13 @@
+2004-11-04  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
+
 2004-11-04  Ben Elliston  <bje@au.ibm.com>
 
        * g++.dg/rtti/tinfo1.C: Remove xfails.
diff --git a/gcc/testsuite/g++.dg/tc1/dr49.C b/gcc/testsuite/g++.dg/tc1/dr49.C
new file mode 100644 (file)
index 0000000..f880e2a
--- /dev/null
@@ -0,0 +1,19 @@
+// { 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
+
diff --git a/gcc/testsuite/g++.dg/template/nontype10.C b/gcc/testsuite/g++.dg/template/nontype10.C
new file mode 100644 (file)
index 0000000..cd3a3aa
--- /dev/null
@@ -0,0 +1,10 @@
+// { 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 "" }
+
diff --git a/gcc/testsuite/g++.dg/template/nontype7.C b/gcc/testsuite/g++.dg/template/nontype7.C
new file mode 100644 (file)
index 0000000..5eac558
--- /dev/null
@@ -0,0 +1,15 @@
+// { 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 }
+
diff --git a/gcc/testsuite/g++.dg/template/nontype8.C b/gcc/testsuite/g++.dg/template/nontype8.C
new file mode 100644 (file)
index 0000000..d2976df
--- /dev/null
@@ -0,0 +1,13 @@
+// { 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
+
diff --git a/gcc/testsuite/g++.dg/template/nontype9.C b/gcc/testsuite/g++.dg/template/nontype9.C
new file mode 100644 (file)
index 0000000..f381240
--- /dev/null
@@ -0,0 +1,18 @@
+// { 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>;
+
index c0a1fa3..588ac1f 100644 (file)
@@ -15,6 +15,6 @@ template <int (D::*fun)() const> int Get();
 
 int main () 
 {
-  Get<&B::I>();   // { dg-error "no matching function" }
-  Get<&D::I>();   // { dg-error "no matching function" }
+  Get<&B::I>();   // { dg-error "" }
+  Get<&D::I>();   // { dg-error "" }
 }
index 0cf50a2..f4e97b9 100644 (file)
@@ -38,7 +38,7 @@ int main()
   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
+  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
 }