This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[C++ PATCH] Rewrite convert_nontype_argument
Jason Merrill wrote:
>>>> ! /* [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 seems wrong to special-case this. Perhaps we should call
>>> can_convert instead?
>>
>> At this point, we have integral types only. Is there anything but
>> the above case that would be caught by can_convert?
>
> Maybe not, but I don't care. :)
> Perhaps we could call it sooner, to deal with more types.
Right. I tried to clean this up by calling the conversion sooner. Then I looked
deeply into the whole function and realized it contained much duplicated and
obsolete code. I ended up rewriting it from scratch, following closely the
standard. This is the result.
I split two functions out of it: one is to convert template argument of
function type (which is used for both member and non-member function), and
another one is Mark's loop to extract the costant inizializer from a variable
used in a constant context (which keeps calling decl_constant_value and
fold_non_dependent_expr until it gets to the true costant). The latter is
called only once for now, but I believe it could be made public and used
somewhere else, it looks generically useful.
I also noticed that lookup_template_class ends up being called twice for each
instantiated template (see the comment I added), which causes the argument
coercion to happens twice, which causes all kind of suckiness in
convert_nontype_argument, in that we have to handle also already coerced
comments. See the comments I added in the code for more information.
The change in mangle.c is just moving a block of code a few lines before.
write_expression was first lowering PTRMEM_CST into ADDR_EXPR<SCOPE_REF>, and
then stripping NOPs. But now we generate the sequence NOP_EXPR<PTRMEM_CST> in
convert_nontype_argument (to do a qualification conversion), so swapping the
two blocks is enough to fix the problem. This looks correct to me, but I always
fear any change to mangle.c...
The new code is about 1/4th of the old code (true lines of code), but it's much
more commented, so it might seem that the gain is less. It's also more
straightforward, close to the standard, and without special cases.
Tested on i686-pc-linux-gnu, OK for mainline?
Giovanni Bajo
cp/
DR 49, 100
* cp-tree.h (TYPE_REFOB_P): New macro.
* mangle.c (write_expression): Strip conversions before lowering
pointer to members.
* pt.c (convert_nontype_argument_function): Rewrite and extract
parts into...
(fold_constant_value, convert_nontype_argument_function): New.
(lookup_template_class): Add comment about useless double call.
testsuite/
* g++.dg/template/nontype7.C: New test.
* g++.dg/template/nontype8.C: Likewise.
* g++.dg/template/nontype9.C: Likewise.
* g++.dg/tc1/dr49.C: Likewise.
* g++.dg/template/ptrmem8.C: Relax dg-error checks.
Index: cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.989
diff -c -3 -p -r1.989 cp-tree.h
*** cp/cp-tree.h 26 Jun 2004 21:11:17 -0000 1.989
--- cp/cp-tree.h 16 Jul 2004 02:12:36 -0000
*************** struct lang_decl GTY(())
*** 2498,2503 ****
--- 2498,2508 ----
&& TREE_CODE (TREE_TYPE (NODE)) != FUNCTION_TYPE \
&& TREE_CODE (TREE_TYPE (NODE)) != METHOD_TYPE \
&& TREE_CODE (TREE_TYPE (NODE)) != VOID_TYPE)
+ #define TYPE_REFOB_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)
#define TYPE_PTROBV_P(NODE) \
(TYPE_PTR_P (NODE) && TREE_CODE (TREE_TYPE (NODE)) != FUNCTION_TYPE)
#define TYPE_PTRFN_P(NODE) \
Index: cp/mangle.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/mangle.c,v
retrieving revision 1.101
diff -c -3 -p -r1.101 mangle.c
*** cp/mangle.c 1 Apr 2004 03:50:39 -0000 1.101
--- cp/mangle.c 16 Jul 2004 02:04:47 -0000
*************** write_expression (tree expr)
*** 1909,1914 ****
--- 1909,1924 ----
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)
*** 1920,1935 ****
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
--- 1930,1935 ----
Index: cp/pt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/pt.c,v
retrieving revision 1.876
diff -c -3 -p -r1.876 pt.c
*** cp/pt.c 26 Jun 2004 21:11:19 -0000 1.876
--- cp/pt.c 16 Jul 2004 11:31:28 -0000
*************** static int maybe_adjust_types_for_deduct
*** 110,115 ****
--- 110,117 ----
static int type_unification_real (tree, tree, tree, tree,
int, unification_kind_t, int, int);
static void note_template_header (int);
+ static tree fold_decl_constant_value (tree);
+ 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)
*** 3149,3154 ****
--- 3151,3229 ----
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). */
+
+ static 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 ("`%E' is not a valid template argument for type `%T' "
+ "because function `%D' 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)
*** 3156,3168 ****
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
--- 3231,3266 ----
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 immediatly 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 ("`%E' is not a valid template argument for type `%T' "
+ "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
*** 3173,3537 ****
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
! && 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);
! }
!
! 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 ("`%E' 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 %E 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 `%E' 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 `%E' cannot be used as template argument",
! expr);
! return NULL_TREE;
}
- }
- else
- {
- if (TYPE_P (expr))
- error ("type '%T' cannot be used as a value for a non-type "
- "template-parameter", expr);
- else if (DECL_P (expr))
- error ("invalid use of '%D' as a non-type template-argument", expr);
- else
- error ("invalid use of '%E' as a non-type template-argument", expr);
! return NULL_TREE;
}
! switch (TREE_CODE (type))
! {
! 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;
!
! /* It's safe to call digest_init in this case; we know we're
! just converting one integral constant expression to another. */
! expr = digest_init (type, expr, (tree*) 0);
!
! 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);
!
! my_friendly_assert (same_type_p (type, TREE_TYPE (expr)),
! 0);
! 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);
! my_friendly_assert (TREE_CODE (expr) == ADDR_EXPR, 20000604);
! 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;
! }
!
! my_friendly_assert (same_type_p (type_referred_to,
! TREE_TYPE (fn)),
! 0);
!
! 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:
! {
! my_friendly_assert (TYPE_PTRMEMFUNC_P (type), 20010112);
!
! /* 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. */
- abort ();
- break;
}
! return error_mark_node;
}
/* Return 1 if PARM_PARMS and ARG_PARMS matches using rule for
--- 3271,3526 ----
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_REFOB_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);
! my_friendly_assert (TREE_CODE (expr_type) == REFERENCE_TYPE, 0);
! my_friendly_assert (TREE_CODE (addr) == ADDR_EXPR, 0);
! my_friendly_assert
! (TREE_CODE (TREE_TYPE (addr)) == POINTER_TYPE, 0);
! my_friendly_assert (same_type_ignoring_top_level_qualifiers_p
! (TREE_TYPE (expr_type),
! TREE_TYPE (TREE_TYPE (addr))), 0);
!
! 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);
! my_friendly_assert (TREE_CODE (expr) == ADDR_EXPR, 0);
! my_friendly_assert (TREE_CODE (TREE_TYPE (expr))
! == POINTER_TYPE, 0);
! /* 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);
! if (!TREE_CONSTANT (expr))
! {
! error ("`%E' is not a valid template argument for type `%T' "
! "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 = cp_convert (type, expr);
! if (expr == error_mark_node)
! return NULL_TREE;
! }
! /* [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 ("`%E' is not a valid template argument for type `%T' "
! "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_REFOB_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 ("`%E' is not a valid template argument for type `%T' "
! "because of conflicts in cv-qualification", expr, type);
! return NULL_TREE;
}
!
! if (!real_lvalue_p (expr))
! {
! error ("`%E' is not a valid template argument for type `%T' "
! "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 ("`%E' is not a valid template argument for type `%T' "
! "because object `%D' 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 ("`%E' is not a valid template argument for type `%T' "
! "because it is a pointer", expr, type);
! inform ("try using `%E' 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. */
+ my_friendly_assert (can_convert (type, TREE_TYPE (expr)), 0);
+ error ("`%E' is not a valid template argument for type `%T' "
+ "because it is of type `%T'", 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
+ abort();
! /* Sanity check: did we actually convert the argument to the
! right type? */
! my_friendly_assert (same_type_p (type, TREE_TYPE (expr)), 0);
! return expr;
}
/* Return 1 if PARM_PARMS and ARG_PARMS matches using rule for
*************** maybe_get_template_decl_from_type_decl (
*** 4132,4138 ****
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,
--- 4121,4134 ----
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 16 Jul 2004 02:04:47 -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 "" }
}
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;
----------------------------------------------------------------
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 }
int i;
template <void (&FN)()>
struct g {
void foo(void) {
FN ();
}
};
void h ()
{
i = 7;
}
template struct g<h>;
----------------------------------------------------------------
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
----------------------------------------------------------------