aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorGiovanni Bajo <giovannibajo@gcc.gnu.org>2004-11-04 13:07:35 +0000
committerGiovanni Bajo <giovannibajo@gcc.gnu.org>2004-11-04 13:07:35 +0000
commitb6ab6892cc9faed44e07be5e7e9d389c35f4dbb6 (patch)
tree83598a1d6f066b2be9579ffd9e08efb5b5b8c2fa /gcc
parenteba7452ba8a7c3d088531c0a9dca639b6d708883 (diff)
downloadgcc-b6ab6892cc9faed44e07be5e7e9d389c35f4dbb6.zip
gcc-b6ab6892cc9faed44e07be5e7e9d389c35f4dbb6.tar.gz
gcc-b6ab6892cc9faed44e07be5e7e9d389c35f4dbb6.tar.bz2
DR 49, 100
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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/ChangeLog16
-rw-r--r--gcc/cp/cp-tree.h12
-rw-r--r--gcc/cp/cvt.c9
-rw-r--r--gcc/cp/mangle.c20
-rw-r--r--gcc/cp/pt.c651
-rw-r--r--gcc/testsuite/ChangeLog10
-rw-r--r--gcc/testsuite/g++.dg/tc1/dr49.C19
-rw-r--r--gcc/testsuite/g++.dg/template/nontype10.C10
-rw-r--r--gcc/testsuite/g++.dg/template/nontype7.C15
-rw-r--r--gcc/testsuite/g++.dg/template/nontype8.C13
-rw-r--r--gcc/testsuite/g++.dg/template/nontype9.C18
-rw-r--r--gcc/testsuite/g++.dg/template/ptrmem8.C4
-rw-r--r--gcc/testsuite/g++.old-deja/g++.other/null1.C2
13 files changed, 454 insertions, 345 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index ef855e3..a531bf0 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -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
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 307809f..6a69eb7 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 3e84ed0..f1968fd 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -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;
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index 5d9995fa..1aeb3be 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -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
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 56f55ed..9614884 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -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,
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 2e16bf9..770171a 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
index 0000000..f880e2a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tc1/dr49.C
@@ -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
index 0000000..cd3a3aa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/nontype10.C
@@ -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
index 0000000..5eac558
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/nontype7.C
@@ -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
index 0000000..d2976df
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/nontype8.C
@@ -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
index 0000000..f381240
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/nontype9.C
@@ -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>;
+
diff --git a/gcc/testsuite/g++.dg/template/ptrmem8.C b/gcc/testsuite/g++.dg/template/ptrmem8.C
index c0a1fa3..588ac1f 100644
--- a/gcc/testsuite/g++.dg/template/ptrmem8.C
+++ b/gcc/testsuite/g++.dg/template/ptrmem8.C
@@ -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 "" }
}
diff --git a/gcc/testsuite/g++.old-deja/g++.other/null1.C b/gcc/testsuite/g++.old-deja/g++.other/null1.C
index 0cf50a2..f4e97b9 100644
--- a/gcc/testsuite/g++.old-deja/g++.other/null1.C
+++ b/gcc/testsuite/g++.old-deja/g++.other/null1.C
@@ -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
}